diff --git a/.gitattributes b/.gitattributes index fa2e3ac3..006bcaff 100644 --- a/.gitattributes +++ b/.gitattributes @@ -25,8 +25,9 @@ *.png binary # git-lfs big files -*.pdf filter=lfs diff=lfs merge=lfs -text -*.glb filter=lfs diff=lfs merge=lfs -text +*.pdf filter=lfs diff=lfs merge=lfs -text +*.glb filter=lfs diff=lfs merge=lfs -text +src/conformance/**/*.png filter=lfs diff=lfs merge=lfs -text # Shell/python scripts that don't end in .sh specification/makeAllExts eol=lf diff --git a/.gitignore b/.gitignore index 2e8bce12..64c5b37d 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ generated-includes cmake_install.cmake cmake_uninstall.cmake cmake-build-* +specification/vendor/bundle/ # Marker file for a snapshot build SNAPSHOT diff --git a/CHANGELOG.CTS.md b/CHANGELOG.CTS.md index 6a354453..292b985a 100644 --- a/CHANGELOG.CTS.md +++ b/CHANGELOG.CTS.md @@ -17,6 +17,51 @@ 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.1.37.0 (2024-06-06) + +- Conformance Tests + - Fix: Action test when constrained to right-hand only. + ([internal MR 3244](https://gitlab.khronos.org/openxr/openxr/merge_requests/3244)) + - Fix: Skip StereoWithFoveatedInset-interactive if runtime does not support it. + ([internal MR 3350](https://gitlab.khronos.org/openxr/openxr/merge_requests/3350)) + - Fix: Dangling pointer in action test. + ([internal MR 3357](https://gitlab.khronos.org/openxr/openxr/merge_requests/3357)) + - Fix: Resolve Vulkan validation warning in some tests. + ([internal MR 3367](https://gitlab.khronos.org/openxr/openxr/merge_requests/3367)) + - Improvement: Use absolute epsilons for pose comparison in action test. + ([internal MR 3244](https://gitlab.khronos.org/openxr/openxr/merge_requests/3244)) + - Improvement: Improve user message in action test. + ([internal MR 3244](https://gitlab.khronos.org/openxr/openxr/merge_requests/3244)) + - Improvement: Only suggest binding the proceed action to ".../click" binding + paths, rather than all binding paths of boolean type, to avoid accidental + activation. + ([internal MR 3312](https://gitlab.khronos.org/openxr/openxr/merge_requests/3312)) + - Improvement: Support hiding parts of models in the glTF/PBR subsystem. + ([internal MR 3314](https://gitlab.khronos.org/openxr/openxr/merge_requests/3314)) + - Improvement: Code cleanup. + ([internal MR 3323](https://gitlab.khronos.org/openxr/openxr/merge_requests/3323)) + - Improvement: Use new `XR_API_VERSION_1_0` and `XR_API_VERSION_1_1` defines. + ([internal MR 3329](https://gitlab.khronos.org/openxr/openxr/merge_requests/3329)) + - Improvement: Relax too strict palm/grip_surface pose assumptions. + ([internal MR 3345](https://gitlab.khronos.org/openxr/openxr/merge_requests/3345)) + - Improvement: Add missing extension name tags to test cases. + ([internal MR 3355](https://gitlab.khronos.org/openxr/openxr/merge_requests/3355)) + - Improvement: Code cleanup and documentation in helper utilities. + ([internal MR 3356](https://gitlab.khronos.org/openxr/openxr/merge_requests/3356)) + - Improvement: Code cleanups and clang-tidy fixes. + ([internal MR 3357](https://gitlab.khronos.org/openxr/openxr/merge_requests/3357), + [internal MR 3357](https://gitlab.khronos.org/openxr/openxr/merge_requests/3357)) + - Improvement: Improve readability of test sources. + ([internal MR 3358](https://gitlab.khronos.org/openxr/openxr/merge_requests/3358)) + - Improvement: Fix duplicated inaccurate code comment. + ([internal MR 3372](https://gitlab.khronos.org/openxr/openxr/merge_requests/3372)) + - New test: Add interactive tests for `XR_KHR_composition_layer_equirect` and + `XR_KHR_composition_layer_equirect2`. + ([internal MR 2882](https://gitlab.khronos.org/openxr/openxr/merge_requests/2882)) + - New test: Add test which creates a session but does not call + `xrDestroySession`. + ([internal MR 3247](https://gitlab.khronos.org/openxr/openxr/merge_requests/3247)) + ## OpenXR CTS 1.1.36.0 (2024-04-25) This new release supports testing both OpenXR 1.0 and OpenXR 1.1 runtimes, and diff --git a/maintainer-scripts/common.sh b/maintainer-scripts/common.sh index c406ad29..2f2a036c 100644 --- a/maintainer-scripts/common.sh +++ b/maintainer-scripts/common.sh @@ -154,6 +154,7 @@ getSDKSourceFilenames() { checkCodespell \ CMakeLists.txt \ format_file.sh \ + open-in-docker.sh \ LICENSE \ openxr-codespell.exclude \ runClangFormat.sh \ diff --git a/specification/Makefile b/specification/Makefile index 226be4b7..f9608ca7 100644 --- a/specification/Makefile +++ b/specification/Makefile @@ -7,7 +7,13 @@ SHELL = /usr/bin/env bash QUIET ?= @ VERYQUIET ?= @ PYTHON ?= python3 +ifneq (,$(strip $(BUNDLER))) +ASCIIDOC ?= bundle exec asciidoctor --trace +HEXAPDF ?= bundle exec hexapdf +else ASCIIDOC ?= asciidoctor +HEXAPDF ?= hexapdf +endif RM = rm -f RMRF = rm -rf MKDIR = mkdir -p @@ -33,7 +39,7 @@ endif VERSIONS := XR_VERSION_1_0 XR_VERSION_1_1 XR_LOADER_VERSION_1_0 VERSIONOPTIONS := $(foreach version,$(VERSIONS),-feature $(version)) -SPECREVISION = 1.1.36 +SPECREVISION = 1.1.37 REVISION_COMPONENTS = $(subst ., ,$(SPECREVISION)) MAJORMINORVER = $(word 1,$(REVISION_COMPONENTS)).$(word 2,$(REVISION_COMPONENTS)) @@ -343,7 +349,7 @@ pdfA4: $(PDFA4SPEC) ASCIIDOCTOR_TARGETS += $(PDFSPEC) $(PDFA4SPEC) # Target-specific variables and deps customizing the AsciiDoctor rule -$(PDFSPEC) $(PDFA4SPEC): BACKEND_ARGS=--backend pdf --require asciidoctor-pdf -a compress -r ./scripts/pdf-index-customizer.rb +$(PDFSPEC) $(PDFA4SPEC): BACKEND_ARGS=--backend pdf --require asciidoctor-pdf -a compress --require ./scripts/pdf-index-customizer.rb $(PDFSPEC): PAGESIZE=LETTER $(PDFA4SPEC): PAGESIZE=A4 $(PDFSPEC) $(PDFA4SPEC): $(COMMONDOCS) @@ -647,14 +653,17 @@ $(REGISTRYOUTDIR)/headers/openxr: | $(REGISTRYOUTDIR)/headers $(QUIET)$(MKDIR) "$@" release-htmlpdf: html pdf $(REGISTRYOUTDIR)/pdf $(REGISTRYOUTDIR)/html - -asciidoctor-pdf-optimize $(PDFSPEC) - $(QUIET)$(CP) $(PDFSPEC) $(REGISTRYOUTDIR)/pdf/$(SPEC_FILENAME_STEM).pdf + $(ECHO) "[hexapdf] $(call MAKE_RELATIVE,$(REGISTRYOUTDIR)/pdf/$(SPEC_FILENAME_STEM).pdf)" + $(QUIET)$(HEXAPDF) optimize --force $(PDFSPEC) $(REGISTRYOUTDIR)/pdf/$(SPEC_FILENAME_STEM).pdf + $(ECHO) "[cp] $(call MAKE_RELATIVE,$(REGISTRYOUTDIR)/html/$(SPEC_FILENAME_STEM).html)" $(QUIET)$(CP) $(HTMLSPEC) $(REGISTRYOUTDIR)/html/$(SPEC_FILENAME_STEM).html .PHONY: release-htmlpdf release: release-htmlpdf manhtmlpages loader styleguide extprocess | $(REGISTRYOUTDIR) $(REGISTRYOUTDIR)/man + $(ECHO) "[cp] $(call MAKE_RELATIVE,$(REGISTRYOUTDIR))/{styleguide,extprocess,loader}.html" $(QUIET)$(CP) $(OUTDIR)/styleguide.html $(OUTDIR)/extprocess.html $(OUTDIR)/loader.html $(REGISTRYOUTDIR) + $(ECHO) "[cp] $(REGISTRYOUTDIR)/man/html" $(QUIET)$(CP) -R $(MANHTMLDIR) $(REGISTRYOUTDIR)/man/html .PHONY: release diff --git a/specification/README.md b/specification/README.md index 8cd8479c..dac636c8 100644 --- a/specification/README.md +++ b/specification/README.md @@ -8,10 +8,13 @@ SPDX-License-Identifier: CC-BY-4.0 Before building the specification, install the necessary prerequisite tools as described later in this document. -You may instead choose to use the `open-in-docker.sh` script, -which will mount the repository in a container built from an image with all the necessary tools installed. -See that script for more details. -You can find the associated Dockerfile at https://github.com/KhronosGroup/DockerContainers/blob/master/Dockerfile.openxr +You may instead choose to use the `open-in-docker.sh` script, available in the +OpenXR-Docs (spec source) and OpenXR-SDK-Source (SDK, for loader docs build) +repo as well as the internal monorepo. This script which will mount the +repository in a container built from an image with all the necessary tools +installed: these are regularly pushed to Docker Hub. See that script for more +details. You can find the associated Dockerfile at + ## Conditional Inclusion of Extensions @@ -91,6 +94,10 @@ specified by the Makefile variable `$(OUTDIR)` (by default, It is recommended to build these targets using a "helper" script from above, unless you want to only build the core spec without any extensions. +Most of these are not supported if building in `OpenXR-SDK-Source`: this +repository is mostly software, and the only contained AsciiDoc content is the +loader design document source. + These targets are currently supported. * API spec: @@ -119,6 +126,10 @@ For example: make styleguide ``` +Many of these are not supported if building in `OpenXR-SDK-Source`: this +repository is mostly software, and the only contained AsciiDoc content is the +loader design document source. + * "OpenXR Documentation and Extensions" guide (aka Style Guide): * `styleguide` - Single-file HTML5 in `$(OUTDIR)/styleguide.html` * OpenXR Extensions Process guide: @@ -219,7 +230,8 @@ parts you don't use) completely before trying to install. > Asciidoctor-pdf versions before `1.5.0.alpha15` have issues with multi-page valid usage blocks, in that the background only renders for the first page. `alpha.15` fixes this issue (as well as a few others); do not use prior -versions. +versions. On the other hand, versions later than 1.6.2 break our index +customization script, so 1.6.2 is ideal. Only the `asciidoctor` and `rouge` gems (and their dependencies) are needed if you do not intend to build PDF versions of the spec and supporting documents. @@ -230,6 +242,14 @@ people submitting MRs with substantial changes to the Specification are responsible for verifying that their branches build *both* `html` and `pdf` targets. +The easiest way to get the right version of these gems is to use "bundler": + +```sh +bundle install +``` + +Then, pass `BUNDLER=1` to each `make` (or wrapper script) invocation. + ### Platforms and System Packages #### Windows diff --git a/specification/registry/xr.xml b/specification/registry/xr.xml index c552f0b5..bc61f4b8 100644 --- a/specification/registry/xr.xml +++ b/specification/registry/xr.xml @@ -58,6 +58,7 @@ maintained in the default branch of the Khronos OpenXR GitHub project. + @@ -133,7 +134,16 @@ 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, 1, 36) +#define XR_CURRENT_API_VERSION XR_MAKE_VERSION(1, 1, 37) + + + // OpenXR 1.0 version number +#define XR_API_VERSION_1_0 XR_MAKE_VERSION(1, 0, XR_VERSION_PATCH(XR_CURRENT_API_VERSION)) + + // OpenXR 1.1 version number +#define XR_API_VERSION_1_1 XR_MAKE_VERSION(1, 1, XR_VERSION_PATCH(XR_CURRENT_API_VERSION)) @@ -8627,7 +8642,7 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( - + @@ -10612,6 +10627,12 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( + + + + + + @@ -15214,6 +15235,55 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/specification/scripts/extensionmetadocgenerator.py b/specification/scripts/extensionmetadocgenerator.py index fa034ed7..64006560 100644 --- a/specification/scripts/extensionmetadocgenerator.py +++ b/specification/scripts/extensionmetadocgenerator.py @@ -154,6 +154,7 @@ def specLink(self, xrefName, xrefText, isRefpage = False): def conditionalLinkCoreAPI(self, apiVersion, linkSuffix, isRefpage): versionMatch = re.match(f"{self.conventions.api_version_prefix}(\\d+)_(\\d+)", apiVersion) + assert versionMatch major = versionMatch.group(1) minor = versionMatch.group(2) @@ -295,9 +296,7 @@ def makeMetafile(self, extensions, SPV_deps, isRefpage = False): separator = '' else: separator = '+' - write(separator + '\n--\n' + - dependencyMarkup(self.depends) + - '--', file=fp) + write(f"{separator}\n--\n{dependencyMarkup(self.depends)}--", file=fp) else: # Do not specify the base API redundantly, but put something # here to avoid formatting trouble. @@ -328,10 +327,7 @@ def versionKey(name): names = sorted(sorted(interacts), key=versionKey) for name in names: - if "_VERSION_" in name: - write(f"* Interacts with {self.conventions.formatVersion(name)}", file=fp) - else: - write(f"* Interacts with {self.conventions.formatExtension(name)}", file=fp) + write(f"* Interacts with {self.conventions.formatVersionOrExtension(name)}", file=fp) if self.name in SPV_deps: self.writeTag('SPIR-V Dependencies', None, isRefpage, fp) diff --git a/specification/scripts/parse_dependency.py b/specification/scripts/parse_dependency.py index 08f04875..b4b05550 100755 --- a/specification/scripts/parse_dependency.py +++ b/specification/scripts/parse_dependency.py @@ -126,7 +126,7 @@ def push_first(toks): exprStack.append(toks[0]) # An identifier (version or extension name) -dependencyIdent = Word(alphanums + '_') +dependencyIdent = Word(f"{alphanums}_") # Infix expression for depends expressions dependencyExpr = pp.infixNotation(dependencyIdent, @@ -303,7 +303,7 @@ def markupTraverse(expr, level = 0, root = True): - root - True only on initial call""" if level > 0: - prefix = '{nbsp}{nbsp}' * level * 2 + ' ' + prefix = f"{'{nbsp}{nbsp}' * level * 2} " else: prefix = '' str = '' @@ -318,9 +318,9 @@ def markupTraverse(expr, level = 0, root = True): str = str + markupTraverse(elem, level = nextlevel, root = False) elif elem in ('+', ','): - str = str + f'{prefix}{opMarkupAsciidoc(elem)} +\n' + str = f"{str}{prefix}{opMarkupAsciidoc(elem)} +\n" else: - str = str + f'{prefix}{leafMarkupAsciidoc(elem)} +\n' + str = f"{str}{prefix}{leafMarkupAsciidoc(elem)} +\n" return str diff --git a/specification/scripts/reg.py b/specification/scripts/reg.py index bb3ec774..801b2f5d 100755 --- a/specification/scripts/reg.py +++ b/specification/scripts/reg.py @@ -771,7 +771,7 @@ def parseTree(self): stage_flag = enum.get('alias') featureName = elem.get('depends') if elem.get('depends') is not None else featureInfo.name if stage_flag in sync_pipeline_stage_condition: - sync_pipeline_stage_condition[stage_flag] += "," + featureName + sync_pipeline_stage_condition[stage_flag] += f",{featureName}" else: sync_pipeline_stage_condition[stage_flag] = featureName elif groupName == "VkAccessFlagBits2": @@ -780,7 +780,7 @@ def parseTree(self): access_flag = enum.get('alias') featureName = elem.get('depends') if elem.get('depends') is not None else featureInfo.name if access_flag in sync_access_condition: - sync_access_condition[access_flag] += "," + featureName + sync_access_condition[access_flag] += f",{featureName}" else: sync_access_condition[access_flag] = featureName diff --git a/specification/scripts/update_version.py b/specification/scripts/update_version.py index f2340538..42373745 100755 --- a/specification/scripts/update_version.py +++ b/specification/scripts/update_version.py @@ -31,13 +31,10 @@ print('Replacing version lines in the registry') for line in fileinput.input('registry/xr.xml', inplace=True): printed = False - if 'XR_CURRENT_API_VERSION' in line: + if 'XR_CURRENT_API_VERSION' in line: if 'XR_MAKE_VERSION' in line: printed = True print('#define XR_CURRENT_API_VERSION XR_MAKE_VERSION(%s, %s, %s)' % spec_version) - if 'type name' in line: - printed = True - print(' ') if not printed: print(f"{line}", end='') diff --git a/specification/scripts/xml_consistency.py b/specification/scripts/xml_consistency.py index e1915c99..3cf7b4a7 100755 --- a/specification/scripts/xml_consistency.py +++ b/specification/scripts/xml_consistency.py @@ -242,7 +242,7 @@ def check_enum_naming(self, enum_type): stripped_enum_type = stripped_enum_type.replace("FlagBits", "") # This is how we apply the "convert type to enum value name" transform: pretend it's a structure type, # then replace "XR_TYPE_" with the generic prefix "XR_" - start = self.conventions.generate_structure_type_from_name(stripped_enum_type).replace("XR_TYPE", "XR") + "_" + start = f"{self.conventions.generate_structure_type_from_name(stripped_enum_type).replace('XR_TYPE', 'XR')}_" value_names = get_enum_value_names(self.db.registry, enum_type) diff --git a/src/common/platform_utils.hpp b/src/common/platform_utils.hpp index 42570cd1..d047c17d 100644 --- a/src/common/platform_utils.hpp +++ b/src/common/platform_utils.hpp @@ -325,6 +325,8 @@ static inline std::string PlatformUtilsGetSecureEnv(const char* name) { #elif defined(XR_OS_ANDROID) +#include + static inline bool PlatformUtilsGetEnvSet(const char* /* name */) { // Stub func return false; @@ -355,6 +357,37 @@ static inline bool PlatformGetGlobalRuntimeFileName(uint16_t major_version, std: return false; } + +// Android system properties are sufficiently different from environment variables that we are not re-using +// PlatformUtilsGetEnv for this purpose +static inline std::string PlatformUtilsGetAndroidSystemProperty(const char* name) { + std::string result; + const prop_info* pi = __system_property_find(name); + if (pi == nullptr) { + return {}; + } + +#if __ANDROID_API__ >= 26 + // use callback to retrieve > 92 character sys prop values, if available + __system_property_read_callback( + pi, + [](void* cookie, const char*, const char* value, uint32_t) { + auto property_value = reinterpret_cast(cookie); + *property_value = value; + }, + reinterpret_cast(&result)); +#endif // __ANDROID_API__ >= 26 + // fallback to __system_property_get if no value retrieved via callback + if (result.empty()) { + char value[PROP_VALUE_MAX] = {}; + if (__system_property_get(name, value) != 0) { + result = value; + } + } + + return result; +} + #else // Not Linux, Apple, nor Windows static inline bool PlatformUtilsGetEnvSet(const char* /* name */) { diff --git a/src/conformance/build.gradle b/src/conformance/build.gradle index f7b17424..6897d202 100644 --- a/src/conformance/build.gradle +++ b/src/conformance/build.gradle @@ -22,9 +22,15 @@ task copyAssets(type: Copy) { include '*.otf' } + // immersive images + from('conformance_test/composition_assets') { + include '*.png' + } + // sample images from('conformance_test/composition_examples') { include '*.png' + include '*.jpg' } from('conformance_test/gltf_examples') { include '*.png' diff --git a/src/conformance/conformance_test/CMakeLists.txt b/src/conformance/conformance_test/CMakeLists.txt index 8a8cbabf..b8c257e9 100644 --- a/src/conformance/conformance_test/CMakeLists.txt +++ b/src/conformance/conformance_test/CMakeLists.txt @@ -30,6 +30,8 @@ file( file( GLOB ASSETS + "composition_assets/*.png" + "composition_examples/*.jpg" "composition_examples/*.png" "SourceCodePro-Regular.otf" "gltf_examples/*.glb" diff --git a/src/conformance/conformance_test/composition_assets/equirect_8k.png b/src/conformance/conformance_test/composition_assets/equirect_8k.png new file mode 100644 index 00000000..89fbaa7b --- /dev/null +++ b/src/conformance/conformance_test/composition_assets/equirect_8k.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:879d5f6d2a6d41a775f6e9fee34a43575ada147f272987abdac8c5f54873054e +size 1876865 diff --git a/src/conformance/conformance_test/composition_assets/equirect_8k.png.license b/src/conformance/conformance_test/composition_assets/equirect_8k.png.license new file mode 100644 index 00000000..cca73d04 --- /dev/null +++ b/src/conformance/conformance_test/composition_assets/equirect_8k.png.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2019-2024, The Khronos Group Inc. + +SPDX-License-Identifier: Apache-2.0 diff --git a/src/conformance/conformance_test/composition_assets/equirect_central_90.png b/src/conformance/conformance_test/composition_assets/equirect_central_90.png new file mode 100644 index 00000000..b21599cd --- /dev/null +++ b/src/conformance/conformance_test/composition_assets/equirect_central_90.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:505d2c7e0b96b7f9491b27f37b852dc7002248f021b264c1c2def04e01229c60 +size 343176 diff --git a/src/conformance/conformance_test/composition_assets/equirect_central_90.png.license b/src/conformance/conformance_test/composition_assets/equirect_central_90.png.license new file mode 100644 index 00000000..cca73d04 --- /dev/null +++ b/src/conformance/conformance_test/composition_assets/equirect_central_90.png.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2019-2024, The Khronos Group Inc. + +SPDX-License-Identifier: Apache-2.0 diff --git a/src/conformance/conformance_test/composition_assets/equirect_central_90_from_equirect_8k.pto b/src/conformance/conformance_test/composition_assets/equirect_central_90_from_equirect_8k.pto new file mode 100644 index 00000000..829df265 --- /dev/null +++ b/src/conformance/conformance_test/composition_assets/equirect_central_90_from_equirect_8k.pto @@ -0,0 +1,54 @@ +# hugin project file +#hugin_ptoversion 2 +p f2 w2048 h2048 v90 k0 E0 R0 n"TIFF_m c:LZW r:CROP" +m i0 + +# image lines +#-hugin cropFactor=1 +i w8192 h4096 f4 v360 Ra0 Rb0 Rc0 Rd0 Re0 Eev0 Er1 Eb1 r0 p0 y0 TrX0 TrY0 TrZ0 Tpy0 Tpp0 j0 a0 b0 c0 d0 e0 g0 t0 Va1 Vb0 Vc0 Vd0 Vx0 Vy0 Vm5 n"equirect_8k.png" + + +# specify variables that should be optimized +v Ra0 +v Rb0 +v Rc0 +v Rd0 +v Re0 +v Vb0 +v Vc0 +v Vd0 +v + + +# control points + +#hugin_optimizeReferenceImage 0 +#hugin_blender enblend +#hugin_remapper nona +#hugin_enblendOptions +#hugin_enfuseOptions +#hugin_hdrmergeOptions -m avg -c +#hugin_verdandiOptions +#hugin_edgeFillMode 0 +#hugin_edgeFillKeepInput false +#hugin_outputLDRBlended true +#hugin_outputLDRLayers false +#hugin_outputLDRExposureRemapped false +#hugin_outputLDRExposureLayers false +#hugin_outputLDRExposureBlended false +#hugin_outputLDRStacks false +#hugin_outputLDRExposureLayersFused false +#hugin_outputHDRBlended false +#hugin_outputHDRLayers false +#hugin_outputHDRStacks false +#hugin_outputLayersCompression LZW +#hugin_outputImageType png +#hugin_outputImageTypeCompression LZW +#hugin_outputJPEGQuality 90 +#hugin_outputImageTypeHDR exr +#hugin_outputImageTypeHDRCompression LZW +#hugin_outputStacksMinOverlap 0.7 +#hugin_outputLayersExposureDiff 0.5 +#hugin_outputRangeCompression 0 +#hugin_optimizerMasterSwitch 1 +#hugin_optimizerPhotoMasterSwitch 21 diff --git a/src/conformance/conformance_test/composition_assets/equirect_central_90_from_equirect_8k.pto.license b/src/conformance/conformance_test/composition_assets/equirect_central_90_from_equirect_8k.pto.license new file mode 100644 index 00000000..cca73d04 --- /dev/null +++ b/src/conformance/conformance_test/composition_assets/equirect_central_90_from_equirect_8k.pto.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2019-2024, The Khronos Group Inc. + +SPDX-License-Identifier: Apache-2.0 diff --git a/src/conformance/conformance_test/composition_assets/test_cube.blend b/src/conformance/conformance_test/composition_assets/test_cube.blend new file mode 100644 index 00000000..bc6d966b Binary files /dev/null and b/src/conformance/conformance_test/composition_assets/test_cube.blend differ diff --git a/src/conformance/conformance_test/composition_assets/test_cube.blend.license b/src/conformance/conformance_test/composition_assets/test_cube.blend.license new file mode 100644 index 00000000..cca73d04 --- /dev/null +++ b/src/conformance/conformance_test/composition_assets/test_cube.blend.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2019-2024, The Khronos Group Inc. + +SPDX-License-Identifier: Apache-2.0 diff --git a/src/conformance/conformance_test/composition_examples/equirect_central_90.jpg b/src/conformance/conformance_test/composition_examples/equirect_central_90.jpg new file mode 100644 index 00000000..fae1142e Binary files /dev/null and b/src/conformance/conformance_test/composition_examples/equirect_central_90.jpg differ diff --git a/src/conformance/conformance_test/composition_examples/equirect_central_90.jpg.license b/src/conformance/conformance_test/composition_examples/equirect_central_90.jpg.license new file mode 100644 index 00000000..cca73d04 --- /dev/null +++ b/src/conformance/conformance_test/composition_examples/equirect_central_90.jpg.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2019-2024, The Khronos Group Inc. + +SPDX-License-Identifier: Apache-2.0 diff --git a/src/conformance/conformance_test/composition_examples/equirect_finite.jpg b/src/conformance/conformance_test/composition_examples/equirect_finite.jpg new file mode 100644 index 00000000..65a52d87 Binary files /dev/null and b/src/conformance/conformance_test/composition_examples/equirect_finite.jpg differ diff --git a/src/conformance/conformance_test/composition_examples/equirect_finite.jpg.license b/src/conformance/conformance_test/composition_examples/equirect_finite.jpg.license new file mode 100644 index 00000000..cca73d04 --- /dev/null +++ b/src/conformance/conformance_test/composition_examples/equirect_finite.jpg.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2019-2024, The Khronos Group Inc. + +SPDX-License-Identifier: Apache-2.0 diff --git a/src/conformance/conformance_test/composition_examples/equirect_finite_pose.jpg b/src/conformance/conformance_test/composition_examples/equirect_finite_pose.jpg new file mode 100644 index 00000000..d3974921 Binary files /dev/null and b/src/conformance/conformance_test/composition_examples/equirect_finite_pose.jpg differ diff --git a/src/conformance/conformance_test/composition_examples/equirect_finite_pose.jpg.license b/src/conformance/conformance_test/composition_examples/equirect_finite_pose.jpg.license new file mode 100644 index 00000000..cca73d04 --- /dev/null +++ b/src/conformance/conformance_test/composition_examples/equirect_finite_pose.jpg.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2019-2024, The Khronos Group Inc. + +SPDX-License-Identifier: Apache-2.0 diff --git a/src/conformance/conformance_test/composition_examples/equirect_local_space.jpg b/src/conformance/conformance_test/composition_examples/equirect_local_space.jpg new file mode 100644 index 00000000..d684bbde Binary files /dev/null and b/src/conformance/conformance_test/composition_examples/equirect_local_space.jpg differ diff --git a/src/conformance/conformance_test/composition_examples/equirect_local_space.jpg.license b/src/conformance/conformance_test/composition_examples/equirect_local_space.jpg.license new file mode 100644 index 00000000..cca73d04 --- /dev/null +++ b/src/conformance/conformance_test/composition_examples/equirect_local_space.jpg.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2019-2024, The Khronos Group Inc. + +SPDX-License-Identifier: Apache-2.0 diff --git a/src/conformance/conformance_test/composition_examples/equirect_view_space.jpg b/src/conformance/conformance_test/composition_examples/equirect_view_space.jpg new file mode 100644 index 00000000..881ecfe3 Binary files /dev/null and b/src/conformance/conformance_test/composition_examples/equirect_view_space.jpg differ diff --git a/src/conformance/conformance_test/composition_examples/equirect_view_space.jpg.license b/src/conformance/conformance_test/composition_examples/equirect_view_space.jpg.license new file mode 100644 index 00000000..cca73d04 --- /dev/null +++ b/src/conformance/conformance_test/composition_examples/equirect_view_space.jpg.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2019-2024, The Khronos Group Inc. + +SPDX-License-Identifier: Apache-2.0 diff --git a/src/conformance/conformance_test/composition_examples/ext_plane_detection_contour.png b/src/conformance/conformance_test/composition_examples/ext_plane_detection_contour.png index 6147309d..ac77c0e9 100644 Binary files a/src/conformance/conformance_test/composition_examples/ext_plane_detection_contour.png and b/src/conformance/conformance_test/composition_examples/ext_plane_detection_contour.png differ diff --git a/src/conformance/conformance_test/composition_examples/eye_visibility.png b/src/conformance/conformance_test/composition_examples/eye_visibility.png index b2925372..61c3916c 100644 Binary files a/src/conformance/conformance_test/composition_examples/eye_visibility.png and b/src/conformance/conformance_test/composition_examples/eye_visibility.png differ diff --git a/src/conformance/conformance_test/composition_examples/grip_and_aim_pose.png b/src/conformance/conformance_test/composition_examples/grip_and_aim_pose.png index 77f21451..6e6fea12 100644 Binary files a/src/conformance/conformance_test/composition_examples/grip_and_aim_pose.png and b/src/conformance/conformance_test/composition_examples/grip_and_aim_pose.png differ diff --git a/src/conformance/conformance_test/composition_examples/grip_axes_diagram.png b/src/conformance/conformance_test/composition_examples/grip_axes_diagram.png index 4f601866..b08b409b 100644 Binary files a/src/conformance/conformance_test/composition_examples/grip_axes_diagram.png and b/src/conformance/conformance_test/composition_examples/grip_axes_diagram.png differ diff --git a/src/conformance/conformance_test/composition_examples/local_floor.png b/src/conformance/conformance_test/composition_examples/local_floor.png index d86b5f0a..1d00ef2c 100644 Binary files a/src/conformance/conformance_test/composition_examples/local_floor.png and b/src/conformance/conformance_test/composition_examples/local_floor.png differ diff --git a/src/conformance/conformance_test/composition_examples/palm_pose.png b/src/conformance/conformance_test/composition_examples/palm_pose.png index d9203ff8..339df1d4 100644 Binary files a/src/conformance/conformance_test/composition_examples/palm_pose.png and b/src/conformance/conformance_test/composition_examples/palm_pose.png differ diff --git a/src/conformance/conformance_test/composition_examples/projection_array.png b/src/conformance/conformance_test/composition_examples/projection_array.png index e42043bf..8cfb9b4e 100644 Binary files a/src/conformance/conformance_test/composition_examples/projection_array.png and b/src/conformance/conformance_test/composition_examples/projection_array.png differ diff --git a/src/conformance/conformance_test/composition_examples/projection_depth.png b/src/conformance/conformance_test/composition_examples/projection_depth.png index fd39f0a3..32d94b29 100644 Binary files a/src/conformance/conformance_test/composition_examples/projection_depth.png and b/src/conformance/conformance_test/composition_examples/projection_depth.png differ diff --git a/src/conformance/conformance_test/composition_examples/projection_mutable.png b/src/conformance/conformance_test/composition_examples/projection_mutable.png index 05bdab3d..31a00411 100644 Binary files a/src/conformance/conformance_test/composition_examples/projection_mutable.png and b/src/conformance/conformance_test/composition_examples/projection_mutable.png differ diff --git a/src/conformance/conformance_test/composition_examples/projection_separate.png b/src/conformance/conformance_test/composition_examples/projection_separate.png index e42043bf..8cfb9b4e 100644 Binary files a/src/conformance/conformance_test/composition_examples/projection_separate.png and b/src/conformance/conformance_test/composition_examples/projection_separate.png differ diff --git a/src/conformance/conformance_test/composition_examples/projection_wide.png b/src/conformance/conformance_test/composition_examples/projection_wide.png index e42043bf..8cfb9b4e 100644 Binary files a/src/conformance/conformance_test/composition_examples/projection_wide.png and b/src/conformance/conformance_test/composition_examples/projection_wide.png differ diff --git a/src/conformance/conformance_test/composition_examples/quad_hands.png b/src/conformance/conformance_test/composition_examples/quad_hands.png index 5c2bd324..d8b234cd 100644 Binary files a/src/conformance/conformance_test/composition_examples/quad_hands.png and b/src/conformance/conformance_test/composition_examples/quad_hands.png differ diff --git a/src/conformance/conformance_test/composition_examples/quad_occlusion.png b/src/conformance/conformance_test/composition_examples/quad_occlusion.png index 00f1467c..c2b282e1 100644 Binary files a/src/conformance/conformance_test/composition_examples/quad_occlusion.png and b/src/conformance/conformance_test/composition_examples/quad_occlusion.png differ diff --git a/src/conformance/conformance_test/composition_examples/quad_poses.png b/src/conformance/conformance_test/composition_examples/quad_poses.png index ba02bc42..ce1b6b62 100644 Binary files a/src/conformance/conformance_test/composition_examples/quad_poses.png and b/src/conformance/conformance_test/composition_examples/quad_poses.png differ diff --git a/src/conformance/conformance_test/composition_examples/source_alpha_blending.png b/src/conformance/conformance_test/composition_examples/source_alpha_blending.png index d1c576aa..fd1cadb2 100644 Binary files a/src/conformance/conformance_test/composition_examples/source_alpha_blending.png and b/src/conformance/conformance_test/composition_examples/source_alpha_blending.png differ diff --git a/src/conformance/conformance_test/composition_examples/stale_swapchain.png b/src/conformance/conformance_test/composition_examples/stale_swapchain.png index 91afdfb8..4207f0b4 100644 Binary files a/src/conformance/conformance_test/composition_examples/stale_swapchain.png and b/src/conformance/conformance_test/composition_examples/stale_swapchain.png differ diff --git a/src/conformance/conformance_test/composition_examples/subimage.png b/src/conformance/conformance_test/composition_examples/subimage.png index 8b246003..79833f2b 100644 Binary files a/src/conformance/conformance_test/composition_examples/subimage.png and b/src/conformance/conformance_test/composition_examples/subimage.png differ diff --git a/src/conformance/conformance_test/composition_examples/visibility_mask_with_red.png b/src/conformance/conformance_test/composition_examples/visibility_mask_with_red.png index 4b976588..4af3bc41 100644 Binary files a/src/conformance/conformance_test/composition_examples/visibility_mask_with_red.png and b/src/conformance/conformance_test/composition_examples/visibility_mask_with_red.png differ diff --git a/src/conformance/conformance_test/conformance_test.cpp b/src/conformance/conformance_test/conformance_test.cpp index 180f23d8..0a26537d 100644 --- a/src/conformance/conformance_test/conformance_test.cpp +++ b/src/conformance/conformance_test/conformance_test.cpp @@ -267,9 +267,9 @@ namespace GlobalData& globalData = GetGlobalData(); globalData.options.desiredApiVersion = arg; if (striequal(globalData.options.desiredApiVersion.c_str(), "1.0")) - globalData.options.desiredApiVersionValue = XR_MAKE_VERSION(1, 0, XR_VERSION_PATCH(XR_CURRENT_API_VERSION)); + globalData.options.desiredApiVersionValue = XR_API_VERSION_1_0; else if (striequal(globalData.options.desiredApiVersion.c_str(), "1.1")) - globalData.options.desiredApiVersionValue = XR_MAKE_VERSION(1, 1, XR_VERSION_PATCH(XR_CURRENT_API_VERSION)); + globalData.options.desiredApiVersionValue = XR_API_VERSION_1_1; else { ReportConsoleOnlyF("invalid arg: %s", globalData.options.desiredApiVersion.c_str()); return ParserResult::runtimeError("invalid OpenXR version '" + arg + "' passed on command line"); diff --git a/src/conformance/conformance_test/gltf_examples/AlphaBlendModeTest.png b/src/conformance/conformance_test/gltf_examples/AlphaBlendModeTest.png index ae72e16c..af01faf5 100644 Binary files a/src/conformance/conformance_test/gltf_examples/AlphaBlendModeTest.png and b/src/conformance/conformance_test/gltf_examples/AlphaBlendModeTest.png differ diff --git a/src/conformance/conformance_test/gltf_examples/MetalRoughSpheres.png b/src/conformance/conformance_test/gltf_examples/MetalRoughSpheres.png index f48b789b..63a69049 100644 Binary files a/src/conformance/conformance_test/gltf_examples/MetalRoughSpheres.png and b/src/conformance/conformance_test/gltf_examples/MetalRoughSpheres.png differ diff --git a/src/conformance/conformance_test/gltf_examples/MetalRoughSpheresNoTextures.png b/src/conformance/conformance_test/gltf_examples/MetalRoughSpheresNoTextures.png index 99b214ca..fac4c8c7 100644 Binary files a/src/conformance/conformance_test/gltf_examples/MetalRoughSpheresNoTextures.png and b/src/conformance/conformance_test/gltf_examples/MetalRoughSpheresNoTextures.png differ diff --git a/src/conformance/conformance_test/gltf_examples/NormalTangentMirrorTest.png b/src/conformance/conformance_test/gltf_examples/NormalTangentMirrorTest.png index 0dd664b1..64440975 100644 Binary files a/src/conformance/conformance_test/gltf_examples/NormalTangentMirrorTest.png and b/src/conformance/conformance_test/gltf_examples/NormalTangentMirrorTest.png differ diff --git a/src/conformance/conformance_test/gltf_examples/NormalTangentTest.png b/src/conformance/conformance_test/gltf_examples/NormalTangentTest.png index 3c063ce5..1a93fbc9 100644 Binary files a/src/conformance/conformance_test/gltf_examples/NormalTangentTest.png and b/src/conformance/conformance_test/gltf_examples/NormalTangentTest.png differ diff --git a/src/conformance/conformance_test/gltf_examples/TextureSettingsTest.png b/src/conformance/conformance_test/gltf_examples/TextureSettingsTest.png index b9f56fe6..7bf2b29d 100644 Binary files a/src/conformance/conformance_test/gltf_examples/TextureSettingsTest.png and b/src/conformance/conformance_test/gltf_examples/TextureSettingsTest.png differ diff --git a/src/conformance/conformance_test/gltf_examples/VertexColorTest.png b/src/conformance/conformance_test/gltf_examples/VertexColorTest.png index 4f982406..c05b019a 100644 Binary files a/src/conformance/conformance_test/gltf_examples/VertexColorTest.png and b/src/conformance/conformance_test/gltf_examples/VertexColorTest.png differ diff --git a/src/conformance/conformance_test/pbr_assets/brdf_lut.png b/src/conformance/conformance_test/pbr_assets/brdf_lut.png index bb5d52f0..4f29f300 100644 Binary files a/src/conformance/conformance_test/pbr_assets/brdf_lut.png and b/src/conformance/conformance_test/pbr_assets/brdf_lut.png differ diff --git a/src/conformance/conformance_test/test_ActionPoses.cpp b/src/conformance/conformance_test/test_ActionPoses.cpp index af356631..209c7620 100644 --- a/src/conformance/conformance_test/test_ActionPoses.cpp +++ b/src/conformance/conformance_test/test_ActionPoses.cpp @@ -278,7 +278,7 @@ namespace Conformance viewState.viewStateFlags & XR_VIEW_STATE_ORIENTATION_VALID_BIT) { const auto& views = std::get>(viewData); - // Render into each view port of the wide swapchain using the projection layer view fov and pose. + // Render into each of the separate swapchains using the projection layer view fov and pose. for (size_t view = 0; view < views.size(); view++) { compositionHelper.AcquireWaitReleaseImage(swapchains[view], // [&](const XrSwapchainImageBaseHeader* swapchainImage) { diff --git a/src/conformance/conformance_test/test_HapticInterrupt.cpp b/src/conformance/conformance_test/test_HapticInterrupt.cpp index 1adce26c..1afce9c2 100644 --- a/src/conformance/conformance_test/test_HapticInterrupt.cpp +++ b/src/conformance/conformance_test/test_HapticInterrupt.cpp @@ -219,7 +219,7 @@ namespace Conformance viewState.viewStateFlags & XR_VIEW_STATE_ORIENTATION_VALID_BIT) { const auto& views = std::get>(viewData); - // Render into each view port of the wide swapchain using the projection layer view fov and pose. + // Render into each of the separate swapchains using the projection layer view fov and pose. for (size_t view = 0; view < views.size(); view++) { compositionHelper.AcquireWaitReleaseImage(swapchains[view], [&](const XrSwapchainImageBaseHeader* swapchainImage) { GetGlobalData().graphicsPlugin->ClearImageSlice(swapchainImage, 0); diff --git a/src/conformance/conformance_test/test_InteractiveThrow.cpp b/src/conformance/conformance_test/test_InteractiveThrow.cpp index d05929ec..e52374c2 100644 --- a/src/conformance/conformance_test/test_InteractiveThrow.cpp +++ b/src/conformance/conformance_test/test_InteractiveThrow.cpp @@ -279,7 +279,7 @@ namespace Conformance viewState.viewStateFlags & XR_VIEW_STATE_ORIENTATION_VALID_BIT) { const auto& views = std::get>(viewData); - // Render into each view port of the wide swapchain using the projection layer view fov and pose. + // Render into each of the separate swapchains using the projection layer view fov and pose. for (size_t view = 0; view < views.size(); view++) { compositionHelper.AcquireWaitReleaseImage(swapchains[view], [&](const XrSwapchainImageBaseHeader* swapchainImage) { GetGlobalData().graphicsPlugin->ClearImageSlice(swapchainImage); diff --git a/src/conformance/conformance_test/test_LayerComposition.cpp b/src/conformance/conformance_test/test_LayerComposition.cpp index 474f6988..076da97b 100644 --- a/src/conformance/conformance_test/test_LayerComposition.cpp +++ b/src/conformance/conformance_test/test_LayerComposition.cpp @@ -530,7 +530,7 @@ namespace Conformance XrPosefCPP()}; XRC_CHECK_THROW_XRCMD(xrCreateReferenceSpace(session, &spaceCreateInfo, &space)); } - gripSpaces.push_back(std::move(space)); + gripSpaces.push_back(space); } // Create 10x10cm L and R quads @@ -720,7 +720,7 @@ namespace Conformance }).Loop(); } - TEST_CASE("ProjectionDepth", "[composition][interactive][no_auto][XR_KHR_composition_layer_depth][XR_FB_composition_layer_depth_test]") + TEST_CASE("ProjectionDepth", "[XR_KHR_composition_layer_depth][XR_FB_composition_layer_depth_test][composition][interactive][no_auto]") { GlobalData& globalData = GetGlobalData(); if (!globalData.IsInstanceExtensionSupported(XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME)) { diff --git a/src/conformance/conformance_test/test_SessionState.cpp b/src/conformance/conformance_test/test_SessionState.cpp index cdc381ee..930d2d2e 100644 --- a/src/conformance/conformance_test/test_SessionState.cpp +++ b/src/conformance/conformance_test/test_SessionState.cpp @@ -14,6 +14,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "catch2/matchers/catch_matchers.hpp" +#include "catch2/matchers/catch_matchers_vector.hpp" #include "utilities/utils.h" #include "conformance_utils.h" #include "conformance_framework.h" @@ -45,6 +47,9 @@ namespace Conformance REQUIRE_MSG(session != XR_NULL_HANDLE_CPP, "If this (XrSession creation) fails, ensure the runtime is configured and the AR/VR device is present."); + XrInstance instance = session.GetInstance(); + XrSystemId systemId = session.GetSystemId(); + auto tryReadEvent = [&](XrEventDataBuffer* evt) { *evt = {XR_TYPE_EVENT_DATA_BUFFER}; XrResult res; @@ -77,7 +82,7 @@ namespace Conformance return false; }; - XrEventDataSessionStateChanged evt; + XrEventDataSessionStateChanged evt{}; REQUIRE(waitForNextSessionState(&evt) == true); REQUIRE_MSG(evt.state == XR_SESSION_STATE_IDLE, "Unexpected session state " << evt.state); @@ -88,10 +93,9 @@ namespace Conformance { // Get the list of supported view configurations uint32_t viewCount = 0; - REQUIRE(XR_SUCCESS == xrEnumerateViewConfigurations(session.instance, session.systemId, 0, &viewCount, nullptr)); + REQUIRE(XR_SUCCESS == xrEnumerateViewConfigurations(instance, systemId, 0, &viewCount, nullptr)); std::vector runtimeViewTypes(viewCount); - REQUIRE(XR_SUCCESS == - xrEnumerateViewConfigurations(session.instance, session.systemId, viewCount, &viewCount, runtimeViewTypes.data())); + REQUIRE(XR_SUCCESS == xrEnumerateViewConfigurations(instance, systemId, viewCount, &viewCount, runtimeViewTypes.data())); for (XrViewConfigurationType viewType : KnownViewTypes) { CAPTURE(viewType); @@ -99,15 +103,13 @@ namespace Conformance // Is this enum valid, check against enabled extensions. bool valid = IsViewConfigurationTypeEnumValid(viewType); - const bool isSupportedType = - std::find(runtimeViewTypes.begin(), runtimeViewTypes.end(), viewType) != runtimeViewTypes.end(); - - if (!valid) { - CHECK_MSG(valid == isSupportedType, "Can not support invalid view configuration type"); + if (!IsViewConfigurationTypeEnumValid(viewType)) { + INFO("Must not enumerate invalid view configuration type"); + CHECK_THAT(runtimeViewTypes, !Catch::Matchers::VectorContains(viewType)); } - // Skip this view config. - if (isSupportedType) { + // Skip this view config if it is supported, since we cannot test correct handling of unsupported values with it. + if (Catch::Matchers::VectorContains(viewType).match(runtimeViewTypes)) { continue; } diff --git a/src/conformance/conformance_test/test_SpaceOffsets.cpp b/src/conformance/conformance_test/test_SpaceOffsets.cpp index 568413f3..1d799baa 100644 --- a/src/conformance/conformance_test/test_SpaceOffsets.cpp +++ b/src/conformance/conformance_test/test_SpaceOffsets.cpp @@ -125,7 +125,7 @@ namespace Conformance const XrSwapchain swapchain = compositionHelper.CreateSwapchain(compositionHelper.DefaultColorSwapchainCreateInfo( viewProperties[j].recommendedImageRectWidth, viewProperties[j].recommendedImageRectHeight)); const_cast(projLayer->views[j].subImage) = compositionHelper.MakeDefaultSubImage(swapchain, 0); - swapchains.push_back(swapchain); + swapchains.emplace_back(swapchain); } } @@ -263,7 +263,7 @@ namespace Conformance XRC_CHECK_THROW_XRCMD(xrCreateActionSpace(compositionHelper.GetSession(), &spaceCreateInfo, &handSpace)); handSpaces.spaces.emplace_back(pose, handSpace); } - spaces.push_back(std::move(handSpaces)); + spaces.emplace_back(std::move(handSpaces)); } constexpr XrVector3f gnomonScale{0.025f, 0.025f, 0.025f}; @@ -510,7 +510,7 @@ namespace Conformance } if (spaceLocation.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) { - cubes.push_back(Cube{spaceLocation.pose, liveCubeScale}); + cubes.emplace_back(spaceLocation.pose, liveCubeScale); } } } @@ -530,7 +530,7 @@ namespace Conformance return tint; }; for (const auto& pastPose : space.pastPosesInLocalSpace) { - meshes.push_back(MeshDrawable{pastGnomonMesh, pastPose, gnomonScale, dimNonFailed()}); + meshes.emplace_back(pastGnomonMesh, pastPose, gnomonScale, dimNonFailed()); } struct predictionTrail @@ -554,12 +554,7 @@ namespace Conformance auto predictedDisplayTimeAtStep = frameState.predictedDisplayTime + step * frameState.predictedDisplayPeriod; gnomon.doSimulationStep({0.f, 0.f, 0.f}, predictedDisplayTimeAtStep); - meshes.push_back(MeshDrawable{ - trail.mesh, - gnomon.pose, - gnomonScale, - dimNonFailed(trail.tint), - }); + meshes.emplace_back(trail.mesh, gnomon.pose, gnomonScale, dimNonFailed(trail.tint)); } } } @@ -574,7 +569,7 @@ namespace Conformance viewState.viewStateFlags & XR_VIEW_STATE_ORIENTATION_VALID_BIT) { const auto& views = std::get>(viewData); - // Render into each view port of the wide swapchain using the projection layer view fov and pose. + // Render into each of the separate swapchains using the projection layer view fov and pose. for (size_t view = 0; view < views.size(); view++) { compositionHelper.AcquireWaitReleaseImage(swapchains[view], [&](const XrSwapchainImageBaseHeader* swapchainImage) { GetGlobalData().graphicsPlugin->ClearImageSlice(swapchainImage); diff --git a/src/conformance/conformance_test/test_Swapchains.cpp b/src/conformance/conformance_test/test_Swapchains.cpp index 61c65c7a..92ec7976 100644 --- a/src/conformance/conformance_test/test_Swapchains.cpp +++ b/src/conformance/conformance_test/test_Swapchains.cpp @@ -53,7 +53,7 @@ namespace Conformance XrSwapchainImageWaitInfo imageWaitInfo{XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO}; imageWaitInfo.timeout = XR_INFINITE_DURATION; - // acquire/wait/render/release all the images. + // Acquire/wait/render/release all the images. for (uint32_t i = 0; i < colorImageCount; ++i) { CAPTURE(i); uint32_t colorImageIndex = UINT32_MAX; @@ -433,6 +433,9 @@ namespace Conformance REQUIRE_THAT(imageFormatArray, VectorHasOnlyUniqueElements()); REQUIRE_THAT(imageFormatArray, !Catch::Matchers::VectorContains(kImageFormatInvalid)); + XrInstance instance = session.GetInstance(); + XrSystemId systemId = session.GetSystemId(); + SECTION("Swapchain creation test parameters") { // At this point, session.viewConfigurationViewVector has the system's set of view configurations, @@ -446,8 +449,7 @@ namespace Conformance for (int64_t imageFormat : imageFormatArray) { SwapchainCreateTestParameters tp; - REQUIRE(globalData.graphicsPlugin->GetSwapchainCreateTestParameters(session.instance, session, session.systemId, - imageFormat, &tp)); + REQUIRE(globalData.graphicsPlugin->GetSwapchainCreateTestParameters(instance, session, systemId, imageFormat, &tp)); // TODO remove this when we can mark it as a stencil-only format. if (tp.imageFormatName == "VK_FORMAT_S8_UINT") { @@ -485,6 +487,8 @@ namespace Conformance // Set up the session we will use for the testing AutoBasicSession session(AutoBasicSession::OptionFlags::beginSession); + XrInstance instance = session.GetInstance(); + XrSystemId systemId = session.GetSystemId(); // Enumerate formats std::vector imageFormatArray; @@ -498,7 +502,7 @@ namespace Conformance } } const XrSwapchainCreateInfo defaultColorCreateInfo = - FindDefaultColorSwapchainCreateInfo(imageFormatArray, session.instance, session.systemId, session); + FindDefaultColorSwapchainCreateInfo(imageFormatArray, instance, systemId, session); // In the past, bugs in the CTS have made this fail when called back to back (or called right before graphics shutdown) // Because it can be hard to debug failures in these tests, try to provoke this particular issue early, @@ -509,8 +513,7 @@ namespace Conformance for (int64_t imageFormat : imageFormatArray) { SwapchainCreateTestParameters tp; - REQUIRE( - globalData.graphicsPlugin->GetSwapchainCreateTestParameters(session.instance, session, session.systemId, imageFormat, &tp)); + REQUIRE(globalData.graphicsPlugin->GetSwapchainCreateTestParameters(instance, session, systemId, imageFormat, &tp)); if (!tp.supportsRendering) { // skip this format @@ -603,12 +606,11 @@ namespace Conformance swapchainImages->GetColorImageArray())); DoRenderTest(swapchainImages, colorImageCount, colorCreateInfo, colorSwapchain.get()); + GetGlobalData().graphicsPlugin->Flush(); } // SwapchainScoped will have xrDestroySwapchain - // now we need to flush GetGlobalData().graphicsPlugin->ClearSwapchainCache(); - GetGlobalData().graphicsPlugin->Flush(); } } } 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 5d8a4483..36431e4b 100644 --- a/src/conformance/conformance_test/test_XR_EXT_debug_utils.cpp +++ b/src/conformance/conformance_test/test_XR_EXT_debug_utils.cpp @@ -244,7 +244,7 @@ namespace Conformance return *it; } - TEST_CASE("XR_EXT_debug_utils", "") + TEST_CASE("XR_EXT_debug_utils", "[XR_EXT_debug_utils]") { GlobalData& globalData = GetGlobalData(); diff --git a/src/conformance/conformance_test/test_XR_EXT_dpad_binding.cpp b/src/conformance/conformance_test/test_XR_EXT_dpad_binding.cpp index fb0a7b01..87786b5b 100644 --- a/src/conformance/conformance_test/test_XR_EXT_dpad_binding.cpp +++ b/src/conformance/conformance_test/test_XR_EXT_dpad_binding.cpp @@ -827,7 +827,7 @@ namespace Conformance } } - TEST_CASE("XR_EXT_dpad_binding", "") + TEST_CASE("XR_EXT_dpad_binding", "[XR_EXT_dpad_binding]") { GlobalData& globalData = GetGlobalData(); if (!globalData.IsInstanceExtensionSupported(XR_EXT_DPAD_BINDING_EXTENSION_NAME) || @@ -1151,7 +1151,7 @@ namespace Conformance } } - TEST_CASE("XR_EXT_dpad_binding-interactive_thumbstick", "[actions][interactive][no_auto]") + TEST_CASE("XR_EXT_dpad_binding-interactive_thumbstick", "[XR_EXT_dpad_binding][actions][interactive][no_auto]") { GlobalData& globalData = GetGlobalData(); if (!globalData.IsInstanceExtensionSupported(XR_EXT_DPAD_BINDING_EXTENSION_NAME) || @@ -1230,7 +1230,7 @@ namespace Conformance } } - TEST_CASE("XR_EXT_dpad_binding-interactive_trackpad", "[actions][interactive][no_auto]") + TEST_CASE("XR_EXT_dpad_binding-interactive_trackpad", "[XR_EXT_dpad_binding][actions][interactive][no_auto]") { GlobalData& globalData = GetGlobalData(); if (!globalData.IsInstanceExtensionSupported(XR_EXT_DPAD_BINDING_EXTENSION_NAME) || diff --git a/src/conformance/conformance_test/test_XR_EXT_eye_gaze_interaction.cpp b/src/conformance/conformance_test/test_XR_EXT_eye_gaze_interaction.cpp index 04a662a5..fff9e6b1 100644 --- a/src/conformance/conformance_test/test_XR_EXT_eye_gaze_interaction.cpp +++ b/src/conformance/conformance_test/test_XR_EXT_eye_gaze_interaction.cpp @@ -270,7 +270,7 @@ namespace Conformance } } - TEST_CASE("XR_EXT_eye_gaze_interaction", "[scenario][interactive][no_auto][XR_EXT_eye_gaze_interaction]") + TEST_CASE("XR_EXT_eye_gaze_interaction", "[XR_EXT_eye_gaze_interaction][scenario][interactive][no_auto]") { GlobalData& globalData = GetGlobalData(); if (!globalData.IsInstanceExtensionSupported(XR_EXT_EYE_GAZE_INTERACTION_EXTENSION_NAME)) { @@ -369,7 +369,7 @@ namespace Conformance } } - TEST_CASE("XR_EXT_eye_gaze_interaction-interactive_gaze_only", "[scenario][interactive][no_auto][XR_EXT_eye_gaze_interaction]") + TEST_CASE("XR_EXT_eye_gaze_interaction-interactive_gaze_only", "[XR_EXT_eye_gaze_interaction][scenario][interactive][no_auto]") { GlobalData& globalData = GetGlobalData(); @@ -557,7 +557,7 @@ namespace Conformance viewState.viewStateFlags & XR_VIEW_STATE_ORIENTATION_VALID_BIT) { const auto& views = std::get>(viewData); - // Render into each view port of the wide swapchain using the projection layer view fov and pose. + // Render into each of the separate swapchains using the projection layer view fov and pose. for (size_t view = 0; view < views.size(); view++) { compositionHelper.AcquireWaitReleaseImage(swapchains[view], [&](const XrSwapchainImageBaseHeader* swapchainImage) { GetGlobalData().graphicsPlugin->ClearImageSlice(swapchainImage); 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 c399f534..eefac984 100644 --- a/src/conformance/conformance_test/test_XR_EXT_hand_tracking.cpp +++ b/src/conformance/conformance_test/test_XR_EXT_hand_tracking.cpp @@ -37,7 +37,7 @@ namespace Conformance MakeSystemPropertiesBoolChecker(XrSystemHandTrackingPropertiesEXT{XR_TYPE_SYSTEM_HAND_TRACKING_PROPERTIES_EXT}, &XrSystemHandTrackingPropertiesEXT::supportsHandTracking); - TEST_CASE("XR_EXT_hand_tracking-create-destroy", "") + TEST_CASE("XR_EXT_hand_tracking-create-destroy", "[XR_EXT_hand_tracking]") { GlobalData& globalData = GetGlobalData(); if (!globalData.IsInstanceExtensionSupported(XR_EXT_HAND_TRACKING_EXTENSION_NAME)) { @@ -96,7 +96,7 @@ namespace Conformance } } - TEST_CASE("XR_EXT_hand_tracking-simple-queries") + TEST_CASE("XR_EXT_hand_tracking-simple-queries", "[XR_EXT_hand_tracking]") { GlobalData& globalData = GetGlobalData(); if (!globalData.IsInstanceExtensionSupported(XR_EXT_HAND_TRACKING_EXTENSION_NAME)) { @@ -296,7 +296,7 @@ namespace Conformance } // Purpose: Ensure that if the hand tracking extension is enabled, you can see some hands! - TEST_CASE("XR_EXT_hand_tracking-interactive", "[scenario][interactive][no_auto]") + TEST_CASE("XR_EXT_hand_tracking-interactive", "[XR_EXT_hand_tracking][scenario][interactive][no_auto]") { const char* instructions = "Small cubes are rendered to represent the joints of each hand. " 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 14e4c267..f145ff0f 100644 --- a/src/conformance/conformance_test/test_XR_EXT_palm_pose.cpp +++ b/src/conformance/conformance_test/test_XR_EXT_palm_pose.cpp @@ -417,7 +417,7 @@ namespace Conformance viewState.viewStateFlags & XR_VIEW_STATE_ORIENTATION_VALID_BIT) { const auto& views = std::get>(viewData); - // Render into each view port of the wide swapchain using the projection layer view fov and pose. + // Render into each of the separate swapchains using the projection layer view fov and pose. for (size_t view = 0; view < views.size(); view++) { compositionHelper.AcquireWaitReleaseImage(swapchains[view], // [&](const XrSwapchainImageBaseHeader* swapchainImage) { @@ -691,7 +691,7 @@ namespace Conformance XrVector3f gripSurfaceXDirection{}; XrQuaternionf_RotateVector3f(&gripSurfaceXDirection, &gripSurfaceLocation.pose.orientation, &xAxis); double xAngleDiff = angleDeg(xAxis, gripSurfaceXDirection); - REQUIRE(xAngleDiff < 45.0f); + REQUIRE(xAngleDiff < 75.0f); } { @@ -714,7 +714,7 @@ namespace Conformance XrVector3f gripSurfaceYDirection{}; XrQuaternionf_RotateVector3f(&gripSurfaceYDirection, &gripSurfaceLocation.pose.orientation, &yAxis); double yAngleDiff = angleDeg(zMAxis, gripSurfaceYDirection); - REQUIRE(std::abs(yAngleDiff) < 45.0f); + REQUIRE(std::abs(yAngleDiff) < 85.0f); } if (i == 0) { @@ -726,12 +726,13 @@ namespace Conformance REQUIRE(gripSurfaceZDirection.x < 0); } else { - // Test that the z axis (direction from the palm center to the wrist) of grip surface points "to the right" in grip space. + // Test that the z axis (direction from the palm center to the wrist) of grip surface points not significantly to the left in grip space, + // i.e. the part of the hand between wrist and grip_surface pose does not point through the controller handle. // This should be true for all usual controllers. If this is not true for your controller, you may need to adapt or discard this test. XrVector3f zAxis = {0, 0, 1}; XrVector3f gripSurfaceZDirection{}; XrQuaternionf_RotateVector3f(&gripSurfaceZDirection, &gripSurfaceLocation.pose.orientation, &zAxis); - REQUIRE(gripSurfaceZDirection.x > 0); + REQUIRE(gripSurfaceZDirection.x >= -10); } { 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 4a0027d0..9516c6e5 100644 --- a/src/conformance/conformance_test/test_XR_EXT_plane_detection.cpp +++ b/src/conformance/conformance_test/test_XR_EXT_plane_detection.cpp @@ -405,7 +405,7 @@ namespace Conformance REQUIRE(XR_SUCCESS == xrDestroyPlaneDetectorEXT(detection)); } - TEST_CASE("XR_EXT_plane_detection-V", "[scenario][interactive][no_auto][XR_EXT_plane_detection]") + TEST_CASE("XR_EXT_plane_detection-V", "[XR_EXT_plane_detection][scenario][interactive][no_auto]") { RunPlaneTest({XR_PLANE_DETECTOR_ORIENTATION_VERTICAL_EXT}, "Planes should be rendered at the vertical surfaces, " @@ -413,7 +413,7 @@ namespace Conformance "Press the select button on either controller to pass the test."); } - TEST_CASE("XR_EXT_plane_detection-HU", "[scenario][interactive][no_auto][XR_EXT_plane_detection]") + TEST_CASE("XR_EXT_plane_detection-HU", "[XR_EXT_plane_detection][scenario][interactive][no_auto]") { RunPlaneTest({XR_PLANE_DETECTOR_ORIENTATION_HORIZONTAL_UPWARD_EXT}, "Planes should be rendered at the horizontal surfaces with upward normals, " @@ -421,7 +421,7 @@ namespace Conformance "Press the select button on either controller to pass the test."); } - TEST_CASE("XR_EXT_plane_detection-HD", "[scenario][interactive][no_auto][XR_EXT_plane_detection]") + TEST_CASE("XR_EXT_plane_detection-HD", "[XR_EXT_plane_detection][scenario][interactive][no_auto]") { RunPlaneTest({XR_PLANE_DETECTOR_ORIENTATION_HORIZONTAL_DOWNWARD_EXT}, "Planes should be rendered at the horizontal surfaces with downward normals, " @@ -429,21 +429,21 @@ namespace Conformance "Press the select button on either controller to pass the test."); } - TEST_CASE("XR_EXT_plane_detection-A", "[scenario][interactive][no_auto][XR_EXT_plane_detection]") + TEST_CASE("XR_EXT_plane_detection-A", "[XR_EXT_plane_detection][scenario][interactive][no_auto]") { RunPlaneTest({XR_PLANE_DETECTOR_ORIENTATION_ARBITRARY_EXT}, "Planes should be rendered at the non horizontal/vertical surfaces. " "Press the select button on either controller to pass the test."); } - TEST_CASE("XR_EXT_plane_detection-empty-list", "[scenario][interactive][no_auto][XR_EXT_plane_detection]") + TEST_CASE("XR_EXT_plane_detection-empty-list", "[XR_EXT_plane_detection][scenario][interactive][no_auto]") { RunPlaneTest({}, "All planes should be rendered. " "Press the select button on either controller to pass the test."); } - TEST_CASE("XR_EXT_plane_detection-nullptr", "[scenario][interactive][no_auto][XR_EXT_plane_detection]") + TEST_CASE("XR_EXT_plane_detection-nullptr", "[XR_EXT_plane_detection][scenario][interactive][no_auto]") { RunPlaneTest({}, "All planes should be rendered. " @@ -451,25 +451,25 @@ namespace Conformance XR_PLANE_DETECTOR_SEMANTIC_TYPE_UNDEFINED_EXT, true); } - TEST_CASE("XR_EXT_plane_detection-ceiling", "[scenario][interactive][no_auto][XR_EXT_plane_detection]") + TEST_CASE("XR_EXT_plane_detection-ceiling", "[XR_EXT_plane_detection][scenario][interactive][no_auto]") { RunPlaneTest({XR_PLANE_DETECTOR_ORIENTATION_HORIZONTAL_DOWNWARD_EXT}, "Make sure a ceiling is detected in the scene.", XR_PLANE_DETECTOR_SEMANTIC_TYPE_CEILING_EXT); } - TEST_CASE("XR_EXT_plane_detection-floor", "[scenario][interactive][no_auto][XR_EXT_plane_detection]") + TEST_CASE("XR_EXT_plane_detection-floor", "[XR_EXT_plane_detection][scenario][interactive][no_auto]") { RunPlaneTest({XR_PLANE_DETECTOR_ORIENTATION_HORIZONTAL_UPWARD_EXT}, "Make sure a floor is detected in the scene.", XR_PLANE_DETECTOR_SEMANTIC_TYPE_FLOOR_EXT); } - TEST_CASE("XR_EXT_plane_detection-wall", "[scenario][interactive][no_auto][XR_EXT_plane_detection]") + TEST_CASE("XR_EXT_plane_detection-wall", "[XR_EXT_plane_detection][scenario][interactive][no_auto]") { RunPlaneTest({XR_PLANE_DETECTOR_ORIENTATION_VERTICAL_EXT}, "Make sure a wall is detected in the scene.", XR_PLANE_DETECTOR_SEMANTIC_TYPE_WALL_EXT); } - TEST_CASE("XR_EXT_plane_detection-platform", "[scenario][interactive][no_auto][XR_EXT_plane_detection]") + TEST_CASE("XR_EXT_plane_detection-platform", "[XR_EXT_plane_detection][scenario][interactive][no_auto]") { RunPlaneTest({XR_PLANE_DETECTOR_ORIENTATION_HORIZONTAL_UPWARD_EXT}, "Make sure a platform is detected in the scene.", XR_PLANE_DETECTOR_SEMANTIC_TYPE_PLATFORM_EXT); @@ -833,25 +833,25 @@ namespace Conformance REQUIRE(XR_SUCCESS == xrDestroyPlaneDetectorEXT(detection)); } - TEST_CASE("XR_EXT_plane_detection-contour-HU", "[scenario][interactive][no_auto][XR_EXT_plane_detection]") + TEST_CASE("XR_EXT_plane_detection-contour-HU", "[XR_EXT_plane_detection][scenario][interactive][no_auto]") { RunPlaneContourTest({XR_PLANE_DETECTOR_ORIENTATION_HORIZONTAL_UPWARD_EXT}, "ext_plane_detection_contour.png", "This should show the plane contours of all upward horizontal planes."); } - TEST_CASE("XR_EXT_plane_detection-contour-HD", "[scenario][interactive][no_auto][XR_EXT_plane_detection]") + TEST_CASE("XR_EXT_plane_detection-contour-HD", "[XR_EXT_plane_detection][scenario][interactive][no_auto]") { RunPlaneContourTest({XR_PLANE_DETECTOR_ORIENTATION_HORIZONTAL_DOWNWARD_EXT}, "ext_plane_detection_contour.png", "This should show the plane contours of all downward horizontal planes."); } - TEST_CASE("XR_EXT_plane_detection-contour-V", "[scenario][interactive][no_auto][XR_EXT_plane_detection]") + TEST_CASE("XR_EXT_plane_detection-contour-V", "[XR_EXT_plane_detection][scenario][interactive][no_auto]") { RunPlaneContourTest({XR_PLANE_DETECTOR_ORIENTATION_VERTICAL_EXT}, "ext_plane_detection_contour.png", "This should show the plane contours of all vertical planes."); } - TEST_CASE("XR_EXT_plane_detection-contour-A", "[scenario][interactive][no_auto][XR_EXT_plane_detection]") + TEST_CASE("XR_EXT_plane_detection-contour-A", "[XR_EXT_plane_detection][scenario][interactive][no_auto]") { RunPlaneContourTest({XR_PLANE_DETECTOR_ORIENTATION_ARBITRARY_EXT}, "ext_plane_detection_contour.png", "This should show the plane contours of all non vertical / horizontal (arbitrary) planes."); diff --git a/src/conformance/conformance_test/test_XR_EXT_thermal_query.cpp b/src/conformance/conformance_test/test_XR_EXT_thermal_query.cpp index f2037772..8bb28fad 100644 --- a/src/conformance/conformance_test/test_XR_EXT_thermal_query.cpp +++ b/src/conformance/conformance_test/test_XR_EXT_thermal_query.cpp @@ -26,7 +26,7 @@ namespace Conformance { - TEST_CASE("XR_EXT_thermal_query", "") + TEST_CASE("XR_EXT_thermal_query", "[XR_EXT_thermal_query]") { GlobalData& globalData = GetGlobalData(); if (!globalData.IsInstanceExtensionSupported("XR_EXT_thermal_query")) { diff --git a/src/conformance/conformance_test/test_XR_KHR_D3D11_enable.cpp b/src/conformance/conformance_test/test_XR_KHR_D3D11_enable.cpp index e463959a..9a766cc5 100644 --- a/src/conformance/conformance_test/test_XR_KHR_D3D11_enable.cpp +++ b/src/conformance/conformance_test/test_XR_KHR_D3D11_enable.cpp @@ -36,7 +36,7 @@ using namespace Microsoft::WRL; namespace Conformance { - TEST_CASE("XR_KHR_D3D11_enable", "") + TEST_CASE("XR_KHR_D3D11_enable", "[XR_KHR_D3D11_enable]") { GlobalData& globalData = GetGlobalData(); if (!globalData.IsInstanceExtensionEnabled(XR_KHR_D3D11_ENABLE_EXTENSION_NAME)) { diff --git a/src/conformance/conformance_test/test_XR_KHR_D3D12_enable.cpp b/src/conformance/conformance_test/test_XR_KHR_D3D12_enable.cpp index 92d555f1..2c1453d3 100644 --- a/src/conformance/conformance_test/test_XR_KHR_D3D12_enable.cpp +++ b/src/conformance/conformance_test/test_XR_KHR_D3D12_enable.cpp @@ -35,7 +35,7 @@ using namespace Microsoft::WRL; namespace Conformance { - TEST_CASE("XR_KHR_D3D12_enable", "") + TEST_CASE("XR_KHR_D3D12_enable", "[XR_KHR_D3D12_enable]") { GlobalData& globalData = GetGlobalData(); if (!globalData.IsInstanceExtensionEnabled(XR_KHR_D3D12_ENABLE_EXTENSION_NAME)) { diff --git a/src/conformance/conformance_test/test_XR_KHR_OpenGL_ES_enable.cpp b/src/conformance/conformance_test/test_XR_KHR_OpenGL_ES_enable.cpp index 38a8959c..19b07ada 100644 --- a/src/conformance/conformance_test/test_XR_KHR_OpenGL_ES_enable.cpp +++ b/src/conformance/conformance_test/test_XR_KHR_OpenGL_ES_enable.cpp @@ -32,7 +32,7 @@ namespace Conformance { - TEST_CASE("XR_KHR_opengl_es_enable", "") + TEST_CASE("XR_KHR_opengl_es_enable", "[XR_KHR_opengl_es_enable]") { GlobalData& globalData = GetGlobalData(); if (!globalData.IsInstanceExtensionEnabled(XR_KHR_OPENGL_ES_ENABLE_EXTENSION_NAME)) { diff --git a/src/conformance/conformance_test/test_XR_KHR_OpenGL_enable.cpp b/src/conformance/conformance_test/test_XR_KHR_OpenGL_enable.cpp index 6f5368a7..cfce8e15 100644 --- a/src/conformance/conformance_test/test_XR_KHR_OpenGL_enable.cpp +++ b/src/conformance/conformance_test/test_XR_KHR_OpenGL_enable.cpp @@ -33,7 +33,7 @@ namespace Conformance { - TEST_CASE("XR_KHR_opengl_enable", "") + TEST_CASE("XR_KHR_opengl_enable", "[XR_KHR_opengl_enable]") { GlobalData& globalData = GetGlobalData(); if (!globalData.IsInstanceExtensionEnabled(XR_KHR_OPENGL_ENABLE_EXTENSION_NAME)) { diff --git a/src/conformance/conformance_test/test_XR_KHR_composition_layer_cube.cpp b/src/conformance/conformance_test/test_XR_KHR_composition_layer_cube.cpp index bc8e839b..c5886de9 100644 --- a/src/conformance/conformance_test/test_XR_KHR_composition_layer_cube.cpp +++ b/src/conformance/conformance_test/test_XR_KHR_composition_layer_cube.cpp @@ -14,6 +14,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "composition_utils.h" #include "conformance_framework.h" #include "conformance_utils.h" #include "utilities/generator.h" @@ -32,7 +33,7 @@ namespace Conformance { // This implements an automated programmatic test of the cubemap layer. However, a separate visual // test is required in order to validate that it looks correct. - TEST_CASE("XR_KHR_composition_layer_cube", "") + TEST_CASE("XR_KHR_composition_layer_cube", "[XR_KHR_composition_layer_cube]") { GlobalData& globalData = GetGlobalData(); if (!globalData.IsInstanceExtensionSupported(XR_KHR_COMPOSITION_LAYER_CUBE_EXTENSION_NAME)) { @@ -81,7 +82,7 @@ namespace Conformance for (XrSpace space : session.spaceVector) { for (XrEyeVisibility eyeVisibility : eyeVisibilityArray) { std::array orientationTestArray{ - XrQuaternionf{0, 0, 0, 1}, // No rotation; looking down the +x axis + Quat::Identity, // No rotation; looking down the +x axis XrQuaternionf{0, 0.7071f, 0, 0.7071f}, // 90 degree rotation around y axis; looking down the -z axis. XrQuaternionf{0, 0, 0.7071f, 0.7071f}, // 90 degree rotation around z axis; looking down the +y axis. XrQuaternionf{-0.709f, 0.383f, -0.381f, -0.454f} // Misc value. diff --git a/src/conformance/conformance_test/test_XR_KHR_composition_layer_cylinder.cpp b/src/conformance/conformance_test/test_XR_KHR_composition_layer_cylinder.cpp index 9fd04593..b1e532f5 100644 --- a/src/conformance/conformance_test/test_XR_KHR_composition_layer_cylinder.cpp +++ b/src/conformance/conformance_test/test_XR_KHR_composition_layer_cylinder.cpp @@ -32,7 +32,7 @@ namespace Conformance { // This implements an automated programmatic test of the cylinder layer. However, a separate visual // test is required in order to validate that it looks correct. - TEST_CASE("XR_KHR_composition_layer_cylinder", "") + TEST_CASE("XR_KHR_composition_layer_cylinder", "[XR_KHR_composition_layer_cylinder]") { GlobalData& globalData = GetGlobalData(); if (!globalData.IsInstanceExtensionSupported(XR_KHR_COMPOSITION_LAYER_CYLINDER_EXTENSION_NAME)) { diff --git a/src/conformance/conformance_test/test_XR_KHR_composition_layer_depth.cpp b/src/conformance/conformance_test/test_XR_KHR_composition_layer_depth.cpp index 2fb6b576..31884f10 100644 --- a/src/conformance/conformance_test/test_XR_KHR_composition_layer_depth.cpp +++ b/src/conformance/conformance_test/test_XR_KHR_composition_layer_depth.cpp @@ -30,7 +30,7 @@ namespace Conformance { // This implements an automated programmatic test of depth layers. However, a separate visual // test is required in order to validate that it looks correct. - TEST_CASE("XR_KHR_composition_layer_depth", "") + TEST_CASE("XR_KHR_composition_layer_depth", "[XR_KHR_composition_layer_depth]") { GlobalData& globalData = GetGlobalData(); if (!globalData.IsInstanceExtensionSupported(XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME)) { diff --git a/src/conformance/conformance_test/test_XR_KHR_composition_layer_equirect.cpp b/src/conformance/conformance_test/test_XR_KHR_composition_layer_equirect.cpp index 3a38b4bb..627e9653 100644 --- a/src/conformance/conformance_test/test_XR_KHR_composition_layer_equirect.cpp +++ b/src/conformance/conformance_test/test_XR_KHR_composition_layer_equirect.cpp @@ -14,25 +14,31 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "RGBAImage.h" +#include "composition_utils.h" #include "conformance_framework.h" #include "conformance_utils.h" +#include "utilities/array_size.h" #include "utilities/bitmask_generator.h" #include "utilities/bitmask_to_string.h" -#include "utilities/generator.h" #include "utilities/xrduration_literals.h" #include +#include +#include #include #include -#include +#include +#include +#include #include namespace Conformance { // This implements an automated programmatic test of the equirect layer. However, a separate visual // test is required in order to validate that it looks correct. - TEST_CASE("XR_KHR_composition_layer_equirect", "") + TEST_CASE("XR_KHR_composition_layer_equirect", "[XR_KHR_composition_layer_equirect]") { GlobalData& globalData = GetGlobalData(); if (!globalData.IsInstanceExtensionSupported(XR_KHR_COMPOSITION_LAYER_EQUIRECT_EXTENSION_NAME)) { @@ -97,7 +103,7 @@ namespace Conformance for (float radius : radiusTestArray) { std::array orientationTestArray{ - XrQuaternionf{0, 0, 0, 1}, // No rotation; looking down the +x axis + Quat::Identity, // No rotation; looking down the +x axis XrQuaternionf{0, 0.7071f, 0, 0.7071f}, // 90 degree rotation around y axis; looking down the -z axis. XrQuaternionf{0, 0, 0.7071f, 0.7071f}, // 90 degree rotation around z axis; looking down the +y axis. XrQuaternionf{-0.709f, 0.383f, -0.381f, -0.454f} // Misc value. @@ -170,4 +176,169 @@ namespace Conformance frameIterator.RunToSessionState(XR_SESSION_STATE_STOPPING); } + + struct EquirectTestCase + { + + const char* name; + const char* description; + XrReferenceSpaceType spaceType; + XrPosef pose; + float radius; + XrVector2f scale; + XrVector2f bias; + const char* imagePath; + XrRect2Df crop; // normalised + const char* exampleImagePath; + }; + + const EquirectTestCase equirectTestCases[] = { + { + "Full sphere at infinity", + "A 360 view of the inside of a cube at infinity", + XR_REFERENCE_SPACE_TYPE_LOCAL, + {Quat::Identity, {0, 0, 0}}, + 0.0, // infinity + {1.0, 1.0}, + {0.0, 0.0}, + "equirect_8k.png", + {{0, 0}, {1, 1}}, + "equirect_local_space.jpg", + }, + { + "Full sphere at infinity (view space)", + "A 360 view of the inside of a cube at infinity, rendered in view space", + XR_REFERENCE_SPACE_TYPE_VIEW, + {Quat::Identity, {0, 0, 0}}, + 0.0, // infinity + {1.0, 1.0}, + {0.0, 0.0}, + "equirect_8k.png", + {{0, 0}, {1, 1}}, + "equirect_view_space.jpg", + }, + { + "Full sphere at 2m", + "A 2m sphere with the same cube test image. " + "Example is shown from above and to the left of the origin to make the perspective effect clear.", + XR_REFERENCE_SPACE_TYPE_LOCAL, + {Quat::Identity, {0, 0, 0}}, + 2.0, + {1.0, 1.0}, + {0.0, 0.0}, + "equirect_8k.png", + {{0, 0}, {1, 1}}, + "equirect_finite.jpg", + }, + { + "Full sphere at 2m with pose", + "A 2m sphere with the same cube test image, forward by 1.5m and rotated downward", + XR_REFERENCE_SPACE_TYPE_LOCAL, + {Quat::FromAxisAngle({1, 0, 0}, Math::DegToRad(45)), {0, 0, -1.5}}, + 2.0, + {1.0, 1.0}, + {0.0, 0.0}, + "equirect_8k.png", + {{0, 0}, {1, 1}}, + "equirect_finite_pose.jpg", + }, + { + "90 degree section at infinity (cropped file)", + "A 90 degree section in both latitude and longitude, rendered at infinity", + XR_REFERENCE_SPACE_TYPE_LOCAL, + {Quat::Identity, {0, 0, 0}}, + 0.0, // infinity + {0.25, 0.5}, + {0.0, 0.0}, + "equirect_central_90.png", + {{0, 0}, {1, 1}}, + "equirect_central_90.jpg", + }, + { + "90 degree section at infinity (cropped image extents)", + "A 90 degree section in both latitude and longitude, rendered at infinity", + XR_REFERENCE_SPACE_TYPE_LOCAL, + {Quat::Identity, {0, 0, 0}}, + 0.0, // infinity + {0.25, 0.5}, + {0.0, 0.0}, + "equirect_8k.png", + {{3 / 8., 2 / 8.}, {1 / 4., 2 / 4.}}, + "equirect_central_90.jpg", + }, + }; + + static RGBAImageCache& EquirectImageCache() + { + static RGBAImageCache imageCache{}; + imageCache.Init(); + return imageCache; + } + TEST_CASE("XR_KHR_composition_layer_equirect-interactive", "[composition][interactive][no_auto]") + { + GlobalData& globalData = GetGlobalData(); + + if (!globalData.IsInstanceExtensionSupported(XR_KHR_COMPOSITION_LAYER_EQUIRECT_EXTENSION_NAME)) { + SKIP(XR_KHR_COMPOSITION_LAYER_EQUIRECT_EXTENSION_NAME " not supported"); + } + + auto testCaseIdx = GENERATE(Catch::Generators::range(0, ArraySize(equirectTestCases))); + auto testCase = equirectTestCases[testCaseIdx]; + // technically redundant because GENERATE() makes a "section", but this makes the test more usable. + DYNAMIC_SECTION("Test condition name: " << testCase.name) + { + INFO("Test condition description: " << testCase.description); + std::string testTitle = SubtestTitle("Equirect layer", testCaseIdx, equirectTestCases); + CompositionHelper compositionHelper(testTitle.c_str(), {XR_KHR_COMPOSITION_LAYER_EQUIRECT_EXTENSION_NAME}); + + std::ostringstream oss; + oss << testTitle << ": " << testCase.name << '\n'; + oss << testCase.description << '\n'; + + InteractiveLayerManager interactiveLayerManager(compositionHelper, testCase.exampleImagePath, oss.str().c_str()); + compositionHelper.GetInteractionManager().AttachActionSets(); + compositionHelper.BeginSession(); + + const XrSpace space = compositionHelper.CreateReferenceSpace(testCase.spaceType); + + std::shared_ptr image = EquirectImageCache().Load(testCase.imagePath); + int32_t imageWidth = image->width; + int32_t imageHeight = image->height; + + XrSwapchainCreateInfo createInfo = compositionHelper.DefaultColorSwapchainCreateInfo( + imageWidth, imageHeight, XR_SWAPCHAIN_CREATE_STATIC_IMAGE_BIT, GetGlobalData().graphicsPlugin->GetSRGBA8Format()); + + // copying to this swapchain, not rendering to it + createInfo.usageFlags |= XR_SWAPCHAIN_USAGE_TRANSFER_DST_BIT; + + XrSwapchain swapchain = compositionHelper.CreateSwapchain(createInfo); + + compositionHelper.AcquireWaitReleaseImage(swapchain, [&](const XrSwapchainImageBaseHeader* swapchainImage) { + GetGlobalData().graphicsPlugin->CopyRGBAImage(swapchainImage, 0, *image); + }); + + XrCompositionLayerEquirectKHR equirectLayer{XR_TYPE_COMPOSITION_LAYER_EQUIRECT_KHR}; + equirectLayer.space = space; + equirectLayer.eyeVisibility = XR_EYE_VISIBILITY_BOTH; + equirectLayer.subImage.swapchain = swapchain; + equirectLayer.subImage.imageRect = CropImage(imageWidth, imageHeight, testCase.crop); + equirectLayer.subImage.imageArrayIndex = 0; + equirectLayer.pose = testCase.pose; + equirectLayer.radius = testCase.radius; + equirectLayer.scale = testCase.scale; + equirectLayer.bias = testCase.bias; + + interactiveLayerManager.AddBackgroundLayer(&equirectLayer); + + RenderLoop(compositionHelper.GetSession(), [&](const XrFrameState& frameState) { + if (!interactiveLayerManager.EndFrame(frameState)) { + // user has marked this test as complete + SUCCEED("User has marked this test as passed"); + return false; + } + return true; + }).Loop(); + } + } + } // namespace Conformance diff --git a/src/conformance/conformance_test/test_XR_KHR_composition_layer_equirect2.cpp b/src/conformance/conformance_test/test_XR_KHR_composition_layer_equirect2.cpp new file mode 100644 index 00000000..6188bc99 --- /dev/null +++ b/src/conformance/conformance_test/test_XR_KHR_composition_layer_equirect2.cpp @@ -0,0 +1,212 @@ +// Copyright (c) 2019-2024, The Khronos Group Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "RGBAImage.h" +#include "composition_utils.h" +#include "conformance_framework.h" +#include "conformance_utils.h" +#include "utilities/array_size.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace Conformance +{ + namespace + { + static const float pi = std::acos(-1.0f); + } + struct Equirect2TestCase + { + + const char* name; + const char* description; + XrReferenceSpaceType spaceType; + XrPosef pose; + float radius; + float centralHorizontalAngle; + float upperVerticalAngle; + float lowerVerticalAngle; + const char* imagePath; + XrRect2Df crop; // normalised + const char* exampleImagePath; + }; + + const Equirect2TestCase equirect2TestCases[] = { + { + "Full sphere at infinity", + "A 360 view of the inside of a cube at infinity", + XR_REFERENCE_SPACE_TYPE_LOCAL, + {Quat::Identity, {0, 0, 0}}, + 0.0f, // infinity + pi * 2.f, + pi / 2.f, + -pi / 2.f, + "equirect_8k.png", + {{0, 0}, {1, 1}}, + "equirect_local_space.jpg", + }, + { + "Full sphere at infinity (view space)", + "A 360 view of the inside of a cube at infinity, rendered in view space", + XR_REFERENCE_SPACE_TYPE_VIEW, + {Quat::Identity, {0, 0, 0}}, + 0.0f, // infinity + pi * 2.f, + pi / 2.f, + -pi / 2.f, + "equirect_8k.png", + {{0, 0}, {1, 1}}, + "equirect_view_space.jpg", + }, + { + "Full sphere at 2m", + "A 2m sphere with the same cube test image. " + "Example is shown from above and to the left of the origin to make the perspective effect clear.", + XR_REFERENCE_SPACE_TYPE_LOCAL, + {Quat::Identity, {0, 0, 0}}, + 2.0f, + pi * 2.f, + pi / 2.f, + -pi / 2.f, + "equirect_8k.png", + {{0, 0}, {1, 1}}, + "equirect_finite.jpg", + }, + { + "Full sphere at 2m with pose", + "A 2m sphere with the same cube test image, forward by 1.5m and rotated downward", + XR_REFERENCE_SPACE_TYPE_LOCAL, + {Quat::FromAxisAngle({1, 0, 0}, Math::DegToRad(45)), {0, 0, -1.5}}, + 2.0f, + pi * 2.f, + pi / 2.f, + -pi / 2.f, + "equirect_8k.png", + {{0, 0}, {1, 1}}, + "equirect_finite_pose.jpg", + }, + { + "90 degree section at infinity (cropped file)", + "A 90 degree section in both latitude and longitude, rendered at infinity", + XR_REFERENCE_SPACE_TYPE_LOCAL, + {Quat::Identity, {0, 0, 0}}, + 0.0, // infinity + pi / 2.f, + pi / 4.f, + -pi / 4.f, + "equirect_central_90.png", + {{0, 0}, {1, 1}}, + "equirect_central_90.jpg", + }, + { + "90 degree section at infinity (cropped image extents)", + "A 90 degree section in both latitude and longitude, rendered at infinity", + XR_REFERENCE_SPACE_TYPE_LOCAL, + {Quat::Identity, {0, 0, 0}}, + 0.0, // infinity + pi / 2.f, + pi / 4.f, + -pi / 4.f, + "equirect_8k.png", + {{3 / 8., 2 / 8.}, {1 / 4., 2 / 4.}}, + "equirect_central_90.jpg", + }, + }; + + static RGBAImageCache& Equirect2ImageCache() + { + static RGBAImageCache imageCache{}; + imageCache.Init(); + return imageCache; + } + TEST_CASE("XR_KHR_composition_layer_equirect2-interactive", "[composition][interactive][no_auto]") + { + GlobalData& globalData = GetGlobalData(); + + if (!globalData.IsInstanceExtensionSupported(XR_KHR_COMPOSITION_LAYER_EQUIRECT2_EXTENSION_NAME)) { + SKIP(XR_KHR_COMPOSITION_LAYER_EQUIRECT2_EXTENSION_NAME " not supported"); + } + + auto testCaseIdx = GENERATE(Catch::Generators::range(0, ArraySize(equirect2TestCases))); + auto testCase = equirect2TestCases[testCaseIdx]; + // technically redundant because GENERATE() makes a "section", but this makes the test more usable. + DYNAMIC_SECTION("Test condition name: " << testCase.name) + { + INFO("Test condition description: " << testCase.description); + + std::string testTitle = SubtestTitle("Equirect2 layer", testCaseIdx, equirect2TestCases); + CompositionHelper compositionHelper(testTitle.c_str(), {XR_KHR_COMPOSITION_LAYER_EQUIRECT2_EXTENSION_NAME}); + + std::ostringstream oss; + oss << testTitle << ": " << testCase.name << '\n'; + oss << testCase.description << '\n'; + + InteractiveLayerManager interactiveLayerManager(compositionHelper, testCase.exampleImagePath, oss.str().c_str()); + compositionHelper.GetInteractionManager().AttachActionSets(); + compositionHelper.BeginSession(); + + const XrSpace space = compositionHelper.CreateReferenceSpace(testCase.spaceType); + + std::shared_ptr image = Equirect2ImageCache().Load(testCase.imagePath); + int32_t imageWidth = image->width; + int32_t imageHeight = image->height; + + XrSwapchainCreateInfo createInfo = compositionHelper.DefaultColorSwapchainCreateInfo( + imageWidth, imageHeight, XR_SWAPCHAIN_CREATE_STATIC_IMAGE_BIT, GetGlobalData().graphicsPlugin->GetSRGBA8Format()); + + // copying to this swapchain, not rendering to it + createInfo.usageFlags |= XR_SWAPCHAIN_USAGE_TRANSFER_DST_BIT; + + XrSwapchain swapchain = compositionHelper.CreateSwapchain(createInfo); + + compositionHelper.AcquireWaitReleaseImage(swapchain, [&](const XrSwapchainImageBaseHeader* swapchainImage) { + GetGlobalData().graphicsPlugin->CopyRGBAImage(swapchainImage, 0, *image); + }); + + XrCompositionLayerEquirect2KHR equirect2Layer{XR_TYPE_COMPOSITION_LAYER_EQUIRECT2_KHR}; + equirect2Layer.space = space; + equirect2Layer.eyeVisibility = XR_EYE_VISIBILITY_BOTH; + equirect2Layer.subImage.swapchain = swapchain; + equirect2Layer.subImage.imageRect = CropImage(imageWidth, imageHeight, testCase.crop); + equirect2Layer.subImage.imageArrayIndex = 0; + equirect2Layer.pose = testCase.pose; + equirect2Layer.radius = testCase.radius; + equirect2Layer.centralHorizontalAngle = testCase.centralHorizontalAngle; + equirect2Layer.upperVerticalAngle = testCase.upperVerticalAngle; + equirect2Layer.lowerVerticalAngle = testCase.lowerVerticalAngle; + + interactiveLayerManager.AddBackgroundLayer(&equirect2Layer); + + RenderLoop(compositionHelper.GetSession(), [&](const XrFrameState& frameState) { + if (!interactiveLayerManager.EndFrame(frameState)) { + // user has marked this test as complete + SUCCEED("User has marked this test as passed"); + return false; + } + return true; + }).Loop(); + } + } + +} // namespace Conformance 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 e0d17d01..93be80e2 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 @@ -31,7 +31,7 @@ namespace Conformance { - TEST_CASE("XR_KHR_convert_timespec_time", "") + TEST_CASE("XR_KHR_convert_timespec_time", "[XR_KHR_convert_timespec_time]") { #ifndef XR_USE_TIMESPEC SKIP("XR_KHR_convert_timespec_time test not enabled in CTS"); diff --git a/src/conformance/conformance_test/test_XR_KHR_visibility_mask.cpp b/src/conformance/conformance_test/test_XR_KHR_visibility_mask.cpp index 9a10a02e..a1559c03 100644 --- a/src/conformance/conformance_test/test_XR_KHR_visibility_mask.cpp +++ b/src/conformance/conformance_test/test_XR_KHR_visibility_mask.cpp @@ -330,7 +330,7 @@ namespace Conformance return {mesh, bgColor}; } - TEST_CASE("XR_KHR_visibility_mask-interactive", "[scenario][interactive][no_auto][XR_KHR_visibility_mask]") + TEST_CASE("XR_KHR_visibility_mask-interactive", "[XR_KHR_visibility_mask][scenario][interactive][no_auto]") { // successcodes="XR_SUCCESS,XR_SESSION_LOSS_PENDING" // errorcodes="XR_ERROR_HANDLE_INVALID,XR_ERROR_INSTANCE_LOST,XR_ERROR_RUNTIME_FAILURE,XR_ERROR_VALIDATION_FAILURE, diff --git a/src/conformance/conformance_test/test_XR_KHR_vulkan_enable.cpp b/src/conformance/conformance_test/test_XR_KHR_vulkan_enable.cpp index 408e18d5..0afddd34 100644 --- a/src/conformance/conformance_test/test_XR_KHR_vulkan_enable.cpp +++ b/src/conformance/conformance_test/test_XR_KHR_vulkan_enable.cpp @@ -33,7 +33,7 @@ namespace Conformance { - TEST_CASE("XR_KHR_vulkan_enable", "") + TEST_CASE("XR_KHR_vulkan_enable", "[XR_KHR_vulkan_enable]") { GlobalData& globalData = GetGlobalData(); if (!globalData.IsInstanceExtensionEnabled(XR_KHR_VULKAN_ENABLE_EXTENSION_NAME)) { diff --git a/src/conformance/conformance_test/test_XR_KHR_vulkan_enable2.cpp b/src/conformance/conformance_test/test_XR_KHR_vulkan_enable2.cpp index ce5c23f2..1e1deb65 100644 --- a/src/conformance/conformance_test/test_XR_KHR_vulkan_enable2.cpp +++ b/src/conformance/conformance_test/test_XR_KHR_vulkan_enable2.cpp @@ -33,7 +33,7 @@ namespace Conformance { - TEST_CASE("XR_KHR_vulkan_enable2", "") + TEST_CASE("XR_KHR_vulkan_enable2", "[XR_KHR_vulkan_enable2]") { GlobalData& globalData = GetGlobalData(); if (!globalData.IsInstanceExtensionEnabled(XR_KHR_VULKAN_ENABLE2_EXTENSION_NAME)) { diff --git a/src/conformance/conformance_test/test_XR_KHR_win32_convert_performance_counter_time.cpp b/src/conformance/conformance_test/test_XR_KHR_win32_convert_performance_counter_time.cpp index b8283cd0..db5eab68 100644 --- a/src/conformance/conformance_test/test_XR_KHR_win32_convert_performance_counter_time.cpp +++ b/src/conformance/conformance_test/test_XR_KHR_win32_convert_performance_counter_time.cpp @@ -30,7 +30,7 @@ namespace Conformance { - TEST_CASE("XR_KHR_win32_convert_performance_counter_time", "") + TEST_CASE("XR_KHR_win32_convert_performance_counter_time", "[XR_KHR_win32_convert_performance_counter_time]") { GlobalData& globalData = GetGlobalData(); if (!globalData.IsInstanceExtensionSupported(XR_KHR_WIN32_CONVERT_PERFORMANCE_COUNTER_TIME_EXTENSION_NAME)) { diff --git a/src/conformance/conformance_test/test_XR_META_performance_metrics.cpp b/src/conformance/conformance_test/test_XR_META_performance_metrics.cpp index c6db2d35..e199ebaa 100644 --- a/src/conformance/conformance_test/test_XR_META_performance_metrics.cpp +++ b/src/conformance/conformance_test/test_XR_META_performance_metrics.cpp @@ -29,7 +29,7 @@ namespace Conformance { - TEST_CASE("XR_META_performance_metrics", "") + TEST_CASE("XR_META_performance_metrics", "[XR_META_performance_metrics]") { GlobalData& globalData = GetGlobalData(); diff --git a/src/conformance/conformance_test/test_XR_MND_headless.cpp b/src/conformance/conformance_test/test_XR_MND_headless.cpp index 0b940185..eb6e9d7d 100644 --- a/src/conformance/conformance_test/test_XR_MND_headless.cpp +++ b/src/conformance/conformance_test/test_XR_MND_headless.cpp @@ -24,7 +24,7 @@ namespace Conformance { - TEST_CASE("XR_MND_headless", "") + TEST_CASE("XR_MND_headless", "[XR_MND_headless]") { GlobalData& globalData = GetGlobalData(); diff --git a/src/conformance/conformance_test/test_XR_MSFT_controller_model.cpp b/src/conformance/conformance_test/test_XR_MSFT_controller_model.cpp index c1372e85..5cb63972 100644 --- a/src/conformance/conformance_test/test_XR_MSFT_controller_model.cpp +++ b/src/conformance/conformance_test/test_XR_MSFT_controller_model.cpp @@ -111,7 +111,7 @@ namespace Conformance } }; - TEST_CASE("XR_MSFT_controller_model-simple", "") + TEST_CASE("XR_MSFT_controller_model-simple", "[XR_MSFT_controller_model]") { GlobalData& globalData = GetGlobalData(); @@ -125,7 +125,7 @@ namespace Conformance ext.CheckInvalidModelKey(session, XR_NULL_CONTROLLER_MODEL_KEY_MSFT); } - TEST_CASE("XR_MSFT_controller_model", "") + TEST_CASE("XR_MSFT_controller_model", "[XR_MSFT_controller_model]") { GlobalData& globalData = GetGlobalData(); @@ -307,7 +307,7 @@ namespace Conformance } } - TEST_CASE("XR_MSFT_controller_model-interactive", "[scenario][interactive][no_auto]") + TEST_CASE("XR_MSFT_controller_model-interactive", "[XR_MSFT_controller_model][scenario][interactive][no_auto]") { GlobalData& globalData = GetGlobalData(); @@ -507,7 +507,7 @@ namespace Conformance viewState.viewStateFlags & XR_VIEW_STATE_ORIENTATION_VALID_BIT) { const auto& views = std::get>(viewData); - // Render into each view port of the wide swapchain using the projection layer view fov and pose. + // Render into each of the separate swapchains using the projection layer view fov and pose. for (size_t view = 0; view < views.size(); view++) { compositionHelper.AcquireWaitReleaseImage(swapchains[view], // [&](const XrSwapchainImageBaseHeader* swapchainImage) { diff --git a/src/conformance/conformance_test/test_XR_VARJO_quad_views.cpp b/src/conformance/conformance_test/test_XR_VARJO_quad_views.cpp index 92af1df8..b52774ec 100644 --- a/src/conformance/conformance_test/test_XR_VARJO_quad_views.cpp +++ b/src/conformance/conformance_test/test_XR_VARJO_quad_views.cpp @@ -205,8 +205,8 @@ namespace Conformance } // Explicitly naming view config type and ignoring whatever was configured on the command line - CompositionHelper compositionHelper("Quad Views", instance.get(), - XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO_WITH_FOVEATED_INSET); + CompositionHelper compositionHelper("Quad Views", instance.get(), XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO_WITH_FOVEATED_INSET, + true); InteractiveLayerManager interactiveLayerManager(compositionHelper, "projection_separate.png", "Stereo inset views."); XrSession session = compositionHelper.GetSession(); diff --git a/src/conformance/conformance_test/test_actions.cpp b/src/conformance/conformance_test/test_actions.cpp index f4a8a93e..94e01e8b 100644 --- a/src/conformance/conformance_test/test_actions.cpp +++ b/src/conformance/conformance_test/test_actions.cpp @@ -337,6 +337,10 @@ namespace Conformance satisfied[i] = kInteractionAvailabilities[i].IsSatisfiedBy(features); } } + // cannot copy, cannot move + InteractionAvailabilityEval(InteractionAvailabilityEval&&) = delete; + InteractionAvailabilityEval(const InteractionAvailabilityEval&) = delete; + std::array satisfied{}; }; @@ -603,7 +607,7 @@ namespace Conformance XrInteractionProfileSuggestedBinding bindings{XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING}; bindings.countSuggestedBindings = 1; - + XrActionSuggestedBinding suggestedBindings{}; XrAction boolAction; XrAction floatAction; XrAction vectorAction; @@ -658,7 +662,7 @@ namespace Conformance selectedAction = hapticAction; } - XrActionSuggestedBinding suggestedBindings{selectedAction, StringToPath(instance, pathData.Path)}; + suggestedBindings = XrActionSuggestedBinding{selectedAction, StringToPath(instance, pathData.Path)}; bindings.suggestedBindings = &suggestedBindings; CAPTURE(kInteractionAvailabilities[(size_t)pathData.Availability]); if (SatisfiedByDefault(pathData.Availability)) { @@ -699,6 +703,8 @@ namespace Conformance TEST_CASE("xrSuggestInteractionProfileBindings_interactive", "[actions][interactive]") { CompositionHelper compositionHelper("xrSuggestInteractionProfileBindings"); + XrInstance instance = compositionHelper.GetInstance(); + XrSession session = compositionHelper.GetSession(); compositionHelper.BeginSession(); ActionLayerManager actionLayerManager(compositionHelper); @@ -707,7 +713,7 @@ namespace Conformance XrActionSetCreateInfo actionSetCreateInfo{XR_TYPE_ACTION_SET_CREATE_INFO}; strcpy(actionSetCreateInfo.localizedActionSetName, "test action set localized name"); strcpy(actionSetCreateInfo.actionSetName, "test_action_set_name"); - REQUIRE_RESULT(xrCreateActionSet(compositionHelper.GetInstance(), &actionSetCreateInfo, &actionSet), XR_SUCCESS); + REQUIRE_RESULT(xrCreateActionSet(instance, &actionSetCreateInfo, &actionSet), XR_SUCCESS); XrAction selectActionA{XR_NULL_HANDLE}; XrActionCreateInfo actionCreateInfo{XR_TYPE_ACTION_CREATE_INFO}; @@ -727,28 +733,26 @@ namespace Conformance bool leftUnderTest = GetGlobalData().leftHandUnderTest; const char* pathStr = leftUnderTest ? "/user/hand/left" : "/user/hand/right"; - XrPath path{StringToPath(compositionHelper.GetInstance(), pathStr)}; + XrPath path{StringToPath(instance, pathStr)}; std::shared_ptr inputDevice = - CreateTestDevice(&actionLayerManager, &compositionHelper.GetInteractionManager(), compositionHelper.GetInstance(), - compositionHelper.GetSession(), - StringToPath(compositionHelper.GetInstance(), GetSimpleInteractionProfile().InteractionProfilePathString), - path, GetSimpleInteractionProfile().InputSourcePaths); + CreateTestDevice(&actionLayerManager, &compositionHelper.GetInteractionManager(), instance, session, + StringToPath(instance, GetSimpleInteractionProfile().InteractionProfilePathString), path, + GetSimpleInteractionProfile().InputSourcePaths); compositionHelper.GetInteractionManager().AddActionSet(actionSet); std::string selectPathStr = std::string(pathStr) + "/input/select/click"; - XrPath selectPath = StringToPath(compositionHelper.GetInstance(), selectPathStr); + XrPath selectPath = StringToPath(instance, selectPathStr); XrActionSuggestedBinding testBinding = {selectActionA, selectPath}; XrInteractionProfileSuggestedBinding bindings{XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING}; - bindings.interactionProfile = StringToPath(compositionHelper.GetInstance(), "/interaction_profiles/khr/simple_controller"); + bindings.interactionProfile = StringToPath(instance, "/interaction_profiles/khr/simple_controller"); bindings.countSuggestedBindings = 1; bindings.suggestedBindings = &testBinding; - REQUIRE_RESULT(xrSuggestInteractionProfileBindings(compositionHelper.GetInstance(), &bindings), XR_SUCCESS); + REQUIRE_RESULT(xrSuggestInteractionProfileBindings(instance, &bindings), XR_SUCCESS); // Calling attach on the interaction manager will call xrSuggestInteractionProfileBindings with the bindings provided here, overwriting the previous bindings compositionHelper.GetInteractionManager().AddActionBindings( - StringToPath(compositionHelper.GetInstance(), GetSimpleInteractionProfile().InteractionProfilePathString), - {{{selectActionB, selectPath}}}); + StringToPath(instance, GetSimpleInteractionProfile().InteractionProfilePathString), {{{selectActionB, selectPath}}}); compositionHelper.GetInteractionManager().AttachActionSets(); actionLayerManager.WaitForSessionFocusWithMessage(); @@ -767,11 +771,11 @@ namespace Conformance // selectActionA should have had its bindings discarded and replaced by selectActionB's bindings getInfo.action = selectActionA; - REQUIRE_RESULT(xrGetActionStateBoolean(compositionHelper.GetSession(), &getInfo, &booleanActionState), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateBoolean(session, &getInfo, &booleanActionState), XR_SUCCESS); REQUIRE_FALSE(booleanActionState.isActive); getInfo.action = selectActionB; - REQUIRE_RESULT(xrGetActionStateBoolean(compositionHelper.GetSession(), &getInfo, &booleanActionState), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateBoolean(session, &getInfo, &booleanActionState), XR_SUCCESS); REQUIRE(booleanActionState.isActive); } } @@ -1025,6 +1029,8 @@ namespace Conformance auto suggestBindingsAndGetCurrentInteractionProfile = [](bool reverse, bool nullPathExpected, const std::string& topLevelPathString) { CompositionHelper compositionHelper("xrSuggestInteractionProfileBindings_order"); + XrInstance instance = compositionHelper.GetInstance(); + XrSession session = compositionHelper.GetSession(); compositionHelper.BeginSession(); ActionLayerManager actionLayerManager(compositionHelper); @@ -1033,7 +1039,7 @@ namespace Conformance XrActionSetCreateInfo actionSetCreateInfo{XR_TYPE_ACTION_SET_CREATE_INFO}; strcpy(actionSetCreateInfo.localizedActionSetName, "test action set localized name"); strcpy(actionSetCreateInfo.actionSetName, "test_action_set_name"); - REQUIRE_RESULT(xrCreateActionSet(compositionHelper.GetInstance(), &actionSetCreateInfo, &actionSet), XR_SUCCESS); + REQUIRE_RESULT(xrCreateActionSet(instance, &actionSetCreateInfo, &actionSet), XR_SUCCESS); XrAction boolAction{XR_NULL_HANDLE}; XrActionCreateInfo actionCreateInfo{XR_TYPE_ACTION_CREATE_INFO}; @@ -1052,7 +1058,7 @@ namespace Conformance return; } std::string interactionProfileName = interactionProfile.InteractionProfilePathString; - XrPath interactionProfilePath = StringToPath(compositionHelper.GetInstance(), interactionProfileName); + XrPath interactionProfilePath = StringToPath(instance, interactionProfileName); bool bindingSuggested = false; for (auto& bindings : interactionProfile.InputSourcePaths) { @@ -1065,7 +1071,7 @@ namespace Conformance if (!SatisfiedByDefault(bindings.Availability)) { continue; } - XrActionSuggestedBinding binding = {boolAction, StringToPath(compositionHelper.GetInstance(), bindings.Path)}; + XrActionSuggestedBinding binding = {boolAction, StringToPath(instance, bindings.Path)}; interactionManager.AddActionBindings(interactionProfilePath, {binding}); bindingSuggested = true; } @@ -1089,12 +1095,11 @@ namespace Conformance } // Hardcoded path valid for simple controller - XrPath userHandLeftXrPath{StringToPath(compositionHelper.GetInstance(), topLevelPathString)}; + XrPath userHandLeftXrPath{StringToPath(instance, topLevelPathString)}; std::shared_ptr inputDevice = - CreateTestDevice(&actionLayerManager, &compositionHelper.GetInteractionManager(), compositionHelper.GetInstance(), - compositionHelper.GetSession(), - StringToPath(compositionHelper.GetInstance(), GetSimpleInteractionProfile().InteractionProfilePathString), - userHandLeftXrPath, GetSimpleInteractionProfile().InputSourcePaths); + CreateTestDevice(&actionLayerManager, &compositionHelper.GetInteractionManager(), instance, session, + StringToPath(instance, GetSimpleInteractionProfile().InteractionProfilePathString), userHandLeftXrPath, + GetSimpleInteractionProfile().InputSourcePaths); // This function calls xrSuggestInteractionProfileBindings() before attaching the actionsets interactionManager.AttachActionSets(&interactionProfileOrder); @@ -1103,10 +1108,8 @@ namespace Conformance inputDevice->SetDeviceActive(/*state = */ true, /*skipInteraction = */ false, boolAction, actionSet); actionLayerManager.WaitForSessionFocusWithMessage(); XrInteractionProfileState interactionProfileState{XR_TYPE_INTERACTION_PROFILE_STATE}; - REQUIRE_RESULT( - xrGetCurrentInteractionProfile(compositionHelper.GetSession(), - StringToPath(compositionHelper.GetInstance(), topLevelPathString), &interactionProfileState), - XR_SUCCESS); + REQUIRE_RESULT(xrGetCurrentInteractionProfile(session, StringToPath(instance, topLevelPathString), &interactionProfileState), + XR_SUCCESS); // Are we expecting the topLevelPath to have a active input? if (nullPathExpected) { @@ -1121,7 +1124,7 @@ namespace Conformance REQUIRE(interactionProfileState.interactionProfile != XR_NULL_PATH); // XrPaths are only valid for the lifetime of the instance so we return a string - return PathToString(compositionHelper.GetInstance(), interactionProfileState.interactionProfile); + return PathToString(instance, interactionProfileState.interactionProfile); } }; @@ -1148,18 +1151,19 @@ namespace Conformance TEST_CASE("xrGetCurrentInteractionProfile", "[actions][interactive]") { CompositionHelper compositionHelper("xrGetCurrentInteractionProfile"); + XrInstance instance = compositionHelper.GetInstance(); + XrSession session = compositionHelper.GetSession(); compositionHelper.BeginSession(); ActionLayerManager actionLayerManager(compositionHelper); - XrPath simpleControllerInteractionProfile = - StringToPath(compositionHelper.GetInstance(), GetSimpleInteractionProfile().InteractionProfilePathString); + XrPath simpleControllerInteractionProfile = StringToPath(instance, GetSimpleInteractionProfile().InteractionProfilePathString); XrActionSet actionSet{XR_NULL_HANDLE}; XrActionSetCreateInfo actionSetCreateInfo{XR_TYPE_ACTION_SET_CREATE_INFO}; strcpy(actionSetCreateInfo.localizedActionSetName, "test action set localized name"); strcpy(actionSetCreateInfo.actionSetName, "test_action_set_name"); - REQUIRE_RESULT(xrCreateActionSet(compositionHelper.GetInstance(), &actionSetCreateInfo, &actionSet), XR_SUCCESS); + REQUIRE_RESULT(xrCreateActionSet(instance, &actionSetCreateInfo, &actionSet), XR_SUCCESS); XrAction selectAction{XR_NULL_HANDLE}; XrActionCreateInfo actionCreateInfo{XR_TYPE_ACTION_CREATE_INFO}; @@ -1168,19 +1172,17 @@ namespace Conformance strcpy(actionCreateInfo.actionName, "test_select_action"); REQUIRE_RESULT(xrCreateAction(actionSet, &actionCreateInfo, &selectAction), XR_SUCCESS); - XrPath leftHandPath{StringToPath(compositionHelper.GetInstance(), "/user/hand/left")}; + XrPath leftHandPath{StringToPath(instance, "/user/hand/left")}; std::shared_ptr leftHandInputDevice = - CreateTestDevice(&actionLayerManager, &compositionHelper.GetInteractionManager(), compositionHelper.GetInstance(), - compositionHelper.GetSession(), - StringToPath(compositionHelper.GetInstance(), GetSimpleInteractionProfile().InteractionProfilePathString), - leftHandPath, GetSimpleInteractionProfile().InputSourcePaths); + CreateTestDevice(&actionLayerManager, &compositionHelper.GetInteractionManager(), instance, session, + StringToPath(instance, GetSimpleInteractionProfile().InteractionProfilePathString), leftHandPath, + GetSimpleInteractionProfile().InputSourcePaths); - XrPath rightHandPath{StringToPath(compositionHelper.GetInstance(), "/user/hand/right")}; + XrPath rightHandPath{StringToPath(instance, "/user/hand/right")}; std::shared_ptr rightHandInputDevice = - CreateTestDevice(&actionLayerManager, &compositionHelper.GetInteractionManager(), compositionHelper.GetInstance(), - compositionHelper.GetSession(), - StringToPath(compositionHelper.GetInstance(), GetSimpleInteractionProfile().InteractionProfilePathString), - rightHandPath, GetSimpleInteractionProfile().InputSourcePaths); + CreateTestDevice(&actionLayerManager, &compositionHelper.GetInteractionManager(), instance, session, + StringToPath(instance, GetSimpleInteractionProfile().InteractionProfilePathString), rightHandPath, + GetSimpleInteractionProfile().InputSourcePaths); XrActionsSyncInfo syncInfo{XR_TYPE_ACTIONS_SYNC_INFO}; XrActiveActionSet activeActionSet{actionSet}; @@ -1195,9 +1197,9 @@ namespace Conformance compositionHelper.GetInteractionManager().AddActionSet(actionSet); compositionHelper.GetInteractionManager().AddActionBindings( - StringToPath(compositionHelper.GetInstance(), GetSimpleInteractionProfile().InteractionProfilePathString), - {{{selectAction, StringToPath(compositionHelper.GetInstance(), "/user/hand/left/input/select/click")}, - {selectAction, StringToPath(compositionHelper.GetInstance(), "/user/hand/right/input/select/click")}}}); + StringToPath(instance, GetSimpleInteractionProfile().InteractionProfilePathString), + {{{selectAction, StringToPath(instance, "/user/hand/left/input/select/click")}, + {selectAction, StringToPath(instance, "/user/hand/right/input/select/click")}}}); compositionHelper.GetInteractionManager().AttachActionSets(); { @@ -1205,13 +1207,11 @@ namespace Conformance { INFO("Basic usage"); - REQUIRE_RESULT(xrGetCurrentInteractionProfile(compositionHelper.GetSession(), leftHandPath, &interactionProfileState), - XR_SUCCESS); + REQUIRE_RESULT(xrGetCurrentInteractionProfile(session, leftHandPath, &interactionProfileState), XR_SUCCESS); } { INFO("XR_NULL_PATH topLevelPath"); - REQUIRE_RESULT(xrGetCurrentInteractionProfile(compositionHelper.GetSession(), XR_NULL_PATH, &interactionProfileState), - XR_ERROR_PATH_INVALID); + REQUIRE_RESULT(xrGetCurrentInteractionProfile(session, XR_NULL_PATH, &interactionProfileState), XR_ERROR_PATH_INVALID); } OPTIONAL_INVALID_HANDLE_VALIDATION_INFO { @@ -1222,21 +1222,19 @@ namespace Conformance { INFO("Invalid top level path"); XrPath invalidTopLevelPath = (XrPath)0x1234; - REQUIRE_RESULT( - xrGetCurrentInteractionProfile(compositionHelper.GetSession(), invalidTopLevelPath, &interactionProfileState), - XR_ERROR_PATH_INVALID); + REQUIRE_RESULT(xrGetCurrentInteractionProfile(session, invalidTopLevelPath, &interactionProfileState), + XR_ERROR_PATH_INVALID); } { INFO("Unsupported top level path"); - XrPath unsupportedTopLevelPath = StringToPath(compositionHelper.GetInstance(), "/invalid/top/level/path"); - REQUIRE_RESULT( - xrGetCurrentInteractionProfile(compositionHelper.GetSession(), unsupportedTopLevelPath, &interactionProfileState), - XR_ERROR_PATH_UNSUPPORTED); + XrPath unsupportedTopLevelPath = StringToPath(instance, "/invalid/top/level/path"); + REQUIRE_RESULT(xrGetCurrentInteractionProfile(session, unsupportedTopLevelPath, &interactionProfileState), + XR_ERROR_PATH_UNSUPPORTED); } { INFO("Invalid type"); interactionProfileState = XrInteractionProfileState{XR_TYPE_ACTION_CREATE_INFO}; - REQUIRE_RESULT(xrGetCurrentInteractionProfile(compositionHelper.GetSession(), leftHandPath, &interactionProfileState), + REQUIRE_RESULT(xrGetCurrentInteractionProfile(session, leftHandPath, &interactionProfileState), XR_ERROR_VALIDATION_FAILURE); interactionProfileState = XrInteractionProfileState{XR_TYPE_INTERACTION_PROFILE_STATE}; } @@ -1271,13 +1269,11 @@ namespace Conformance REQUIRE(ReadUntilEvent(XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED, 1s)); if (globalData.leftHandUnderTest) { - REQUIRE_RESULT(xrGetCurrentInteractionProfile(compositionHelper.GetSession(), leftHandPath, &interactionProfileState), - XR_SUCCESS); + REQUIRE_RESULT(xrGetCurrentInteractionProfile(session, leftHandPath, &interactionProfileState), XR_SUCCESS); REQUIRE(simpleControllerInteractionProfile == interactionProfileState.interactionProfile); } if (globalData.rightHandUnderTest) { - REQUIRE_RESULT(xrGetCurrentInteractionProfile(compositionHelper.GetSession(), rightHandPath, &interactionProfileState), - XR_SUCCESS); + REQUIRE_RESULT(xrGetCurrentInteractionProfile(session, rightHandPath, &interactionProfileState), XR_SUCCESS); REQUIRE(simpleControllerInteractionProfile == interactionProfileState.interactionProfile); } } @@ -1291,25 +1287,24 @@ namespace Conformance if (globalData.IsInstanceExtensionSupported(XR_EXT_ACTIVE_ACTION_SET_PRIORITY_EXTENSION_NAME)) extensions.push_back(XR_EXT_ACTIVE_ACTION_SET_PRIORITY_EXTENSION_NAME); CompositionHelper compositionHelper("xrSyncActions", extensions); + XrInstance instance = compositionHelper.GetInstance(); + XrSession session = compositionHelper.GetSession(); ActionLayerManager actionLayerManager(compositionHelper); - XrPath simpleControllerInteractionProfile = - StringToPath(compositionHelper.GetInstance(), GetSimpleInteractionProfile().InteractionProfilePathString); + XrPath simpleControllerInteractionProfile = StringToPath(instance, GetSimpleInteractionProfile().InteractionProfilePathString); std::string leftHandPathString = "/user/hand/left"; - XrPath leftHandPath{StringToPath(compositionHelper.GetInstance(), "/user/hand/left")}; + XrPath leftHandPath{StringToPath(instance, "/user/hand/left")}; std::shared_ptr leftHandInputDevice = - CreateTestDevice(&actionLayerManager, &compositionHelper.GetInteractionManager(), compositionHelper.GetInstance(), - compositionHelper.GetSession(), simpleControllerInteractionProfile, leftHandPath, - GetSimpleInteractionProfile().InputSourcePaths); + CreateTestDevice(&actionLayerManager, &compositionHelper.GetInteractionManager(), instance, session, + simpleControllerInteractionProfile, leftHandPath, GetSimpleInteractionProfile().InputSourcePaths); std::string rightHandPathString = "/user/hand/right"; - XrPath rightHandPath{StringToPath(compositionHelper.GetInstance(), "/user/hand/right")}; + XrPath rightHandPath{StringToPath(instance, "/user/hand/right")}; std::shared_ptr rightHandInputDevice = - CreateTestDevice(&actionLayerManager, &compositionHelper.GetInteractionManager(), compositionHelper.GetInstance(), - compositionHelper.GetSession(), simpleControllerInteractionProfile, rightHandPath, - GetSimpleInteractionProfile().InputSourcePaths); + CreateTestDevice(&actionLayerManager, &compositionHelper.GetInteractionManager(), instance, session, + simpleControllerInteractionProfile, rightHandPath, GetSimpleInteractionProfile().InputSourcePaths); bool leftUnderTest = GetGlobalData().leftHandUnderTest; std::string defaultDevicePathStr = leftUnderTest ? leftHandPathString : rightHandPathString; @@ -1320,7 +1315,7 @@ namespace Conformance XrActionSetCreateInfo actionSetCreateInfo{XR_TYPE_ACTION_SET_CREATE_INFO}; strcpy(actionSetCreateInfo.localizedActionSetName, "test action set localized name"); strcpy(actionSetCreateInfo.actionSetName, "test_action_set_name"); - REQUIRE_RESULT(xrCreateActionSet(compositionHelper.GetInstance(), &actionSetCreateInfo, &actionSet), XR_SUCCESS); + REQUIRE_RESULT(xrCreateActionSet(instance, &actionSetCreateInfo, &actionSet), XR_SUCCESS); XrAction action{XR_NULL_HANDLE}; XrActionCreateInfo actionCreateInfo{XR_TYPE_ACTION_CREATE_INFO}; @@ -1346,7 +1341,7 @@ namespace Conformance { INFO("No action sets attached"); - REQUIRE_RESULT_SUCCEEDED(xrSyncActions(compositionHelper.GetSession(), &syncInfo)); + REQUIRE_RESULT_SUCCEEDED(xrSyncActions(session, &syncInfo)); } { INFO("With action sets attached"); @@ -1354,13 +1349,13 @@ namespace Conformance compositionHelper.GetInteractionManager().AddActionSet(actionSet); compositionHelper.GetInteractionManager().AttachActionSets(); - REQUIRE_RESULT_SUCCEEDED(xrSyncActions(compositionHelper.GetSession(), &syncInfo)); + REQUIRE_RESULT_SUCCEEDED(xrSyncActions(session, &syncInfo)); } } SECTION("Active action sets") { XrInteractionProfileState interactionProfileState{XR_TYPE_INTERACTION_PROFILE_STATE}; - REQUIRE_RESULT(xrGetCurrentInteractionProfile(compositionHelper.GetSession(), defaultDevicePath, &interactionProfileState), + REQUIRE_RESULT(xrGetCurrentInteractionProfile(session, defaultDevicePath, &interactionProfileState), XR_ERROR_ACTIONSET_NOT_ATTACHED); compositionHelper.GetInteractionManager().AddActionSet(actionSet); @@ -1368,9 +1363,7 @@ namespace Conformance { INFO("Interaction profile selection changes must: only happen when flink:xrSyncActions is called."); - REQUIRE_RESULT( - xrGetCurrentInteractionProfile(compositionHelper.GetSession(), defaultDevicePath, &interactionProfileState), - XR_SUCCESS); + REQUIRE_RESULT(xrGetCurrentInteractionProfile(session, defaultDevicePath, &interactionProfileState), XR_SUCCESS); // per spec: "Interaction profile selection changes must: only happen when flink:xrSyncActions is called." REQUIRE(interactionProfileState.interactionProfile == XR_NULL_PATH); } @@ -1380,9 +1373,9 @@ namespace Conformance syncInfo.activeActionSets = &activeActionSet; syncInfo.countActiveActionSets = 1; - REQUIRE_RESULT(xrSyncActions(compositionHelper.GetSession(), &syncInfo), XR_SESSION_NOT_FOCUSED); + REQUIRE_RESULT(xrSyncActions(session, &syncInfo), XR_SESSION_NOT_FOCUSED); - REQUIRE_RESULT(xrGetActionStateBoolean(compositionHelper.GetSession(), &getInfo, &actionStateBoolean), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateBoolean(session, &getInfo, &actionStateBoolean), XR_SUCCESS); REQUIRE_FALSE(actionStateBoolean.isActive); } } @@ -1393,7 +1386,7 @@ namespace Conformance SECTION("Parameter validation") { std::string selectPathStr = defaultDevicePathStr + "/input/select/click"; - XrPath selectPath = StringToPath(compositionHelper.GetInstance(), selectPathStr); + XrPath selectPath = StringToPath(instance, selectPathStr); compositionHelper.GetInteractionManager().AddActionSet(actionSet); compositionHelper.GetInteractionManager().AddActionBindings(simpleControllerInteractionProfile, {{action, selectPath}}); compositionHelper.GetInteractionManager().AttachActionSets(); @@ -1415,7 +1408,7 @@ namespace Conformance actionLayerManager.SyncActionsUntilFocusWithMessage(syncInfo); - REQUIRE_RESULT(xrGetActionStateBoolean(compositionHelper.GetSession(), &getInfo, &actionStateBoolean), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateBoolean(session, &getInfo, &actionStateBoolean), XR_SUCCESS); REQUIRE(actionStateBoolean.isActive); REQUIRE_FALSE(actionStateBoolean.currentState); @@ -1426,40 +1419,37 @@ namespace Conformance actionLayerManager.SyncActionsUntilFocusWithMessage(syncInfo); - REQUIRE_RESULT(xrGetActionStateBoolean(compositionHelper.GetSession(), &getInfo, &actionStateBoolean), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateBoolean(session, &getInfo, &actionStateBoolean), XR_SUCCESS); REQUIRE(actionStateBoolean.isActive); REQUIRE(actionStateBoolean.currentState); - REQUIRE_RESULT(xrGetActionStateBoolean(compositionHelper.GetSession(), &getInfo, &actionStateBoolean), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateBoolean(session, &getInfo, &actionStateBoolean), XR_SUCCESS); REQUIRE(actionStateBoolean.isActive); REQUIRE(actionStateBoolean.currentState); } OPTIONAL_DISCONNECTABLE_DEVICE_INFO { - actionLayerManager.DisplayMessage("Turn off " + defaultDevicePathStr + " and wait for 20s"); - defaultInputDevice->SetDeviceActive(false, true); + defaultInputDevice->SetDeviceActive(false, true, XR_NULL_HANDLE, XR_NULL_HANDLE, " and wait for 20s"); WaitUntilPredicateWithTimeout( [&]() { actionLayerManager.GetRenderLoop().IterateFrame(); - REQUIRE_RESULT(xrGetActionStateBoolean(compositionHelper.GetSession(), &getInfo, &actionStateBoolean), - XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateBoolean(session, &getInfo, &actionStateBoolean), XR_SUCCESS); REQUIRE(actionStateBoolean.isActive); REQUIRE(actionStateBoolean.currentState); return false; }, 20s, kActionWaitDelay); - actionLayerManager.DisplayMessage("Wait for 5s"); - actionLayerManager.SyncActionsUntilFocusWithMessage(syncInfo); + actionLayerManager.DisplayMessage("Wait for 5s"); + WaitUntilPredicateWithTimeout( [&]() { actionLayerManager.GetRenderLoop().IterateFrame(); - REQUIRE_RESULT(xrGetActionStateBoolean(compositionHelper.GetSession(), &getInfo, &actionStateBoolean), - XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateBoolean(session, &getInfo, &actionStateBoolean), XR_SUCCESS); REQUIRE_FALSE(actionStateBoolean.isActive); REQUIRE_FALSE(actionStateBoolean.currentState); return false; @@ -1484,7 +1474,7 @@ namespace Conformance strcpy(setCreateInfo.actionSetName, "high_priority_action_set"); strcpy(setCreateInfo.localizedActionSetName, "high priority action set"); setCreateInfo.priority = 3; - REQUIRE_RESULT(xrCreateActionSet(compositionHelper.GetInstance(), &setCreateInfo, &highPriorityActionSet), XR_SUCCESS); + REQUIRE_RESULT(xrCreateActionSet(instance, &setCreateInfo, &highPriorityActionSet), XR_SUCCESS); XrAction highPrioritySelectAction{XR_NULL_HANDLE}; XrAction highPrioritySelectAction2{XR_NULL_HANDLE}; @@ -1504,7 +1494,7 @@ namespace Conformance strcpy(setCreateInfo.actionSetName, "low_priority_action_set"); strcpy(setCreateInfo.localizedActionSetName, "low priority action set"); setCreateInfo.priority = 2; - REQUIRE_RESULT(xrCreateActionSet(compositionHelper.GetInstance(), &setCreateInfo, &lowPriorityActionSet), XR_SUCCESS); + REQUIRE_RESULT(xrCreateActionSet(instance, &setCreateInfo, &lowPriorityActionSet), XR_SUCCESS); XrAction lowPrioritySelectAction{XR_NULL_HANDLE}; XrAction lowPriorityMenuAction{XR_NULL_HANDLE}; @@ -1525,21 +1515,18 @@ namespace Conformance compositionHelper.GetInteractionManager().AddActionBindings( simpleControllerInteractionProfile, { - {highPrioritySelectAction, StringToPath(compositionHelper.GetInstance(), "/user/hand/left/input/select/click")}, - {highPrioritySelectAction, StringToPath(compositionHelper.GetInstance(), "/user/hand/right/input/select/click")}, - {highPrioritySelectAction2, StringToPath(compositionHelper.GetInstance(), "/user/hand/left/input/select/click")}, - {highPrioritySelectAction2, StringToPath(compositionHelper.GetInstance(), "/user/hand/right/input/select/click")}, - {lowPrioritySelectAction, StringToPath(compositionHelper.GetInstance(), "/user/hand/left/input/select/click")}, - {lowPrioritySelectAction, StringToPath(compositionHelper.GetInstance(), "/user/hand/right/input/select/click")}, - {lowPriorityMenuAction, StringToPath(compositionHelper.GetInstance(), "/user/hand/left/input/menu/click")}, - {lowPriorityMenuAction, StringToPath(compositionHelper.GetInstance(), "/user/hand/right/input/menu/click")}, - {lowPrioritySelectAndMenuAction, - StringToPath(compositionHelper.GetInstance(), "/user/hand/left/input/select/click")}, - {lowPrioritySelectAndMenuAction, StringToPath(compositionHelper.GetInstance(), "/user/hand/left/input/menu/click")}, - {lowPrioritySelectAndMenuAction, - StringToPath(compositionHelper.GetInstance(), "/user/hand/right/input/select/click")}, - {lowPrioritySelectAndMenuAction, - StringToPath(compositionHelper.GetInstance(), "/user/hand/right/input/menu/click")}, + {highPrioritySelectAction, StringToPath(instance, "/user/hand/left/input/select/click")}, + {highPrioritySelectAction, StringToPath(instance, "/user/hand/right/input/select/click")}, + {highPrioritySelectAction2, StringToPath(instance, "/user/hand/left/input/select/click")}, + {highPrioritySelectAction2, StringToPath(instance, "/user/hand/right/input/select/click")}, + {lowPrioritySelectAction, StringToPath(instance, "/user/hand/left/input/select/click")}, + {lowPrioritySelectAction, StringToPath(instance, "/user/hand/right/input/select/click")}, + {lowPriorityMenuAction, StringToPath(instance, "/user/hand/left/input/menu/click")}, + {lowPriorityMenuAction, StringToPath(instance, "/user/hand/right/input/menu/click")}, + {lowPrioritySelectAndMenuAction, StringToPath(instance, "/user/hand/left/input/select/click")}, + {lowPrioritySelectAndMenuAction, StringToPath(instance, "/user/hand/left/input/menu/click")}, + {lowPrioritySelectAndMenuAction, StringToPath(instance, "/user/hand/right/input/select/click")}, + {lowPrioritySelectAndMenuAction, StringToPath(instance, "/user/hand/right/input/menu/click")}, }); compositionHelper.GetInteractionManager().AddActionSet(highPriorityActionSet); @@ -1563,7 +1550,7 @@ namespace Conformance getInfo.action = action; getInfo.subactionPath = subactionPath; XrActionStateBoolean booleanData{XR_TYPE_ACTION_STATE_BOOLEAN}; - REQUIRE_RESULT(xrGetActionStateBoolean(compositionHelper.GetSession(), &getInfo, &booleanData), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateBoolean(session, &getInfo, &booleanData), XR_SUCCESS); return static_cast(booleanData.isActive); }; @@ -1820,14 +1807,12 @@ namespace Conformance XrActionSet subactionPathFreeActionSet{XR_NULL_HANDLE}; strcpy(actionSetCreateInfo.localizedActionSetName, "test action set localized name 2"); strcpy(actionSetCreateInfo.actionSetName, "test_action_set_name_2"); - REQUIRE_RESULT(xrCreateActionSet(compositionHelper.GetInstance(), &actionSetCreateInfo, &subactionPathFreeActionSet), - XR_SUCCESS); + REQUIRE_RESULT(xrCreateActionSet(instance, &actionSetCreateInfo, &subactionPathFreeActionSet), XR_SUCCESS); XrActionSet unboundActionActionSet{XR_NULL_HANDLE}; strcpy(actionSetCreateInfo.localizedActionSetName, "test action set localized name 5"); strcpy(actionSetCreateInfo.actionSetName, "test_action_set_name_5"); - REQUIRE_RESULT(xrCreateActionSet(compositionHelper.GetInstance(), &actionSetCreateInfo, &unboundActionActionSet), - XR_SUCCESS); + REQUIRE_RESULT(xrCreateActionSet(instance, &actionSetCreateInfo, &unboundActionActionSet), XR_SUCCESS); XrAction leftHandAction{XR_NULL_HANDLE}; actionCreateInfo.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT; @@ -1850,9 +1835,8 @@ namespace Conformance REQUIRE_RESULT(xrCreateAction(unboundActionActionSet, &actionCreateInfo, &unboundAction), XR_SUCCESS); compositionHelper.GetInteractionManager().AddActionBindings( - simpleControllerInteractionProfile, - {{leftHandAction, StringToPath(compositionHelper.GetInstance(), "/user/hand/left/input/select/click")}, - {rightHandAction, StringToPath(compositionHelper.GetInstance(), "/user/hand/right/input/select/click")}}); + simpleControllerInteractionProfile, {{leftHandAction, StringToPath(instance, "/user/hand/left/input/select/click")}, + {rightHandAction, StringToPath(instance, "/user/hand/right/input/select/click")}}); compositionHelper.GetInteractionManager().AddActionSet(actionSet); compositionHelper.GetInteractionManager().AddActionSet(subactionPathFreeActionSet); compositionHelper.GetInteractionManager().AddActionSet(unboundActionActionSet); @@ -1882,18 +1866,17 @@ namespace Conformance actionLayerManager.SyncActionsUntilFocusWithMessage(syncInfo); getInfo.action = leftHandAction; - REQUIRE_RESULT(xrGetActionStateBoolean(compositionHelper.GetSession(), &getInfo, &actionStateBoolean), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateBoolean(session, &getInfo, &actionStateBoolean), XR_SUCCESS); REQUIRE(actionStateBoolean.isActive); getInfo.action = rightHandAction; - REQUIRE_RESULT(xrGetActionStateBoolean(compositionHelper.GetSession(), &getInfo, &actionStateBoolean), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateBoolean(session, &getInfo, &actionStateBoolean), XR_SUCCESS); REQUIRE_FALSE(actionStateBoolean.isActive); { INFO("Values match those specified for isActive == XR_FALSE"); // Set these to the wrong thing if not active, to make sure runtime overwrites the values PoisonStructContents(actionStateBoolean); - REQUIRE_RESULT(xrGetActionStateBoolean(compositionHelper.GetSession(), &getInfo, &actionStateBoolean), - XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateBoolean(session, &getInfo, &actionStateBoolean), XR_SUCCESS); REQUIRE_FALSE(actionStateBoolean.isActive); // The conformance layer will verify that the other fields have been cleared appropriately. } @@ -1905,19 +1888,18 @@ namespace Conformance actionLayerManager.SyncActionsUntilFocusWithMessage(syncInfo); getInfo.action = leftHandAction; - REQUIRE_RESULT(xrGetActionStateBoolean(compositionHelper.GetSession(), &getInfo, &actionStateBoolean), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateBoolean(session, &getInfo, &actionStateBoolean), XR_SUCCESS); REQUIRE_FALSE(actionStateBoolean.isActive); getInfo.action = rightHandAction; - REQUIRE_RESULT(xrGetActionStateBoolean(compositionHelper.GetSession(), &getInfo, &actionStateBoolean), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateBoolean(session, &getInfo, &actionStateBoolean), XR_SUCCESS); REQUIRE(actionStateBoolean.isActive); # { INFO("Values match those specified for isActive == XR_FALSE"); // Set these to the wrong thing if not active, to make sure runtime overwrites the values PoisonStructContents(actionStateBoolean); - REQUIRE_RESULT(xrGetActionStateBoolean(compositionHelper.GetSession(), &getInfo, &actionStateBoolean), - XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateBoolean(session, &getInfo, &actionStateBoolean), XR_SUCCESS); REQUIRE(actionStateBoolean.isActive); // The conformance layer will verify that the other fields have been cleared appropriately. } @@ -1933,11 +1915,11 @@ namespace Conformance actionLayerManager.SyncActionsUntilFocusWithMessage(syncInfo); getInfo.action = leftHandAction; - REQUIRE_RESULT(xrGetActionStateBoolean(compositionHelper.GetSession(), &getInfo, &actionStateBoolean), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateBoolean(session, &getInfo, &actionStateBoolean), XR_SUCCESS); REQUIRE(actionStateBoolean.isActive); getInfo.action = rightHandAction; - REQUIRE_RESULT(xrGetActionStateBoolean(compositionHelper.GetSession(), &getInfo, &actionStateBoolean), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateBoolean(session, &getInfo, &actionStateBoolean), XR_SUCCESS); REQUIRE(actionStateBoolean.isActive); } @@ -1952,13 +1934,12 @@ namespace Conformance INFO("Subaction path used but not declared"); subactionPathFreeActiveActionSet.subactionPath = defaultDevicePath; - REQUIRE_RESULT(xrSyncActions(compositionHelper.GetSession(), &syncInfo), XR_ERROR_PATH_UNSUPPORTED); + REQUIRE_RESULT(xrSyncActions(session, &syncInfo), XR_ERROR_PATH_UNSUPPORTED); XrActionSet unattachedActionSet{XR_NULL_HANDLE}; strcpy(actionSetCreateInfo.localizedActionSetName, "test action set localized name 3"); strcpy(actionSetCreateInfo.actionSetName, "test_action_set_name_3"); - REQUIRE_RESULT(xrCreateActionSet(compositionHelper.GetInstance(), &actionSetCreateInfo, &unattachedActionSet), - XR_SUCCESS); + REQUIRE_RESULT(xrCreateActionSet(instance, &actionSetCreateInfo, &unattachedActionSet), XR_SUCCESS); INFO("Unbound action"); syncInfo.activeActionSets = &unboundActionActiveActionSet; @@ -1969,19 +1950,19 @@ namespace Conformance XrActiveActionSet activeActionSet2 = {unattachedActionSet}; syncInfo.countActiveActionSets = 1; syncInfo.activeActionSets = &activeActionSet2; - REQUIRE_RESULT(xrSyncActions(compositionHelper.GetSession(), &syncInfo), XR_ERROR_ACTIONSET_NOT_ATTACHED); + REQUIRE_RESULT(xrSyncActions(session, &syncInfo), XR_ERROR_ACTIONSET_NOT_ATTACHED); XrActiveActionSet bothSets[2] = {{actionSet}, {unattachedActionSet}}; syncInfo.countActiveActionSets = 2; syncInfo.activeActionSets = bothSets; - REQUIRE_RESULT(xrSyncActions(compositionHelper.GetSession(), &syncInfo), XR_ERROR_ACTIONSET_NOT_ATTACHED); + REQUIRE_RESULT(xrSyncActions(session, &syncInfo), XR_ERROR_ACTIONSET_NOT_ATTACHED); } { INFO("Invalid subaction path"); syncInfo.countActiveActionSets = 1; syncInfo.activeActionSets = &activeActionSet; activeActionSet.subactionPath = (XrPath)0x1234; - REQUIRE_RESULT(xrSyncActions(compositionHelper.GetSession(), &syncInfo), XR_ERROR_PATH_INVALID); + REQUIRE_RESULT(xrSyncActions(session, &syncInfo), XR_ERROR_PATH_INVALID); } } } @@ -2005,16 +1986,18 @@ namespace Conformance auto TestInteractionProfile = [&](const InteractionProfileAvailMetadata& ipMetadata, const std::string& topLevelPathString) { CompositionHelper compositionHelper("Input device state query"); + XrInstance instance = compositionHelper.GetInstance(); + XrSession session = compositionHelper.GetSession(); compositionHelper.BeginSession(); ActionLayerManager actionLayerManager(compositionHelper); actionLayerManager.WaitForSessionFocusWithMessage(); - XrPath interactionProfile = StringToPath(compositionHelper.GetInstance(), ipMetadata.InteractionProfilePathString); - XrPath inputDevicePath{StringToPath(compositionHelper.GetInstance(), topLevelPathString.data())}; + XrPath interactionProfile = StringToPath(instance, ipMetadata.InteractionProfilePathString); + XrPath inputDevicePath{StringToPath(instance, topLevelPathString.data())}; std::shared_ptr inputDevice = - CreateTestDevice(&actionLayerManager, &compositionHelper.GetInteractionManager(), compositionHelper.GetInstance(), - compositionHelper.GetSession(), interactionProfile, inputDevicePath, ipMetadata.InputSourcePaths); + CreateTestDevice(&actionLayerManager, &compositionHelper.GetInteractionManager(), instance, session, interactionProfile, + inputDevicePath, ipMetadata.InputSourcePaths); XrActionSet actionSet{XR_NULL_HANDLE}; @@ -2024,7 +2007,7 @@ namespace Conformance XrActionSetCreateInfo actionSetCreateInfo{XR_TYPE_ACTION_SET_CREATE_INFO}; strcpy(actionSetCreateInfo.localizedActionSetName, localizedActionSetName.c_str()); strcpy(actionSetCreateInfo.actionSetName, actionSetName.c_str()); - REQUIRE_RESULT(xrCreateActionSet(compositionHelper.GetInstance(), &actionSetCreateInfo, &actionSet), XR_SUCCESS); + REQUIRE_RESULT(xrCreateActionSet(instance, &actionSetCreateInfo, &actionSet), XR_SUCCESS); uint32_t uniqueActionNameCounter = 0; auto GetActionNames = [&uniqueActionNameCounter]() mutable -> std::tuple { @@ -2094,7 +2077,7 @@ namespace Conformance strcpy(actionCreateInfo.actionName, std::get<0>(actionNames).c_str()); REQUIRE_RESULT(xrCreateAction(actionSet, &actionCreateInfo, &action), XR_SUCCESS); - XrPath bindingPath = StringToPath(compositionHelper.GetInstance(), inputSourceData.Path); + XrPath bindingPath = StringToPath(instance, inputSourceData.Path); compositionHelper.GetInteractionManager().AddActionBindings(interactionProfile, {{action, bindingPath}}); ActionInfo info{}; @@ -2139,7 +2122,7 @@ namespace Conformance REQUIRE_RESULT(xrCreateAction(actionSet, &actionCreateInfo, &xAction), XR_SUCCESS); std::string xSubBindingPath = std::string(inputSourceData.Path) + "/x"; - bindingPath = StringToPath(compositionHelper.GetInstance(), xSubBindingPath); + bindingPath = StringToPath(instance, xSubBindingPath); compositionHelper.GetInteractionManager().AddActionBindings(interactionProfile, {{xAction, bindingPath}}); actionNames = GetActionNames(); @@ -2148,7 +2131,7 @@ namespace Conformance REQUIRE_RESULT(xrCreateAction(actionSet, &actionCreateInfo, &yAction), XR_SUCCESS); std::string ySubBindingPath = std::string(inputSourceData.Path) + "/y"; - bindingPath = StringToPath(compositionHelper.GetInstance(), ySubBindingPath); + bindingPath = StringToPath(instance, ySubBindingPath); compositionHelper.GetInteractionManager().AddActionBindings(interactionProfile, {{yAction, bindingPath}}); break; } @@ -2237,7 +2220,7 @@ namespace Conformance strcpy(actionCreateInfo.actionName, std::get<0>(actionNames).c_str()); REQUIRE_RESULT(xrCreateAction(actionSet, &actionCreateInfo, &action), XR_SUCCESS); - XrPath bindingPath = StringToPath(compositionHelper.GetInstance(), inputSourceData.Path); + XrPath bindingPath = StringToPath(instance, inputSourceData.Path); compositionHelper.GetInteractionManager().AddActionBindings(interactionProfile, {{action, bindingPath}}); ActionInfo info{}; @@ -2268,7 +2251,7 @@ namespace Conformance continue; } - XrPath bindingPath = StringToPath(compositionHelper.GetInstance(), inputSourceData.Path); + XrPath bindingPath = StringToPath(instance, inputSourceData.Path); compositionHelper.GetInteractionManager().AddActionBindings(interactionProfile, {{action, bindingPath}}); } @@ -2323,8 +2306,7 @@ namespace Conformance actionLayerManager.SyncActionsUntilFocusWithMessage(syncInfo); XrInteractionProfileState interactionProfileState{XR_TYPE_INTERACTION_PROFILE_STATE}; - REQUIRE_RESULT(xrGetCurrentInteractionProfile(compositionHelper.GetSession(), inputDevicePath, &interactionProfileState), - XR_SUCCESS); + REQUIRE_RESULT(xrGetCurrentInteractionProfile(session, inputDevicePath, &interactionProfileState), XR_SUCCESS); REQUIRE(interactionProfile == interactionProfileState.interactionProfile); XrActionStateBoolean booleanState{XR_TYPE_ACTION_STATE_BOOLEAN}; @@ -2350,11 +2332,11 @@ namespace Conformance XrActionStateVector2f previousVectorState{XR_TYPE_ACTION_STATE_VECTOR2F}; getInfo.action = allBooleanAction.Action; - REQUIRE_RESULT(xrGetActionStateBoolean(compositionHelper.GetSession(), &getInfo, &previousBoolState), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateBoolean(session, &getInfo, &previousBoolState), XR_SUCCESS); getInfo.action = allFloatAction.Action; - REQUIRE_RESULT(xrGetActionStateFloat(compositionHelper.GetSession(), &getInfo, &previousFloatState), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateFloat(session, &getInfo, &previousFloatState), XR_SUCCESS); getInfo.action = allVectorAction.Action; - REQUIRE_RESULT(xrGetActionStateVector2f(compositionHelper.GetSession(), &getInfo, &previousVectorState), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateVector2f(session, &getInfo, &previousVectorState), XR_SUCCESS); // Synthetic values for automation (these loop around by cStepSize in x then y order). float synthesizedX = 0.f; @@ -2377,12 +2359,11 @@ namespace Conformance actionLayerManager.SyncActionsUntilFocusWithMessage(syncInfo); getInfo.action = allBooleanAction.Action; - REQUIRE_RESULT(xrGetActionStateBoolean(compositionHelper.GetSession(), &getInfo, &combinedBoolState), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateBoolean(session, &getInfo, &combinedBoolState), XR_SUCCESS); getInfo.action = allFloatAction.Action; - REQUIRE_RESULT(xrGetActionStateFloat(compositionHelper.GetSession(), &getInfo, &combinedFloatState), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateFloat(session, &getInfo, &combinedFloatState), XR_SUCCESS); getInfo.action = allVectorAction.Action; - REQUIRE_RESULT(xrGetActionStateVector2f(compositionHelper.GetSession(), &getInfo, &combinedVectorState), - XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateVector2f(session, &getInfo, &combinedVectorState), XR_SUCCESS); REQUIRE((bool)combinedBoolState.isActive == (booleanActions.size() > 0)); REQUIRE((bool)combinedFloatState.isActive == (floatActions.size() > 0)); @@ -2465,7 +2446,7 @@ namespace Conformance for (auto& actionInfo : booleanActions) { getInfo.action = actionInfo.Action; - REQUIRE_RESULT(xrGetActionStateBoolean(compositionHelper.GetSession(), &getInfo, &booleanState), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateBoolean(session, &getInfo, &booleanState), XR_SUCCESS); if (booleanState.isActive) { auto key = int32_t(booleanState.currentState); update(key, actionInfo); @@ -2478,7 +2459,7 @@ namespace Conformance for (auto& actionInfo : floatActions) { getInfo.action = actionInfo.Action; - REQUIRE_RESULT(xrGetActionStateFloat(compositionHelper.GetSession(), &getInfo, &floatState), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateFloat(session, &getInfo, &floatState), XR_SUCCESS); if (floatState.isActive) { auto key = int32_t(std::roundf(floatState.currentState / cStepSize)); update(key, actionInfo); @@ -2492,7 +2473,7 @@ namespace Conformance for (auto& actionInfo : vectorActions) { getInfo.action = actionInfo.Action; - REQUIRE_RESULT(xrGetActionStateVector2f(compositionHelper.GetSession(), &getInfo, &vectorState), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateVector2f(session, &getInfo, &vectorState), XR_SUCCESS); if (vectorState.isActive) { auto i = int32_t(std::roundf(vectorState.currentState.x / cStepSize)) + cStepSizeOffset; auto j = int32_t(std::roundf(vectorState.currentState.y / cStepSize)) + cStepSizeOffset; @@ -2509,7 +2490,7 @@ namespace Conformance // Verify the x action matches the parent vector. getInfo.action = actionInfo.XAction; - REQUIRE_RESULT(xrGetActionStateFloat(compositionHelper.GetSession(), &getInfo, &floatState), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateFloat(session, &getInfo, &floatState), XR_SUCCESS); REQUIRE(floatState.isActive); REQUIRE(floatState.currentState == vectorState.currentState.x); ++combinedFloatCount; @@ -2529,7 +2510,7 @@ namespace Conformance // Verify the y action matches the parent vector. getInfo.action = actionInfo.YAction; - REQUIRE_RESULT(xrGetActionStateFloat(compositionHelper.GetSession(), &getInfo, &floatState), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateFloat(session, &getInfo, &floatState), XR_SUCCESS); REQUIRE(floatState.isActive); REQUIRE(floatState.currentState == vectorState.currentState.y); ++combinedFloatCount; @@ -2601,20 +2582,17 @@ namespace Conformance } } for (const auto& actionInfo : booleanActions) { - inputDevice->SetButtonStateBool(StringToPath(compositionHelper.GetInstance(), actionInfo.Data.Path), - synthesizedX > 0.5f, true); + inputDevice->SetButtonStateBool(StringToPath(instance, actionInfo.Data.Path), synthesizedX > 0.5f, true); } for (const auto& actionInfo : floatActions) { - inputDevice->SetButtonStateFloat(StringToPath(compositionHelper.GetInstance(), actionInfo.Data.Path), - synthesizedX, 0, true); + inputDevice->SetButtonStateFloat(StringToPath(instance, actionInfo.Data.Path), synthesizedX, 0, true); } for (const auto& actionInfo : vectorActions) { float x = (synthesizedX - 0.5f) * 2.f; float y = (synthesizedY - 0.5f) * 2.f; - inputDevice->SetButtonStateVector2(StringToPath(compositionHelper.GetInstance(), actionInfo.Data.Path), - {x, y}, 0, true); + inputDevice->SetButtonStateVector2(StringToPath(instance, actionInfo.Data.Path), {x, y}, 0, true); } } @@ -2664,19 +2642,19 @@ namespace Conformance getInfo.action = poseActionData.Action; actionLayerManager.SyncActionsUntilFocusWithMessage(syncInfo); - REQUIRE_RESULT(xrGetActionStatePose(compositionHelper.GetSession(), &getInfo, &poseState), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStatePose(session, &getInfo, &poseState), XR_SUCCESS); REQUIRE(poseState.isActive); inputDevice->SetDeviceActive(false); actionLayerManager.SyncActionsUntilFocusWithMessage(syncInfo); - REQUIRE_RESULT(xrGetActionStatePose(compositionHelper.GetSession(), &getInfo, &poseState), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStatePose(session, &getInfo, &poseState), XR_SUCCESS); REQUIRE_FALSE(poseState.isActive); inputDevice->SetDeviceActive(true); actionLayerManager.SyncActionsUntilFocusWithMessage(syncInfo); - REQUIRE_RESULT(xrGetActionStatePose(compositionHelper.GetSession(), &getInfo, &poseState), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStatePose(session, &getInfo, &poseState), XR_SUCCESS); REQUIRE(poseState.isActive); } } @@ -2688,8 +2666,6 @@ namespace Conformance for (const auto& hapticActionData : hapticActions) { CAPTURE(hapticActionData.Data.Path); - XrPath inputSourcePath = StringToPath(compositionHelper.GetInstance(), booleanActions[0].Data.Path); - XrHapticActionInfo hapticActionInfo{XR_TYPE_HAPTIC_ACTION_INFO}; hapticActionInfo.action = hapticActionData.Action; @@ -2700,13 +2676,29 @@ namespace Conformance XrActionStateGetInfo getInfo{XR_TYPE_ACTION_STATE_GET_INFO}; + std::vector selectedBooleanActions; + std::copy_if(std::begin(booleanActions), std::end(booleanActions), std::back_inserter(selectedBooleanActions), + [&](const ActionInfo& action) { + const std::string path = std::string(action.Data.Path); + const std::string suffix = "/click"; + return path.size() >= suffix.size() && + path.compare(path.size() - suffix.size(), suffix.size(), suffix) == 0; + }); + + bool foundClickActions = selectedBooleanActions.size() > 0; + if (!foundClickActions) { + // At time of writing, only hand and eye interaction profiles have no path ending in /click. + selectedBooleanActions = booleanActions; + } + + XrPath inputSourcePath = StringToPath(instance, selectedBooleanActions[0].Data.Path); + XrAction currentBooleanAction{XR_NULL_HANDLE}; auto GetBooleanButtonState = [&]() -> bool { actionLayerManager.SyncActionsUntilFocusWithMessage(syncInfo); - for (const auto& booleanActionData : booleanActions) { + for (const auto& booleanActionData : selectedBooleanActions) { getInfo.action = booleanActionData.Action; - REQUIRE_RESULT(xrGetActionStateBoolean(compositionHelper.GetSession(), &getInfo, &booleanState), - XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateBoolean(session, &getInfo, &booleanState), XR_SUCCESS); if (booleanState.changedSinceLastSync && booleanState.currentState) { currentBooleanAction = booleanActionData.Action; return true; @@ -2715,14 +2707,26 @@ namespace Conformance return false; }; - actionLayerManager.DisplayMessage("Press any button when you feel the 3 second haptic vibration"); + std::string buttonPromptPrefix = + foundClickActions ? "Activate any boolean .../click action " : "Activate any boolean action "; + std::string buttonPromptSuffix = ""; + if (foundClickActions) { + std::vector actions; + std::transform(selectedBooleanActions.begin(), selectedBooleanActions.end(), std::back_inserter(actions), + [](ActionInfo& action) { return action.Action; }); + std::string localizedActions = actionLayerManager.ListActionsLocalized(syncInfo, actions, ", ", "; on ", ": "); + buttonPromptSuffix = ", e.g.:\non " + localizedActions; + } + + std::string prompt = buttonPromptPrefix + "when you feel the 3 second haptic vibration" + buttonPromptSuffix; + actionLayerManager.DisplayMessage(prompt); actionLayerManager.IterateFrame(); actionLayerManager.Sleep_For(3s); hapticPacket.duration = std::chrono::duration_cast(3s).count(); - REQUIRE_RESULT(xrApplyHapticFeedback(compositionHelper.GetSession(), &hapticActionInfo, - reinterpret_cast(&hapticPacket)), - XR_SUCCESS); + REQUIRE_RESULT( + xrApplyHapticFeedback(session, &hapticActionInfo, reinterpret_cast(&hapticPacket)), + XR_SUCCESS); { // For automation only @@ -2733,6 +2737,7 @@ namespace Conformance currentBooleanAction = XR_NULL_HANDLE; WaitUntilPredicateWithTimeout( [&]() { + actionLayerManager.DisplayMessage(prompt); actionLayerManager.GetRenderLoop().IterateFrame(); return GetBooleanButtonState(); }, @@ -2744,16 +2749,20 @@ namespace Conformance inputDevice->SetButtonStateBool(inputSourcePath, false, true); } - REQUIRE_RESULT(xrStopHapticFeedback(compositionHelper.GetSession(), &hapticActionInfo), XR_SUCCESS); + REQUIRE_RESULT(xrStopHapticFeedback(session, &hapticActionInfo), XR_SUCCESS); - actionLayerManager.DisplayMessage("Press any button when you feel the short haptic pulse"); + actionLayerManager.IterateFrame(); + actionLayerManager.Sleep_For(0.25s); + + prompt = buttonPromptPrefix + "when you feel the short haptic pulse" + buttonPromptSuffix; + actionLayerManager.DisplayMessage(prompt); actionLayerManager.IterateFrame(); actionLayerManager.Sleep_For(3s); hapticPacket.duration = XR_MIN_HAPTIC_DURATION; - REQUIRE_RESULT(xrApplyHapticFeedback(compositionHelper.GetSession(), &hapticActionInfo, - reinterpret_cast(&hapticPacket)), - XR_SUCCESS); + REQUIRE_RESULT( + xrApplyHapticFeedback(session, &hapticActionInfo, reinterpret_cast(&hapticPacket)), + XR_SUCCESS); { inputDevice->SetButtonStateBool(inputSourcePath, false, true); @@ -2763,6 +2772,7 @@ namespace Conformance currentBooleanAction = XR_NULL_HANDLE; WaitUntilPredicateWithTimeout( [&]() { + actionLayerManager.DisplayMessage(prompt); actionLayerManager.GetRenderLoop().IterateFrame(); return GetBooleanButtonState(); }, @@ -2785,27 +2795,27 @@ namespace Conformance INFO("Boolean->Float"); for (const auto& booleanToFloatActionData : floatActionsCoercedToBoolean) { CAPTURE(booleanToFloatActionData.Data.Path); - XrPath inputSourcePath = StringToPath(compositionHelper.GetInstance(), booleanToFloatActionData.Data.Path); + XrPath inputSourcePath = StringToPath(instance, booleanToFloatActionData.Data.Path); XrActionStateGetInfo getInfo{XR_TYPE_ACTION_STATE_GET_INFO}; getInfo.action = booleanToFloatActionData.Action; inputDevice->SetButtonStateFloat(inputSourcePath, 0.0f, cEpsilon, false, actionSet); - REQUIRE_RESULT(xrGetActionStateBoolean(compositionHelper.GetSession(), &getInfo, &booleanState), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateBoolean(session, &getInfo, &booleanState), XR_SUCCESS); REQUIRE(booleanState.isActive); REQUIRE_FALSE(booleanState.currentState); inputDevice->SetButtonStateFloat(inputSourcePath, 1.0f, cEpsilon, false, actionSet); - REQUIRE_RESULT(xrGetActionStateBoolean(compositionHelper.GetSession(), &getInfo, &booleanState), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateBoolean(session, &getInfo, &booleanState), XR_SUCCESS); REQUIRE(booleanState.isActive); REQUIRE(booleanState.currentState); REQUIRE(booleanState.lastChangeTime > 0); inputDevice->SetButtonStateFloat(inputSourcePath, 0.0f, cEpsilon, false, actionSet); - REQUIRE_RESULT(xrGetActionStateBoolean(compositionHelper.GetSession(), &getInfo, &booleanState), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateBoolean(session, &getInfo, &booleanState), XR_SUCCESS); REQUIRE(booleanState.isActive); REQUIRE_FALSE(booleanState.currentState); REQUIRE(booleanState.lastChangeTime > 0); @@ -2814,27 +2824,27 @@ namespace Conformance INFO("Float->Boolean"); for (const auto& floatToBooleanActionData : booleanActionsCoercedToFloat) { CAPTURE(floatToBooleanActionData.Data.Path); - XrPath inputSourcePath = StringToPath(compositionHelper.GetInstance(), floatToBooleanActionData.Data.Path); + XrPath inputSourcePath = StringToPath(instance, floatToBooleanActionData.Data.Path); XrActionStateGetInfo getInfo{XR_TYPE_ACTION_STATE_GET_INFO}; getInfo.action = floatToBooleanActionData.Action; inputDevice->SetButtonStateBool(inputSourcePath, false, false, actionSet); - REQUIRE_RESULT(xrGetActionStateFloat(compositionHelper.GetSession(), &getInfo, &floatState), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateFloat(session, &getInfo, &floatState), XR_SUCCESS); REQUIRE(floatState.isActive); REQUIRE(floatState.currentState == Catch::Approx(0.0f).margin(cLargeEpsilon)); inputDevice->SetButtonStateBool(inputSourcePath, true, false, actionSet); - REQUIRE_RESULT(xrGetActionStateFloat(compositionHelper.GetSession(), &getInfo, &floatState), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateFloat(session, &getInfo, &floatState), XR_SUCCESS); REQUIRE(floatState.isActive); REQUIRE(floatState.currentState == Catch::Approx(1.0f).margin(cLargeEpsilon)); REQUIRE(floatState.lastChangeTime > 0); inputDevice->SetButtonStateBool(inputSourcePath, false, false, actionSet); - REQUIRE_RESULT(xrGetActionStateFloat(compositionHelper.GetSession(), &getInfo, &floatState), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateFloat(session, &getInfo, &floatState), XR_SUCCESS); REQUIRE(floatState.isActive); REQUIRE(floatState.currentState == Catch::Approx(0.0f).margin(cLargeEpsilon)); REQUIRE(floatState.lastChangeTime > 0); @@ -2849,22 +2859,22 @@ namespace Conformance if (actionData.Data.Type == XR_ACTION_TYPE_BOOLEAN_INPUT) { XrActionStateBoolean state{XR_TYPE_ACTION_STATE_BOOLEAN}; - REQUIRE_RESULT(xrGetActionStateBoolean(compositionHelper.GetSession(), &getInfo, &state), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateBoolean(session, &getInfo, &state), XR_SUCCESS); REQUIRE_FALSE(state.isActive); } else if (actionData.Data.Type == XR_ACTION_TYPE_FLOAT_INPUT) { XrActionStateFloat state{XR_TYPE_ACTION_STATE_FLOAT}; - REQUIRE_RESULT(xrGetActionStateFloat(compositionHelper.GetSession(), &getInfo, &state), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateFloat(session, &getInfo, &state), XR_SUCCESS); REQUIRE_FALSE(state.isActive); } else if (actionData.Data.Type == XR_ACTION_TYPE_VECTOR2F_INPUT) { XrActionStateVector2f state{XR_TYPE_ACTION_STATE_VECTOR2F}; - REQUIRE_RESULT(xrGetActionStateVector2f(compositionHelper.GetSession(), &getInfo, &state), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateVector2f(session, &getInfo, &state), XR_SUCCESS); REQUIRE_FALSE(state.isActive); } else if (actionData.Data.Type == XR_ACTION_TYPE_POSE_INPUT) { XrActionStatePose state{XR_TYPE_ACTION_STATE_POSE}; - REQUIRE_RESULT(xrGetActionStatePose(compositionHelper.GetSession(), &getInfo, &state), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStatePose(session, &getInfo, &state), XR_SUCCESS); REQUIRE_FALSE(state.isActive); } } @@ -2892,16 +2902,18 @@ namespace Conformance TEST_CASE("StateQueryFunctionsAndHaptics", "[actions]") { CompositionHelper compositionHelper("Input device state query"); + XrInstance instance = compositionHelper.GetInstance(); + XrSession session = compositionHelper.GetSession(); XrActionSet actionSet{XR_NULL_HANDLE}; XrActionSetCreateInfo actionSetCreateInfo{XR_TYPE_ACTION_SET_CREATE_INFO}; strcpy(actionSetCreateInfo.localizedActionSetName, "test action set localized name"); strcpy(actionSetCreateInfo.actionSetName, "test_action_set_name"); - REQUIRE_RESULT(xrCreateActionSet(compositionHelper.GetInstance(), &actionSetCreateInfo, &actionSet), XR_SUCCESS); + REQUIRE_RESULT(xrCreateActionSet(instance, &actionSetCreateInfo, &actionSet), XR_SUCCESS); - XrPath leftHandPath = StringToPath(compositionHelper.GetInstance(), "/user/hand/left"); - XrPath rightHandPath = StringToPath(compositionHelper.GetInstance(), "/user/hand/right"); - XrPath gamepadPath = StringToPath(compositionHelper.GetInstance(), "/user/gamepad"); + XrPath leftHandPath = StringToPath(instance, "/user/hand/left"); + XrPath rightHandPath = StringToPath(instance, "/user/hand/right"); + XrPath gamepadPath = StringToPath(instance, "/user/gamepad"); XrPath bothHands[] = {leftHandPath, rightHandPath}; XrAction booleanAction{XR_NULL_HANDLE}; @@ -2954,13 +2966,12 @@ namespace Conformance ActionLayerManager actionLayerManager(compositionHelper); actionLayerManager.WaitForSessionFocusWithMessage(); - XrPath simpleControllerInteractionProfile = - StringToPath(compositionHelper.GetInstance(), GetSimpleInteractionProfile().InteractionProfilePathString); + XrPath simpleControllerInteractionProfile = StringToPath(instance, GetSimpleInteractionProfile().InteractionProfilePathString); - XrPath leftHandSelectClickPath = StringToPath(compositionHelper.GetInstance(), "/user/hand/left/input/select/click"); - XrPath rightHandSelectClickPath = StringToPath(compositionHelper.GetInstance(), "/user/hand/right/input/select/click"); - XrPath leftHandMenuClickPath = StringToPath(compositionHelper.GetInstance(), "/user/hand/left/input/menu/click"); - XrPath rightHandMenuClickPath = StringToPath(compositionHelper.GetInstance(), "/user/hand/right/input/menu/click"); + XrPath leftHandSelectClickPath = StringToPath(instance, "/user/hand/left/input/select/click"); + XrPath rightHandSelectClickPath = StringToPath(instance, "/user/hand/right/input/select/click"); + XrPath leftHandMenuClickPath = StringToPath(instance, "/user/hand/left/input/menu/click"); + XrPath rightHandMenuClickPath = StringToPath(instance, "/user/hand/right/input/menu/click"); compositionHelper.GetInteractionManager().AddActionSet(actionSet); compositionHelper.GetInteractionManager().AddActionBindings(simpleControllerInteractionProfile, @@ -2991,16 +3002,16 @@ namespace Conformance SECTION("Basic usage") { getInfo.action = booleanAction; - REQUIRE_RESULT(xrGetActionStateBoolean(compositionHelper.GetSession(), &getInfo, &booleanState), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateBoolean(session, &getInfo, &booleanState), XR_SUCCESS); getInfo.action = floatAction; - REQUIRE_RESULT(xrGetActionStateFloat(compositionHelper.GetSession(), &getInfo, &floatState), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateFloat(session, &getInfo, &floatState), XR_SUCCESS); getInfo.action = vectorAction; - REQUIRE_RESULT(xrGetActionStateVector2f(compositionHelper.GetSession(), &getInfo, &vectorState), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStateVector2f(session, &getInfo, &vectorState), XR_SUCCESS); getInfo.action = poseAction; - REQUIRE_RESULT(xrGetActionStatePose(compositionHelper.GetSession(), &getInfo, &poseState), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStatePose(session, &getInfo, &poseState), XR_SUCCESS); } OPTIONAL_INVALID_HANDLE_VALIDATION_SECTION { @@ -3020,165 +3031,141 @@ namespace Conformance getInfo.action = poseAction; REQUIRE_RESULT(xrGetActionStatePose(invalidSession, &getInfo, &poseState), XR_ERROR_HANDLE_INVALID); - REQUIRE_RESULT(xrApplyHapticFeedback(compositionHelper.GetSession(), &hapticActionInfo, - reinterpret_cast(&hapticPacket)), - XR_SUCCESS); - REQUIRE_RESULT(xrStopHapticFeedback(compositionHelper.GetSession(), &hapticActionInfo), XR_SUCCESS); + REQUIRE_RESULT( + xrApplyHapticFeedback(session, &hapticActionInfo, reinterpret_cast(&hapticPacket)), + XR_SUCCESS); + REQUIRE_RESULT(xrStopHapticFeedback(session, &hapticActionInfo), XR_SUCCESS); } SECTION("Invalid action") { getInfo.action = (XrAction)0x1234; - REQUIRE_RESULT(xrGetActionStateBoolean(compositionHelper.GetSession(), &getInfo, &booleanState), - XR_ERROR_HANDLE_INVALID); - REQUIRE_RESULT(xrGetActionStateFloat(compositionHelper.GetSession(), &getInfo, &floatState), - XR_ERROR_HANDLE_INVALID); - REQUIRE_RESULT(xrGetActionStateVector2f(compositionHelper.GetSession(), &getInfo, &vectorState), - XR_ERROR_HANDLE_INVALID); - REQUIRE_RESULT(xrGetActionStatePose(compositionHelper.GetSession(), &getInfo, &poseState), XR_ERROR_HANDLE_INVALID); + REQUIRE_RESULT(xrGetActionStateBoolean(session, &getInfo, &booleanState), XR_ERROR_HANDLE_INVALID); + REQUIRE_RESULT(xrGetActionStateFloat(session, &getInfo, &floatState), XR_ERROR_HANDLE_INVALID); + REQUIRE_RESULT(xrGetActionStateVector2f(session, &getInfo, &vectorState), XR_ERROR_HANDLE_INVALID); + REQUIRE_RESULT(xrGetActionStatePose(session, &getInfo, &poseState), XR_ERROR_HANDLE_INVALID); hapticActionInfo.action = getInfo.action; - REQUIRE_RESULT(xrApplyHapticFeedback(compositionHelper.GetSession(), &hapticActionInfo, - reinterpret_cast(&hapticPacket)), - XR_ERROR_HANDLE_INVALID); - REQUIRE_RESULT(xrStopHapticFeedback(compositionHelper.GetSession(), &hapticActionInfo), XR_ERROR_HANDLE_INVALID); + REQUIRE_RESULT( + xrApplyHapticFeedback(session, &hapticActionInfo, reinterpret_cast(&hapticPacket)), + XR_ERROR_HANDLE_INVALID); + REQUIRE_RESULT(xrStopHapticFeedback(session, &hapticActionInfo), XR_ERROR_HANDLE_INVALID); } } SECTION("Invalid subaction path") { getInfo.subactionPath = (XrPath)0x1234; getInfo.action = booleanAction; - REQUIRE_RESULT(xrGetActionStateBoolean(compositionHelper.GetSession(), &getInfo, &booleanState), XR_ERROR_PATH_INVALID); + REQUIRE_RESULT(xrGetActionStateBoolean(session, &getInfo, &booleanState), XR_ERROR_PATH_INVALID); getInfo.action = floatAction; - REQUIRE_RESULT(xrGetActionStateFloat(compositionHelper.GetSession(), &getInfo, &floatState), XR_ERROR_PATH_INVALID); + REQUIRE_RESULT(xrGetActionStateFloat(session, &getInfo, &floatState), XR_ERROR_PATH_INVALID); getInfo.action = vectorAction; - REQUIRE_RESULT(xrGetActionStateVector2f(compositionHelper.GetSession(), &getInfo, &vectorState), XR_ERROR_PATH_INVALID); + REQUIRE_RESULT(xrGetActionStateVector2f(session, &getInfo, &vectorState), XR_ERROR_PATH_INVALID); getInfo.action = poseAction; - REQUIRE_RESULT(xrGetActionStatePose(compositionHelper.GetSession(), &getInfo, &poseState), XR_ERROR_PATH_INVALID); + REQUIRE_RESULT(xrGetActionStatePose(session, &getInfo, &poseState), XR_ERROR_PATH_INVALID); hapticActionInfo.subactionPath = getInfo.subactionPath; - REQUIRE_RESULT(xrApplyHapticFeedback(compositionHelper.GetSession(), &hapticActionInfo, - reinterpret_cast(&hapticPacket)), + REQUIRE_RESULT(xrApplyHapticFeedback(session, &hapticActionInfo, reinterpret_cast(&hapticPacket)), XR_ERROR_PATH_INVALID); - REQUIRE_RESULT(xrStopHapticFeedback(compositionHelper.GetSession(), &hapticActionInfo), XR_ERROR_PATH_INVALID); + REQUIRE_RESULT(xrStopHapticFeedback(session, &hapticActionInfo), XR_ERROR_PATH_INVALID); } SECTION("Unspecified subaction path") { getInfo.subactionPath = gamepadPath; getInfo.action = booleanAction; - REQUIRE_RESULT(xrGetActionStateBoolean(compositionHelper.GetSession(), &getInfo, &booleanState), - XR_ERROR_PATH_UNSUPPORTED); + REQUIRE_RESULT(xrGetActionStateBoolean(session, &getInfo, &booleanState), XR_ERROR_PATH_UNSUPPORTED); getInfo.action = floatAction; - REQUIRE_RESULT(xrGetActionStateFloat(compositionHelper.GetSession(), &getInfo, &floatState), XR_ERROR_PATH_UNSUPPORTED); + REQUIRE_RESULT(xrGetActionStateFloat(session, &getInfo, &floatState), XR_ERROR_PATH_UNSUPPORTED); getInfo.action = vectorAction; - REQUIRE_RESULT(xrGetActionStateVector2f(compositionHelper.GetSession(), &getInfo, &vectorState), - XR_ERROR_PATH_UNSUPPORTED); + REQUIRE_RESULT(xrGetActionStateVector2f(session, &getInfo, &vectorState), XR_ERROR_PATH_UNSUPPORTED); getInfo.action = poseAction; - REQUIRE_RESULT(xrGetActionStatePose(compositionHelper.GetSession(), &getInfo, &poseState), XR_ERROR_PATH_UNSUPPORTED); + REQUIRE_RESULT(xrGetActionStatePose(session, &getInfo, &poseState), XR_ERROR_PATH_UNSUPPORTED); hapticActionInfo.subactionPath = getInfo.subactionPath; - REQUIRE_RESULT(xrApplyHapticFeedback(compositionHelper.GetSession(), &hapticActionInfo, - reinterpret_cast(&hapticPacket)), + REQUIRE_RESULT(xrApplyHapticFeedback(session, &hapticActionInfo, reinterpret_cast(&hapticPacket)), XR_ERROR_PATH_UNSUPPORTED); - REQUIRE_RESULT(xrStopHapticFeedback(compositionHelper.GetSession(), &hapticActionInfo), XR_ERROR_PATH_UNSUPPORTED); + REQUIRE_RESULT(xrStopHapticFeedback(session, &hapticActionInfo), XR_ERROR_PATH_UNSUPPORTED); { INFO("Action created with no subaction paths, cannot be queried with any"); getInfo.action = confirmAction; getInfo.subactionPath = leftHandPath; - REQUIRE_RESULT(xrGetActionStateBoolean(compositionHelper.GetSession(), &getInfo, &booleanState), - XR_ERROR_PATH_UNSUPPORTED); + REQUIRE_RESULT(xrGetActionStateBoolean(session, &getInfo, &booleanState), XR_ERROR_PATH_UNSUPPORTED); } } SECTION("Type mismatch") { getInfo.action = booleanAction; hapticActionInfo.action = booleanAction; - REQUIRE_RESULT(xrGetActionStateFloat(compositionHelper.GetSession(), &getInfo, &floatState), - XR_ERROR_ACTION_TYPE_MISMATCH); - REQUIRE_RESULT(xrGetActionStateVector2f(compositionHelper.GetSession(), &getInfo, &vectorState), - XR_ERROR_ACTION_TYPE_MISMATCH); - REQUIRE_RESULT(xrGetActionStatePose(compositionHelper.GetSession(), &getInfo, &poseState), + REQUIRE_RESULT(xrGetActionStateFloat(session, &getInfo, &floatState), XR_ERROR_ACTION_TYPE_MISMATCH); + REQUIRE_RESULT(xrGetActionStateVector2f(session, &getInfo, &vectorState), XR_ERROR_ACTION_TYPE_MISMATCH); + REQUIRE_RESULT(xrGetActionStatePose(session, &getInfo, &poseState), XR_ERROR_ACTION_TYPE_MISMATCH); + REQUIRE_RESULT(xrApplyHapticFeedback(session, &hapticActionInfo, reinterpret_cast(&hapticPacket)), XR_ERROR_ACTION_TYPE_MISMATCH); - REQUIRE_RESULT(xrApplyHapticFeedback(compositionHelper.GetSession(), &hapticActionInfo, - reinterpret_cast(&hapticPacket)), - XR_ERROR_ACTION_TYPE_MISMATCH); - REQUIRE_RESULT(xrStopHapticFeedback(compositionHelper.GetSession(), &hapticActionInfo), XR_ERROR_ACTION_TYPE_MISMATCH); + REQUIRE_RESULT(xrStopHapticFeedback(session, &hapticActionInfo), XR_ERROR_ACTION_TYPE_MISMATCH); getInfo.action = floatAction; hapticActionInfo.action = floatAction; - REQUIRE_RESULT(xrGetActionStateBoolean(compositionHelper.GetSession(), &getInfo, &booleanState), - XR_ERROR_ACTION_TYPE_MISMATCH); - REQUIRE_RESULT(xrGetActionStateVector2f(compositionHelper.GetSession(), &getInfo, &vectorState), + REQUIRE_RESULT(xrGetActionStateBoolean(session, &getInfo, &booleanState), XR_ERROR_ACTION_TYPE_MISMATCH); + REQUIRE_RESULT(xrGetActionStateVector2f(session, &getInfo, &vectorState), XR_ERROR_ACTION_TYPE_MISMATCH); + REQUIRE_RESULT(xrGetActionStatePose(session, &getInfo, &poseState), XR_ERROR_ACTION_TYPE_MISMATCH); + REQUIRE_RESULT(xrApplyHapticFeedback(session, &hapticActionInfo, reinterpret_cast(&hapticPacket)), XR_ERROR_ACTION_TYPE_MISMATCH); - REQUIRE_RESULT(xrGetActionStatePose(compositionHelper.GetSession(), &getInfo, &poseState), - XR_ERROR_ACTION_TYPE_MISMATCH); - REQUIRE_RESULT(xrApplyHapticFeedback(compositionHelper.GetSession(), &hapticActionInfo, - reinterpret_cast(&hapticPacket)), - XR_ERROR_ACTION_TYPE_MISMATCH); - REQUIRE_RESULT(xrStopHapticFeedback(compositionHelper.GetSession(), &hapticActionInfo), XR_ERROR_ACTION_TYPE_MISMATCH); + REQUIRE_RESULT(xrStopHapticFeedback(session, &hapticActionInfo), XR_ERROR_ACTION_TYPE_MISMATCH); getInfo.action = vectorAction; hapticActionInfo.action = vectorAction; - REQUIRE_RESULT(xrGetActionStateBoolean(compositionHelper.GetSession(), &getInfo, &booleanState), - XR_ERROR_ACTION_TYPE_MISMATCH); - REQUIRE_RESULT(xrGetActionStateFloat(compositionHelper.GetSession(), &getInfo, &floatState), - XR_ERROR_ACTION_TYPE_MISMATCH); - REQUIRE_RESULT(xrGetActionStatePose(compositionHelper.GetSession(), &getInfo, &poseState), + REQUIRE_RESULT(xrGetActionStateBoolean(session, &getInfo, &booleanState), XR_ERROR_ACTION_TYPE_MISMATCH); + REQUIRE_RESULT(xrGetActionStateFloat(session, &getInfo, &floatState), XR_ERROR_ACTION_TYPE_MISMATCH); + REQUIRE_RESULT(xrGetActionStatePose(session, &getInfo, &poseState), XR_ERROR_ACTION_TYPE_MISMATCH); + REQUIRE_RESULT(xrApplyHapticFeedback(session, &hapticActionInfo, reinterpret_cast(&hapticPacket)), XR_ERROR_ACTION_TYPE_MISMATCH); - REQUIRE_RESULT(xrApplyHapticFeedback(compositionHelper.GetSession(), &hapticActionInfo, - reinterpret_cast(&hapticPacket)), - XR_ERROR_ACTION_TYPE_MISMATCH); - REQUIRE_RESULT(xrStopHapticFeedback(compositionHelper.GetSession(), &hapticActionInfo), XR_ERROR_ACTION_TYPE_MISMATCH); + REQUIRE_RESULT(xrStopHapticFeedback(session, &hapticActionInfo), XR_ERROR_ACTION_TYPE_MISMATCH); getInfo.action = poseAction; hapticActionInfo.action = poseAction; - REQUIRE_RESULT(xrGetActionStateFloat(compositionHelper.GetSession(), &getInfo, &floatState), - XR_ERROR_ACTION_TYPE_MISMATCH); - REQUIRE_RESULT(xrGetActionStateFloat(compositionHelper.GetSession(), &getInfo, &floatState), - XR_ERROR_ACTION_TYPE_MISMATCH); - REQUIRE_RESULT(xrGetActionStateVector2f(compositionHelper.GetSession(), &getInfo, &vectorState), + REQUIRE_RESULT(xrGetActionStateFloat(session, &getInfo, &floatState), XR_ERROR_ACTION_TYPE_MISMATCH); + REQUIRE_RESULT(xrGetActionStateFloat(session, &getInfo, &floatState), XR_ERROR_ACTION_TYPE_MISMATCH); + REQUIRE_RESULT(xrGetActionStateVector2f(session, &getInfo, &vectorState), XR_ERROR_ACTION_TYPE_MISMATCH); + REQUIRE_RESULT(xrApplyHapticFeedback(session, &hapticActionInfo, reinterpret_cast(&hapticPacket)), XR_ERROR_ACTION_TYPE_MISMATCH); - REQUIRE_RESULT(xrApplyHapticFeedback(compositionHelper.GetSession(), &hapticActionInfo, - reinterpret_cast(&hapticPacket)), - XR_ERROR_ACTION_TYPE_MISMATCH); - REQUIRE_RESULT(xrStopHapticFeedback(compositionHelper.GetSession(), &hapticActionInfo), XR_ERROR_ACTION_TYPE_MISMATCH); + REQUIRE_RESULT(xrStopHapticFeedback(session, &hapticActionInfo), XR_ERROR_ACTION_TYPE_MISMATCH); getInfo.action = hapticAction; hapticActionInfo.action = hapticAction; - REQUIRE_RESULT(xrGetActionStateFloat(compositionHelper.GetSession(), &getInfo, &floatState), - XR_ERROR_ACTION_TYPE_MISMATCH); - REQUIRE_RESULT(xrGetActionStateFloat(compositionHelper.GetSession(), &getInfo, &floatState), - XR_ERROR_ACTION_TYPE_MISMATCH); - REQUIRE_RESULT(xrGetActionStateVector2f(compositionHelper.GetSession(), &getInfo, &vectorState), - XR_ERROR_ACTION_TYPE_MISMATCH); + REQUIRE_RESULT(xrGetActionStateFloat(session, &getInfo, &floatState), XR_ERROR_ACTION_TYPE_MISMATCH); + REQUIRE_RESULT(xrGetActionStateFloat(session, &getInfo, &floatState), XR_ERROR_ACTION_TYPE_MISMATCH); + REQUIRE_RESULT(xrGetActionStateVector2f(session, &getInfo, &vectorState), XR_ERROR_ACTION_TYPE_MISMATCH); } } } } TEST_CASE("action_space_creation_pre_suggest", "[actions][interactive]") { + bool useLeftHand = GetGlobalData().leftHandUnderTest; + // Creates two ActionSpaces // - one is created before xrSuggestInteractionProfileBindings and // - the other is created after. // These two action spaces should both return (the same) valid data. CompositionHelper compositionHelper("action_space_creation_pre_suggest"); + XrInstance instance = compositionHelper.GetInstance(); + XrSession session = compositionHelper.GetSession(); compositionHelper.BeginSession(); ActionLayerManager actionLayerManager(compositionHelper); - XrPath simpleControllerInteractionProfile = - StringToPath(compositionHelper.GetInstance(), GetSimpleInteractionProfile().InteractionProfilePathString); + XrPath simpleControllerInteractionProfile = StringToPath(instance, GetSimpleInteractionProfile().InteractionProfilePathString); XrActionSet actionSet{XR_NULL_HANDLE}; XrActionSetCreateInfo actionSetCreateInfo{XR_TYPE_ACTION_SET_CREATE_INFO}; strcpy(actionSetCreateInfo.localizedActionSetName, "test action set localized name"); strcpy(actionSetCreateInfo.actionSetName, "test_action_set_name"); - REQUIRE_RESULT(xrCreateActionSet(compositionHelper.GetInstance(), &actionSetCreateInfo, &actionSet), XR_SUCCESS); + REQUIRE_RESULT(xrCreateActionSet(instance, &actionSetCreateInfo, &actionSet), XR_SUCCESS); XrAction poseAction{XR_NULL_HANDLE}; XrActionCreateInfo createInfo{XR_TYPE_ACTION_CREATE_INFO}; @@ -3194,17 +3181,16 @@ namespace Conformance earlySpaceCreateInfo.poseInActionSpace = XrPosefCPP(); earlySpaceCreateInfo.action = poseAction; XrSpace earlyActionSpace{XR_NULL_HANDLE}; - REQUIRE_RESULT(xrCreateActionSpace(compositionHelper.GetSession(), &earlySpaceCreateInfo, &earlyActionSpace), XR_SUCCESS); + REQUIRE_RESULT(xrCreateActionSpace(session, &earlySpaceCreateInfo, &earlyActionSpace), XR_SUCCESS); - std::shared_ptr leftHandInputDevice = CreateTestDevice( - &actionLayerManager, &compositionHelper.GetInteractionManager(), compositionHelper.GetInstance(), - compositionHelper.GetSession(), simpleControllerInteractionProfile, - StringToPath(compositionHelper.GetInstance(), "/user/hand/left"), GetSimpleInteractionProfile().InputSourcePaths); + std::shared_ptr handInputDevice = CreateTestDevice( + &actionLayerManager, &compositionHelper.GetInteractionManager(), instance, session, simpleControllerInteractionProfile, + StringToPath(instance, useLeftHand ? "/user/hand/left" : "/user/hand/right"), GetSimpleInteractionProfile().InputSourcePaths); compositionHelper.GetInteractionManager().AddActionSet(actionSet); compositionHelper.GetInteractionManager().AddActionBindings( simpleControllerInteractionProfile, - {{poseAction, StringToPath(compositionHelper.GetInstance(), "/user/hand/left/input/grip/pose")}}); + {{poseAction, StringToPath(instance, useLeftHand ? "/user/hand/left/input/grip/pose" : "/user/hand/right/input/grip/pose")}}); compositionHelper.GetInteractionManager().AttachActionSets(); // Create an ActionSpace after xrSuggestInteractionProfileBindings @@ -3212,7 +3198,7 @@ namespace Conformance lateSpaceCreateInfo.poseInActionSpace = XrPosefCPP(); lateSpaceCreateInfo.action = poseAction; XrSpace lateActionSpace{XR_NULL_HANDLE}; - REQUIRE_RESULT(xrCreateActionSpace(compositionHelper.GetSession(), &lateSpaceCreateInfo, &lateActionSpace), XR_SUCCESS); + REQUIRE_RESULT(xrCreateActionSpace(session, &lateSpaceCreateInfo, &lateActionSpace), XR_SUCCESS); actionLayerManager.WaitForSessionFocusWithMessage(); @@ -3220,9 +3206,9 @@ namespace Conformance XrReferenceSpaceCreateInfo createSpaceInfo{XR_TYPE_REFERENCE_SPACE_CREATE_INFO}; createSpaceInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL; createSpaceInfo.poseInReferenceSpace = XrPosefCPP(); - REQUIRE_RESULT(xrCreateReferenceSpace(compositionHelper.GetSession(), &createSpaceInfo, &localSpace), XR_SUCCESS); + REQUIRE_RESULT(xrCreateReferenceSpace(session, &createSpaceInfo, &localSpace), XR_SUCCESS); - leftHandInputDevice->SetDeviceActive(true); + handInputDevice->SetDeviceActive(true); XrActionsSyncInfo syncInfo{XR_TYPE_ACTIONS_SYNC_INFO}; syncInfo.countActiveActionSets = 1; @@ -3232,7 +3218,7 @@ namespace Conformance XrSpaceLocation earlyLocation{XR_TYPE_SPACE_LOCATION, nullptr}; XrSpaceLocation lateLocation{XR_TYPE_SPACE_LOCATION, nullptr}; - REQUIRE(actionLayerManager.WaitForLocatability("left", lateActionSpace, localSpace, &lateLocation, true)); + REQUIRE(actionLayerManager.WaitForLocatability(useLeftHand ? "left" : "right", lateActionSpace, localSpace, &lateLocation, true)); XrTime locateTime = actionLayerManager.GetRenderLoop().GetLastPredictedDisplayTime(); // Ensure using the same time for the pose checks. @@ -3253,20 +3239,21 @@ namespace Conformance GlobalData& globalData = GetGlobalData(); CompositionHelper compositionHelper("Action Spaces"); + XrInstance instance = compositionHelper.GetInstance(); + XrSession session = compositionHelper.GetSession(); compositionHelper.BeginSession(); ActionLayerManager actionLayerManager(compositionHelper); - XrPath simpleControllerInteractionProfile = - StringToPath(compositionHelper.GetInstance(), GetSimpleInteractionProfile().InteractionProfilePathString); - XrPath leftHandPath{StringToPath(compositionHelper.GetInstance(), "/user/hand/left")}; - XrPath rightHandPath{StringToPath(compositionHelper.GetInstance(), "/user/hand/right")}; + XrPath simpleControllerInteractionProfile = StringToPath(instance, GetSimpleInteractionProfile().InteractionProfilePathString); + XrPath leftHandPath{StringToPath(instance, "/user/hand/left")}; + XrPath rightHandPath{StringToPath(instance, "/user/hand/right")}; const XrPath bothHands[2] = {leftHandPath, rightHandPath}; XrActionSet actionSet{XR_NULL_HANDLE}; XrActionSetCreateInfo actionSetCreateInfo{XR_TYPE_ACTION_SET_CREATE_INFO}; strcpy(actionSetCreateInfo.localizedActionSetName, "test action set localized name"); strcpy(actionSetCreateInfo.actionSetName, "test_action_set_name"); - REQUIRE_RESULT(xrCreateActionSet(compositionHelper.GetInstance(), &actionSetCreateInfo, &actionSet), XR_SUCCESS); + REQUIRE_RESULT(xrCreateActionSet(instance, &actionSetCreateInfo, &actionSet), XR_SUCCESS); XrAction poseAction{XR_NULL_HANDLE}; XrActionCreateInfo createInfo{XR_TYPE_ACTION_CREATE_INFO}; @@ -3278,20 +3265,17 @@ namespace Conformance REQUIRE_RESULT(xrCreateAction(actionSet, &createInfo, &poseAction), XR_SUCCESS); std::shared_ptr leftHandInputDevice = - CreateTestDevice(&actionLayerManager, &compositionHelper.GetInteractionManager(), compositionHelper.GetInstance(), - compositionHelper.GetSession(), simpleControllerInteractionProfile, leftHandPath, - GetSimpleInteractionProfile().InputSourcePaths); + CreateTestDevice(&actionLayerManager, &compositionHelper.GetInteractionManager(), instance, session, + simpleControllerInteractionProfile, leftHandPath, GetSimpleInteractionProfile().InputSourcePaths); std::shared_ptr rightHandInputDevice = - CreateTestDevice(&actionLayerManager, &compositionHelper.GetInteractionManager(), compositionHelper.GetInstance(), - compositionHelper.GetSession(), simpleControllerInteractionProfile, rightHandPath, - GetSimpleInteractionProfile().InputSourcePaths); + CreateTestDevice(&actionLayerManager, &compositionHelper.GetInteractionManager(), instance, session, + simpleControllerInteractionProfile, rightHandPath, GetSimpleInteractionProfile().InputSourcePaths); compositionHelper.GetInteractionManager().AddActionSet(actionSet); compositionHelper.GetInteractionManager().AddActionBindings( - simpleControllerInteractionProfile, - {{poseAction, StringToPath(compositionHelper.GetInstance(), "/user/hand/left/input/grip/pose")}, - {poseAction, StringToPath(compositionHelper.GetInstance(), "/user/hand/right/input/grip/pose")}}); + simpleControllerInteractionProfile, {{poseAction, StringToPath(instance, "/user/hand/left/input/grip/pose")}, + {poseAction, StringToPath(instance, "/user/hand/right/input/grip/pose")}}); compositionHelper.GetInteractionManager().AttachActionSets(); actionLayerManager.WaitForSessionFocusWithMessage(); @@ -3304,7 +3288,7 @@ namespace Conformance XrReferenceSpaceCreateInfo createSpaceInfo{XR_TYPE_REFERENCE_SPACE_CREATE_INFO}; createSpaceInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL; createSpaceInfo.poseInReferenceSpace = {{0, 0, 0, 1}, {0, 0, 0}}; - REQUIRE_RESULT(xrCreateReferenceSpace(compositionHelper.GetSession(), &createSpaceInfo, &localSpace), XR_SUCCESS); + REQUIRE_RESULT(xrCreateReferenceSpace(session, &createSpaceInfo, &localSpace), XR_SUCCESS); XrActionSpaceCreateInfo spaceCreateInfo{XR_TYPE_ACTION_SPACE_CREATE_INFO}; spaceCreateInfo.poseInActionSpace = {{0, 0, 0, 1}, {0, 0, 0}}; @@ -3312,17 +3296,17 @@ namespace Conformance // Can track left or right, but may: only switch at xrSyncActions. XrSpace actionSpaceWithoutSubactionPath{XR_NULL_HANDLE}; - REQUIRE_RESULT(xrCreateActionSpace(compositionHelper.GetSession(), &spaceCreateInfo, &actionSpaceWithoutSubactionPath), XR_SUCCESS); + REQUIRE_RESULT(xrCreateActionSpace(session, &spaceCreateInfo, &actionSpaceWithoutSubactionPath), XR_SUCCESS); // Only tracks left spaceCreateInfo.subactionPath = leftHandPath; XrSpace leftSpace{XR_NULL_HANDLE}; - REQUIRE_RESULT(xrCreateActionSpace(compositionHelper.GetSession(), &spaceCreateInfo, &leftSpace), XR_SUCCESS); + REQUIRE_RESULT(xrCreateActionSpace(session, &spaceCreateInfo, &leftSpace), XR_SUCCESS); // Only tracks right spaceCreateInfo.subactionPath = rightHandPath; XrSpace rightSpace{XR_NULL_HANDLE}; - REQUIRE_RESULT(xrCreateActionSpace(compositionHelper.GetSession(), &spaceCreateInfo, &rightSpace), XR_SUCCESS); + REQUIRE_RESULT(xrCreateActionSpace(session, &spaceCreateInfo, &rightSpace), XR_SUCCESS); if (globalData.leftHandUnderTest) { leftHandInputDevice->SetDeviceActive(true); @@ -3343,14 +3327,13 @@ namespace Conformance syncInfo.activeActionSets = bothSets; auto PosesAreEqual = [](XrPosef a, XrPosef b) -> bool { - constexpr float e = 0.001f; // 1mm - return (a.position.x == Catch::Approx(b.position.x).epsilon(e)) && - (a.position.y == Catch::Approx(b.position.y).epsilon(e)) && - (a.position.z == Catch::Approx(b.position.z).epsilon(e)) && - (a.orientation.x == Catch::Approx(b.orientation.x).epsilon(e)) && - (a.orientation.y == Catch::Approx(b.orientation.y).epsilon(e)) && - (a.orientation.z == Catch::Approx(b.orientation.z).epsilon(e)) && - (a.orientation.w == Catch::Approx(b.orientation.w).epsilon(e)); + constexpr float e = 0.002f; // 1mm (margin below means absolute delta) + return (a.position.x == Catch::Approx(b.position.x).margin(e)) && (a.position.y == Catch::Approx(b.position.y).margin(e)) && + (a.position.z == Catch::Approx(b.position.z).margin(e)) && + (a.orientation.x == Catch::Approx(b.orientation.x).margin(e)) && + (a.orientation.y == Catch::Approx(b.orientation.y).margin(e)) && + (a.orientation.z == Catch::Approx(b.orientation.z).margin(e)) && + (a.orientation.w == Catch::Approx(b.orientation.w).margin(e)); }; if (globalData.leftHandUnderTest && globalData.rightHandUnderTest) { @@ -3476,8 +3459,7 @@ namespace Conformance { XrActionStateGetInfo getInfo{XR_TYPE_ACTION_STATE_GET_INFO}; getInfo.action = poseAction; - REQUIRE_RESULT(xrGetActionStatePose(compositionHelper.GetSession(), &getInfo, &poseActionState), - XR_ERROR_HANDLE_INVALID); + REQUIRE_RESULT(xrGetActionStatePose(session, &getInfo, &poseActionState), XR_ERROR_HANDLE_INVALID); } REQUIRE_RESULT(xrLocateSpace(actionSpaceWithoutSubactionPath, localSpace, @@ -3527,7 +3509,7 @@ namespace Conformance auto getActionStatePoseActive = [&] { const auto getInfo = XrActionStateGetInfo{XR_TYPE_ACTION_STATE_GET_INFO, nullptr, poseAction, controllerSubactionPath}; XrActionStatePose statePose{XR_TYPE_ACTION_STATE_POSE}; - REQUIRE_RESULT(xrGetActionStatePose(compositionHelper.GetSession(), &getInfo, &statePose), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStatePose(session, &getInfo, &statePose), XR_SUCCESS); return statePose.isActive == XR_TRUE; }; @@ -3536,7 +3518,8 @@ namespace Conformance INFO("Off controller should not have active pose state"); REQUIRE(getActionStatePoseActive() == false); } - availableInputDevice->SetDeviceActiveWithoutWaiting(true, " - Will wait 20s whether or not the controller is found"); + availableInputDevice->SetDeviceActiveWithoutWaiting( + true, "\nSlowly move the controller for 20 seconds to prevent it from going idle and becoming inactive."); { INFO("Pose state should not become active again without a call to xrSyncActions"); REQUIRE(getActionStatePoseActive() == false); @@ -3651,7 +3634,7 @@ namespace Conformance const auto getInfo = XrActionStateGetInfo{XR_TYPE_ACTION_STATE_GET_INFO, nullptr, poseAction, controllerSubactionPath}; XrActionStatePose statePose{XR_TYPE_ACTION_STATE_POSE}; - REQUIRE_RESULT(xrGetActionStatePose(compositionHelper.GetSession(), &getInfo, &statePose), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStatePose(session, &getInfo, &statePose), XR_SUCCESS); REQUIRE(statePose.isActive == XR_FALSE); } { @@ -3661,7 +3644,7 @@ namespace Conformance const auto getInfo = XrActionStateGetInfo{XR_TYPE_ACTION_STATE_GET_INFO, nullptr, poseAction, XR_NULL_PATH}; XrActionStatePose statePose{XR_TYPE_ACTION_STATE_POSE}; - REQUIRE_RESULT(xrGetActionStatePose(compositionHelper.GetSession(), &getInfo, &statePose), XR_SUCCESS); + REQUIRE_RESULT(xrGetActionStatePose(session, &getInfo, &statePose), XR_SUCCESS); REQUIRE(statePose.isActive == XR_FALSE); } } @@ -3706,8 +3689,7 @@ namespace Conformance XrActionStatePose poseActionState{XR_TYPE_ACTION_STATE_POSE}; XrActionStateGetInfo getInfo{XR_TYPE_ACTION_STATE_GET_INFO}; getInfo.action = poseAction; - REQUIRE_RESULT(xrGetActionStatePose(compositionHelper.GetSession(), &getInfo, &poseActionState), - XR_ERROR_HANDLE_INVALID); + REQUIRE_RESULT(xrGetActionStatePose(session, &getInfo, &poseActionState), XR_ERROR_HANDLE_INVALID); } } } @@ -3717,12 +3699,14 @@ namespace Conformance TEST_CASE("xrEnumerateBoundSourcesForAction_and_xrGetInputSourceLocalizedName", "[actions][interactive]") { CompositionHelper compositionHelper("BoundSources and LocalizedName"); + XrInstance instance = compositionHelper.GetInstance(); + XrSession session = compositionHelper.GetSession(); XrActionSet actionSet{XR_NULL_HANDLE}; XrActionSetCreateInfo actionSetCreateInfo{XR_TYPE_ACTION_SET_CREATE_INFO}; strcpy(actionSetCreateInfo.localizedActionSetName, "test action set localized name"); strcpy(actionSetCreateInfo.actionSetName, "test_action_set_name"); - REQUIRE_RESULT(xrCreateActionSet(compositionHelper.GetInstance(), &actionSetCreateInfo, &actionSet), XR_SUCCESS); + REQUIRE_RESULT(xrCreateActionSet(instance, &actionSetCreateInfo, &actionSet), XR_SUCCESS); XrAction action{XR_NULL_HANDLE}; XrActionCreateInfo actionCreateInfo{XR_TYPE_ACTION_CREATE_INFO}; @@ -3739,18 +3723,17 @@ namespace Conformance bool leftUnderTest = GetGlobalData().leftHandUnderTest; const char* pathStr = leftUnderTest ? "/user/hand/left" : "/user/hand/right"; - XrPath path{StringToPath(compositionHelper.GetInstance(), pathStr)}; + XrPath path{StringToPath(instance, pathStr)}; std::shared_ptr inputDevice = - CreateTestDevice(&actionLayerManager, &compositionHelper.GetInteractionManager(), compositionHelper.GetInstance(), - compositionHelper.GetSession(), - StringToPath(compositionHelper.GetInstance(), GetSimpleInteractionProfile().InteractionProfilePathString), - path, GetSimpleInteractionProfile().InputSourcePaths); + CreateTestDevice(&actionLayerManager, &compositionHelper.GetInteractionManager(), instance, session, + StringToPath(instance, GetSimpleInteractionProfile().InteractionProfilePathString), path, + GetSimpleInteractionProfile().InputSourcePaths); compositionHelper.GetInteractionManager().AddActionSet(actionSet); compositionHelper.GetInteractionManager().AddActionBindings( - StringToPath(compositionHelper.GetInstance(), "/interaction_profiles/khr/simple_controller"), - {{action, StringToPath(compositionHelper.GetInstance(), "/user/hand/left/input/select/click")}, - {action, StringToPath(compositionHelper.GetInstance(), "/user/hand/right/input/select/click")}}); + StringToPath(instance, "/interaction_profiles/khr/simple_controller"), + {{action, StringToPath(instance, "/user/hand/left/input/select/click")}, + {action, StringToPath(instance, "/user/hand/right/input/select/click")}}); compositionHelper.GetInteractionManager().AttachActionSets(); inputDevice->SetDeviceActive(true); @@ -3766,14 +3749,13 @@ namespace Conformance info.action = action; SECTION("Basic usage") { - std::vector enumerateResult = - REQUIRE_TWO_CALL(XrPath, {}, xrEnumerateBoundSourcesForAction, compositionHelper.GetSession(), &info); + std::vector enumerateResult = REQUIRE_TWO_CALL(XrPath, {}, xrEnumerateBoundSourcesForAction, session, &info); // Note that runtimes may return bound sources even when not focused, though they don't have to actionLayerManager.SyncActionsUntilFocusWithMessage(syncInfo); - enumerateResult = REQUIRE_TWO_CALL(XrPath, {}, xrEnumerateBoundSourcesForAction, compositionHelper.GetSession(), &info); + enumerateResult = REQUIRE_TWO_CALL(XrPath, {}, xrEnumerateBoundSourcesForAction, session, &info); REQUIRE(enumerateResult.size() > 0); @@ -3782,42 +3764,35 @@ namespace Conformance SECTION("xrGetInputSourceLocalizedName") { getInfo.whichComponents = XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT; - std::string localizedStringResult = - REQUIRE_TWO_CALL(char, {}, xrGetInputSourceLocalizedName, compositionHelper.GetSession(), &getInfo).data(); + std::string localizedStringResult = REQUIRE_TWO_CALL(char, {}, xrGetInputSourceLocalizedName, session, &getInfo).data(); REQUIRE_FALSE(localizedStringResult.empty()); getInfo.whichComponents = XR_INPUT_SOURCE_LOCALIZED_NAME_INTERACTION_PROFILE_BIT; - localizedStringResult = - REQUIRE_TWO_CALL(char, {}, xrGetInputSourceLocalizedName, compositionHelper.GetSession(), &getInfo).data(); + localizedStringResult = REQUIRE_TWO_CALL(char, {}, xrGetInputSourceLocalizedName, session, &getInfo).data(); REQUIRE_FALSE(localizedStringResult.empty()); getInfo.whichComponents = XR_INPUT_SOURCE_LOCALIZED_NAME_COMPONENT_BIT; - localizedStringResult = - REQUIRE_TWO_CALL(char, {}, xrGetInputSourceLocalizedName, compositionHelper.GetSession(), &getInfo).data(); + localizedStringResult = REQUIRE_TWO_CALL(char, {}, xrGetInputSourceLocalizedName, session, &getInfo).data(); REQUIRE_FALSE(localizedStringResult.empty()); getInfo.whichComponents = XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT | XR_INPUT_SOURCE_LOCALIZED_NAME_INTERACTION_PROFILE_BIT; - localizedStringResult = - REQUIRE_TWO_CALL(char, {}, xrGetInputSourceLocalizedName, compositionHelper.GetSession(), &getInfo).data(); + localizedStringResult = REQUIRE_TWO_CALL(char, {}, xrGetInputSourceLocalizedName, session, &getInfo).data(); REQUIRE_FALSE(localizedStringResult.empty()); getInfo.whichComponents = XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT | XR_INPUT_SOURCE_LOCALIZED_NAME_COMPONENT_BIT; - localizedStringResult = - REQUIRE_TWO_CALL(char, {}, xrGetInputSourceLocalizedName, compositionHelper.GetSession(), &getInfo).data(); + localizedStringResult = REQUIRE_TWO_CALL(char, {}, xrGetInputSourceLocalizedName, session, &getInfo).data(); REQUIRE_FALSE(localizedStringResult.empty()); getInfo.whichComponents = XR_INPUT_SOURCE_LOCALIZED_NAME_INTERACTION_PROFILE_BIT | XR_INPUT_SOURCE_LOCALIZED_NAME_COMPONENT_BIT; - localizedStringResult = - REQUIRE_TWO_CALL(char, {}, xrGetInputSourceLocalizedName, compositionHelper.GetSession(), &getInfo).data(); + localizedStringResult = REQUIRE_TWO_CALL(char, {}, xrGetInputSourceLocalizedName, session, &getInfo).data(); REQUIRE_FALSE(localizedStringResult.empty()); getInfo.whichComponents = XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT | XR_INPUT_SOURCE_LOCALIZED_NAME_INTERACTION_PROFILE_BIT | XR_INPUT_SOURCE_LOCALIZED_NAME_COMPONENT_BIT; - localizedStringResult = - REQUIRE_TWO_CALL(char, {}, xrGetInputSourceLocalizedName, compositionHelper.GetSession(), &getInfo).data(); + localizedStringResult = REQUIRE_TWO_CALL(char, {}, xrGetInputSourceLocalizedName, session, &getInfo).data(); REQUIRE_FALSE(localizedStringResult.empty()); uint32_t sourceCountOutput; @@ -3825,20 +3800,17 @@ namespace Conformance SECTION("Invalid components") { getInfo.whichComponents = 0; - REQUIRE_RESULT( - xrGetInputSourceLocalizedName(compositionHelper.GetSession(), &getInfo, 0, &sourceCountOutput, &buffer), - XR_ERROR_VALIDATION_FAILURE); + REQUIRE_RESULT(xrGetInputSourceLocalizedName(session, &getInfo, 0, &sourceCountOutput, &buffer), + XR_ERROR_VALIDATION_FAILURE); } SECTION("Invalid path") { getInfo.sourcePath = XR_NULL_PATH; - REQUIRE_RESULT( - xrGetInputSourceLocalizedName(compositionHelper.GetSession(), &getInfo, 0, &sourceCountOutput, &buffer), - XR_ERROR_PATH_INVALID); + REQUIRE_RESULT(xrGetInputSourceLocalizedName(session, &getInfo, 0, &sourceCountOutput, &buffer), + XR_ERROR_PATH_INVALID); getInfo.sourcePath = (XrPath)0x1234; - REQUIRE_RESULT( - xrGetInputSourceLocalizedName(compositionHelper.GetSession(), &getInfo, 0, &sourceCountOutput, &buffer), - XR_ERROR_PATH_INVALID); + REQUIRE_RESULT(xrGetInputSourceLocalizedName(session, &getInfo, 0, &sourceCountOutput, &buffer), + XR_ERROR_PATH_INVALID); } } } @@ -3857,7 +3829,7 @@ namespace Conformance info.action = (XrAction)0x1234; uint32_t sourceCountOutput; XrPath buffer; - REQUIRE_RESULT(xrEnumerateBoundSourcesForAction(compositionHelper.GetSession(), &info, 0, &sourceCountOutput, &buffer), + REQUIRE_RESULT(xrEnumerateBoundSourcesForAction(session, &info, 0, &sourceCountOutput, &buffer), XR_ERROR_HANDLE_INVALID); } } diff --git a/src/conformance/conformance_test/test_glTFRendering.cpp b/src/conformance/conformance_test/test_glTFRendering.cpp index 8c5b571e..9c1c0a0c 100644 --- a/src/conformance/conformance_test/test_glTFRendering.cpp +++ b/src/conformance/conformance_test/test_glTFRendering.cpp @@ -95,7 +95,7 @@ namespace Conformance spaceCreateInfo.subactionPath = subactionPaths[i]; spaceCreateInfo.poseInActionSpace = XrPosefCPP{}; XRC_CHECK_THROW_XRCMD(xrCreateActionSpace(session, &spaceCreateInfo, &space)); - gripSpaces.emplace_back(std::move(space)); + gripSpaces.emplace_back(space); } } @@ -227,7 +227,7 @@ namespace Conformance viewState.viewStateFlags & XR_VIEW_STATE_ORIENTATION_VALID_BIT) { const auto& views = std::get>(viewData); - // Render into each view port of the wide swapchain using the projection layer view fov and pose. + // Render into each of the separate swapchains using the projection layer view fov and pose. for (size_t view = 0; view < views.size(); view++) { compositionHelper.AcquireWaitReleaseImage(swapchains[view], [&](const XrSwapchainImageBaseHeader* swapchainImage) { GetGlobalData().graphicsPlugin->ClearImageSlice(swapchainImage); diff --git a/src/conformance/conformance_test/test_multithreading.cpp b/src/conformance/conformance_test/test_multithreading.cpp index 05a4d04b..c5ec5741 100644 --- a/src/conformance/conformance_test/test_multithreading.cpp +++ b/src/conformance/conformance_test/test_multithreading.cpp @@ -377,7 +377,7 @@ namespace Conformance XrSessionActionSetsAttachInfo attachInfo{XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO}; attachInfo.countActionSets = 1; attachInfo.actionSets = &env.GetAutoBasicSession().actionSet; - XRC_CHECK_THROW_XRCMD(xrAttachSessionActionSets(env.GetAutoBasicSession().session, &attachInfo)); + XRC_CHECK_THROW_XRCMD(xrAttachSessionActionSets(env.GetAutoBasicSession(), &attachInfo)); } // Get frames iterating to the point of app focused state. This will draw frames along the way. @@ -871,7 +871,7 @@ namespace Conformance RandEngine& randEngine = GetGlobalData().GetRandEngine(); // References to AutoBasicSession members. - XrSession& session = env.GetAutoBasicSession().session; + XrSession session = env.GetAutoBasicSession().GetSession(); XrActionSet& actionSet = env.GetAutoBasicSession().actionSet; std::vector& actionVector = env.GetAutoBasicSession().actionVector; // This actions are part of the actionSet. std::array& handSubactionArray = env.GetAutoBasicSession().handSubactionArray; diff --git a/src/conformance/conformance_test/test_xrCreateSession.cpp b/src/conformance/conformance_test/test_xrCreateSession.cpp index 43e3ef5d..3a82ec1f 100644 --- a/src/conformance/conformance_test/test_xrCreateSession.cpp +++ b/src/conformance/conformance_test/test_xrCreateSession.cpp @@ -157,5 +157,36 @@ namespace Conformance REQUIRE(destroyInstanceResult == XR_SUCCESS); } } + + SECTION("not calling xrDestroySession") + { + GlobalData& globalData = GetGlobalData(); + + for (int i = 0; i < 2; ++i) { + CAPTURE(i); + + std::shared_ptr graphicsPlugin; + + AutoBasicInstance instance{AutoBasicInstance::createSystemId}; + XrSystemId systemId = instance.systemId; + + if (!globalData.options.graphicsPlugin.empty()) { + REQUIRE_NOTHROW(graphicsPlugin = Conformance::CreateGraphicsPlugin(globalData.options.graphicsPlugin.c_str(), + globalData.GetPlatformPlugin())); + REQUIRE(graphicsPlugin->Initialize()); + } + + REQUIRE(graphicsPlugin->InitializeDevice(instance, systemId)); + + XrSessionCreateInfo sessionCreateInfo{XR_TYPE_SESSION_CREATE_INFO, nullptr, 0, systemId}; + sessionCreateInfo.next = graphicsPlugin->GetGraphicsBinding(); + + XrSession session = XR_NULL_HANDLE_CPP; + REQUIRE(xrCreateSession(instance, &sessionCreateInfo, &session) == XR_SUCCESS); + + // leak the session handle + // AutoBasicInstance will cleanup for us + } + } } } // namespace Conformance diff --git a/src/conformance/conformance_test/test_xrCreateSwapchain.cpp b/src/conformance/conformance_test/test_xrCreateSwapchain.cpp index 59297cc8..856095d2 100644 --- a/src/conformance/conformance_test/test_xrCreateSwapchain.cpp +++ b/src/conformance/conformance_test/test_xrCreateSwapchain.cpp @@ -43,7 +43,7 @@ namespace Conformance CAPTURE(formatName); SwapchainCreateTestParameters tp{}; - CHECK(graphicsPlugin->GetSwapchainCreateTestParameters(session.instance, session, session.systemId, format, &tp)); + CHECK(graphicsPlugin->GetSwapchainCreateTestParameters(session.GetInstance(), session, session.GetSystemId(), format, &tp)); XrSwapchainCreateInfo createInfo{ XR_TYPE_SWAPCHAIN_CREATE_INFO, diff --git a/src/conformance/conformance_test/test_xrLocateViews.cpp b/src/conformance/conformance_test/test_xrLocateViews.cpp index ba0ec5cd..7a25aac6 100644 --- a/src/conformance/conformance_test/test_xrLocateViews.cpp +++ b/src/conformance/conformance_test/test_xrLocateViews.cpp @@ -14,6 +14,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "catch2/matchers/catch_matchers_vector.hpp" #include "conformance_utils.h" #include "conformance_framework.h" #include "matchers.h" @@ -106,12 +107,15 @@ namespace Conformance { // Ensure unsupported view configuration types fail and supported types pass + XrInstance instance = session.GetInstance(); + XrSystemId systemId = session.GetSystemId(); + // Get the list of supported view configurations uint32_t viewConfigCount = 0; - REQUIRE(XR_SUCCESS == xrEnumerateViewConfigurations(session.instance, session.systemId, 0, &viewConfigCount, nullptr)); + REQUIRE(XR_SUCCESS == xrEnumerateViewConfigurations(instance, systemId, 0, &viewConfigCount, nullptr)); std::vector runtimeViewTypes(viewConfigCount); - REQUIRE(XR_SUCCESS == xrEnumerateViewConfigurations(session.instance, session.systemId, viewConfigCount, &viewConfigCount, - runtimeViewTypes.data())); + REQUIRE(XR_SUCCESS == + xrEnumerateViewConfigurations(instance, systemId, viewConfigCount, &viewConfigCount, runtimeViewTypes.data())); CAPTURE(locateInfo.displayTime); @@ -123,8 +127,7 @@ namespace Conformance // Is this enum valid, check against enabled extensions. bool valid = IsViewConfigurationTypeEnumValid(viewType); - const bool isSupportedType = - std::find(runtimeViewTypes.begin(), runtimeViewTypes.end(), viewType) != runtimeViewTypes.end(); + const bool isSupportedType = Catch::Matchers::VectorContains(viewType).match(runtimeViewTypes); CAPTURE(valid); CAPTURE(isSupportedType); diff --git a/src/conformance/framework/RGBAImage.cpp b/src/conformance/framework/RGBAImage.cpp index db6c0c9f..9fb9032c 100644 --- a/src/conformance/framework/RGBAImage.cpp +++ b/src/conformance/framework/RGBAImage.cpp @@ -252,7 +252,7 @@ namespace Conformance RGBA8Color* const destImageRow = pixels.data() + (destY * width); for (int cx = 0; cx < characterWidth; cx++) { - const int destX = (int)(bakedChar.xoff + xadvance + 0.5f) + cx; + const int destX = (int)std::lround(bakedChar.xoff + xadvance) + cx; if (destX < 0 || destX >= width || destX < rect.offset.x || destX >= rect.offset.x + rect.extent.width) { continue; // Don't bother copying if out of bounds. } @@ -348,7 +348,9 @@ namespace Conformance void RGBAImageCache::Init() { - m_cacheMutex = std::make_unique(); + if (!m_cacheMutex) { + m_cacheMutex = std::make_unique(); + } } std::shared_ptr RGBAImageCache::Load(const char* path) diff --git a/src/conformance/framework/action_utils.cpp b/src/conformance/framework/action_utils.cpp index 555b27ca..a4871409 100644 --- a/src/conformance/framework/action_utils.cpp +++ b/src/conformance/framework/action_utils.cpp @@ -17,9 +17,40 @@ #include "action_utils.h" #include "composition_utils.h" #include "report.h" +#include "utilities/throw_helpers.h" + +#include using namespace std::chrono_literals; +namespace +{ + static std::vector EnumerateBoundSourcesForAction(XrSession session, const XrBoundSourcesForActionEnumerateInfo& info) + { + std::vector boundSources; + uint32_t countOutput; + XRC_CHECK_THROW_XRCMD(xrEnumerateBoundSourcesForAction(session, &info, 0, &countOutput, nullptr)); + if (countOutput != 0) { + boundSources.resize(countOutput, XR_NULL_PATH); + XRC_CHECK_THROW_XRCMD( + xrEnumerateBoundSourcesForAction(session, &info, (uint32_t)boundSources.size(), &countOutput, boundSources.data())); + } + return boundSources; + } + static std::string GetInputSourceLocalizedName(XrSession session, const XrInputSourceLocalizedNameGetInfo& getInfo) + { + uint32_t countOutput; + XRC_CHECK_THROW_XRCMD(xrGetInputSourceLocalizedName(session, &getInfo, 0, &countOutput, nullptr)); + if (countOutput != 0) { + std::vector localizedStringBuf(countOutput, '\0'); + XRC_CHECK_THROW_XRCMD(xrGetInputSourceLocalizedName(session, &getInfo, (uint32_t)localizedStringBuf.size(), &countOutput, + localizedStringBuf.data())); + return std::string(localizedStringBuf.data()); + } + return ""; + } +} // namespace + namespace Conformance { @@ -152,6 +183,68 @@ namespace Conformance m_lastMessage = message; } + std::string ActionLayerManager::ListActionsLocalized(XrActionsSyncInfo syncInfo, nonstd::span actions, + const char* actionDelimiter, const char* pathDelimiter, const char* pathSuffix) + { + std::vector localizedUserPathsAndProfiles; + std::map> pathsByLocalizedUserPathAndProfile; + + for (auto& action : actions) { + XrBoundSourcesForActionEnumerateInfo info{XR_TYPE_BOUND_SOURCES_FOR_ACTION_ENUMERATE_INFO}; + info.action = action; + + SyncActionsUntilFocusWithMessage(syncInfo); + + std::vector boundSources = EnumerateBoundSourcesForAction(m_compositionHelper.GetSession(), info); + + for (XrPath path : boundSources) { + XrInputSourceLocalizedNameGetInfo getInfo{XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO}; + getInfo.sourcePath = path; + getInfo.whichComponents = + XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT | XR_INPUT_SOURCE_LOCALIZED_NAME_INTERACTION_PROFILE_BIT; + + std::string localizedUserPathAndProfile = GetInputSourceLocalizedName(m_compositionHelper.GetSession(), getInfo); + + auto& paths = pathsByLocalizedUserPathAndProfile[localizedUserPathAndProfile]; + if (paths.size() == 0) { + localizedUserPathsAndProfiles.push_back(localizedUserPathAndProfile); + } + paths.push_back(path); + } + } + + std::ostringstream oss; + + bool firstPathAndProfile = true; + for (auto& localizedUserPathAndProfile : localizedUserPathsAndProfiles) { + if (!firstPathAndProfile) { + oss << pathDelimiter; + } + firstPathAndProfile = false; + + oss << localizedUserPathAndProfile << pathSuffix; + + auto paths = pathsByLocalizedUserPathAndProfile[localizedUserPathAndProfile]; + + bool firstComponent = true; + for (XrPath path : paths) { + if (!firstComponent) { + oss << actionDelimiter; + } + firstComponent = false; + + XrInputSourceLocalizedNameGetInfo getInfo{XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO}; + getInfo.sourcePath = path; + getInfo.whichComponents = XR_INPUT_SOURCE_LOCALIZED_NAME_COMPONENT_BIT; + + std::string localizedComponent = GetInputSourceLocalizedName(m_compositionHelper.GetSession(), getInfo); + + oss << localizedComponent; + } + } + return oss.str(); + } // namespace Conformance + ActionLayerManager::MessageQuad::MessageQuad(CompositionHelper& compositionHelper, std::unique_ptr image, XrSpace compositionSpace) : m_compositionHelper(compositionHelper) diff --git a/src/conformance/framework/action_utils.h b/src/conformance/framework/action_utils.h index 6922083e..daba1059 100644 --- a/src/conformance/framework/action_utils.h +++ b/src/conformance/framework/action_utils.h @@ -21,6 +21,7 @@ #include "input_testinputdevice.h" #include "utilities/event_reader.h" +#include #include #include @@ -34,32 +35,74 @@ namespace Conformance extern const std::chrono::nanoseconds kActionWaitDelay; - // Manages showing a quad with help text. + /// Manages showing a quad with help text. struct ActionLayerManager : public ITestMessageDisplay { ActionLayerManager(CompositionHelper& compositionHelper); - EventReader& GetEventReader() + /// Access the contained @ref EventReader + EventReader& GetEventReader() noexcept { return m_eventReader; } - RenderLoop& GetRenderLoop() + /// Access the contained @ref RenderLoop + RenderLoop& GetRenderLoop() noexcept { return m_renderLoop; } + /// Wait until your callback returns true, while displaying a text message on the display. + /// + /// This helper: + /// + /// - DOES submit frames + /// - DOES NOT call `xrSyncActions` + /// - DOES NOT poll events through this object's EventReader (though the CompositionHelper will poll events) bool WaitWithMessage(const char* waitMessage, std::function frameCallback); + /// Submit frames until focus is available, based on waiting for the session state event, + /// in case focus was lost at some point. + /// + /// This helper: + /// + /// - DOES submit frames (wraps a call to @ref WaitWithMessage) + /// - DOES NOT call `xrSyncActions` + /// - DOES call `xrPollEvent` + /// - DOES poll events through this object's EventReader void WaitForSessionFocusWithMessage(); + /// Waits until xrLocateSpace reports that position/orientation valid flags match @p expectLocatability. + /// + /// This helper: + /// + /// - DOES submit frames (wraps a call to @ref WaitWithMessage) + /// - DOES NOT call `xrSyncActions` - you must call it beforehand at least once with the right action set(s) to make + /// your action space active! + /// - DOES NOT poll events through this object's EventReader (though the CompositionHelper will poll events) + /// + /// @param hand Hand name for message + /// bool WaitForLocatability(const std::string& hand, XrSpace space, XrSpace localSpace, XrSpaceLocation* location, bool expectLocatability); - // Sync until focus is available, in case focus was lost at some point. + /// Sync actions until focus is available, observed by xrSyncActions returning XR_SUCCESS instead of XR_SESSION_NOT_FOCUSED, + /// in case focus was lost at some point. + /// + /// This helper: + /// + /// - DOES submit frames (wraps a call to @ref WaitWithMessage) + /// - DOES call `xrSyncActions` - if you do not want to sync actions, see @ref WaitForSessionFocusWithMessage + /// - DOES NOT poll events through this object's EventReader (though the CompositionHelper will poll events) void SyncActionsUntilFocusWithMessage(const XrActionsSyncInfo& syncInfo); - // "Sleep", but keep the render loop going on this thread + /// "Sleep", but keep the render loop going on this thread + /// + /// This helper: + /// + /// - DOES submit frames + /// - DOES NOT call `xrSyncActions` + /// - DOES NOT poll events through this object's EventReader (though the CompositionHelper will poll events) template void Sleep_For(const std::chrono::duration& sleep_duration) { @@ -69,9 +112,22 @@ namespace Conformance } } + /// Call `xrEndFrame` via the @ref CompositionHelper, then let it poll for events to decide whether to stop. + /// + /// If there was a call to @ref DisplayMessage, a layer for the message will be submitted. bool EndFrame(const XrFrameState& frameState); + + /// Calls `xrWaitFrame`, `xrBeginFrame`, and `xrEndFrame`, delegating to the owned @ref RenderLoop void IterateFrame() override; + + /// Display a message on the console and in the immersive environment. + /// + /// Prepares a static swapchain with the message for use the next time @ref EndFrame is called, + /// directly or indirectly, through this helper object. + /// (Does not directly submit frames!) void DisplayMessage(const std::string& message) override; + std::string ListActionsLocalized(XrActionsSyncInfo syncInfo, nonstd::span actions, const char* actionDelimiter, + const char* pathDelimiter, const char* pathSuffix); private: std::mutex m_mutex; diff --git a/src/conformance/framework/composition_utils.h b/src/conformance/framework/composition_utils.h index 569801d6..2e934ae7 100644 --- a/src/conformance/framework/composition_utils.h +++ b/src/conformance/framework/composition_utils.h @@ -64,7 +64,12 @@ namespace Conformance { } + /// Call `xrWaitFrame`, `xrBeginFrame`, `xrEndFrame`. + /// Returns whatever your @ref EndFrame function returned bool IterateFrame(); + + /// Call @ref IterateFrame repeatedly until your @ref EndFrame returns false, + /// checking that no exceptions are thrown void Loop(); XrTime GetLastPredictedDisplayTime() const; diff --git a/src/conformance/framework/conformance_utils.cpp b/src/conformance/framework/conformance_utils.cpp index b0c65f19..43aeaeff 100644 --- a/src/conformance/framework/conformance_utils.cpp +++ b/src/conformance/framework/conformance_utils.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -667,8 +668,8 @@ namespace Conformance : autoBasicSession(autoBasicSession_), sessionState(autoBasicSession->GetSessionState()), frameState(), viewVector() { XRC_CHECK_THROW(autoBasicSession); - XRC_CHECK_THROW(autoBasicSession->instance); - XRC_CHECK_THROW(autoBasicSession->session); + XRC_CHECK_THROW(autoBasicSession->GetInstance()); + XRC_CHECK_THROW(autoBasicSession->GetSession()); XRC_CHECK_THROW(!autoBasicSession->viewConfigurationTypeVector.empty()); XRC_CHECK_THROW(!autoBasicSession->environmentBlendModeVector.empty()); } @@ -681,7 +682,7 @@ namespace Conformance FrameIterator::TickResult FrameIterator::PollEvent() { XrEventDataBuffer eventData{XR_TYPE_EVENT_DATA_BUFFER}; - XrResult result = xrPollEvent(autoBasicSession->instance, &eventData); + XrResult result = xrPollEvent(autoBasicSession->GetInstance(), &eventData); switch (result) { case XR_SUCCESS: { @@ -747,11 +748,11 @@ namespace Conformance } XrResult result; - + XrSession session = autoBasicSession->GetSession(); // xrWaitFrame may block. XrFrameWaitInfo frameWaitInfo{XR_TYPE_FRAME_WAIT_INFO}; frameState = XrFrameState{XR_TYPE_FRAME_STATE}; - result = xrWaitFrame(autoBasicSession->session, &frameWaitInfo, &frameState); + result = xrWaitFrame(session, &frameWaitInfo, &frameState); if (XR_FAILED(result)) return RunResult::Error; @@ -762,13 +763,13 @@ namespace Conformance XrViewState viewState{XR_TYPE_VIEW_STATE}; uint32_t viewCount = (uint32_t)autoBasicSession->viewConfigurationViewVector.size(); viewVector.resize(viewCount, {XR_TYPE_VIEW}); - result = xrLocateViews(autoBasicSession->session, &viewLocateInfo, &viewState, viewCount, &viewCount, viewVector.data()); + result = xrLocateViews(session, &viewLocateInfo, &viewState, viewCount, &viewCount, viewVector.data()); if (XR_FAILED(result)) return RunResult::Error; viewVector.resize(viewCount); XrFrameBeginInfo frameBeginInfo{XR_TYPE_FRAME_BEGIN_INFO}; - result = xrBeginFrame(autoBasicSession->session, &frameBeginInfo); + result = xrBeginFrame(session, &frameBeginInfo); if (XR_FAILED(result)) return RunResult::Error; @@ -847,7 +848,7 @@ namespace Conformance frameEndInfo.layerCount = 1; frameEndInfo.layers = headerPtrArray; - XrResult result = xrEndFrame(autoBasicSession->session, &frameEndInfo); + XrResult result = xrEndFrame(autoBasicSession->GetSession(), &frameEndInfo); if (XR_FAILED(result)) return RunResult::Error; @@ -901,7 +902,7 @@ namespace Conformance XrSessionBeginInfo sessionBeginInfo{ XR_TYPE_SESSION_BEGIN_INFO, globalData.GetPlatformPlugin()->PopulateNextFieldForStruct(XR_TYPE_SESSION_BEGIN_INFO), globalData.options.viewConfigurationValue}; - REQUIRE(xrBeginSession(autoBasicSession->session, &sessionBeginInfo) == XR_SUCCESS); + REQUIRE(xrBeginSession(autoBasicSession->GetSession(), &sessionBeginInfo) == XR_SUCCESS); } // Fall-through because frames must be submitted to get promoted from READY to SYNCHRONIZED. @@ -1058,6 +1059,13 @@ namespace Conformance } } + std::string SubtestTitle(const char* testName, size_t subtestIdx, size_t subtestCount) + { + std::ostringstream os; + os << testName << ": subtest " << (subtestIdx + 1) << " of " << subtestCount; + return os.str(); + } + // Encapsulates xrEnumerateSwapchainFormats/xrCreateSwapchain XrResult CreateColorSwapchain(XrSession session, IGraphicsPlugin* graphicsPlugin, XrSwapchain* swapchain, XrExtent2Di* widthHeight, uint32_t arraySize, bool cubemap, XrSwapchainCreateInfo* createInfoReturn) diff --git a/src/conformance/framework/conformance_utils.h b/src/conformance/framework/conformance_utils.h index d883a74d..43340daf 100644 --- a/src/conformance/framework/conformance_utils.h +++ b/src/conformance/framework/conformance_utils.h @@ -639,7 +639,7 @@ namespace Conformance return session != XR_NULL_HANDLE; } - public: + private: int optionFlags{0}; //< Enum OptionFlags XrInstance instance{XR_NULL_HANDLE}; @@ -653,6 +653,7 @@ namespace Conformance std::unique_ptr m_eventQueue; std::unique_ptr m_privateEventReader; + public: std::array handSubactionArray; // "/user/hand/left", "/user/hand/right" // Optional created types. @@ -674,6 +675,10 @@ namespace Conformance /// @relates AutoBasicSession std::ostream& operator<<(std::ostream& os, AutoBasicSession const& sess); + /// Calls your @p predicate repeatedly, pausing @p delay in between, until either it returns `true` or @p timeout has elapsed. + /// + /// @note This does not inherently submit frames and is thus likely to cause problems if a session is running unless your predicate submits a frame! + /// It is intended for use outside of a frame loop. bool WaitUntilPredicateWithTimeout(const std::function& predicate, const std::chrono::nanoseconds timeout, const std::chrono::nanoseconds delay); @@ -853,4 +858,24 @@ namespace Conformance s.type = type; s.next = next; } + + /// Make a test title given a short test name, a subtest index, and the number of subtests. + std::string SubtestTitle(const char* testName, size_t subtestIdx, size_t subtestCount); + + /// Make a test title given a short test name, a subtest index, and the array of subtests. + template + inline std::string SubtestTitle(const char* testName, size_t subtestIdx, const T (&subtestArray)[Size]) + { + (void)subtestArray; + return SubtestTitle(testName, subtestIdx, Size); + } + + /// Make pixel subrects based on normalized subrects and pixel dimensions + inline XrRect2Di CropImage(int32_t width, int32_t height, XrRect2Df crop) + { + return { + {int32_t(crop.offset.x * width), int32_t(crop.offset.y * height)}, + {int32_t(crop.extent.width * width), int32_t(crop.extent.height * height)}, + }; + } } // namespace Conformance diff --git a/src/conformance/framework/graphics_plugin_d3d12.cpp b/src/conformance/framework/graphics_plugin_d3d12.cpp index 667142e7..2bb891f1 100644 --- a/src/conformance/framework/graphics_plugin_d3d12.cpp +++ b/src/conformance/framework/graphics_plugin_d3d12.cpp @@ -1012,6 +1012,8 @@ namespace Conformance const XrSwapchainImageBaseHeader* colorSwapchainImage, const RenderParams& params) { if (params.cubes.empty() && params.meshes.empty() && params.glTFs.empty()) { + // Early exit, but need to wait as being done at end of method + WaitForGpu(); return; } D3D12SwapchainImageData* swapchainData; diff --git a/src/conformance/framework/graphics_plugin_opengl.cpp b/src/conformance/framework/graphics_plugin_opengl.cpp index e0edc7b3..ee2e24de 100644 --- a/src/conformance/framework/graphics_plugin_opengl.cpp +++ b/src/conformance/framework/graphics_plugin_opengl.cpp @@ -267,7 +267,11 @@ namespace Conformance OpenGLFallbackDepthTexture() = default; ~OpenGLFallbackDepthTexture() { - Reset(); + if (Allocated()) { + // As implementation as ::Reset(), but should not throw in destructor + glDeleteTextures(1, &m_texture); + } + m_texture = 0; } void Reset() { diff --git a/src/conformance/framework/input_testinputdevice.cpp b/src/conformance/framework/input_testinputdevice.cpp index 4f7c31b5..cf220d95 100644 --- a/src/conformance/framework/input_testinputdevice.cpp +++ b/src/conformance/framework/input_testinputdevice.cpp @@ -304,10 +304,10 @@ namespace Conformance } void SetDeviceActive(bool state, bool skipInteraction = false, XrAction detectionBoolAction = XR_NULL_HANDLE, - XrActionSet detectionActionSet = XR_NULL_HANDLE) override + XrActionSet detectionActionSet = XR_NULL_HANDLE, const char* extraMessage = nullptr) override { - SetDeviceActiveWithoutWaiting(state); + SetDeviceActiveWithoutWaiting(state, extraMessage); if (skipInteraction) { // Skip human interaction, this is just a hint to the runtime via the extension return; diff --git a/src/conformance/framework/input_testinputdevice.h b/src/conformance/framework/input_testinputdevice.h index bf028b8f..47c59677 100644 --- a/src/conformance/framework/input_testinputdevice.h +++ b/src/conformance/framework/input_testinputdevice.h @@ -44,8 +44,10 @@ namespace Conformance /// @param skipInteraction Skip human interaction (i.e. this is a hint for the conformance extension) /// @param detectionBoolAction Boolean action used to determine if device became active /// @param detectionActionSet Action Set associated with detectionBoolAction + /// @param extraMessage text to append to the end of the message (after "Turn on/off " and the top level user path), + /// if any. virtual void SetDeviceActive(bool state, bool skipInteraction = false, XrAction detectionBoolAction = XR_NULL_HANDLE, - XrActionSet detectionActionSet = XR_NULL_HANDLE) = 0; + XrActionSet detectionActionSet = XR_NULL_HANDLE, const char* extraMessage = nullptr) = 0; /// Call xrLocateSpace until XR_SPACE_LOCATION_ORIENTATION_VALID matches the desired state struct WaitUntilLosesOrGainsOrientationValidity diff --git a/src/conformance/framework/pbr/D3D11/D3D11Model.cpp b/src/conformance/framework/pbr/D3D11/D3D11Model.cpp index 1e7b5896..91f7f736 100644 --- a/src/conformance/framework/pbr/D3D11/D3D11Model.cpp +++ b/src/conformance/framework/pbr/D3D11/D3D11Model.cpp @@ -40,6 +40,9 @@ namespace Pbr if (primitive.GetMaterial()->Hidden) continue; + if (!IsAnyNodeVisible(primitive.GetNodes())) + continue; + primitive.GetMaterial()->Bind(context, pbrResources); primitive.Render(context); } @@ -82,12 +85,12 @@ namespace Pbr void D3D11ModelInstance::UpdateTransforms(Pbr::D3D11Resources const& /*pbrResources*/, _In_ ID3D11DeviceContext* context) { // If none of the node transforms have changed, no need to recompute/update the model transform structured buffer. - if (WereNodeLocalTransformsUpdated()) { - ResolveTransforms(true); + if (ResolvedTransformsNeedUpdate()) { + ResolveTransformsAndVisibilities(true); // Update node transform structured buffer. context->UpdateSubresource(m_modelTransformsStructuredBuffer.Get(), 0, nullptr, GetResolvedTransforms().data(), 0, 0); - ClearTransformsUpdatedFlag(); + MarkResolvedTransformsUpdated(); } } } // namespace Pbr diff --git a/src/conformance/framework/pbr/D3D11/D3D11Primitive.cpp b/src/conformance/framework/pbr/D3D11/D3D11Primitive.cpp index b769e667..c0b313df 100644 --- a/src/conformance/framework/pbr/D3D11/D3D11Primitive.cpp +++ b/src/conformance/framework/pbr/D3D11/D3D11Primitive.cpp @@ -77,11 +77,13 @@ namespace namespace Pbr { D3D11Primitive::D3D11Primitive(UINT indexCount, Microsoft::WRL::ComPtr indexBuffer, - Microsoft::WRL::ComPtr vertexBuffer, std::shared_ptr material) + Microsoft::WRL::ComPtr vertexBuffer, std::shared_ptr material, + std::vector nodeIndices) : m_indexCount(indexCount) , m_indexBuffer(std::move(indexBuffer)) , m_vertexBuffer(std::move(vertexBuffer)) , m_material(std::move(material)) + , m_nodeIndices(std::move(nodeIndices)) { } @@ -89,13 +91,14 @@ namespace Pbr const std::shared_ptr& material, bool updatableBuffers) : D3D11Primitive((UINT)primitiveBuilder.Indices.size(), CreateIndexBuffer(pbrResources.GetDevice().Get(), primitiveBuilder, updatableBuffers), - CreateVertexBuffer(pbrResources.GetDevice().Get(), primitiveBuilder, updatableBuffers), std::move(material)) + CreateVertexBuffer(pbrResources.GetDevice().Get(), primitiveBuilder, updatableBuffers), std::move(material), + primitiveBuilder.NodeIndicesVector()) { } D3D11Primitive D3D11Primitive::Clone(Pbr::D3D11Resources const& pbrResources) const { - return D3D11Primitive(m_indexCount, m_indexBuffer, m_vertexBuffer, m_material->Clone(pbrResources)); + return D3D11Primitive(m_indexCount, m_indexBuffer, m_vertexBuffer, m_material->Clone(pbrResources), m_nodeIndices); } void D3D11Primitive::UpdateBuffers(_In_ ID3D11Device* device, _In_ ID3D11DeviceContext* context, diff --git a/src/conformance/framework/pbr/D3D11/D3D11Primitive.h b/src/conformance/framework/pbr/D3D11/D3D11Primitive.h index 92f709bb..8dc73b1d 100644 --- a/src/conformance/framework/pbr/D3D11/D3D11Primitive.h +++ b/src/conformance/framework/pbr/D3D11/D3D11Primitive.h @@ -26,7 +26,7 @@ namespace Pbr D3D11Primitive() = delete; D3D11Primitive(UINT indexCount, Microsoft::WRL::ComPtr indexBuffer, Microsoft::WRL::ComPtr vertexBuffer, - std::shared_ptr material); + std::shared_ptr material, std::vector nodeIndices); D3D11Primitive(Pbr::D3D11Resources const& pbrResources, const Pbr::PrimitiveBuilder& primitiveBuilder, const std::shared_ptr& material, bool updatableBuffers = false); @@ -44,6 +44,12 @@ namespace Pbr m_material = std::move(material); } + /// Get the nodes that the primitive represents + const std::vector& GetNodes() const + { + return m_nodeIndices; + } + protected: friend class D3D11ModelInstance; void Render(_In_ ID3D11DeviceContext* context) const; @@ -54,5 +60,6 @@ namespace Pbr Microsoft::WRL::ComPtr m_indexBuffer; Microsoft::WRL::ComPtr m_vertexBuffer; std::shared_ptr m_material; + std::vector m_nodeIndices; }; } // namespace Pbr diff --git a/src/conformance/framework/pbr/D3D12/D3D12Model.cpp b/src/conformance/framework/pbr/D3D12/D3D12Model.cpp index e0f96c1a..f3a8a775 100644 --- a/src/conformance/framework/pbr/D3D12/D3D12Model.cpp +++ b/src/conformance/framework/pbr/D3D12/D3D12Model.cpp @@ -45,6 +45,9 @@ namespace Pbr if (primitive.GetMaterial()->Hidden) continue; + if (!IsAnyNodeVisible(primitive.GetNodes())) + continue; + primitive.Render(directCommandList, pbrResources, colorRenderTargetFormat, depthRenderTargetFormat); } } @@ -94,15 +97,15 @@ namespace Pbr void D3D12ModelInstance::UpdateTransforms(Pbr::D3D12Resources& pbrResources) { // If none of the node transforms have changed, no need to recompute/update the model transform structured buffer. - if (WereNodeLocalTransformsUpdated()) { - ResolveTransforms(true); + if (ResolvedTransformsNeedUpdate()) { + ResolveTransformsAndVisibilities(true); // Update node transform structured buffer. auto& resolvedTransforms = GetResolvedTransforms(); pbrResources.WithCopyCommandList([&](ID3D12GraphicsCommandList* cmdList) { m_modelTransformsStructuredBuffer.AsyncUpload(cmdList, resolvedTransforms.data(), resolvedTransforms.size()); }); - ClearTransformsUpdatedFlag(); + MarkResolvedTransformsUpdated(); } } } // namespace Pbr diff --git a/src/conformance/framework/pbr/D3D12/D3D12Primitive.cpp b/src/conformance/framework/pbr/D3D12/D3D12Primitive.cpp index 186a6272..dc7f1ab3 100644 --- a/src/conformance/framework/pbr/D3D12/D3D12Primitive.cpp +++ b/src/conformance/framework/pbr/D3D12/D3D12Primitive.cpp @@ -24,18 +24,21 @@ using namespace DirectX; namespace Pbr { D3D12Primitive::D3D12Primitive(UINT indexCount, Conformance::D3D12BufferWithUpload indexBuffer, UINT vertexCount, - Conformance::D3D12BufferWithUpload vertexBuffer, std::shared_ptr material) + Conformance::D3D12BufferWithUpload vertexBuffer, std::shared_ptr material, + std::vector nodeIndices) : m_indexCount(indexCount) , m_indexBuffer(std::move(indexBuffer)) , m_vertexCount(vertexCount) , m_vertexBuffer(std::move(vertexBuffer)) , m_material(std::move(material)) + , m_nodeIndices(std::move(nodeIndices)) { } D3D12Primitive::D3D12Primitive(Pbr::D3D12Resources& pbrResources, const Pbr::PrimitiveBuilder& primitiveBuilder, const std::shared_ptr& material) - : D3D12Primitive((UINT)primitiveBuilder.Indices.size(), {}, (UINT)primitiveBuilder.Vertices.size(), {}, std::move(material)) + : D3D12Primitive((UINT)primitiveBuilder.Indices.size(), {}, (UINT)primitiveBuilder.Vertices.size(), {}, std::move(material), + primitiveBuilder.NodeIndicesVector()) { m_indexBuffer.Allocate(pbrResources.GetDevice().Get(), primitiveBuilder.Indices.size()); m_vertexBuffer.Allocate(pbrResources.GetDevice().Get(), primitiveBuilder.Vertices.size()); @@ -58,7 +61,7 @@ namespace Pbr D3D12Primitive D3D12Primitive::Clone(Pbr::D3D12Resources const& pbrResources) const { - return D3D12Primitive(m_indexCount, m_indexBuffer, m_vertexCount, m_vertexBuffer, m_material->Clone(pbrResources)); + return D3D12Primitive(m_indexCount, m_indexBuffer, m_vertexCount, m_vertexBuffer, m_material->Clone(pbrResources), m_nodeIndices); } void D3D12Primitive::UpdateBuffers(Pbr::D3D12Resources& pbrResources, const Pbr::PrimitiveBuilder& primitiveBuilder) diff --git a/src/conformance/framework/pbr/D3D12/D3D12Primitive.h b/src/conformance/framework/pbr/D3D12/D3D12Primitive.h index 26b6f383..a3c3be5e 100644 --- a/src/conformance/framework/pbr/D3D12/D3D12Primitive.h +++ b/src/conformance/framework/pbr/D3D12/D3D12Primitive.h @@ -25,7 +25,8 @@ namespace Pbr D3D12Primitive() = delete; D3D12Primitive(UINT indexCount, Conformance::D3D12BufferWithUpload indexBuffer, UINT vertexCount, - Conformance::D3D12BufferWithUpload vertexBuffer, std::shared_ptr material); + Conformance::D3D12BufferWithUpload vertexBuffer, std::shared_ptr material, + std::vector nodeIndices); D3D12Primitive(Pbr::D3D12Resources& pbrResources, const Pbr::PrimitiveBuilder& primitiveBuilder, const std::shared_ptr& material); @@ -43,6 +44,12 @@ namespace Pbr m_material = std::move(material); } + /// Get the nodes that the primitive represents + const std::vector& GetNodes() const + { + return m_nodeIndices; + } + protected: friend class D3D12ModelInstance; void Render(_In_ ID3D12GraphicsCommandList* directCommandList, D3D12Resources& pbrResources, DXGI_FORMAT colorRenderTargetFormat, @@ -59,5 +66,6 @@ namespace Pbr std::shared_ptr m_material; Microsoft::WRL::ComPtr m_srvHeap; Microsoft::WRL::ComPtr m_samplerHeap; + std::vector m_nodeIndices; }; } // namespace Pbr diff --git a/src/conformance/framework/pbr/GltfLoader.cpp b/src/conformance/framework/pbr/GltfLoader.cpp index 70049669..bf75fed9 100644 --- a/src/conformance/framework/pbr/GltfLoader.cpp +++ b/src/conformance/framework/pbr/GltfLoader.cpp @@ -87,6 +87,8 @@ namespace primitiveBuilder.Indices[startIndex + i + 1] = startVertex + primitive.Indices[i + 2]; primitiveBuilder.Indices[startIndex + i + 2] = startVertex + primitive.Indices[i + 1]; } + + primitiveBuilder.NodeIndices.insert(transformIndex); } } diff --git a/src/conformance/framework/pbr/OpenGL/GLModel.cpp b/src/conformance/framework/pbr/OpenGL/GLModel.cpp index 9e80598f..51a03066 100644 --- a/src/conformance/framework/pbr/OpenGL/GLModel.cpp +++ b/src/conformance/framework/pbr/OpenGL/GLModel.cpp @@ -48,6 +48,9 @@ namespace Pbr if (primitive.GetMaterial()->Hidden) continue; + if (!IsAnyNodeVisible(primitive.GetNodes())) + continue; + primitive.GetMaterial()->Bind(pbrResources); primitive.Render(pbrResources.GetFillMode()); } @@ -78,15 +81,15 @@ namespace Pbr void GLModelInstance::UpdateTransforms(Pbr::GLResources const& /* pbrResources */) { // If none of the node transforms have changed, no need to recompute/update the model transform structured buffer. - if (WereNodeLocalTransformsUpdated()) { - ResolveTransforms(false); + if (ResolvedTransformsNeedUpdate()) { + ResolveTransformsAndVisibilities(false); // Update node transform structured buffer. auto& resolvedTransforms = GetResolvedTransforms(); XRC_CHECK_THROW_GLCMD(glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_modelTransformsStructuredBuffer.get())); XRC_CHECK_THROW_GLCMD( glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, sizeof(XrMatrix4x4f) * resolvedTransforms.size(), resolvedTransforms.data())); - ClearTransformsUpdatedFlag(); + MarkResolvedTransformsUpdated(); } } } // namespace Pbr diff --git a/src/conformance/framework/pbr/OpenGL/GLPrimitive.cpp b/src/conformance/framework/pbr/OpenGL/GLPrimitive.cpp index 813e9782..5c93a937 100644 --- a/src/conformance/framework/pbr/OpenGL/GLPrimitive.cpp +++ b/src/conformance/framework/pbr/OpenGL/GLPrimitive.cpp @@ -105,18 +105,19 @@ namespace namespace Pbr { GLPrimitive::GLPrimitive(GLsizei indexCount, ScopedGLBuffer indexBuffer, ScopedGLBuffer vertexBuffer, ScopedGLVertexArray vao, - std::shared_ptr material) + std::shared_ptr material, std::vector nodeIndices) : m_indexCount(indexCount) , m_indexBuffer(std::move(indexBuffer)) , m_vertexBuffer(std::move(vertexBuffer)) , m_vao(std::move(vao)) , m_material(std::move(material)) + , m_nodeIndices(std::move(nodeIndices)) { } GLPrimitive::GLPrimitive(const Pbr::PrimitiveBuilder& primitiveBuilder, const std::shared_ptr& material) : GLPrimitive((GLsizei)primitiveBuilder.Indices.size(), CreateIndexBuffer(primitiveBuilder), CreateVertexBuffer(primitiveBuilder), - ScopedGLVertexArray{}, std::move(material)) + ScopedGLVertexArray{}, std::move(material), primitiveBuilder.NodeIndicesVector()) { m_vao = CreateVAO(m_vertexBuffer, m_indexBuffer); } diff --git a/src/conformance/framework/pbr/OpenGL/GLPrimitive.h b/src/conformance/framework/pbr/OpenGL/GLPrimitive.h index 5d948470..bc5b2ac0 100644 --- a/src/conformance/framework/pbr/OpenGL/GLPrimitive.h +++ b/src/conformance/framework/pbr/OpenGL/GLPrimitive.h @@ -30,7 +30,7 @@ namespace Pbr GLPrimitive() = delete; GLPrimitive(GLsizei indexCount, ScopedGLBuffer indexBuffer, ScopedGLBuffer vertexBuffer, ScopedGLVertexArray vao, - std::shared_ptr material); + std::shared_ptr material, std::vector nodeIndices); GLPrimitive(const Pbr::PrimitiveBuilder& primitiveBuilder, const std::shared_ptr& material); void UpdateBuffers(const Pbr::PrimitiveBuilder& primitiveBuilder); @@ -47,6 +47,12 @@ namespace Pbr m_material = std::move(material); } + /// Get the nodes that the primitive represents + const std::vector& GetNodes() const + { + return m_nodeIndices; + } + protected: friend class GLModelInstance; void Render(FillMode fillMode) const; @@ -58,5 +64,6 @@ namespace Pbr ScopedGLBuffer m_vertexBuffer; ScopedGLVertexArray m_vao; std::shared_ptr m_material; + std::vector m_nodeIndices; }; } // namespace Pbr diff --git a/src/conformance/framework/pbr/PbrCommon.cpp b/src/conformance/framework/pbr/PbrCommon.cpp index 9cd626ef..1a5f0241 100644 --- a/src/conformance/framework/pbr/PbrCommon.cpp +++ b/src/conformance/framework/pbr/PbrCommon.cpp @@ -121,6 +121,8 @@ namespace Pbr } } + NodeIndices.insert(transformIndex); + return *this; } @@ -204,6 +206,8 @@ namespace Pbr } } + NodeIndices.insert(transformIndex); + return *this; } @@ -251,6 +255,12 @@ namespace Pbr vert.TexCoord0 = uvs[j]; Vertices.push_back(vert); } + NodeIndices.insert(transformIndex); return *this; } + + std::vector PrimitiveBuilder::NodeIndicesVector() const + { + return std::vector(NodeIndices.begin(), NodeIndices.end()); + } } // namespace Pbr diff --git a/src/conformance/framework/pbr/PbrCommon.h b/src/conformance/framework/pbr/PbrCommon.h index 11c0329a..1a12e816 100644 --- a/src/conformance/framework/pbr/PbrCommon.h +++ b/src/conformance/framework/pbr/PbrCommon.h @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -92,6 +93,7 @@ namespace Pbr { std::vector Vertices; std::vector Indices; + std::set NodeIndices; PrimitiveBuilder& AddSphere(float diameter, uint32_t tessellation, Pbr::NodeIndex_t transformIndex = Pbr::RootNodeIndex, RGBAColor vertexColor = RGBA::White); @@ -103,5 +105,7 @@ namespace Pbr RGBAColor vertexColor = RGBA::White); PrimitiveBuilder& AddQuad(XrVector2f sideLengths, XrVector2f textureCoord = {1, 1}, Pbr::NodeIndex_t transformIndex = Pbr::RootNodeIndex, RGBAColor vertexColor = RGBA::White); + + std::vector NodeIndicesVector() const; }; } // namespace Pbr diff --git a/src/conformance/framework/pbr/PbrMaterial.h b/src/conformance/framework/pbr/PbrMaterial.h index d6223f7a..ee911ad3 100644 --- a/src/conformance/framework/pbr/PbrMaterial.h +++ b/src/conformance/framework/pbr/PbrMaterial.h @@ -100,7 +100,7 @@ namespace Pbr mutable bool m_parametersChanged{true}; std::aligned_storage_t m_parametersStorage; - ConstantBufferData m_parameters; + ConstantBufferData m_parameters{}; BlendState m_alphaBlended{BlendState::NotAlphaBlended}; DoubleSided m_doubleSided{DoubleSided::NotDoubleSided}; diff --git a/src/conformance/framework/pbr/PbrModel.h b/src/conformance/framework/pbr/PbrModel.h index fe8bb027..30d22453 100644 --- a/src/conformance/framework/pbr/PbrModel.h +++ b/src/conformance/framework/pbr/PbrModel.h @@ -12,6 +12,8 @@ #include "common/xr_linear.h" +#include + #include #include #include @@ -20,6 +22,13 @@ namespace Pbr { + enum class NodeVisibility + { + Invisible, + Visible, + Inherit, + }; + // Node for creating a hierarchy of transforms. These transforms are referenced by vertices in the model's primitives. struct Node { @@ -136,6 +145,9 @@ namespace Pbr { const auto nodeCount = m_model->GetNodeCount(); + m_nodeLocalVisibilities.resize(nodeCount, NodeVisibility::Inherit); + m_resolvedVisibilities.resize(nodeCount, true); + m_nodeLocalTransforms.reserve(m_model->GetNodeCount()); for (const Node& node : m_model->GetNodes()) { m_nodeLocalTransforms.push_back(node.GetLocalTransform()); @@ -146,11 +158,19 @@ namespace Pbr } public: + /// Sets the visibility of a node. Nodes otherwise inherit + void SetNodeVisibility(NodeIndex_t nodeIndex, NodeVisibility visibility) + { + m_nodeLocalVisibilities[nodeIndex] = visibility; + // Visibility is implemented by scaling to 0 + m_resolvedTransformsNeedUpdate = true; + } + /// Overrides the local transform of a node void SetNodeTransform(NodeIndex_t nodeIndex, const XrMatrix4x4f& transform) { m_nodeLocalTransforms[nodeIndex] = transform; - m_nodeLocalTransformsUpdated = true; + m_resolvedTransformsNeedUpdate = true; } /// Combine a transform with the original transform from the asset @@ -164,15 +184,15 @@ namespace Pbr } protected: - bool WereNodeLocalTransformsUpdated() const noexcept + bool ResolvedTransformsNeedUpdate() const noexcept { - return m_nodeLocalTransformsUpdated; + return m_resolvedTransformsNeedUpdate; } - void ClearTransformsUpdatedFlag() noexcept + void MarkResolvedTransformsUpdated() noexcept { - m_nodeLocalTransformsUpdated = false; + m_resolvedTransformsNeedUpdate = false; } - void ResolveTransforms(bool transpose) + void ResolveTransformsAndVisibilities(bool transpose) { const auto& nodes = m_model->GetNodes(); @@ -182,11 +202,18 @@ namespace Pbr XrMatrix4x4f identityMatrix; XrMatrix4x4f_CreateIdentity(&identityMatrix); for (const auto& node : nodes) { - assert(node.GetParentNodeIndex() == Model::RootParentNodeIndex || node.GetParentNodeIndex() < node.GetNodeIndex()); - const XrMatrix4x4f& parentTransform = (node.GetParentNodeIndex() == Model::RootParentNodeIndex) - ? identityMatrix - : m_resolvedTransforms[node.GetParentNodeIndex()]; + bool parentIsRoot = node.GetParentNodeIndex() == Model::RootParentNodeIndex; + assert(parentIsRoot || node.GetParentNodeIndex() < node.GetNodeIndex()); + + bool parentVisibility = (parentIsRoot) ? true : m_resolvedVisibilities[node.GetParentNodeIndex()]; + NodeVisibility nodeVisibility = m_nodeLocalVisibilities[node.GetNodeIndex()]; + + m_resolvedVisibilities[node.GetNodeIndex()] = + nodeVisibility == NodeVisibility::Inherit ? parentVisibility : nodeVisibility == NodeVisibility::Visible; + + const XrMatrix4x4f& parentTransform = (parentIsRoot) ? identityMatrix : m_resolvedTransforms[node.GetParentNodeIndex()]; const XrMatrix4x4f& nodeTransform = m_nodeLocalTransforms[node.GetNodeIndex()]; + if (transpose) { XrMatrix4x4f nodeTransformTranspose; XrMatrix4x4f_Transpose(&nodeTransformTranspose, &nodeTransform); @@ -196,6 +223,14 @@ namespace Pbr XrMatrix4x4f_Multiply(&m_resolvedTransforms[node.GetNodeIndex()], &parentTransform, &nodeTransform); } } + + // After all node transforms and visibilities have been propagated, + // zero the transforms of invisible nodes. + for (const auto& node : nodes) { + if (!m_resolvedVisibilities[node.GetNodeIndex()]) { + XrMatrix4x4f_CreateScale(&m_resolvedTransforms[node.GetNodeIndex()], 0, 0, 0); + } + } } const Model& GetModel() const @@ -208,11 +243,23 @@ namespace Pbr return m_resolvedTransforms; } + bool IsAnyNodeVisible(nonstd::span nodeIndices) const + { + for (NodeIndex_t node : nodeIndices) { + if (m_resolvedVisibilities[node]) { + return true; + } + } + return false; + } + private: - bool m_nodeLocalTransformsUpdated{true}; + bool m_resolvedTransformsNeedUpdate{true}; // Derived classes may depend on this being immutable. std::shared_ptr m_model; + std::vector m_nodeLocalVisibilities; + std::vector m_resolvedVisibilities; // This is initialized to the local transform of every node, // but can be updated for this instance. std::vector m_nodeLocalTransforms; diff --git a/src/conformance/framework/pbr/Vulkan/VkModel.cpp b/src/conformance/framework/pbr/Vulkan/VkModel.cpp index e1e2a307..ccdb5e1a 100644 --- a/src/conformance/framework/pbr/Vulkan/VkModel.cpp +++ b/src/conformance/framework/pbr/Vulkan/VkModel.cpp @@ -46,6 +46,9 @@ namespace Pbr if (primitive.GetMaterial()->Hidden) continue; + if (!IsAnyNodeVisible(primitive.GetNodes())) + continue; + primitive.Render(directCommandBuffer, pbrResources, descriptorSet, renderPass, sampleCount, m_modelConstantBuffer.MakeDescriptor(), m_modelTransformsStructuredBuffer.MakeDescriptor()); } @@ -90,12 +93,12 @@ namespace Pbr void VulkanModelInstance::UpdateTransforms(Pbr::VulkanResources& /* pbrResources */) { // If none of the node transforms have changed, no need to recompute/update the model transform structured buffer. - if (WereNodeLocalTransformsUpdated()) { - ResolveTransforms(false); + if (ResolvedTransformsNeedUpdate()) { + ResolveTransformsAndVisibilities(false); // Update node transform structured buffer. m_modelTransformsStructuredBuffer.Update(GetResolvedTransforms()); - ClearTransformsUpdatedFlag(); + MarkResolvedTransformsUpdated(); } } } // namespace Pbr diff --git a/src/conformance/framework/pbr/Vulkan/VkPrimitive.cpp b/src/conformance/framework/pbr/Vulkan/VkPrimitive.cpp index 03d0f569..fa16771d 100644 --- a/src/conformance/framework/pbr/Vulkan/VkPrimitive.cpp +++ b/src/conformance/framework/pbr/Vulkan/VkPrimitive.cpp @@ -43,14 +43,15 @@ namespace namespace Pbr { VulkanPrimitive::VulkanPrimitive(Conformance::VertexBuffer&& vertexAndIndexBuffer, - std::shared_ptr material) - : m_vertexAndIndexBuffer(std::move(vertexAndIndexBuffer)), m_material(std::move(material)) + std::shared_ptr material, std::vector nodeIndices) + : m_vertexAndIndexBuffer(std::move(vertexAndIndexBuffer)), m_material(std::move(material)), m_nodeIndices(std::move(nodeIndices)) { } VulkanPrimitive::VulkanPrimitive(Pbr::VulkanResources const& pbrResources, const Pbr::PrimitiveBuilder& primitiveBuilder, const std::shared_ptr& material) - : VulkanPrimitive(CreateVertexBuffer(pbrResources.GetDevice(), pbrResources.GetMemoryAllocator(), primitiveBuilder), material) + : VulkanPrimitive(CreateVertexBuffer(pbrResources.GetDevice(), pbrResources.GetMemoryAllocator(), primitiveBuilder), + std::move(material), primitiveBuilder.NodeIndicesVector()) { } diff --git a/src/conformance/framework/pbr/Vulkan/VkPrimitive.h b/src/conformance/framework/pbr/Vulkan/VkPrimitive.h index 520f60c7..987b12bb 100644 --- a/src/conformance/framework/pbr/Vulkan/VkPrimitive.h +++ b/src/conformance/framework/pbr/Vulkan/VkPrimitive.h @@ -35,7 +35,8 @@ namespace Pbr using Collection = std::vector; VulkanPrimitive() = delete; - VulkanPrimitive(Conformance::VertexBuffer&& vertexAndIndexBuffer, std::shared_ptr material); + VulkanPrimitive(Conformance::VertexBuffer&& vertexAndIndexBuffer, std::shared_ptr material, + std::vector nodeIndices); VulkanPrimitive(Pbr::VulkanResources const& pbrResources, const Pbr::PrimitiveBuilder& primitiveBuilder, const std::shared_ptr& material); @@ -51,6 +52,12 @@ namespace Pbr m_material = std::move(material); } + /// Get the nodes that the primitive represents + const std::vector& GetNodes() const + { + return m_nodeIndices; + } + protected: friend class VulkanModelInstance; void Render(Conformance::CmdBuffer& directCommandBuffer, VulkanResources& pbrResources, VkDescriptorSet descriptorSet, @@ -63,5 +70,6 @@ namespace Pbr private: Conformance::VertexBuffer m_vertexAndIndexBuffer; std::shared_ptr m_material; + std::vector m_nodeIndices; }; } // namespace Pbr diff --git a/src/conformance/framework/pbr/Vulkan/VkResources.cpp b/src/conformance/framework/pbr/Vulkan/VkResources.cpp index 17a6e267..721a5803 100644 --- a/src/conformance/framework/pbr/Vulkan/VkResources.cpp +++ b/src/conformance/framework/pbr/Vulkan/VkResources.cpp @@ -373,7 +373,14 @@ namespace Pbr VulkanResources::VulkanResources(VulkanResources&& resources) noexcept = default; - VulkanResources::~VulkanResources() = default; + VulkanResources::~VulkanResources() + { + // stagingBuffers are queued to be cleared in Wait(). If Wait() has not been called, clear them here. + for (auto stagingBuffer : m_impl->Resources.StagingBuffers) { + stagingBuffer.Reset(GetDevice()); + } + m_impl->Resources.StagingBuffers.clear(); + } /* IResources implementations */ std::shared_ptr VulkanResources::CreateFlatMaterial(RGBAColor baseColorFactor, float roughnessFactor, float metallicFactor, diff --git a/src/conformance/framework/platform_plugin_posix.cpp b/src/conformance/framework/platform_plugin_posix.cpp index 84ce9d19..0370eb9b 100644 --- a/src/conformance/framework/platform_plugin_posix.cpp +++ b/src/conformance/framework/platform_plugin_posix.cpp @@ -66,7 +66,7 @@ namespace Conformance } private: - bool initialized; + bool initialized{}; }; std::shared_ptr CreatePlatformPlugin() diff --git a/src/conformance/platform_specific/android_resources/mipmap-hdpi/ic_launcher.png b/src/conformance/platform_specific/android_resources/mipmap-hdpi/ic_launcher.png index 2d33730e..be5977cb 100644 Binary files a/src/conformance/platform_specific/android_resources/mipmap-hdpi/ic_launcher.png and b/src/conformance/platform_specific/android_resources/mipmap-hdpi/ic_launcher.png differ diff --git a/src/conformance/platform_specific/android_resources/mipmap-hdpi/ic_launcher_round.png b/src/conformance/platform_specific/android_resources/mipmap-hdpi/ic_launcher_round.png index 2d33730e..be5977cb 100644 Binary files a/src/conformance/platform_specific/android_resources/mipmap-hdpi/ic_launcher_round.png and b/src/conformance/platform_specific/android_resources/mipmap-hdpi/ic_launcher_round.png differ diff --git a/src/conformance/platform_specific/android_resources/mipmap-mdpi/ic_launcher.png b/src/conformance/platform_specific/android_resources/mipmap-mdpi/ic_launcher.png index eaa38980..b2766b76 100644 Binary files a/src/conformance/platform_specific/android_resources/mipmap-mdpi/ic_launcher.png and b/src/conformance/platform_specific/android_resources/mipmap-mdpi/ic_launcher.png differ diff --git a/src/conformance/platform_specific/android_resources/mipmap-mdpi/ic_launcher_round.png b/src/conformance/platform_specific/android_resources/mipmap-mdpi/ic_launcher_round.png index eaa38980..b2766b76 100644 Binary files a/src/conformance/platform_specific/android_resources/mipmap-mdpi/ic_launcher_round.png and b/src/conformance/platform_specific/android_resources/mipmap-mdpi/ic_launcher_round.png differ diff --git a/src/conformance/platform_specific/android_resources/mipmap-xhdpi/ic_launcher.png b/src/conformance/platform_specific/android_resources/mipmap-xhdpi/ic_launcher.png index 4d86949e..0ba9b3d2 100644 Binary files a/src/conformance/platform_specific/android_resources/mipmap-xhdpi/ic_launcher.png and b/src/conformance/platform_specific/android_resources/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/conformance/platform_specific/android_resources/mipmap-xhdpi/ic_launcher_round.png b/src/conformance/platform_specific/android_resources/mipmap-xhdpi/ic_launcher_round.png index 4d86949e..0ba9b3d2 100644 Binary files a/src/conformance/platform_specific/android_resources/mipmap-xhdpi/ic_launcher_round.png and b/src/conformance/platform_specific/android_resources/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/src/conformance/platform_specific/android_resources/mipmap-xxhdpi/ic_launcher.png b/src/conformance/platform_specific/android_resources/mipmap-xxhdpi/ic_launcher.png index 0599c2d2..8d187d05 100644 Binary files a/src/conformance/platform_specific/android_resources/mipmap-xxhdpi/ic_launcher.png and b/src/conformance/platform_specific/android_resources/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/conformance/platform_specific/android_resources/mipmap-xxhdpi/ic_launcher_round.png b/src/conformance/platform_specific/android_resources/mipmap-xxhdpi/ic_launcher_round.png index 0599c2d2..8d187d05 100644 Binary files a/src/conformance/platform_specific/android_resources/mipmap-xxhdpi/ic_launcher_round.png and b/src/conformance/platform_specific/android_resources/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/src/conformance/platform_specific/android_resources/mipmap-xxxhdpi/ic_launcher.png b/src/conformance/platform_specific/android_resources/mipmap-xxxhdpi/ic_launcher.png index 61152a64..94403b46 100644 Binary files a/src/conformance/platform_specific/android_resources/mipmap-xxxhdpi/ic_launcher.png and b/src/conformance/platform_specific/android_resources/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/conformance/platform_specific/android_resources/mipmap-xxxhdpi/ic_launcher_round.png b/src/conformance/platform_specific/android_resources/mipmap-xxxhdpi/ic_launcher_round.png index 61152a64..94403b46 100644 Binary files a/src/conformance/platform_specific/android_resources/mipmap-xxxhdpi/ic_launcher_round.png and b/src/conformance/platform_specific/android_resources/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/src/loader/manifest_file.cpp b/src/loader/manifest_file.cpp index 0caf05c9..ca212d34 100644 --- a/src/loader/manifest_file.cpp +++ b/src/loader/manifest_file.cpp @@ -773,8 +773,7 @@ void ApiLayerManifestFile::AddManifestFilesAndroid(const std::string &openxr_com } std::istringstream json_stream(std::string{buf, length}); - CreateIfValid(ManifestFileType::MANIFEST_TYPE_EXPLICIT_API_LAYER, filename, json_stream, - &ApiLayerManifestFile::LocateLibraryInAssets, manifest_files); + CreateIfValid(type, filename, json_stream, &ApiLayerManifestFile::LocateLibraryInAssets, manifest_files); } } #endif // defined(XR_USE_PLATFORM_ANDROID) && defined(XR_KHR_LOADER_INIT_SUPPORT)