diff --git a/.reuse/dep5 b/.reuse/dep5 index cedb22dd..c905fc9a 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -1,7 +1,7 @@ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: OpenXR Upstream-Contact: Ryan Pavlik -Source: https://khronos.org/registry/OpenXR/ +Source: https://registry.khronos.org/OpenXR/ Files: changes/conformance/* changes/registry/* diff --git a/CHANGELOG.CTS.md b/CHANGELOG.CTS.md index e09d4125..f0856a21 100644 --- a/CHANGELOG.CTS.md +++ b/CHANGELOG.CTS.md @@ -17,6 +17,54 @@ particular, since it is primarily software, pull requests may be integrated as they are accepted even between periodic updates. However, versions that are not signed tags on the `approved` branch are not valid for conformance submission. +## OpenXR CTS 1.0.30.0 (2023-10-12) + +- Conformance Tests + - Fix: Replace early returns with `SKIP()`. + ([internal MR 2898](https://gitlab.khronos.org/openxr/openxr/merge_requests/2898), + [OpenXR-CTS issue 60](https://github.com/KhronosGroup/OpenXR-CTS/issues/60), + [internal issue 2072](https://gitlab.khronos.org/openxr/openxr/issues/2072)) + - Fix: Remove infinite loop in `Timed_Pipelined_Frame_Submission` in error case. + ([internal MR 2915](https://gitlab.khronos.org/openxr/openxr/merge_requests/2915)) + - Fix: Test failure count API + ([internal MR 2940](https://gitlab.khronos.org/openxr/openxr/merge_requests/2940), + [internal issue 2072](https://gitlab.khronos.org/openxr/openxr/issues/2072), + [internal MR 2965](https://gitlab.khronos.org/openxr/openxr/merge_requests/2965), + [internal MR 2999](https://gitlab.khronos.org/openxr/openxr/merge_requests/2999)) + - Fix: Fix waiting for `xrWaitSwapchainImage` timeout cases. + ([internal MR 2944](https://gitlab.khronos.org/openxr/openxr/merge_requests/2944)) + - Fix: Enable build with clang, clang-cl, and GCC (MinGW64) on Windows. + ([internal MR 2948](https://gitlab.khronos.org/openxr/openxr/merge_requests/2948), + [internal MR 2975](https://gitlab.khronos.org/openxr/openxr/merge_requests/2975)) + - Fix: Do not request hand tracking joint poses with an `XrTime` of 0: it is + invalid. Be sure to be in "FOCUSED" since we want input data. + ([internal MR 2977](https://gitlab.khronos.org/openxr/openxr/merge_requests/2977)) + - Improvement: Add validation of test tags to CTS. + ([internal MR 2924](https://gitlab.khronos.org/openxr/openxr/merge_requests/2924), + [internal issue 2050](https://gitlab.khronos.org/openxr/openxr/issues/2050), + [internal issue 2062](https://gitlab.khronos.org/openxr/openxr/issues/2062)) + - Improvement: Make conformance layer initialization clear and consistent with + other layers in the official OpenXR repos. + ([internal MR 2926](https://gitlab.khronos.org/openxr/openxr/merge_requests/2926)) + - Improvement: Update Khronos registry URLs in comments. + ([internal MR 2935](https://gitlab.khronos.org/openxr/openxr/merge_requests/2935)) + - Improvement: Add conformance test library API output value for Catch2 error + conditions. + ([internal MR 2940](https://gitlab.khronos.org/openxr/openxr/merge_requests/2940), + [internal issue 2072](https://gitlab.khronos.org/openxr/openxr/issues/2072), + [internal MR 2965](https://gitlab.khronos.org/openxr/openxr/merge_requests/2965), + [internal MR 2999](https://gitlab.khronos.org/openxr/openxr/merge_requests/2999)) + - Improvement: Add OpenGL 3.3 functions to gfxwrapper, an internal utility + library used by the CTS. + ([internal MR 2941](https://gitlab.khronos.org/openxr/openxr/merge_requests/2941)) + - New test: Add additional tests for `XR_EXT_debug_utils` based on the test app + `loader_test`. + ([internal MR 2861](https://gitlab.khronos.org/openxr/openxr/merge_requests/2861)) + - New test: Check that no interaction profile is returned as current before + calling `xrSyncActions`. + ([internal MR 2897](https://gitlab.khronos.org/openxr/openxr/merge_requests/2897), + [internal issue 1942](https://gitlab.khronos.org/openxr/openxr/issues/1942)) + ## OpenXR CTS 1.0.29.0 (2023-09-07) - Conformance Tests diff --git a/CMakeLists.txt b/CMakeLists.txt index 43ab332b..03df11be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,6 +25,7 @@ cmake_minimum_required(VERSION 3.0) project(OPENXR) find_package(PythonInterp 3) +include(CTest) # Enable IDE GUI folders. "Helper targets" that don't have interesting source code should set their FOLDER property to this set_property(GLOBAL PROPERTY USE_FOLDERS ON) diff --git a/maintainer-scripts/common.sh b/maintainer-scripts/common.sh index 065300c2..540578be 100644 --- a/maintainer-scripts/common.sh +++ b/maintainer-scripts/common.sh @@ -187,6 +187,7 @@ getSDKSourceFilenames() { maintainer-scripts/build-and-publish-aar-snapshot.sh \ maintainer-scripts/publish-aar \ specification/.gitignore \ + specification/config/attribs.adoc \ specification/registry/*.xml \ specification/scripts \ specification/loader \ diff --git a/specification/Makefile b/specification/Makefile index fcf69338..c9f16147 100644 --- a/specification/Makefile +++ b/specification/Makefile @@ -32,7 +32,7 @@ ifneq (,$(strip $(VERY_STRICT))) ASCIIDOC := $(ASCIIDOC) --failure-level WARN endif -SPECREVISION = 1.0.29 +SPECREVISION = 1.0.30 REVISION_COMPONENTS = $(subst ., ,$(SPECREVISION)) MAJORMINORVER = $(word 1,$(REVISION_COMPONENTS)).$(word 2,$(REVISION_COMPONENTS)) @@ -57,7 +57,7 @@ PYAPIMAP := $(GENDIR)/apimap.py RBAPIMAP := $(GENDIR)/apimap.rb METADIR := $(GENDIR)/meta -VK_REF_PAGE_ROOT := https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html +VK_REF_PAGE_ROOT := https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/ MAKE_RELATIVE = $(patsubst $(CURDIR)/%,%,$(1)) @@ -406,7 +406,7 @@ MANSOURCES = $(CORESOURCES) $(VENSOURCES) $(KHRSOURCES) MANGENERATED = $(wildcard $(REFPATH)/*) MANHTML = $(MANSOURCES:$(REFPATH)/%.txt=$(MANHTMLDIR)/%.html) MANDEPS = $(GENINCLUDE) $(GENDEPENDS) -HTML_SPEC_RELATIVE ?= ../../$(SPEC_FILENAME_STEM).html +HTML_SPEC_RELATIVE ?= ../../html/$(SPEC_FILENAME_STEM).html # Asciidoctor options to build refpages # @@ -421,8 +421,7 @@ ADOCREFOPTS = -a stylesheet=khronos.css \ -a refprefix='refpage.' \ -a isrefpage \ -a html_spec_relative='$(HTML_SPEC_RELATIVE)' \ - -a imagesdir=$(CURDIR)/sources \ - -a vkRefPageRoot='$(VK_REF_PAGE_ROOT)' + -a imagesdir=$(CURDIR)/sources # Pure makefile lowercase function, generated by a script. make_lower = $(subst A,a,$(subst B,b,$(subst C,c,$(subst D,d,$(subst E,e,$(subst F,f,$(subst G,g,$(subst H,h,$(subst I,i,$(subst J,j,$(subst K,k,$(subst L,l,$(subst M,m,$(subst N,n,$(subst O,o,$(subst P,p,$(subst Q,q,$(subst R,r,$(subst S,s,$(subst T,t,$(subst U,u,$(subst V,v,$(subst W,w,$(subst X,x,$(subst Y,y,$(subst Z,z,$(1))))))))))))))))))))))))))) diff --git a/specification/registry/xr.xml b/specification/registry/xr.xml index add5a2c7..7bbddee4 100644 --- a/specification/registry/xr.xml +++ b/specification/registry/xr.xml @@ -82,7 +82,6 @@ maintained in the default branch of the Khronos OpenXR GitHub project. - @@ -132,7 +131,7 @@ maintained in the default branch of the Khronos OpenXR GitHub project. updates them automatically by processing a line at a time. --> // OpenXR current version number. -#define XR_CURRENT_API_VERSION XR_MAKE_VERSION(1, 0, 29) +#define XR_CURRENT_API_VERSION XR_MAKE_VERSION(1, 0, 30) - typedef void *(*PFN_xrEglGetProcAddressMNDX)(const char *name); + typedef PFN_xrVoidFunction (*PFN_xrEglGetProcAddressMNDX)(const char *name); XrStructureType type @@ -1491,7 +1490,7 @@ maintained in the default branch of the Khronos OpenXR GitHub project. XrStructureType type const void* next XrSpatialGraphNodeTypeMSFT nodeType - uint8_t nodeId[XR_GUID_SIZE_MSFT] + uint8_t nodeId[XR_GUID_SIZE_MSFT] XrPosef pose @@ -1508,7 +1507,7 @@ maintained in the default branch of the Khronos OpenXR GitHub project. XrStructureType type void* next - uint8_t nodeId[XR_GUID_SIZE_MSFT] + uint8_t nodeId[XR_GUID_SIZE_MSFT] XrPosef poseInNodeSpace @@ -1890,8 +1889,8 @@ maintained in the default branch of the Khronos OpenXR GitHub project. XrStructureType type void* next - char parentNodeName[XR_MAX_CONTROLLER_MODEL_NODE_NAME_SIZE_MSFT] - char nodeName[XR_MAX_CONTROLLER_MODEL_NODE_NAME_SIZE_MSFT] + char parentNodeName[XR_MAX_CONTROLLER_MODEL_NODE_NAME_SIZE_MSFT] + char nodeName[XR_MAX_CONTROLLER_MODEL_NODE_NAME_SIZE_MSFT] XrStructureType type @@ -2281,7 +2280,7 @@ maintained in the default branch of the Khronos OpenXR GitHub project. XrStructureType type void* next uint32_t vendorId - char modelName[XR_MAX_RENDER_MODEL_NAME_SIZE_FB] + char modelName[XR_MAX_RENDER_MODEL_NAME_SIZE_FB] XrRenderModelKeyFB modelKey uint32_t modelVersion XrRenderModelFlagsFB flags @@ -2508,7 +2507,7 @@ maintained in the default branch of the Khronos OpenXR GitHub project. uint64_t trackedKeyboardId XrVector3f size XrKeyboardTrackingFlagsFB flags - char name[XR_MAX_KEYBOARD_TRACKING_NAME_SIZE_FB] + char name[XR_MAX_KEYBOARD_TRACKING_NAME_SIZE_FB] XrStructureType type @@ -2631,12 +2630,12 @@ maintained in the default branch of the Khronos OpenXR GitHub project. XrStructureType type const void* next - XrColor4f textureColorMap[XR_PASSTHROUGH_COLOR_MAP_MONO_SIZE_FB] + XrColor4f textureColorMap[XR_PASSTHROUGH_COLOR_MAP_MONO_SIZE_FB] XrStructureType type const void* next - uint8_t textureColorMap[XR_PASSTHROUGH_COLOR_MAP_MONO_SIZE_FB] + uint8_t textureColorMap[XR_PASSTHROUGH_COLOR_MAP_MONO_SIZE_FB] XrStructureType type @@ -5102,7 +5101,7 @@ maintained in the default branch of the Khronos OpenXR GitHub project. XrResult xrSetColorSpaceFB XrSession session - const XrColorSpaceFB colorspace + const XrColorSpaceFB colorSpace @@ -5524,7 +5523,7 @@ maintained in the default branch of the Khronos OpenXR GitHub project. XrResult xrSetMarkerTrackingPredictionVARJO XrSession session uint64_t markerId - XrBool32 enabled + XrBool32 enable @@ -6272,6 +6271,18 @@ maintained in the default branch of the Khronos OpenXR GitHub project. + + + + + + + + + + + + @@ -6316,6 +6327,32 @@ maintained in the default branch of the Khronos OpenXR GitHub project. + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -6619,7 +6656,7 @@ maintained in the default branch of the Khronos OpenXR GitHub project. - + @@ -10510,10 +10547,11 @@ maintained in the default branch of the Khronos OpenXR GitHub project. - + + @@ -10530,6 +10568,13 @@ maintained in the default branch of the Khronos OpenXR GitHub project. + + + + + + + @@ -10538,19 +10583,22 @@ maintained in the default branch of the Khronos OpenXR GitHub project. + + + - - - - + + + + @@ -11384,10 +11432,33 @@ maintained in the default branch of the Khronos OpenXR GitHub project. - + - - + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/specification/scripts/check_spec_links.py b/specification/scripts/check_spec_links.py index 080e5b2f..5408affa 100755 --- a/specification/scripts/check_spec_links.py +++ b/specification/scripts/check_spec_links.py @@ -22,7 +22,20 @@ ### # "Configuration" constants -EXTRA_DEFINES = ('XRAPI_ATTR', 'XRAPI_CALL', 'XRAPI_PTR', 'XR_NO_STDINT_H') +FREEFORM_CATEGORY = 'freeform' +REFLINK_MACRO = 'reflink' + +EXTRA_DEFINES = ( + 'XRAPI_ATTR', + 'XRAPI_CALL', + 'XRAPI_PTR', + 'XR_NO_STDINT_H', +) + +# TODO move permissions into XML eventually +EXTRA_REFPAGES = ( + 'org.khronos.openxr.permission.ext.HAND_TRACKING', +) # These are marked with the code: macro SYSTEM_TYPES = set(('void', 'char', 'float', 'size_t', @@ -61,12 +74,17 @@ def populateMacros(self): # TODO: What about flag wildcards? There are a few such uses... self.addMacro('elink', ('enums', 'flags',), link=True) self.addMacro('basetype', ('basetypes',), link=True) + self.addMacro(REFLINK_MACRO, (FREEFORM_CATEGORY,), link=True) + def populateEntities(self): # These are not mentioned in the XML for name in EXTRA_DEFINES: - self.addEntity(name, 'dlink', category='configdefines', - generates=False) + self.addEntity(name, 'dlink', + category=FREEFORM_CATEGORY, generates=False) + for name in EXTRA_REFPAGES: + self.addEntity(name, REFLINK_MACRO, + category=FREEFORM_CATEGORY, generates=False) def handleType(self, name, info, requires): """Extend superclass implementation for OpenXR bitmasks.""" @@ -118,6 +136,14 @@ def processBlockOpen(self, block_type, context=None, delimiter=None): super().processBlockOpen(block_type, context=context, delimiter=delimiter) + def perform_entity_check(self, t): + """Returns True if an entity check should be performed on this + refpage type. + + May override.""" + + return t != FREEFORM_CATEGORY + @property def allowEnumXrefs(self): """Returns True if enums can be specified in the 'xrefs' attribute diff --git a/specification/scripts/genRef.py b/specification/scripts/genRef.py index 5a72a6ac..04184010 100644 --- a/specification/scripts/genRef.py +++ b/specification/scripts/genRef.py @@ -882,15 +882,14 @@ def genExtension(baseDir, extpath, name, info): fp = open(pageName, 'w', encoding='utf-8') # OpenXR-specific - sections = OrderedDict() - sections['Specification'] = 'See link:{html_spec_relative}#%s[ %s] in the main specification for complete information.' % ( - name, name) + ref_page_sections = OrderedDict() + ref_page_sections['Specification'] = f'See link:{{html_spec_relative}}#{name}[{name}] in the main specification for complete information.' refPageShell(name, "{} extension".format(ext_type), fp, appbody, - sections=sections, + sections=ref_page_sections, tail_content=tail_content) # Restore leveloffset for boilerplate in refPageTail diff --git a/specification/scripts/spec-macros/extension.rb b/specification/scripts/spec-macros/extension.rb index be76084c..fdf4dcc4 100644 --- a/specification/scripts/spec-macros/extension.rb +++ b/specification/scripts/spec-macros/extension.rb @@ -145,7 +145,8 @@ def text # Generic reference page link to any entity with an anchor/refpage class ReflinkInlineMacro < LinkInlineMacroBase named :reflink - match /reflink:([-\w]+)/ + # Permissions included periods in the middle, so we want to accept periods in the middle of names but not the end. + match /reflink:(([-\w]+[.])*[-\w]+)/ end # Link to an extension appendix/refpage diff --git a/specification/scripts/spec_tools/entity_db.py b/specification/scripts/spec_tools/entity_db.py index 99a7d62e..e408c0a6 100644 --- a/specification/scripts/spec_tools/entity_db.py +++ b/specification/scripts/spec_tools/entity_db.py @@ -287,7 +287,7 @@ def getMemberNames(self, commandOrStruct): ret = [] for member in members: name_tag = member.find('name') - if name_tag: + if name_tag is not None: ret.append(name_tag.text) return ret diff --git a/specification/scripts/spec_tools/macro_checker.py b/specification/scripts/spec_tools/macro_checker.py index a8a75aa8..17f42e2c 100644 --- a/specification/scripts/spec_tools/macro_checker.py +++ b/specification/scripts/spec_tools/macro_checker.py @@ -47,20 +47,27 @@ def __init__(self, enabled_messages, entity_db, # apiPrefix, followed by some word characters or * as many times as desired, # NOT followed by >> and NOT preceded by one of the characters in that first character class. # (which distinguish "names being used somewhere other than prose"). - self.suspected_missing_macro_re = re.compile( - r'\b(?{}[\w*]+)\b(?!>>)'.format( - self.entity_db.case_insensitive_name_prefix_pattern) + self.suspected_missing_macro_re = re.compile( fr''' + \b(?{self.entity_db.case_insensitive_name_prefix_pattern}[\w*]+) # Something that looks like our entity names + \b(?!>>) # NOT followed by >> + ''', re.VERBOSE ) self.heading_command_re = re.compile( - r'=+ (?P{}[\w]+)'.format(self.entity_db.name_prefix) + fr'=+ (?P{self.entity_db.name_prefix}[\w]+)' ) macros_pattern = '|'.join((re.escape(macro) for macro in self.entity_db.macros)) # the "formatting" group is to strip matching */**/_/__ # surrounding an entire macro. - self.macro_re = re.compile( - r'(?P\**|_*)(?P{}):(?P[\w*]+((?P[\[][^\]]*[\]]))?)(?P=formatting)'.format(macros_pattern)) + self.macro_re = re.compile(fr''' + (?P\**|_*) # opening formatting + (?P{macros_pattern}): # macro name and colon + (?P(([-\w]+[.])*)[-\w*]+(?P\[([^\]]*)\])?) + (?P=formatting) # matching trailing formatting + ''', + re.VERBOSE) def haveLinkTarget(self, entity): """Report if we have parsed an API include (or heading) for an entity. diff --git a/specification/scripts/validitygenerator.py b/specification/scripts/validitygenerator.py index b121f705..5b18c112 100644 --- a/specification/scripts/validitygenerator.py +++ b/specification/scripts/validitygenerator.py @@ -1086,8 +1086,8 @@ def makeStructureExtensionPointer(self, blockname, param): self.logMsg( 'diag', 'makeStructureExtensionPointer: struct', struct, 'IS NOT required') - entry += '{} must: be {} or a valid pointer to the <>'.format( - self.makeParameterName(param_name), self.null) + link = "link:{uri-next-chain}[next structure in a structure chain]" + entry += f'{self.makeParameterName(param_name)} must: be {self.null} or a valid pointer to the {link}' if not extensionstructs: return entry entry += '. See also: ' diff --git a/specification/scripts/xrconventions.py b/specification/scripts/xrconventions.py index 2d59c0c5..0c650699 100644 --- a/specification/scripts/xrconventions.py +++ b/specification/scripts/xrconventions.py @@ -205,7 +205,7 @@ def specURL(self, spectype='api'): instead. N.b. this may need to change on a per-refpage basis if there are multiple documents involved. """ - return 'https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html' + return 'https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html' @property def xml_api_name(self): diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b952079a..0891b294 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -450,7 +450,7 @@ if(NOT MSVC) endforeach() if(APPLE) set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-undefined,error") - else() + elseif(NOT WIN32) set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined") endif() endif() diff --git a/src/common/gfxwrapper_opengl.c b/src/common/gfxwrapper_opengl.c index 413dc514..1c9e0916 100644 --- a/src/common/gfxwrapper_opengl.c +++ b/src/common/gfxwrapper_opengl.c @@ -291,6 +291,7 @@ PFNGLGENVERTEXARRAYSPROC glGenVertexArrays; PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays; PFNGLBINDVERTEXARRAYPROC glBindVertexArray; PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer; +PFNGLVERTEXATTRIBIPOINTERPROC glVertexAttribIPointer; PFNGLVERTEXATTRIBDIVISORPROC glVertexAttribDivisor; PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray; PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray; @@ -393,6 +394,21 @@ PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT; PFNGLXDELAYBEFORESWAPNVPROC glXDelayBeforeSwapNV; #endif +PFNGLBINDSAMPLERPROC glBindSampler; +PFNGLDELETESAMPLERSPROC glDeleteSamplers; +PFNGLGENSAMPLERSPROC glGenSamplers; +PFNGLGETSAMPLERPARAMETERIIVPROC glGetSamplerParameterIiv; +PFNGLGETSAMPLERPARAMETERIUIVPROC glGetSamplerParameterIuiv; +PFNGLGETSAMPLERPARAMETERFVPROC glGetSamplerParameterfv; +PFNGLGETSAMPLERPARAMETERIVPROC glGetSamplerParameteriv; +PFNGLISSAMPLERPROC glIsSampler; +PFNGLSAMPLERPARAMETERIIVPROC glSamplerParameterIiv; +PFNGLSAMPLERPARAMETERIUIVPROC glSamplerParameterIuiv; +PFNGLSAMPLERPARAMETERFPROC glSamplerParameterf; +PFNGLSAMPLERPARAMETERFVPROC glSamplerParameterfv; +PFNGLSAMPLERPARAMETERIPROC glSamplerParameteri; +PFNGLSAMPLERPARAMETERIVPROC glSamplerParameteriv; + void GlBootstrapExtensions() { #if defined(OS_WINDOWS) wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)GetExtension("wglChoosePixelFormatARB"); @@ -445,6 +461,7 @@ void GlInitExtensions() { glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSPROC)GetExtension("glDeleteVertexArrays"); glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC)GetExtension("glBindVertexArray"); glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)GetExtension("glVertexAttribPointer"); + glVertexAttribIPointer = (PFNGLVERTEXATTRIBIPOINTERPROC)GetExtension("glVertexAttribIPointer"); glVertexAttribDivisor = (PFNGLVERTEXATTRIBDIVISORPROC)GetExtension("glVertexAttribDivisor"); glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC)GetExtension("glDisableVertexAttribArray"); glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)GetExtension("glEnableVertexAttribArray"); @@ -539,6 +556,21 @@ void GlInitExtensions() { glDebugMessageControl = (PFNGLDEBUGMESSAGECONTROLPROC)GetExtension("glDebugMessageControl"); glDebugMessageCallback = (PFNGLDEBUGMESSAGECALLBACKPROC)GetExtension("glDebugMessageCallback"); + glBindSampler = (PFNGLBINDSAMPLERPROC)GetExtension("glBindSampler"); + glDeleteSamplers = (PFNGLDELETESAMPLERSPROC)GetExtension("glDeleteSamplers"); + glGenSamplers = (PFNGLGENSAMPLERSPROC)GetExtension("glGenSamplers"); + glGetSamplerParameterIiv = (PFNGLGETSAMPLERPARAMETERIIVPROC)GetExtension("glGetSamplerParameterIiv"); + glGetSamplerParameterIuiv = (PFNGLGETSAMPLERPARAMETERIUIVPROC)GetExtension("glGetSamplerParameterIuiv"); + glGetSamplerParameterfv = (PFNGLGETSAMPLERPARAMETERFVPROC)GetExtension("glGetSamplerParameterfv"); + glGetSamplerParameteriv = (PFNGLGETSAMPLERPARAMETERIVPROC)GetExtension("glGetSamplerParameteriv"); + glIsSampler = (PFNGLISSAMPLERPROC)GetExtension("glIsSampler"); + glSamplerParameterIiv = (PFNGLSAMPLERPARAMETERIIVPROC)GetExtension("glSamplerParameterIiv"); + glSamplerParameterIuiv = (PFNGLSAMPLERPARAMETERIUIVPROC)GetExtension("glSamplerParameterIuiv"); + glSamplerParameterf = (PFNGLSAMPLERPARAMETERFPROC)GetExtension("glSamplerParameterf"); + glSamplerParameterfv = (PFNGLSAMPLERPARAMETERFVPROC)GetExtension("glSamplerParameterfv"); + glSamplerParameteri = (PFNGLSAMPLERPARAMETERIPROC)GetExtension("glSamplerParameteri"); + glSamplerParameteriv = (PFNGLSAMPLERPARAMETERIVPROC)GetExtension("glSamplerParameteriv"); + glExtensions.timer_query = GlCheckExtension("GL_EXT_timer_query"); glExtensions.texture_clamp_to_border = true; // always available glExtensions.buffer_storage = @@ -1626,7 +1658,7 @@ void ksGpuContext_UnsetCurrent(ksGpuContext *context) { #if defined(OS_WINDOWS) wglMakeCurrent(context->hDC, NULL); #elif defined(OS_LINUX_XLIB) || defined(OS_LINUX_XCB_GLX) - glXMakeCurrent(context->xDisplay, None, NULL); + glXMakeCurrent(context->xDisplay, /* None */ 0L, NULL); #elif defined(OS_LINUX_XCB) xcb_glx_make_current(context->connection, 0, 0, 0); #elif defined(OS_APPLE_MACOS) diff --git a/src/common/gfxwrapper_opengl.h b/src/common/gfxwrapper_opengl.h index 6d8e1518..3a229833 100644 --- a/src/common/gfxwrapper_opengl.h +++ b/src/common/gfxwrapper_opengl.h @@ -190,7 +190,7 @@ Platform headers / declarations #define USE_SYNC_OBJECT 0 // 0 = GLsync, 1 = EGLSyncKHR, 2 = storage buffer #if !defined(_XOPEN_SOURCE) -#if __STDC_VERSION__ >= 199901L +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L #define _XOPEN_SOURCE 600 #else #define _XOPEN_SOURCE 500 @@ -213,6 +213,18 @@ Platform headers / declarations #include // for resolution changes #include +#ifdef Success +#undef Success +#endif // Success + +#ifdef Always +#undef Always +#endif // Always + +#ifdef None +#undef None +#endif // None + #elif defined(OS_LINUX_XCB) || defined(OS_LINUX_XCB_GLX) #define XR_USE_PLATFORM_XCB 1 @@ -225,6 +237,18 @@ Platform headers / declarations #include #include +#ifdef Success +#undef Success +#endif // Success + +#ifdef Always +#undef Always +#endif // Always + +#ifdef None +#undef None +#endif // None + #elif defined(OS_LINUX_WAYLAND) #define XR_USE_PLATFORM_WAYLAND 1 @@ -495,6 +519,7 @@ extern PFNGLGENVERTEXARRAYSPROC glGenVertexArrays; extern PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays; extern PFNGLBINDVERTEXARRAYPROC glBindVertexArray; extern PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer; +extern PFNGLVERTEXATTRIBIPOINTERPROC glVertexAttribIPointer; extern PFNGLVERTEXATTRIBDIVISORPROC glVertexAttribDivisor; extern PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray; extern PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray; @@ -597,6 +622,21 @@ extern PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT; extern PFNGLXDELAYBEFORESWAPNVPROC glXDelayBeforeSwapNV; #endif +extern PFNGLBINDSAMPLERPROC glBindSampler; +extern PFNGLDELETESAMPLERSPROC glDeleteSamplers; +extern PFNGLGENSAMPLERSPROC glGenSamplers; +extern PFNGLGETSAMPLERPARAMETERIIVPROC glGetSamplerParameterIiv; +extern PFNGLGETSAMPLERPARAMETERIUIVPROC glGetSamplerParameterIuiv; +extern PFNGLGETSAMPLERPARAMETERFVPROC glGetSamplerParameterfv; +extern PFNGLGETSAMPLERPARAMETERIVPROC glGetSamplerParameteriv; +extern PFNGLISSAMPLERPROC glIsSampler; +extern PFNGLSAMPLERPARAMETERIIVPROC glSamplerParameterIiv; +extern PFNGLSAMPLERPARAMETERIUIVPROC glSamplerParameterIuiv; +extern PFNGLSAMPLERPARAMETERFPROC glSamplerParameterf; +extern PFNGLSAMPLERPARAMETERFVPROC glSamplerParameterfv; +extern PFNGLSAMPLERPARAMETERIPROC glSamplerParameteri; +extern PFNGLSAMPLERPARAMETERIVPROC glSamplerParameteriv; + #elif defined(OS_APPLE_MACOS) #ifdef GL_GLEXT_FUNCTION_POINTERS diff --git a/src/conformance/conformance_cli/main.cpp b/src/conformance/conformance_cli/main.cpp index 2b8dba84..cc7ccf64 100644 --- a/src/conformance/conformance_cli/main.cpp +++ b/src/conformance/conformance_cli/main.cpp @@ -62,13 +62,14 @@ int main(int argc, const char** argv) launchSettings.argv = argv; launchSettings.message = OnTestMessage; - uint32_t failureCount = 0; - XrcResult result = xrcRunConformanceTests(&launchSettings, &failureCount); + XrcTestResult testResult; + uint64_t failureCount = 0; + XrcResult result = xrcRunConformanceTests(&launchSettings, &testResult, &failureCount); if (result != XRC_SUCCESS) { return 2; // Tests failed to run. } xrcCleanup(); - return failureCount == 0 ? 0 : 1; + return testResult == XRC_TEST_RESULT_SUCCESS ? 0 : 1; } diff --git a/src/conformance/conformance_layer/Negotiate.cpp b/src/conformance/conformance_layer/Negotiate.cpp index dfeafdc1..431a8b8f 100644 --- a/src/conformance/conformance_layer/Negotiate.cpp +++ b/src/conformance/conformance_layer/Negotiate.cpp @@ -20,8 +20,6 @@ namespace { - constexpr XrVersion LayerApiVersion = XR_CURRENT_API_VERSION; - XRAPI_ATTR XrResult XRAPI_CALL ConformanceLayer_RegisterInstance(const XrInstanceCreateInfo* createInfo, const XrApiLayerCreateInfo* apiLayerInfo, XrInstance* instance) { @@ -67,32 +65,42 @@ namespace #define LAYER_EXPORT #endif +// Function used to negotiate an interface betewen the loader and an API layer. Each library exposing one or +// more API layers needs to expose at least this function. extern "C" LAYER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrNegotiateLoaderApiLayerInterface(const XrNegotiateLoaderInfo* loaderInfo, const char* /*apiLayerName*/, XrNegotiateApiLayerRequest* apiLayerRequest) { - if (loaderInfo->structType != XR_LOADER_INTERFACE_STRUCT_LOADER_INFO || loaderInfo->structVersion != XR_LOADER_INFO_STRUCT_VERSION || - loaderInfo->structSize != sizeof(XrNegotiateLoaderInfo)) { - return XR_ERROR_INITIALIZATION_FAILED; // TODO: Log reason somehow. + if (loaderInfo == nullptr || loaderInfo->structType != XR_LOADER_INTERFACE_STRUCT_LOADER_INFO || + loaderInfo->structVersion != XR_LOADER_INFO_STRUCT_VERSION || loaderInfo->structSize != sizeof(XrNegotiateLoaderInfo)) { + // TODO: Log reason somehow. + // LogPlatformUtilsError("loaderInfo struct is not valid"); + return XR_ERROR_INITIALIZATION_FAILED; } if (loaderInfo->minInterfaceVersion > XR_CURRENT_LOADER_API_LAYER_VERSION || loaderInfo->maxInterfaceVersion < XR_CURRENT_LOADER_API_LAYER_VERSION) { - return XR_ERROR_INITIALIZATION_FAILED; // TODO: Log reason somehow. + // TODO: Log reason somehow. + // LogPlatformUtilsError("loader interface version is not in the range [minInterfaceVersion, maxInterfaceVersion]"); + return XR_ERROR_INITIALIZATION_FAILED; } - if (XR_CURRENT_API_VERSION > loaderInfo->maxApiVersion || XR_CURRENT_API_VERSION < loaderInfo->minApiVersion) { - return XR_ERROR_INITIALIZATION_FAILED; // TODO: Log reason somehow. + if (loaderInfo->minApiVersion > XR_CURRENT_API_VERSION || loaderInfo->maxApiVersion < XR_CURRENT_API_VERSION) { + // TODO: Log reason somehow. + // LogPlatformUtilsError("loader api version is not in the range [minApiVersion, maxApiVersion]"); + return XR_ERROR_INITIALIZATION_FAILED; } - if (apiLayerRequest->structType != XR_LOADER_INTERFACE_STRUCT_API_LAYER_REQUEST || + if (apiLayerRequest == nullptr || apiLayerRequest->structType != XR_LOADER_INTERFACE_STRUCT_API_LAYER_REQUEST || apiLayerRequest->structVersion != XR_API_LAYER_INFO_STRUCT_VERSION || apiLayerRequest->structSize != sizeof(XrNegotiateApiLayerRequest)) { - return XR_ERROR_INITIALIZATION_FAILED; // TODO: Log reason somehow. + // TODO: Log reason somehow. + // LogPlatformUtilsError("apiLayerRequest is not valid"); + return XR_ERROR_INITIALIZATION_FAILED; } apiLayerRequest->layerInterfaceVersion = XR_CURRENT_LOADER_API_LAYER_VERSION; - apiLayerRequest->layerApiVersion = LayerApiVersion; + apiLayerRequest->layerApiVersion = XR_CURRENT_API_VERSION; apiLayerRequest->getInstanceProcAddr = ConformanceLayer_xrGetInstanceProcAddr; apiLayerRequest->createApiLayerInstance = ConformanceLayer_RegisterInstance; diff --git a/src/conformance/conformance_test/CMakeLists.txt b/src/conformance/conformance_test/CMakeLists.txt index cb78c7d7..fab414c3 100644 --- a/src/conformance/conformance_test/CMakeLists.txt +++ b/src/conformance/conformance_test/CMakeLists.txt @@ -45,19 +45,21 @@ target_include_directories(conformance_test PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../framework ${PROJECT_SOURCE_DIR}/src/common - ${PROJECT_SOURCE_DIR}/src/external - - # Earcut algorithm for simple polygon triangulation - ${PROJECT_SOURCE_DIR}/src/external/earcut/include - # for openxr.h: ${PROJECT_BINARY_DIR}/include # for common_config.h: ${PROJECT_BINARY_DIR}/src - +) +target_include_directories(conformance_test + SYSTEM PRIVATE # for helper headers ${PROJECT_SOURCE_DIR}/external/include + + ${PROJECT_SOURCE_DIR}/src/external + + # Earcut algorithm for simple polygon triangulation + ${PROJECT_SOURCE_DIR}/src/external/earcut/include ) source_group("Header Files" FILES ${LOCAL_HEADERS}) @@ -68,15 +70,21 @@ target_link_libraries(conformance_test PRIVATE conformance_framework) if(WIN32) target_compile_definitions(conformance_test PRIVATE _CRT_SECURE_NO_WARNINGS) if(MSVC) - target_compile_options(conformance_test PRIVATE /Zc:wchar_t /Zc:forScope /W4 /WX /wd4996) + target_compile_options(conformance_test PRIVATE /Zc:wchar_t /Zc:forScope /W4 /wd4996) + if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + # If actually msvc and not clang-cl + target_compile_options(conformance_test PRIVATE /WX) + endif() endif() - endif() if(CMAKE_SYSTEM_NAME STREQUAL "Linux") target_compile_options(conformance_test PRIVATE -Wall) target_link_libraries(conformance_test PRIVATE m) endif() +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GCC") + target_compile_options(conformance_test PRIVATE -Wno-missing-field-initializers) +endif() if(ANDROID) target_sources( conformance_test diff --git a/src/conformance/conformance_test/conformance_test.cpp b/src/conformance/conformance_test/conformance_test.cpp index 0e303f83..f04ea6f2 100644 --- a/src/conformance/conformance_test/conformance_test.cpp +++ b/src/conformance/conformance_test/conformance_test.cpp @@ -44,6 +44,7 @@ #include #include #include +#include using namespace Conformance; @@ -150,18 +151,78 @@ namespace // Ensure conformance is configured correctly. TEST_CASE("ValidateEnvironment") { - GlobalData& globalData = GetGlobalData(); + // Ensure that the conformance layer is loaded (or print a warning if it + // is not) + SECTION("Conformance layer") + { + GlobalData& globalData = GetGlobalData(); - if (!globalData.options.invalidHandleValidation) { - REQUIRE_MSG(globalData.IsAPILayerEnabled("XR_APILAYER_KHRONOS_runtime_conformance"), - "Conformance layer required to pass conformance"); + if (!globalData.options.invalidHandleValidation) { + REQUIRE_MSG(globalData.IsAPILayerEnabled("XR_APILAYER_KHRONOS_runtime_conformance"), + "Conformance layer required to pass conformance"); - // Conformance listens for failures from the conformance layer through the debug messenger extension. - REQUIRE_MSG(IsInstanceExtensionEnabled(XR_EXT_DEBUG_UTILS_EXTENSION_NAME), - "Debug utils extension required by conformance layer"); + // Conformance listens for failures from the conformance layer through the debug messenger extension. + REQUIRE_MSG(IsInstanceExtensionEnabled(XR_EXT_DEBUG_UTILS_EXTENSION_NAME), + "Debug utils extension required by conformance layer"); + } + else { + WARN("Conformance API layer not supported due to handle validation tests; do not submit this log for official conformance"); + } } - else { - WARN("Conformance API layer not supported due to handle validation tests; do not submit this log for official conformance"); + + uint32_t testCasesCount = 0; + REQUIRE(XRC_SUCCESS == xrcEnumerateTestCases(0, &testCasesCount, nullptr)); + + std::vector testCases(testCasesCount); + REQUIRE(XRC_SUCCESS == xrcEnumerateTestCases(testCasesCount, &testCasesCount, testCases.data())); + + SECTION("Validate Test Case Names") + { + for (const auto& testCase : testCases) { + std::string testName = testCase.testName; + + // Spaces in test names break our Android runner + INFO(testName); + REQUIRE(testName.find(" ") == std::string::npos); + } + } + + SECTION("Validate Test Case Tags") + { + for (const auto& testCase : testCases) { + std::string testTags = testCase.tags; + INFO(testCase.testName); + INFO(testTags); + + // readme.md instructions use [interactive] with [actions], [composition], and [scenario] + // Let's ensure that these cover all of the possible test cases. + const std::array interactiveTestTypes = { + "[actions]", + "[composition]", + "[scenario]", + }; + if (testTags.find("[interactive]") != std::string::npos) { + { + bool foundInteractiveTestType = false; + for (const auto& testType : interactiveTestTypes) { + if (testTags.find(testType) != std::string::npos) { + foundInteractiveTestType = true; + } + } + INFO("An interactive test should also have a tag for either actions, composition, or scenario"); + REQUIRE(foundInteractiveTestType); + } + + { + INFO("Interactive tests are typically either [actions] or [no_auto]"); + // [interactive] tests are almost always not automatable [no_auto] except when + // they are [actions] tests using `XR_EXT_conformance_automation` + bool isNoAuto = testTags.find("[no_auto]") != std::string::npos; + bool isActions = testTags.find("[actions]") != std::string::npos; + REQUIRE((isNoAuto || isActions)); + } + } + } } } @@ -415,6 +476,18 @@ namespace m_sectionIndent--; } + void noMatchingTestCases(Catch::StringRef /* unmatchedSpec */) override + { + Conformance::GlobalData& globalData = Conformance::GetGlobalData(); + globalData.conformanceReport.unmatchedTestSpecs = true; + } + + void testRunEnded(Catch::TestRunStats const& testRunStats) override + { + Conformance::GlobalData& globalData = Conformance::GetGlobalData(); + globalData.conformanceReport.totals = testRunStats.totals; + } + int m_sectionIndent{0}; }; CATCH_REGISTER_LISTENER(ConformanceTestListener) @@ -488,7 +561,8 @@ XrcResult XRAPI_CALL xrcEnumerateTestCases(uint32_t capacityInput, uint32_t* cou return XRC_SUCCESS; } -XrcResult XRAPI_CALL xrcRunConformanceTests(const ConformanceLaunchSettings* conformanceLaunchSettings, uint32_t* failureCount) +XrcResult XRAPI_CALL xrcRunConformanceTests(const ConformanceLaunchSettings* conformanceLaunchSettings, XrcTestResult* testResult, + uint64_t* failureCount) { using namespace Conformance; @@ -501,6 +575,8 @@ XrcResult XRAPI_CALL xrcRunConformanceTests(const ConformanceLaunchSettings* con g_conformanceLaunchSettings = conformanceLaunchSettings; XrcResult result = XRC_SUCCESS; + *testResult = XRC_TEST_RESULT_SUCCESS; + *failureCount = 0; bool conformanceTestsRun = false; try { Conformance::g_reportCallback = [&](const char* message) { conformanceLaunchSettings->message(MessageType_Stdout, message); }; @@ -529,6 +605,7 @@ XrcResult XRAPI_CALL xrcRunConformanceTests(const ConformanceLaunchSettings* con ReportConsoleOnlyF("Test failure: Command line arguments were invalid or insufficient."); return XRC_ERROR_COMMAND_LINE_INVALID; } + auto& catchConfig = CreateOrGetCatchSession().config(); auto& catchConfigData = CreateOrGetCatchSession().configData(); bool skipActuallyTesting = catchConfigData.listTests || catchConfigData.listTags || catchConfigData.listListeners || catchConfigData.listReporters; @@ -548,8 +625,30 @@ XrcResult XRAPI_CALL xrcRunConformanceTests(const ConformanceLaunchSettings* con } if (initialized) { - *failureCount = CreateOrGetCatchSession().run(); + int exitCode = CreateOrGetCatchSession().run(); + + Conformance::GlobalData& globalData = Conformance::GetGlobalData(); + *failureCount = globalData.conformanceReport.testFailureCount; + const auto& totals = globalData.conformanceReport.totals; conformanceTestsRun = true; + + // a list option was used so no tests could have run + if (skipActuallyTesting) { + *testResult = XRC_TEST_RESULT_SUCCESS; + } + else if (globalData.conformanceReport.unmatchedTestSpecs && catchConfig.warnAboutUnmatchedTestSpecs()) { + *testResult = XRC_TEST_RESULT_UNMATCHED_TEST_SPEC; + } + else if (totals.testCases.total() == 0 && !catchConfig.zeroTestsCountAsSuccess()) { + *testResult = XRC_TEST_RESULT_NO_TESTS_SELECTED; + } + else if (totals.testCases.total() > 0 && totals.testCases.total() == totals.testCases.skipped && + !catchConfig.zeroTestsCountAsSuccess()) { + *testResult = XRC_TEST_RESULT_ALL_TESTS_SKIPPED; + } + else if (exitCode != 0) { + *testResult = XRC_TEST_RESULT_SOME_TESTS_FAILED; + } } else { ReportF("Test failure: Test data initialization failed."); diff --git a/src/conformance/conformance_test/conformance_test.h b/src/conformance/conformance_test/conformance_test.h index 7e2adfeb..85dcba60 100644 --- a/src/conformance/conformance_test/conformance_test.h +++ b/src/conformance/conformance_test/conformance_test.h @@ -60,12 +60,24 @@ enum XrcResult XRC_ERROR_INTERNAL_ERROR = -4, }; +enum XrcTestResult +{ + XRC_TEST_RESULT_SUCCESS = 0, + XRC_TEST_RESULT_UNMATCHED_TEST_SPEC = 1, + /// Not returned if --allow-running-no-tests is passed + XRC_TEST_RESULT_NO_TESTS_SELECTED = 2, + /// Not returned if --allow-running-no-tests is passed + XRC_TEST_RESULT_ALL_TESTS_SKIPPED = 3, + XRC_TEST_RESULT_SOME_TESTS_FAILED = 4, +}; + /// Clean up after enumerating test cases or running tests. Idempotent: may call more than once. extern "C" CONFORMANCE_EXPORT XrcResult XRAPI_CALL xrcCleanup(void); extern "C" CONFORMANCE_EXPORT XrcResult XRAPI_CALL xrcEnumerateTestCases(uint32_t capacityInput, uint32_t* countOutput, ConformanceTestCase* testCases); -/// Returns failure count, including tests and initialization failure. +/// Returns XRC_SUCCESS if test execution was successful - tests may still have failed, or another failure condition may have been hit. +/// In case of Catch2-defined error conditions, testResult is set to a value other than XRC_TEST_RESULT_SUCCESS. extern "C" CONFORMANCE_EXPORT XrcResult XRAPI_CALL xrcRunConformanceTests(const ConformanceLaunchSettings* conformanceLaunchSettings, - uint32_t* failureCount); + XrcTestResult* testResult, uint64_t* failureCount); diff --git a/src/conformance/conformance_test/test_FrameSubmission.cpp b/src/conformance/conformance_test/test_FrameSubmission.cpp index 77662b8d..78ca7f23 100644 --- a/src/conformance/conformance_test/test_FrameSubmission.cpp +++ b/src/conformance/conformance_test/test_FrameSubmission.cpp @@ -41,7 +41,7 @@ namespace Conformance GlobalData& globalData = GetGlobalData(); if (!globalData.IsUsingGraphicsPlugin()) { // Nothing to check - no graphics plugin means no frame submission - return; + SKIP("Cannot test frame submission without a graphics plugin"); } SECTION("Before_xrBeginSession") @@ -260,10 +260,13 @@ namespace Conformance auto appThread = std::thread([&]() { ATTACH_THREAD; auto queueFrameRender = [&](const XrFrameState& frameState) { - { - std::unique_lock lock(displayMutex); - queuedFramesForRender.push(frameState); - } + std::unique_lock lock(displayMutex); + queuedFramesForRender.push(frameState); + displayCv.notify_one(); + }; + auto signalNoMoreFrames = [&]() { + std::unique_lock lock(displayMutex); + frameSubmissionCompleted = true; displayCv.notify_one(); }; @@ -272,6 +275,7 @@ namespace Conformance XrFrameState frameState{XR_TYPE_FRAME_STATE}; appThreadResult = xrWaitFrame(compositionHelper.GetSession(), nullptr, &frameState); if (appThreadResult != XR_SUCCESS) { + signalNoMoreFrames(); DETACH_THREAD; return; } @@ -292,6 +296,8 @@ namespace Conformance Stopwatch waitTimer(true); appThreadResult = xrWaitFrame(compositionHelper.GetSession(), nullptr, &frameState); if (appThreadResult != XR_SUCCESS) { + signalNoMoreFrames(); + DETACH_THREAD; return; } @@ -309,22 +315,18 @@ namespace Conformance } // Signal that no more frames are coming and wait for the render thread to exit. - { - std::unique_lock lock(displayMutex); - frameSubmissionCompleted = true; - displayCv.notify_one(); - } + signalNoMoreFrames(); DETACH_THREAD; }); - while (true) { + while (appThreadResult == XR_SUCCESS) { // Dequeue a frame to render. XrFrameState frameState; { std::unique_lock lock(displayMutex); displayCv.wait(lock, [&] { return !queuedFramesForRender.empty() || frameSubmissionCompleted; }); if (queuedFramesForRender.empty()) { - assert(frameSubmissionCompleted); + REQUIRE(frameSubmissionCompleted); break; } frameState = queuedFramesForRender.front(); diff --git a/src/conformance/conformance_test/test_LayerComposition.cpp b/src/conformance/conformance_test/test_LayerComposition.cpp index 049d9340..1a9344ae 100644 --- a/src/conformance/conformance_test/test_LayerComposition.cpp +++ b/src/conformance/conformance_test/test_LayerComposition.cpp @@ -574,7 +574,7 @@ namespace Conformance compositionHelper.CreateReferenceSpace(XR_REFERENCE_SPACE_TYPE_LOCAL, XrPosef{Quat::Identity, {0, 0, 0}}); if (!compositionHelper.GetViewConfigurationProperties().fovMutable) { - return; + SKIP("View configuration does not support mutable FoV"); } const std::vector viewProperties = compositionHelper.EnumerateConfigurationViews(); diff --git a/src/conformance/conformance_test/test_Swapchains.cpp b/src/conformance/conformance_test/test_Swapchains.cpp index 42eddf4e..90078fae 100644 --- a/src/conformance/conformance_test/test_Swapchains.cpp +++ b/src/conformance/conformance_test/test_Swapchains.cpp @@ -51,7 +51,7 @@ namespace Conformance projectionView.subImage.imageArrayIndex = 0; XrSwapchainImageWaitInfo imageWaitInfo{XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO}; - imageWaitInfo.timeout = 500_xrMilliseconds; + imageWaitInfo.timeout = XR_INFINITE_DURATION; // acquire/wait/render/release all the images. for (uint32_t i = 0; i < colorImageCount; ++i) { @@ -189,7 +189,7 @@ namespace Conformance // Wait/release all the images. for (uint32_t i = 0; i < imageCount; ++i) { XrSwapchainImageWaitInfo imageWaitInfo{XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO}; - imageWaitInfo.timeout = 500_xrMilliseconds; // Call can block waiting for image to become available for writing. + imageWaitInfo.timeout = XR_INFINITE_DURATION; // Call can block waiting for image to become available for writing. REQUIRE_RESULT_UNQUALIFIED_SUCCESS(xrWaitSwapchainImage(swapchain, &imageWaitInfo)); // Another wait should fail with XR_ERROR_CALL_ORDER_INVALID. @@ -393,7 +393,7 @@ namespace Conformance const GlobalData& globalData = GetGlobalData(); if (!globalData.IsUsingGraphicsPlugin()) { // Nothing to check - no graphics plugin means no swapchain - return; + SKIP("Cannot test swapchains without a graphics plugin"); } // Set up the session we will use for the testing @@ -480,7 +480,7 @@ namespace Conformance const GlobalData& globalData = GetGlobalData(); if (!globalData.IsUsingGraphicsPlugin()) { // Nothing to check - no graphics plugin means no swapchain - return; + SKIP("Cannot test swapchains without a graphics plugin"); } // Set up the session we will use for the testing @@ -619,7 +619,7 @@ namespace Conformance GlobalData& globalData = GetGlobalData(); if (!globalData.IsUsingGraphicsPlugin()) { // Nothing to check - no graphics plugin means no swapchain - return; + SKIP("Cannot test swapchains without a graphics plugin"); } // Set up the session we will use for the testing @@ -654,7 +654,7 @@ namespace Conformance // Wait/release all the images. for (uint32_t i = 0; i < imageCount; ++i) { XrSwapchainImageWaitInfo imageWaitInfo{XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO}; - imageWaitInfo.timeout = 500_xrMilliseconds; // Call can block waiting for image to become available for writing. + imageWaitInfo.timeout = XR_INFINITE_DURATION; // Call can block waiting for image to become available for writing. REQUIRE_RESULT_UNQUALIFIED_SUCCESS(xrWaitSwapchainImage(swapchain, &imageWaitInfo)); // Another wait should fail with XR_ERROR_CALL_ORDER_INVALID. diff --git a/src/conformance/conformance_test/test_XR_EXT_debug_utils.cpp b/src/conformance/conformance_test/test_XR_EXT_debug_utils.cpp index 751defe6..63947d5b 100644 --- a/src/conformance/conformance_test/test_XR_EXT_debug_utils.cpp +++ b/src/conformance/conformance_test/test_XR_EXT_debug_utils.cpp @@ -84,7 +84,7 @@ namespace Conformance const XrDebugUtilsMessengerCallbackDataEXT* callbackData, void* userData) { - REQUIRE(userData != NULL); + REQUIRE(userData != nullptr); auto pMessages = reinterpret_cast*>(userData); DebugUtilsCallbackInfo callbackInfo; @@ -225,6 +225,25 @@ namespace Conformance return false; } + static const DebugUtilsCallbackInfo& findMessageByMessageId(const std::vector& callbackInfos, + const char* messageId) + { + { + size_t messageMatchCount = 0; + for (const auto& callbackInfo : callbackInfos) { + if (strcmp(callbackInfo.callbackData.messageId, messageId) == 0) { + messageMatchCount++; + } + } + REQUIRE(messageMatchCount == 1); + } + auto it = std::find_if(callbackInfos.begin(), callbackInfos.end(), [messageId](const auto& callbackInfo) { + return strcmp(callbackInfo.callbackData.messageId, messageId) == 0; + }); + REQUIRE(it != callbackInfos.end()); + return *it; + } + TEST_CASE("XR_EXT_debug_utils", "") { GlobalData& globalData = GetGlobalData(); @@ -720,24 +739,6 @@ namespace Conformance SECTION("Test object names") { - auto findMessageByMessageId = [](const std::vector& callbackInfos, - const char* messageId) -> const DebugUtilsCallbackInfo& { - { - size_t messageMatchCount = 0; - for (const auto& callbackInfo : callbackInfos) { - if (strcmp(callbackInfo.callbackData.messageId, messageId) == 0) { - messageMatchCount++; - } - } - REQUIRE(messageMatchCount == 1); - } - auto it = std::find_if(callbackInfos.begin(), callbackInfos.end(), [messageId](const auto& callbackInfo) { - return strcmp(callbackInfo.callbackData.messageId, messageId) == 0; - }); - REQUIRE(it != callbackInfos.end()); - return *it; - }; - AutoBasicInstance instance({XR_EXT_DEBUG_UTILS_EXTENSION_NAME}); auto pfn_create_debug_utils_messager_ext = @@ -770,8 +771,6 @@ namespace Conformance object.objectName = "My Instance Obj"; REQUIRE_RESULT(XR_SUCCESS, pfn_set_obj_name(instance, &object)); - // TODO: validate objectName works (in a separate test case) - { XrDebugUtilsMessengerCallbackDataEXT callback_data{XR_TYPE_DEBUG_UTILS_MESSENGER_CALLBACK_DATA_EXT}; callback_data.messageId = "General Error"; @@ -800,19 +799,6 @@ namespace Conformance XrDebugUtilsLabelEXT first_label = {XR_TYPE_DEBUG_UTILS_LABEL_EXT}; first_label.labelName = first_individual_label_name; - // TODO: Invalid parameters might be better as a separate test case - SECTION("Invalid parameters") - { - // Try invalid session on each of the label functions - REQUIRE_RESULT(XR_ERROR_HANDLE_INVALID, pfn_begin_debug_utils_label_region_ext(XR_NULL_HANDLE, &first_label)); - REQUIRE_RESULT(XR_ERROR_HANDLE_INVALID, pfn_end_debug_utils_label_region_ext(XR_NULL_HANDLE)); - REQUIRE_RESULT(XR_ERROR_HANDLE_INVALID, pfn_insert_debug_utils_label_ext(XR_NULL_HANDLE, &first_label)); - - // Try with nullptr for the label - REQUIRE_RESULT(XR_ERROR_VALIDATION_FAILURE, pfn_begin_debug_utils_label_region_ext(session, nullptr)); - REQUIRE_RESULT(XR_ERROR_VALIDATION_FAILURE, pfn_insert_debug_utils_label_ext(session, nullptr)); - } - // Set it up to put in the session and instance to any debug utils messages XrDebugUtilsMessengerCallbackDataEXT callback_data{XR_TYPE_DEBUG_UTILS_MESSENGER_CALLBACK_DATA_EXT}; callback_data.messageId = "General Error"; @@ -921,7 +907,6 @@ namespace Conformance // End the last (most recent) label region { REQUIRE_RESULT(XR_SUCCESS, pfn_end_debug_utils_label_region_ext(session)); - // TODO: need a test for end a label region that has not been started } // Trigger a message and make sure we see "First Label Region" @@ -964,7 +949,159 @@ namespace Conformance REQUIRE_RESULT(XR_SUCCESS, pfn_destroy_debug_utils_messager_ext(debug_utils_messenger)); } - // https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#XR_EXT_debug_utils + SECTION("Object naming") + { + AutoBasicInstance instance({XR_EXT_DEBUG_UTILS_EXTENSION_NAME}); + AutoBasicSession session(AutoBasicSession::createSession, instance); + + auto pfn_create_debug_utils_messager_ext = + GetInstanceExtensionFunction(instance, "xrCreateDebugUtilsMessengerEXT"); + auto pfn_destroy_debug_utils_messager_ext = + GetInstanceExtensionFunction(instance, "xrDestroyDebugUtilsMessengerEXT"); + auto pfn_submit_dmsg = GetInstanceExtensionFunction(instance, "xrSubmitDebugUtilsMessageEXT"); + auto pfn_set_obj_name = + GetInstanceExtensionFunction(instance, "xrSetDebugUtilsObjectNameEXT"); + + // Create the debug utils messenger + std::vector callbackInfo; + + XrDebugUtilsMessengerCreateInfoEXT dbg_msg_ci = {XR_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT}; + dbg_msg_ci.messageSeverities = XR_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT; + dbg_msg_ci.messageTypes = XR_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT; + dbg_msg_ci.userCallback = addToDebugUtilsCallbackInfoVector; + dbg_msg_ci.userData = reinterpret_cast(&callbackInfo); + + XrDebugUtilsMessengerEXT debug_utils_messenger = XR_NULL_HANDLE; + REQUIRE_RESULT(XR_SUCCESS, pfn_create_debug_utils_messager_ext(instance, &dbg_msg_ci, &debug_utils_messenger)); + + // Set object name + XrDebugUtilsObjectNameInfoEXT referenceObject{XR_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT}; + referenceObject.objectType = XR_OBJECT_TYPE_INSTANCE; + referenceObject.objectHandle = MakeHandleGeneric(instance.GetInstance()); + referenceObject.objectName = "My Instance Obj"; + REQUIRE_RESULT(XR_SUCCESS, pfn_set_obj_name(instance, &referenceObject)); + + // Check object names + { + std::array objects; + objects.fill({XR_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT}); + // We pass an object with a name we expect to be overridden with the correct name + objects[0].objectType = XR_OBJECT_TYPE_INSTANCE; + objects[0].objectHandle = MakeHandleGeneric(instance.GetInstance()); + objects[0].objectName = "Not my instance"; + // and we pass an object with a name we expect to stay + objects[1].objectType = XR_OBJECT_TYPE_SESSION; + objects[1].objectHandle = MakeHandleGeneric(session.GetSession()); + objects[1].objectName = "My Session Obj"; + + XrDebugUtilsMessengerCallbackDataEXT callback_data{XR_TYPE_DEBUG_UTILS_MESSENGER_CALLBACK_DATA_EXT}; + callback_data.messageId = "Object Name Test"; + callback_data.functionName = "MyTestFunctionName"; + callback_data.message = "Object name"; + callback_data.objectCount = static_cast(objects.size()); + callback_data.objects = objects.data(); + REQUIRE_RESULT(XR_SUCCESS, pfn_submit_dmsg(instance, XR_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT, + XR_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT, &callback_data)); + + const auto& cb = findMessageByMessageId(callbackInfo, callback_data.messageId); + + REQUIRE(cb.callbackData.objectCount == 2); + + // We expect that the Instance name will be filled by the debug utils implementation + REQUIRE(cb.callbackData.objects[0].objectName != nullptr); + REQUIRE_THAT(cb.callbackData.objects[0].objectName, Catch::Matchers::Equals(referenceObject.objectName)); + + // We expect that the passed name will not be overridden / removed + REQUIRE(cb.callbackData.objects[1].objectName != nullptr); + REQUIRE_THAT(cb.callbackData.objects[1].objectName, Catch::Matchers::Equals(objects[1].objectName)); + } + + // Unset object name + // https://registry.khronos.org/OpenXR/specs/1.0/man/html/xrSetDebugUtilsObjectNameEXT.html + // If XrDebugUtilsObjectNameInfoEXT::objectName is an empty string, then any previously set name is removed. + XrDebugUtilsObjectNameInfoEXT unsetObject{XR_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT}; + unsetObject.objectType = XR_OBJECT_TYPE_INSTANCE; + unsetObject.objectHandle = MakeHandleGeneric(instance.GetInstance()); + unsetObject.objectName = ""; + REQUIRE_RESULT(XR_SUCCESS, pfn_set_obj_name(instance, &unsetObject)); + + { + XrDebugUtilsObjectNameInfoEXT object{XR_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT}; + object.objectType = XR_OBJECT_TYPE_INSTANCE; + object.objectHandle = MakeHandleGeneric(instance.GetInstance()); + object.objectName = nullptr; + + XrDebugUtilsMessengerCallbackDataEXT callback_data{XR_TYPE_DEBUG_UTILS_MESSENGER_CALLBACK_DATA_EXT}; + callback_data.messageId = "Object Name Test Removed"; + callback_data.functionName = "MyTestFunctionName"; + callback_data.message = "Object name"; + callback_data.objectCount = 1; + callback_data.objects = &object; + REQUIRE_RESULT(XR_SUCCESS, pfn_submit_dmsg(instance, XR_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT, + XR_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT, &callback_data)); + + const auto& cb = findMessageByMessageId(callbackInfo, callback_data.messageId); + + REQUIRE(cb.callbackData.objectCount == 1); + + // We expect that the Instance name will NOT be filled by the debug utils implementation + REQUIRE(cb.callbackData.objects[0].objectName == nullptr); + } + + // Destroy what we created + REQUIRE_RESULT(XR_SUCCESS, pfn_destroy_debug_utils_messager_ext(debug_utils_messenger)); + } + + SECTION("Invalid parameters") + { + AutoBasicInstance instance({XR_EXT_DEBUG_UTILS_EXTENSION_NAME}); + + auto pfn_begin_debug_utils_label_region_ext = GetInstanceExtensionFunction( + instance, "xrSessionBeginDebugUtilsLabelRegionEXT"); + auto pfn_end_debug_utils_label_region_ext = + GetInstanceExtensionFunction(instance, "xrSessionEndDebugUtilsLabelRegionEXT"); + auto pfn_insert_debug_utils_label_ext = + GetInstanceExtensionFunction(instance, "xrSessionInsertDebugUtilsLabelEXT"); + + AutoBasicSession session(AutoBasicSession::createSession | AutoBasicSession::createSpaces | AutoBasicSession::createSwapchains, + instance); + FrameIterator frameIterator(&session); + + { + // auto pfn_set_obj_name = + // GetInstanceExtensionFunction(instance, "xrSetDebugUtilsObjectNameEXT"); + // Cannot try invalid instance on set object name as loader will crash + // REQUIRE_RESULT(XR_ERROR_HANDLE_INVALID, pfn_set_obj_name(XR_NULL_HANDLE, nullptr)); + // Cannot try nullptr for the object name info as loader will crash + // REQUIRE_RESULT(XR_ERROR_HANDLE_INVALID, pfn_set_obj_name(instance, nullptr)); + } + + // Try invalid session on each of the label functions + { + // Create a label struct for initial testing + XrDebugUtilsLabelEXT label = {XR_TYPE_DEBUG_UTILS_LABEL_EXT}; + label.labelName = "individual label"; + + REQUIRE_RESULT(XR_ERROR_HANDLE_INVALID, pfn_begin_debug_utils_label_region_ext(XR_NULL_HANDLE, &label)); + REQUIRE_RESULT(XR_ERROR_HANDLE_INVALID, pfn_end_debug_utils_label_region_ext(XR_NULL_HANDLE)); + REQUIRE_RESULT(XR_ERROR_HANDLE_INVALID, pfn_insert_debug_utils_label_ext(XR_NULL_HANDLE, &label)); + } + + // Try with nullptr for the label + { + REQUIRE_RESULT(XR_ERROR_VALIDATION_FAILURE, pfn_begin_debug_utils_label_region_ext(session, nullptr)); + REQUIRE_RESULT(XR_ERROR_VALIDATION_FAILURE, pfn_insert_debug_utils_label_ext(session, nullptr)); + } + + // Try to end a label region that has not been started + { + // This seems like an error condition but the OpenXR Loader does not return an error + // here so we need the same behavior. + REQUIRE_RESULT(XR_SUCCESS, pfn_end_debug_utils_label_region_ext(session)); + } + } + + // https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XR_EXT_debug_utils // The OpenXR spec provides some examples of how to use the extension; they are not full // examples but let's make sure that something equivalent to them works. // Example 1 / multiple callbacks diff --git a/src/conformance/conformance_test/test_XR_EXT_hand_tracking.cpp b/src/conformance/conformance_test/test_XR_EXT_hand_tracking.cpp index adea1a48..7e6d09f5 100644 --- a/src/conformance/conformance_test/test_XR_EXT_hand_tracking.cpp +++ b/src/conformance/conformance_test/test_XR_EXT_hand_tracking.cpp @@ -137,9 +137,13 @@ namespace Conformance localSpaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL; REQUIRE_RESULT(xrCreateReferenceSpace(session, &localSpaceCreateInfo, &localSpace), XR_SUCCESS); - // Wait until the runtime is ready for us to begin a session + // Wait until the runtime has transitioned to the sync/visible/focused state for us to begin a session, + // this is to ensure that `frameIterator` has a valid, non-zero predicated display time. FrameIterator frameIterator(&session); - frameIterator.RunToSessionState(XR_SESSION_STATE_READY); + frameIterator.RunToSessionState(XR_SESSION_STATE_FOCUSED); + + // We are relying on a non-zero predicted display time here. + REQUIRE(frameIterator.frameState.predictedDisplayTime != 0); for (auto hand : {LEFT_HAND, RIGHT_HAND}) { std::array, HAND_COUNT> jointLocations; @@ -199,9 +203,13 @@ namespace Conformance localSpaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL; REQUIRE_RESULT(xrCreateReferenceSpace(session, &localSpaceCreateInfo, &localSpace), XR_SUCCESS); - // Wait until the runtime is ready for us to begin a session + // Wait until the runtime has transitioned to the sync/visible/focused state for us to begin a session, + // this is to ensure that `frameIterator` has a valid, non-zero predicated display time. FrameIterator frameIterator(&session); - frameIterator.RunToSessionState(XR_SESSION_STATE_READY); + frameIterator.RunToSessionState(XR_SESSION_STATE_FOCUSED); + + // We are relying on a non-zero predicted display time here. + REQUIRE(frameIterator.frameState.predictedDisplayTime != 0); // The application must input jointCount as described by the XrHandJointSetEXT when creating the XrHandTrackerEXT. // Otherwise, the runtime must return XR_ERROR_VALIDATION_FAILURE. diff --git a/src/conformance/conformance_test/test_XR_EXT_local_floor.cpp b/src/conformance/conformance_test/test_XR_EXT_local_floor.cpp index 3c80ca86..5914621d 100644 --- a/src/conformance/conformance_test/test_XR_EXT_local_floor.cpp +++ b/src/conformance/conformance_test/test_XR_EXT_local_floor.cpp @@ -31,7 +31,7 @@ namespace Conformance { GlobalData& globalData = GetGlobalData(); if (!globalData.IsInstanceExtensionSupported(XR_EXT_LOCAL_FLOOR_EXTENSION_NAME)) { - return; + SKIP(XR_EXT_LOCAL_FLOOR_EXTENSION_NAME " not supported"); } SECTION("Extension not enabled") diff --git a/src/conformance/conformance_test/test_XR_EXT_palm_pose.cpp b/src/conformance/conformance_test/test_XR_EXT_palm_pose.cpp index 7955250a..d76f21b5 100644 --- a/src/conformance/conformance_test/test_XR_EXT_palm_pose.cpp +++ b/src/conformance/conformance_test/test_XR_EXT_palm_pose.cpp @@ -413,7 +413,7 @@ namespace Conformance { GlobalData& globalData = GetGlobalData(); if (!globalData.IsInstanceExtensionSupported(XR_EXT_PALM_POSE_EXTENSION_NAME)) { - SKIP(); + SKIP(XR_EXT_PALM_POSE_EXTENSION_NAME " not supported"); } AutoBasicInstance instance({XR_EXT_PALM_POSE_EXTENSION_NAME}); diff --git a/src/conformance/conformance_test/test_XR_EXT_plane_detection.cpp b/src/conformance/conformance_test/test_XR_EXT_plane_detection.cpp index ceb03f55..45119382 100644 --- a/src/conformance/conformance_test/test_XR_EXT_plane_detection.cpp +++ b/src/conformance/conformance_test/test_XR_EXT_plane_detection.cpp @@ -78,7 +78,7 @@ namespace Conformance // Runtime does not support extension - it should not be possible to get function pointers. AutoBasicInstance instance; ValidateInstanceExtensionFunctionNotSupported(instance, "xrCreatePlaneDetectorEXT"); - return; + SKIP(XR_EXT_PLANE_DETECTION_EXTENSION_NAME " not supported"); } SECTION("Extension not enabled") @@ -127,11 +127,11 @@ namespace Conformance { GlobalData& globalData = GetGlobalData(); if (!globalData.IsInstanceExtensionSupported(XR_EXT_PLANE_DETECTION_EXTENSION_NAME)) { - SKIP(); + SKIP(XR_EXT_PLANE_DETECTION_EXTENSION_NAME " not supported"); } if (!globalData.IsUsingGraphicsPlugin()) { - SKIP(); + SKIP("Not using graphics, which the test requires"); } CompositionHelper compositionHelper("XR_EXT_plane_detection", {XR_EXT_PLANE_DETECTION_EXTENSION_NAME}); @@ -480,7 +480,7 @@ namespace Conformance // basic setup stuff GlobalData& globalData = GetGlobalData(); if (!globalData.IsInstanceExtensionSupported(XR_EXT_PLANE_DETECTION_EXTENSION_NAME)) { - SKIP(); + SKIP(XR_EXT_PLANE_DETECTION_EXTENSION_NAME " not supported"); } CompositionHelper compositionHelper("XR_EXT_plane_detection", {XR_EXT_PLANE_DETECTION_EXTENSION_NAME}); @@ -605,11 +605,11 @@ namespace Conformance GlobalData& globalData = GetGlobalData(); if (!globalData.IsInstanceExtensionSupported(XR_EXT_PLANE_DETECTION_EXTENSION_NAME)) { - SKIP(); + SKIP(XR_EXT_PLANE_DETECTION_EXTENSION_NAME " not supported"); } if (!globalData.IsUsingGraphicsPlugin()) { - SKIP(); + SKIP("Not using graphics, which the test requires"); } CompositionHelper compositionHelper("XR_EXT_plane_detection", {XR_EXT_PLANE_DETECTION_EXTENSION_NAME}); diff --git a/src/conformance/conformance_test/test_XR_KHR_convert_timespec_time.cpp b/src/conformance/conformance_test/test_XR_KHR_convert_timespec_time.cpp index b7a2d4de..9af5aba1 100644 --- a/src/conformance/conformance_test/test_XR_KHR_convert_timespec_time.cpp +++ b/src/conformance/conformance_test/test_XR_KHR_convert_timespec_time.cpp @@ -33,7 +33,9 @@ namespace Conformance TEST_CASE("XR_KHR_convert_timespec_time", "") { -#ifdef XR_USE_TIMESPEC +#ifndef XR_USE_TIMESPEC + SKIP("XR_KHR_convert_timespec_time test not enabled in CTS"); +#else GlobalData& globalData = GetGlobalData(); if (!globalData.IsInstanceExtensionSupported("XR_KHR_convert_timespec_time")) { SKIP(XR_KHR_CONVERT_TIMESPEC_TIME_EXTENSION_NAME " not supported"); diff --git a/src/conformance/conformance_test/test_XrCompositionLayerProjection.cpp b/src/conformance/conformance_test/test_XrCompositionLayerProjection.cpp index 54738e6a..d0116b56 100644 --- a/src/conformance/conformance_test/test_XrCompositionLayerProjection.cpp +++ b/src/conformance/conformance_test/test_XrCompositionLayerProjection.cpp @@ -70,7 +70,7 @@ namespace Conformance GlobalData& globalData = GetGlobalData(); if (!globalData.IsUsingGraphicsPlugin()) { // Nothing to check - no graphics plugin means no frame submission - return; + SKIP("Cannot test using frame submission without a graphics plugin"); } AutoBasicSession session(AutoBasicSession::beginSession | AutoBasicSession::createSpaces); diff --git a/src/conformance/conformance_test/test_XrCompositionLayerQuad.cpp b/src/conformance/conformance_test/test_XrCompositionLayerQuad.cpp index 00384567..591f4956 100644 --- a/src/conformance/conformance_test/test_XrCompositionLayerQuad.cpp +++ b/src/conformance/conformance_test/test_XrCompositionLayerQuad.cpp @@ -38,7 +38,7 @@ namespace Conformance GlobalData& globalData = GetGlobalData(); if (!globalData.IsUsingGraphicsPlugin()) { // Nothing to check - no graphics plugin means no frame submission - return; + SKIP("Cannot test using frame submission without a graphics plugin"); } AutoBasicSession session(AutoBasicSession::beginSession | AutoBasicSession::createSpaces); diff --git a/src/conformance/conformance_test/test_actions.cpp b/src/conformance/conformance_test/test_actions.cpp index fdb1132b..294bda64 100644 --- a/src/conformance/conformance_test/test_actions.cpp +++ b/src/conformance/conformance_test/test_actions.cpp @@ -1200,9 +1200,22 @@ namespace Conformance } SECTION("Active action sets") { + XrInteractionProfileState interactionProfileState{XR_TYPE_INTERACTION_PROFILE_STATE}; + REQUIRE_RESULT(xrGetCurrentInteractionProfile(compositionHelper.GetSession(), defaultDevicePath, &interactionProfileState), + XR_ERROR_ACTIONSET_NOT_ATTACHED); + compositionHelper.GetInteractionManager().AddActionSet(actionSet); compositionHelper.GetInteractionManager().AttachActionSets(); + { + INFO("Interaction profile selection changes must: only happen when flink:xrSyncActions is called."); + REQUIRE_RESULT( + xrGetCurrentInteractionProfile(compositionHelper.GetSession(), defaultDevicePath, &interactionProfileState), + XR_SUCCESS); + // per spec: "Interaction profile selection changes must: only happen when flink:xrSyncActions is called." + REQUIRE(interactionProfileState.interactionProfile == XR_NULL_PATH); + } + XrActionsSyncInfo syncInfo{XR_TYPE_ACTIONS_SYNC_INFO}; XrActiveActionSet activeActionSet{actionSet}; syncInfo.activeActionSets = &activeActionSet; diff --git a/src/conformance/conformance_test/test_multithreading.cpp b/src/conformance/conformance_test/test_multithreading.cpp index 77632e72..81c903bb 100644 --- a/src/conformance/conformance_test/test_multithreading.cpp +++ b/src/conformance/conformance_test/test_multithreading.cpp @@ -724,7 +724,7 @@ namespace Conformance SleepMs(5); XrSwapchainImageWaitInfo waitInfo{XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO}; - waitInfo.timeout = 10000000; // 10ms + waitInfo.timeout = XR_INFINITE_DURATION; XRC_CHECK_THROW_XRCMD(xrWaitSwapchainImage(swapchain, &waitInfo)); SleepMs(5); diff --git a/src/conformance/framework/CMakeLists.txt b/src/conformance/framework/CMakeLists.txt index 44ca88f6..c2311e77 100644 --- a/src/conformance/framework/CMakeLists.txt +++ b/src/conformance/framework/CMakeLists.txt @@ -73,13 +73,21 @@ if(TARGET openxr-gfxwrapper) target_link_libraries(conformance_framework PUBLIC openxr-gfxwrapper) endif() +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GCC") + target_compile_options(conformance_framework PUBLIC -Wno-missing-field-initializers) +endif() + if(WIN32) target_compile_definitions(conformance_framework PUBLIC _CRT_SECURE_NO_WARNINGS) if(MSVC) target_compile_options(conformance_framework - PUBLIC /Zc:wchar_t /Zc:forScope /W4 /WX /wd4996) + PUBLIC /Zc:wchar_t /Zc:forScope /W4 /wd4996) + if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + # If actually msvc and not clang-cl + target_compile_options(conformance_framework PRIVATE /WX) + endif() # Right now can't build this on MinGW because of directxcolors, directxmath, etc. target_link_libraries(conformance_framework PUBLIC d3d11 d3d12 d3dcompiler diff --git a/src/conformance/framework/composition_utils.cpp b/src/conformance/framework/composition_utils.cpp index 64dc6d9a..e3ea3955 100644 --- a/src/conformance/framework/composition_utils.cpp +++ b/src/conformance/framework/composition_utils.cpp @@ -343,8 +343,9 @@ namespace Conformance XRC_CHECK_THROW_XRCMD(xrAcquireSwapchainImage(swapchain, &acquireInfo, &colorImageIndex)); XrSwapchainImageWaitInfo waitInfo{XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO}; - waitInfo.timeout = 500_xrMilliseconds; // Call can block waiting for image to become available for writing. + waitInfo.timeout = XR_INFINITE_DURATION; // Call can block waiting for image to become available for writing. XRC_CHECK_THROW_XRCMD(xrWaitSwapchainImage(swapchain, &waitInfo)); + m_swapchainImages[swapchain]->AcquireAndWaitDepthSwapchainImage(colorImageIndex); std::unique_lock lock(m_mutex); diff --git a/src/conformance/framework/conformance_framework.h b/src/conformance/framework/conformance_framework.h index 7cb29a8b..8d9f96ce 100644 --- a/src/conformance/framework/conformance_framework.h +++ b/src/conformance/framework/conformance_framework.h @@ -305,6 +305,8 @@ namespace Conformance XrVersion apiVersion{XR_CURRENT_API_VERSION}; uint64_t testSuccessCount{}; uint64_t testFailureCount{}; + bool unmatchedTestSpecs{false}; + Catch::Totals totals{}; TimedSubmissionResults timedSubmission; std::vector> swapchainFormats; }; diff --git a/src/conformance/framework/graphics_plugin_opengl.cpp b/src/conformance/framework/graphics_plugin_opengl.cpp index b2d06d96..670aa0b1 100644 --- a/src/conformance/framework/graphics_plugin_opengl.cpp +++ b/src/conformance/framework/graphics_plugin_opengl.cpp @@ -43,12 +43,12 @@ // XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT: can be bound to a framebuffer as color // XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT: can be bound to a framebuffer as depth (or stencil-only GL_STENCIL_INDEX8) // XR_SWAPCHAIN_USAGE_UNORDERED_ACCESS_BIT: image load/store and core since 4.2. -// List of supported formats is in https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_shader_image_load_store.txt +// List of supported formats is in https://registry.khronos.org/OpenGL/extensions/ARB/ARB_shader_image_load_store.txt // XR_SWAPCHAIN_USAGE_TRANSFER_SRC_BIT & XR_SWAPCHAIN_USAGE_TRANSFER_DST_BIT: must be compatible format with glCopyTexImage* calls // XR_SWAPCHAIN_USAGE_SAMPLED_BIT: can be sampled in a shader // XR_SWAPCHAIN_USAGE_MUTABLE_FORMAT_BIT: all GL formats are typed, but some can be reinterpreted with a different view. // OpenGL 4.2 / 4.3 with MSAA. Only for color formats and compressed ones -// List with compatible textures: https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_texture_view.txt +// List with compatible textures: https://registry.khronos.org/OpenGL/extensions/ARB/ARB_texture_view.txt // Note: no GL formats are "mutableFormats" in the sense of SwapchainCreateTestParameters as this is intended for TYPELESS, // however, some are "supportsMutableFormat" diff --git a/src/conformance/framework/input_testinputdevice.cpp b/src/conformance/framework/input_testinputdevice.cpp index 53f34765..85084ab5 100644 --- a/src/conformance/framework/input_testinputdevice.cpp +++ b/src/conformance/framework/input_testinputdevice.cpp @@ -182,7 +182,10 @@ namespace Conformance // Checks the isActive on a boolean action to determine if a controller is on auto findController = [&]() -> ControllerState { - XrActiveActionSet activeActionSets[] = {{m_actionSet}, {detectionActionSet}}; + XrActiveActionSet activeActionSets[] = { + {m_actionSet, XR_NULL_PATH}, + {detectionActionSet, XR_NULL_PATH}, + }; XrActionsSyncInfo syncInfo{XR_TYPE_ACTIONS_SYNC_INFO}; syncInfo.countActiveActionSets = detectionActionSet == XR_NULL_HANDLE ? 1 : 2; syncInfo.activeActionSets = activeActionSets; @@ -342,7 +345,10 @@ namespace Conformance XrAction actionToDetect = m_actionMap.at(button); auto GetButtonState = [&](XrAction action) -> bool { - XrActiveActionSet activeActionSet[] = {{m_actionSet}, {extraActionSet}}; + XrActiveActionSet activeActionSet[] = { + {m_actionSet, XR_NULL_PATH}, + {extraActionSet, XR_NULL_PATH}, + }; XrActionsSyncInfo syncInfo{XR_TYPE_ACTIONS_SYNC_INFO}; syncInfo.countActiveActionSets = extraActionSet == XR_NULL_HANDLE ? 1 : 2; syncInfo.activeActionSets = activeActionSet; @@ -404,7 +410,10 @@ namespace Conformance XrAction actionToDetect = m_actionMap.at(button); auto FloatStateWithinEpsilon = [&](XrAction action, float target, float epsilon) -> bool { - XrActiveActionSet activeActionSet[] = {{m_actionSet}, {extraActionSet}}; + XrActiveActionSet activeActionSet[] = { + {m_actionSet, XR_NULL_PATH}, + {extraActionSet, XR_NULL_PATH}, + }; XrActionsSyncInfo syncInfo{XR_TYPE_ACTIONS_SYNC_INFO}; syncInfo.countActiveActionSets = extraActionSet == XR_NULL_HANDLE ? 1 : 2; syncInfo.activeActionSets = activeActionSet; @@ -470,7 +479,10 @@ namespace Conformance XrAction actionToDetect = m_actionMap.at(button); auto VectorStateWithinEpsilon = [&](XrAction action, XrVector2f target, float epsilon) -> bool { - XrActiveActionSet activeActionSet[] = {{m_actionSet}, {extraActionSet}}; + XrActiveActionSet activeActionSet[] = { + {m_actionSet, XR_NULL_PATH}, + {extraActionSet, XR_NULL_PATH}, + }; XrActionsSyncInfo syncInfo{XR_TYPE_ACTIONS_SYNC_INFO}; syncInfo.countActiveActionSets = extraActionSet == XR_NULL_HANDLE ? 1 : 2; syncInfo.activeActionSets = activeActionSet; diff --git a/src/conformance/framework/input_testinputdevice.h b/src/conformance/framework/input_testinputdevice.h index c42ea52d..c7ad1378 100644 --- a/src/conformance/framework/input_testinputdevice.h +++ b/src/conformance/framework/input_testinputdevice.h @@ -31,7 +31,7 @@ namespace Conformance { const char* Path; XrActionType Type; - bool systemOnly; + bool systemOnly = false; }; using InputSourcePathCollection = std::initializer_list; diff --git a/src/conformance/framework/swapchain_image_data.cpp b/src/conformance/framework/swapchain_image_data.cpp index c1631dd2..d7d8eff9 100644 --- a/src/conformance/framework/swapchain_image_data.cpp +++ b/src/conformance/framework/swapchain_image_data.cpp @@ -31,9 +31,8 @@ namespace Conformance XRC_CHECK_THROW_XRCMD(xrAcquireSwapchainImage(m_depthSwapchain, &acquireInfo, &depthImageIndex)); XrSwapchainImageWaitInfo waitInfo{XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO}; - waitInfo.timeout = 500_xrMilliseconds; // Call can block waiting for image to become available for writing. + waitInfo.timeout = XR_INFINITE_DURATION; // Call can block waiting for image to become available for writing. XRC_CHECK_THROW_XRCMD(xrWaitSwapchainImage(m_depthSwapchain, &waitInfo)); - m_colorToAcquiredDepthIndices.emplace_back(colorImageIndex, depthImageIndex); } diff --git a/src/conformance/platform_specific/android_main.cpp b/src/conformance/platform_specific/android_main.cpp index 3fb36f29..734ac48b 100644 --- a/src/conformance/platform_specific/android_main.cpp +++ b/src/conformance/platform_specific/android_main.cpp @@ -308,10 +308,10 @@ void android_main(struct android_app* app) launchSettings.argv = args.data(); launchSettings.message = OnTestMessage; - uint32_t failureCount = 0; - XrcResult result = xrcRunConformanceTests(&launchSettings, &failureCount); - const int exitResult = result == XRC_SUCCESS ? 0 : (int)std::max((uint32_t)1, failureCount); - ALOGV("Exit Result %d", exitResult); + XrcTestResult testResult; + uint64_t failureCount = 0; + XrcResult result = xrcRunConformanceTests(&launchSettings, &testResult, &failureCount); + ALOGV("Execution result %d, Test result %d (%" PRIu64 " failures)", result, testResult, failureCount); // Clean up conformance test xrcCleanup(); diff --git a/src/loader/CMakeLists.txt b/src/loader/CMakeLists.txt index a98d3808..c6905f0b 100644 --- a/src/loader/CMakeLists.txt +++ b/src/loader/CMakeLists.txt @@ -261,14 +261,20 @@ if(DYNAMIC_LOADER AND BUILD_CONFORMANCE_CLI) endif() if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang") + if(NOT MSVC) + # Do not do this for clang-cl + target_compile_options( + openxr_loader + PRIVATE "$<$:-fno-rtti>" + -ffunction-sections + -fdata-sections + ) + endif() target_compile_options( openxr_loader PRIVATE -Wextra -fno-strict-aliasing -fno-builtin-memcmp - "$<$:-fno-rtti>" - -ffunction-sections - -fdata-sections ) # Make build depend on the version script/export map target_sources(openxr_loader PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/openxr-loader.map) diff --git a/src/loader/api_layer_interface.cpp b/src/loader/api_layer_interface.cpp index 5560c31a..c9e24ec4 100644 --- a/src/loader/api_layer_interface.cpp +++ b/src/loader/api_layer_interface.cpp @@ -237,21 +237,23 @@ XrResult ApiLayerInterface::LoadApiLayers(const std::string& openxr_command, uin for (const auto& layer_name : enabled_explicit_api_layer_names) { bool found_this_layer = false; - for (auto it = explicit_layer_manifest_files.begin(); it != explicit_layer_manifest_files.end();) { - bool erased_layer_manifest_file = false; - - if (layers_already_found.count(layer_name) > 0) { - found_this_layer = true; - } else if (layer_name == (*it)->LayerName()) { - found_this_layer = true; - layers_already_found.insert(layer_name); - enabled_layer_manifest_files_in_init_order.push_back(std::move(*it)); - it = explicit_layer_manifest_files.erase(it); - erased_layer_manifest_file = true; - } + if (layers_already_found.count(layer_name) > 0) { + found_this_layer = true; + } else { + for (auto it = explicit_layer_manifest_files.begin(); it != explicit_layer_manifest_files.end();) { + bool erased_layer_manifest_file = false; + + if (layer_name == (*it)->LayerName()) { + found_this_layer = true; + layers_already_found.insert(layer_name); + enabled_layer_manifest_files_in_init_order.push_back(std::move(*it)); + it = explicit_layer_manifest_files.erase(it); + erased_layer_manifest_file = true; + } - if (!erased_layer_manifest_file) { - it++; + if (!erased_layer_manifest_file) { + it++; + } } } diff --git a/src/loader/images/high_level_loader_black.png b/src/loader/images/high_level_loader_black.png deleted file mode 100644 index c4479bb9..00000000 Binary files a/src/loader/images/high_level_loader_black.png and /dev/null differ diff --git a/src/loader/images/high_level_loader_black.png.license b/src/loader/images/high_level_loader_black.png.license deleted file mode 100644 index c57cf3ca..00000000 --- a/src/loader/images/high_level_loader_black.png.license +++ /dev/null @@ -1,3 +0,0 @@ -Copyright 2017, Lunar-G - -SPDX-License-Identifier: CC-BY-4.0 diff --git a/src/loader/images/instance_call_chain_black.png b/src/loader/images/instance_call_chain_black.png deleted file mode 100644 index 39ff943a..00000000 Binary files a/src/loader/images/instance_call_chain_black.png and /dev/null differ diff --git a/src/loader/images/instance_call_chain_black.png.license b/src/loader/images/instance_call_chain_black.png.license deleted file mode 100644 index c57cf3ca..00000000 --- a/src/loader/images/instance_call_chain_black.png.license +++ /dev/null @@ -1,3 +0,0 @@ -Copyright 2017, Lunar-G - -SPDX-License-Identifier: CC-BY-4.0 diff --git a/src/loader/images/loader_layer_order_calls_black.png b/src/loader/images/loader_layer_order_calls_black.png deleted file mode 100644 index b577142e..00000000 Binary files a/src/loader/images/loader_layer_order_calls_black.png and /dev/null differ diff --git a/src/loader/images/loader_layer_order_calls_black.png.license b/src/loader/images/loader_layer_order_calls_black.png.license deleted file mode 100644 index c57cf3ca..00000000 --- a/src/loader/images/loader_layer_order_calls_black.png.license +++ /dev/null @@ -1,3 +0,0 @@ -Copyright 2017, Lunar-G - -SPDX-License-Identifier: CC-BY-4.0 diff --git a/src/scripts/validation_layer_generator.py b/src/scripts/validation_layer_generator.py index c037afcc..edd8384d 100644 --- a/src/scripts/validation_layer_generator.py +++ b/src/scripts/validation_layer_generator.py @@ -106,6 +106,9 @@ def beginFile(self, genOpts): preamble += '#include \n' preamble += '#include \n' preamble += '\n' + preamble += '#ifdef __clang__\n' + preamble += '#pragma GCC diagnostic ignored "-Wunused-parameter"\n' + preamble += '#endif\n' write(preamble, file=self.outFile) # Write out all the information for the appropriate file, @@ -834,6 +837,10 @@ def writeVerifyExtensions(self): else: verify_extensions += self.writeIndent(indent) verify_extensions += '// No system extensions to check dependencies for\n' + for arg in ('gen_instance_info', 'command', 'struct_name', 'objects_info', 'extensions'): + verify_extensions += self.writeIndent(indent) + verify_extensions += f'(void){arg};\n' + verify_extensions += self.writeIndent(indent) verify_extensions += 'return true;\n' verify_extensions += '}\n\n' @@ -1874,7 +1881,11 @@ def writeValidateStructFuncs(self): struct_check += ' const %s* value) {\n' % xr_struct.name setup_bail = False struct_check += ' XrResult xr_result = XR_SUCCESS;\n' + struct_check += ' (void)xr_result;\n' + for arg in ('instance_info', 'command_name', 'objects_info', 'check_members', 'value'): + struct_check += self.writeIndent(indent) + struct_check += f'(void){arg};\n' # Check to see if this struct is the base of a relation group relation_group = self.getRelationGroupForBaseStruct(xr_struct.name) if relation_group is not None: