diff --git a/.github/workflows/android-cts-build.yml b/.github/workflows/android-cts-build.yml index 100154ea..43635ba7 100644 --- a/.github/workflows/android-cts-build.yml +++ b/.github/workflows/android-cts-build.yml @@ -26,7 +26,7 @@ jobs: fetch-depth: "${{ github.event.release && '0' || '1'}}" - name: Get modern CMake and Ninja - uses: lukka/get-cmake@v3.30.3 + uses: lukka/get-cmake@v3.30.5 - name: set up JDK 11 uses: actions/setup-java@v4 diff --git a/.github/workflows/android-cts-pr.yml b/.github/workflows/android-cts-pr.yml index acae4f12..c9bc83c7 100644 --- a/.github/workflows/android-cts-pr.yml +++ b/.github/workflows/android-cts-pr.yml @@ -19,7 +19,7 @@ jobs: lfs: true - name: Get modern CMake and Ninja - uses: lukka/get-cmake@v3.30.3 + uses: lukka/get-cmake@v3.30.5 - name: set up JDK 11 uses: actions/setup-java@v4 diff --git a/.github/workflows/msvc-build-preset.yml b/.github/workflows/msvc-build-preset.yml index 877b9cc5..5b94b950 100644 --- a/.github/workflows/msvc-build-preset.yml +++ b/.github/workflows/msvc-build-preset.yml @@ -36,7 +36,7 @@ jobs: fetch-depth: "${{ github.event.release && '0' || '1'}}" - name: Get modern CMake and Ninja - uses: lukka/get-cmake@v3.30.3 + uses: lukka/get-cmake@v3.30.5 - name: Add msbuild to PATH uses: microsoft/setup-msbuild@v2 diff --git a/.reuse/dep5 b/.reuse/dep5 index ac74aa84..ac059d99 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -50,7 +50,7 @@ Files: src/external/tinygltf/README.md src/external/tinygltf/tiny_gltf.h Copyright: 2015-Present, Syoyo Fujita, AurĂ©lien Chatelain and many contributors License: MIT -Comment: Unmodified, vendored copy of a subset of the tiny-gltf repo v2.8.9 +Comment: Unmodified, vendored copy of a subset of the tiny-gltf repo v2.9.3 Files: src/external/d3dx12/* Copyright: Copyright (c) Microsoft Corporation. diff --git a/changes/conformance/mr.2578.gl.md b/changes/conformance/mr.2578.gl.md new file mode 100644 index 00000000..6b860722 --- /dev/null +++ b/changes/conformance/mr.2578.gl.md @@ -0,0 +1,2 @@ +- Improvement: Update `stb_image` from 2.27 to 2.30 (latest upstream) for fixes, security improvements, and optimizations. +- Improvement: Update `stb_typetype` to latest upstream for float comparison fixes. diff --git a/changes/conformance/mr.3017.gl.md b/changes/conformance/mr.3017.gl.md new file mode 100644 index 00000000..aa564c9e --- /dev/null +++ b/changes/conformance/mr.3017.gl.md @@ -0,0 +1 @@ +Improvement: Reorganize session state tests and use test sections, annotating assertions with spec links. diff --git a/changes/conformance/mr.3351.gl.md b/changes/conformance/mr.3351.gl.md new file mode 100644 index 00000000..fc9ec4a6 --- /dev/null +++ b/changes/conformance/mr.3351.gl.md @@ -0,0 +1,4 @@ +--- +- issue.2239.gl +--- +- New test: Show a perceptively-smooth gradient in two swapchain formats or two layer types (projection vs quad), to do basic rendering verification. This primarily catches issues where non-linear textures are being interpreted as linear or vice-versa in a runtime's compositor. diff --git a/changes/conformance/mr.3366.gl.md b/changes/conformance/mr.3366.gl.md new file mode 100644 index 00000000..6d6c98b4 --- /dev/null +++ b/changes/conformance/mr.3366.gl.md @@ -0,0 +1 @@ +Improvement: Update tinygltf to version 2.9.3. diff --git a/changes/conformance/mr.3489.gl.md b/changes/conformance/mr.3489.gl.md new file mode 100644 index 00000000..b414ea44 --- /dev/null +++ b/changes/conformance/mr.3489.gl.md @@ -0,0 +1 @@ +Improvement: List failed test cases, and unmatched test specs, at end of CTS run. diff --git a/changes/conformance/mr.3493.gl.md b/changes/conformance/mr.3493.gl.md new file mode 100644 index 00000000..b6afca7f --- /dev/null +++ b/changes/conformance/mr.3493.gl.md @@ -0,0 +1 @@ +- Fix: Update generated code to allow dash in interaction profile paths. diff --git a/changes/conformance/mr.3499.gl.md b/changes/conformance/mr.3499.gl.md new file mode 100644 index 00000000..af0bcd80 --- /dev/null +++ b/changes/conformance/mr.3499.gl.md @@ -0,0 +1,4 @@ +--- +- mr.3499.gl +--- +Improvement: Add warnings if supporting OpenXR 1.1 but not supporting optional, but recommended, extensions providing compatibility for OpenXR 1.0 apps/engines. diff --git a/changes/conformance/mr.3506.gl.md b/changes/conformance/mr.3506.gl.md new file mode 100644 index 00000000..ed72abca --- /dev/null +++ b/changes/conformance/mr.3506.gl.md @@ -0,0 +1 @@ +Fix: Allow interaction profiles which do not expose boolean paths to be tested. diff --git a/changes/conformance/mr.3507.gl.1.md b/changes/conformance/mr.3507.gl.1.md new file mode 100644 index 00000000..ad188b5d --- /dev/null +++ b/changes/conformance/mr.3507.gl.1.md @@ -0,0 +1 @@ +- Improvement: Make generated interaction profile sources reproducible/deterministic. diff --git a/changes/conformance/mr.3507.gl.2.md b/changes/conformance/mr.3507.gl.2.md new file mode 100644 index 00000000..96dcd82e --- /dev/null +++ b/changes/conformance/mr.3507.gl.2.md @@ -0,0 +1,5 @@ +--- +- mr.3507.gl.2 +- mr.3521.gl +--- +- Fix: Correctly parse and process interaction profile path and binding path dependencies from XML, and clean up code in `interaction_profile_processor.py`. diff --git a/changes/conformance/mr.3507.gl.md b/changes/conformance/mr.3507.gl.md new file mode 100644 index 00000000..9369faca --- /dev/null +++ b/changes/conformance/mr.3507.gl.md @@ -0,0 +1,4 @@ +--- +- issue.2350.gl +--- +- Fix: Include interaction profile dependencies in generated binding path dependencies. diff --git a/changes/conformance/mr.3517.gl.md b/changes/conformance/mr.3517.gl.md new file mode 100644 index 00000000..618ebadd --- /dev/null +++ b/changes/conformance/mr.3517.gl.md @@ -0,0 +1,6 @@ +--- +- mr.3517.gl +- mr.3541.gl +- mr.3544.gl +--- +- Fix: Add missing XML description of `EXT_hand_interaction` additions to several interaction profiles, affecting generated interaction profile tests. diff --git a/changes/conformance/mr.3521.gl.md b/changes/conformance/mr.3521.gl.md new file mode 100644 index 00000000..f77c5879 --- /dev/null +++ b/changes/conformance/mr.3521.gl.md @@ -0,0 +1 @@ +- Improvement: Correct the build-time dependencies of the generated interaction profile data. diff --git a/changes/conformance/mr.3523.gl.md b/changes/conformance/mr.3523.gl.md new file mode 100644 index 00000000..372f9359 --- /dev/null +++ b/changes/conformance/mr.3523.gl.md @@ -0,0 +1 @@ +New test: When using `XR_FB_space_warp`, verify runtime returns `XR_ERROR_VALIDATION_FAILURE` if `nearZ` is equal to `farZ` diff --git a/changes/conformance/mr.3524.gl.md b/changes/conformance/mr.3524.gl.md new file mode 100644 index 00000000..dda7f881 --- /dev/null +++ b/changes/conformance/mr.3524.gl.md @@ -0,0 +1,4 @@ +--- +- mr.3506.gl +--- +Improvement: Note in the Usage document that `/interaction_profiles/ext/hand_interaction_ext` and `/interaction_profiles/ext/eye_gaze_interaction` are generic interaction profiles that should be tested if supported. diff --git a/changes/conformance/mr.3527.gl.md b/changes/conformance/mr.3527.gl.md new file mode 100644 index 00000000..098d2da2 --- /dev/null +++ b/changes/conformance/mr.3527.gl.md @@ -0,0 +1 @@ +Improvement: added support for `PRIMARY_STEREO_WITH_FOVEATED_INSET` in `xrLocateViews` tests. diff --git a/changes/conformance/mr.3534.gl.md b/changes/conformance/mr.3534.gl.md new file mode 100644 index 00000000..b6484234 --- /dev/null +++ b/changes/conformance/mr.3534.gl.md @@ -0,0 +1,5 @@ +--- +- mr.3534.gl +- mr.3559.gl +--- +Improvement: Add additional tests for `xrGetInputSourceLocalizedName` diff --git a/changes/conformance/mr.3547.gl.md b/changes/conformance/mr.3547.gl.md new file mode 100644 index 00000000..940c0ca2 --- /dev/null +++ b/changes/conformance/mr.3547.gl.md @@ -0,0 +1 @@ +- Fix: remove unused `functionPtr` field in global `FunctionInfoMap` as it is invalid if called with a different instance diff --git a/changes/conformance/mr.3553.gl.md b/changes/conformance/mr.3553.gl.md new file mode 100644 index 00000000..c6c3c4fc --- /dev/null +++ b/changes/conformance/mr.3553.gl.md @@ -0,0 +1,2 @@ +- Improvement: Add command lines for Android for the self tests in the Usage document. +- Fix: Correct an outdated description of the self tests in the Usage document, glTF files now load asynchronously in those tests. diff --git a/changes/conformance/mr.3562.gl.md b/changes/conformance/mr.3562.gl.md new file mode 100644 index 00000000..73492d2b --- /dev/null +++ b/changes/conformance/mr.3562.gl.md @@ -0,0 +1 @@ +- Fix: Internal cross-references in the "Submission Package" section of the Usage document. diff --git a/changes/registry/mr.3513.gl.md b/changes/registry/mr.3513.gl.md deleted file mode 100644 index dab7f51f..00000000 --- a/changes/registry/mr.3513.gl.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -- issue.2350.gl -- issue.2375.gl ---- -Fix: Correct XML description of OpenXR 1.1 related additions to the promoted Meta Touch Plus, Touch Pro, and Touch (Rift CV1) controller interaction profiles. diff --git a/specification/Makefile b/specification/Makefile index f9e92a09..76ae74a7 100644 --- a/specification/Makefile +++ b/specification/Makefile @@ -39,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.41 +SPECREVISION = 1.1.42 REVISION_COMPONENTS = $(subst ., ,$(SPECREVISION)) MAJORMINORVER = $(word 1,$(REVISION_COMPONENTS)).$(word 2,$(REVISION_COMPONENTS)) @@ -334,7 +334,8 @@ html: $(HTMLSPEC) ASCIIDOCTOR_TARGETS += $(HTMLSPEC) # Target-specific variables and deps customizing the AsciiDoctor rule -$(HTMLSPEC): ATTRIBOPTS += -a sectanchors +# EXTRAATRIBS is for build-time customization +$(HTMLSPEC): ATTRIBOPTS += -a sectanchors $(EXTRAATTRIBS) $(HTMLSPEC): ADOCOPTS += $(ADOCHTMLOPTS) $(HTMLSPEC): $(COMMONDOCS) @@ -349,7 +350,8 @@ 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 --require ./scripts/pdf-index-customizer.rb +# EXTRAATRIBS is for build-time customization +$(PDFSPEC) $(PDFA4SPEC): BACKEND_ARGS=--backend pdf --require asciidoctor-pdf -a compress --require ./scripts/pdf-index-customizer.rb $(EXTRAATTRIBS) $(PDFSPEC): PAGESIZE=LETTER $(PDFA4SPEC): PAGESIZE=A4 $(PDFSPEC) $(PDFA4SPEC): $(COMMONDOCS) diff --git a/specification/registry/xr.xml b/specification/registry/xr.xml index d7a4b1c8..17ea6811 100644 --- a/specification/registry/xr.xml +++ b/specification/registry/xr.xml @@ -135,7 +135,7 @@ maintained in the default branch of the Khronos OpenXR GitHub project. updates them automatically by processing a line at a time. --> // OpenXR current version number. -#define XR_CURRENT_API_VERSION XR_MAKE_VERSION(1, 1, 41) +#define XR_CURRENT_API_VERSION XR_MAKE_VERSION(1, 1, 42) + + + + + + + + + + + + + + + + + + + + + + + - + @@ -8474,8 +8499,6 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( - - @@ -8491,15 +8514,13 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( - - - + @@ -8588,14 +8609,19 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( - + + + + + + - + @@ -8623,9 +8649,8 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( - - + @@ -8689,7 +8714,6 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( - @@ -8718,7 +8742,6 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( - @@ -8747,9 +8770,8 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( - - + @@ -8781,7 +8803,6 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( - @@ -8810,7 +8831,6 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( - @@ -8835,7 +8855,6 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( - @@ -8860,7 +8879,6 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( - @@ -8915,7 +8933,6 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( - @@ -8924,7 +8941,6 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( - @@ -9763,6 +9779,11 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( + @@ -10208,7 +10229,7 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( - + @@ -10229,6 +10250,12 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( + + + + + + @@ -10659,8 +10686,20 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( - + + + + + + + + + + + + + @@ -11242,6 +11281,17 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( + + + + + + + + + + + @@ -11349,6 +11399,11 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( + @@ -11367,12 +11422,6 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( - - - - - - @@ -12367,6 +12416,17 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( + + + + + + + + + + + @@ -12595,6 +12655,40 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -15873,6 +15967,11 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( + + + + + diff --git a/specification/scripts/docgenerator.py b/specification/scripts/docgenerator.py index 72960e49..e31f5a6e 100644 --- a/specification/scripts/docgenerator.py +++ b/specification/scripts/docgenerator.py @@ -34,6 +34,7 @@ class _Enumerant: comment: str extname: Optional[str] = None deprecated: Optional[str] = None + alias: Optional[str] = None def orgLevelKey(name): @@ -447,7 +448,6 @@ def genStruct(self, typeinfo, typeName, alias): self.writeInclude('structs', typeName, body) - def _maybe_return_enumerant_object_for_table( self, elems, elem, missing_comments: List[str] ) -> Optional[_Enumerant]: @@ -466,15 +466,21 @@ def _maybe_return_enumerant_object_for_table( return comment = elem.get("comment") + alias = elem.get("alias") if comment is None: if name.endswith("_UNKNOWN") and num_val == 0: # This is a placeholder for 0-initialization to be clearly invalid. # Just skip this silently return - # Skip but record this in case it is an odd-one-out missing - # a comment. - missing_comments.append(name) - return + if alias is not None: + # oh it's an alias. That's fine. We can generate a comment. + comment = f"Alias for ename:{alias}" + + else: + # Skip but record this in case it is an odd-one-out missing + # a comment. + missing_comments.append(name) + return assert num_val is not None @@ -531,13 +537,18 @@ def genEnumTable(self, groupinfo, groupName): if maybe_data: values.append(maybe_data) - if values: - # If any had a comment, output it. + if values and any(v.alias is None for v in values): + # If any had a (non-alias) comment, output it. if missing_comments: - self.logMsg('warn', 'The following values for', groupName, - 'were omitted from the table due to missing comment attributes:', - ', '.join(missing_comments)) + # Warn if it looks like we have comments, but some were missed. + if len(missing_comments) < len(values): + self.logMsg('warn', 'The following value(s) for', groupName, + 'were omitted from the table due to missing comment attributes:', + ', '.join(missing_comments)) + else: + self.logMsg('warn', 'The enumeration ', groupName, + 'appears to be missing comments for most of its elements') group_type = groupinfo.elem.get('type') if groupName == self.result_type: diff --git a/specification/scripts/update_version.py b/specification/scripts/update_version.py index 1c4ba8c9..a6d4bbc9 100755 --- a/specification/scripts/update_version.py +++ b/specification/scripts/update_version.py @@ -17,12 +17,10 @@ def update_makefile(fn, spec_version): for line in fileinput.input(fn, inplace=True): - printed = False if 'SPECREVISION = ' in line: - printed = True print('SPECREVISION = %s.%s.%s' % spec_version) - if not printed: - print(f"{line}", end='') + else: + print(line, end='') if __name__ == "__main__": @@ -32,8 +30,6 @@ def update_makefile(fn, spec_version): config = configparser.ConfigParser() config.read_file(fp) versions = config['Version'] - major_version = versions['MAJOR'] - minor_version = versions['MINOR'] spec_version = (versions['MAJOR'], versions['MINOR'], versions['PATCH']) # Now update the version in the appropriate places in the @@ -41,13 +37,10 @@ def update_makefile(fn, spec_version): # 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_MAKE_VERSION' in line: - printed = True - print('#define XR_CURRENT_API_VERSION XR_MAKE_VERSION(%s, %s, %s)' % spec_version) - if not printed: - print(f"{line}", end='') + if 'XR_CURRENT_API_VERSION' in line and 'XR_MAKE_VERSION' in line: + print('#define XR_CURRENT_API_VERSION XR_MAKE_VERSION(%s, %s, %s)' % spec_version) + else: + print(line, end='') # Now update the version in the appropriate places in the # specification make file (Makefile). diff --git a/src/common/platform_utils.hpp b/src/common/platform_utils.hpp index 19cd41cb..59b5bf2f 100644 --- a/src/common/platform_utils.hpp +++ b/src/common/platform_utils.hpp @@ -71,6 +71,8 @@ #define XR_ARCH_ABI "riscv64" #elif defined(__sparc__) && defined(__arch64__) #define XR_ARCH_ABI "sparc64" +#elif defined(__loongarch64) +#define XR_ARCH_ABI "loong64" #else #error "No architecture string known!" #endif diff --git a/src/conformance/conformance_test/composition_examples/gradient_linearity.png b/src/conformance/conformance_test/composition_examples/gradient_linearity.png new file mode 100644 index 00000000..32220f75 --- /dev/null +++ b/src/conformance/conformance_test/composition_examples/gradient_linearity.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fda1ad7752044e8a8087f415c9cb6df65b7a5dbdd77db80b2e97736508151caa +size 39900 diff --git a/src/conformance/conformance_test/composition_examples/gradient_linearity.png.license b/src/conformance/conformance_test/composition_examples/gradient_linearity.png.license new file mode 100644 index 00000000..c94a118e --- /dev/null +++ b/src/conformance/conformance_test/composition_examples/gradient_linearity.png.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2024, The Khronos Group Inc. + +SPDX-License-Identifier: Apache-2.0 diff --git a/src/conformance/conformance_test/conformance_test.cpp b/src/conformance/conformance_test/conformance_test.cpp index efed4359..a3eec100 100644 --- a/src/conformance/conformance_test/conformance_test.cpp +++ b/src/conformance/conformance_test/conformance_test.cpp @@ -177,6 +177,65 @@ namespace } } + TEST_CASE("ValidateExpectedFeatures") + { + // Create and destroy an XrInstance with a particular apiVersion to validate that + // it is possible and supported. + auto validateOpenXRVersionSupported = [](XrVersion apiVersion) -> bool { + GlobalData& globalData = GetGlobalData(); + + XrInstanceCreateInfo createInfo{XR_TYPE_INSTANCE_CREATE_INFO}; + createInfo.applicationInfo.applicationVersion = 1; + strcpy(createInfo.applicationInfo.applicationName, "conformance test"); + createInfo.applicationInfo.apiVersion = apiVersion; + createInfo.enabledApiLayerCount = (uint32_t)globalData.enabledAPILayerNames.size(); + createInfo.enabledApiLayerNames = globalData.enabledAPILayerNames.data(); + + StringVec extensions(globalData.enabledInstanceExtensionNames); + createInfo.enabledExtensionCount = (uint32_t)extensions.size(); + createInfo.enabledExtensionNames = extensions.data(); + + if (globalData.requiredPlatformInstanceCreateStruct != nullptr) { + createInfo.next = globalData.requiredPlatformInstanceCreateStruct; + } + + XrInstance instance{XR_NULL_HANDLE}; + XrResult result = xrCreateInstance(&createInfo, &instance); + if (XR_FAILED(result)) { + return false; + } + + xrDestroyInstance(instance); + return true; + }; + + SECTION("OpenXR 1.1") + { + GlobalData& globalData = GetGlobalData(); + + FeatureSet available; + globalData.PopulateVersionAndAvailableExtensions(available); + + if (available.Get(FeatureBitIndex::BIT_XR_VERSION_1_1)) { + bool openxr1_1_supported = validateOpenXRVersionSupported(XR_API_VERSION_1_1); + bool openxr1_0_supported = validateOpenXRVersionSupported(XR_API_VERSION_1_0); + REQUIRE(openxr1_1_supported); + + if (!available.Get(FeatureBitIndex::BIT_XR_KHR_locate_spaces)) { + WARN( + "Runtime supports OpenXR 1.1 but does not support XR_KHR_locate_spaces; this is not strictly required but is surprising."); + } + if (!available.Get(FeatureBitIndex::BIT_XR_KHR_maintenance1)) { + WARN( + "Runtime supports OpenXR 1.1 but does not support XR_KHR_maintenance1; this is not strictly required but is surprising."); + } + if (!openxr1_0_supported) { + WARN("Runtime supports OpenXR 1.1 but does not support OpenXR 1.0; this is not strictly required but is surprising."); + } + } + } + } + // Ensure conformance is configured correctly. TEST_CASE("ValidateEnvironment") { @@ -339,6 +398,8 @@ namespace globalData.options.viewConfiguration = arg; if (striequal(globalData.options.viewConfiguration.c_str(), "stereo")) globalData.options.viewConfigurationValue = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; + else if (striequal(globalData.options.viewConfiguration.c_str(), "stereoFoveated")) + globalData.options.viewConfigurationValue = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO_WITH_FOVEATED_INSET; else if (striequal(globalData.options.viewConfiguration.c_str(), "mono")) globalData.options.viewConfigurationValue = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_MONO; else { @@ -410,8 +471,8 @@ namespace ("Choose which hands to test: left, right, or both. Default is both.") .optional() - | Opt(parseViewConfig, "Stereo|Mono") // view configuration - ["-V"]["--viewConfiguration"] // + | Opt(parseViewConfig, "Stereo|StereoFoveated|Mono") // view configuration + ["-V"]["--viewConfiguration"] // ("Specify view configuration. Default is Stereo.") .optional() @@ -519,8 +580,8 @@ namespace Base::testCaseEnded(testCaseStats); Conformance::GlobalData& globalData = Conformance::GetGlobalData(); - globalData.conformanceReport.testSuccessCount += testCaseStats.totals.testCases.passed; - globalData.conformanceReport.testFailureCount += testCaseStats.totals.testCases.failed; + globalData.conformanceReport.results[testCaseStats.testInfo->name].testSuccessCount += testCaseStats.totals.testCases.passed; + globalData.conformanceReport.results[testCaseStats.testInfo->name].testFailureCount += testCaseStats.totals.testCases.failed; } void sectionStarting(Catch::SectionInfo const& sectionInfo) override @@ -547,10 +608,10 @@ namespace m_sectionIndent--; } - void noMatchingTestCases(Catch::StringRef /* unmatchedSpec */) override + void noMatchingTestCases(Catch::StringRef unmatchedSpec) override { Conformance::GlobalData& globalData = Conformance::GetGlobalData(); - globalData.conformanceReport.unmatchedTestSpecs = true; + globalData.conformanceReport.unmatchedTestSpecs.push_back(unmatchedSpec.data()); } void testRunEnded(Catch::TestRunStats const& testRunStats) override @@ -704,7 +765,7 @@ XrcResult XRAPI_CALL xrcRunConformanceTests(const ConformanceLaunchSettings* con int exitCode = CreateOrGetCatchSession().run(); Conformance::GlobalData& globalData = Conformance::GetGlobalData(); - *failureCount = globalData.conformanceReport.testFailureCount; + *failureCount = globalData.conformanceReport.TestFailureCount(); const auto& totals = globalData.conformanceReport.totals; conformanceTestsRun = true; @@ -712,7 +773,7 @@ XrcResult XRAPI_CALL xrcRunConformanceTests(const ConformanceLaunchSettings* con if (skipActuallyTesting) { *testResult = XRC_TEST_RESULT_SUCCESS; } - else if (globalData.conformanceReport.unmatchedTestSpecs && catchConfig.warnAboutUnmatchedTestSpecs()) { + else if (!globalData.conformanceReport.unmatchedTestSpecs.empty() && catchConfig.warnAboutUnmatchedTestSpecs()) { *testResult = XRC_TEST_RESULT_UNMATCHED_TEST_SPEC; } else if (totals.testCases.total() == 0 && !catchConfig.zeroTestsCountAsSuccess()) { diff --git a/src/conformance/conformance_test/test_GradientFormatsLinearVsNonLinear.cpp b/src/conformance/conformance_test/test_GradientFormatsLinearVsNonLinear.cpp new file mode 100644 index 00000000..b4dac66e --- /dev/null +++ b/src/conformance/conformance_test/test_GradientFormatsLinearVsNonLinear.cpp @@ -0,0 +1,299 @@ +// 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 "composition_utils.h" +#include "graphics_plugin.h" +#include "utilities/array_size.h" +#include "utilities/bitmask_to_string.h" +#include "utilities/swapchain_parameters.h" +#include "utilities/throw_helpers.h" +#include "utilities/types_and_constants.h" +#include "utilities/xr_math_operators.h" + +#include + +#include + +#include +#include + +namespace Conformance +{ + using namespace openxr::math_operators; + + static XrSwapchainCreateInfo MakeDefaultSwapchainCreateInfo(int64_t imageFormat, const SwapchainCreateTestParameters& tp) + { + XrSwapchainCreateInfo createInfo{XR_TYPE_SWAPCHAIN_CREATE_INFO}; + createInfo.faceCount = 1; + createInfo.format = imageFormat; + XrSwapchainCreateFlagsRefCPP(createInfo.createFlags) = tp.createFlagsVector[0]; + XrSwapchainUsageFlagsRefCPP(createInfo.usageFlags) = tp.usageFlagsVector[0]; + createInfo.sampleCount = 1; + createInfo.width = 64; + createInfo.height = 64; + createInfo.arraySize = tp.arrayCountVector[0]; + createInfo.mipCount = tp.mipCountVector[0]; + return createInfo; + } + + namespace + { + + constexpr int32_t labelImageWidth = 512; + constexpr int32_t labelImageHeight = 32; + constexpr int32_t labelFontSize = 18; // some format names are long! + constexpr float labelWidth = 2.0f; + constexpr float labelHeight = labelWidth * labelImageHeight / labelImageWidth; + constexpr float labelMargin = 0.2f; + inline XrCompositionLayerQuad* MakeFormatLabel(CompositionHelper& compositionHelper, XrSpace viewSpace, const std::string& label, + const XrVector3f& position) + { + + return compositionHelper.CreateQuadLayer( + compositionHelper.CreateStaticSwapchainImage( + CreateTextImage(labelImageWidth, labelImageHeight, label.c_str(), labelFontSize, WordWrap::Disabled)), + viewSpace, labelWidth, {Quat::Identity, position}); + } + + constexpr int32_t aspectRatio = 8; + constexpr int32_t gradientImageHeight = 32; + constexpr int32_t gradientImageWidth = gradientImageHeight * aspectRatio; + constexpr float gradientWidth = 1.0f; + constexpr float gradientHeight = gradientWidth / aspectRatio; + constexpr float quadZ = -3; // How far away quads are placed. + constexpr float margin = 0.02f; // the gap between quads + constexpr float yOffset = (gradientHeight + margin) / 2; + + inline MeshHandle MakeGradientMesh(IGraphicsPlugin& graphicsPlugin, SwapchainFormat::RawColorComponents referenceComponents) + { + // 0-2 + // |/| + // 1-3 + std::vector vertices; + vertices.reserve(2 * gradientImageWidth); + for (int col = 0; col < gradientImageWidth; col++) { + float value = col / (float)gradientImageWidth; + + // if any of the other format's color channels aren't present, + // they should be sampled as zero, so also zero them here to match + using Components = SwapchainFormat::RawColorComponents; + XrColor4f color{ + referenceComponents & Components::r ? value : 0.0f, + referenceComponents & Components::g ? value : 0.0f, + referenceComponents & Components::b ? value : 0.0f, + 1.0, + }; + + color = ColorUtils::FromSRGB(color); // perceptual gradient instead of linear + float x = -(gradientWidth / 2) + gradientWidth * value; + vertices.push_back(Geometry::Vertex{{x, gradientHeight / 2, 0}, {color.r, color.g, color.b}}); + vertices.push_back(Geometry::Vertex{{x, -gradientHeight / 2, 0}, {color.r, color.g, color.b}}); + } + const uint16_t quadIndices[] = {1, 0, 2, 2, 3, 1}; + std::vector indices; + indices.reserve((gradientImageWidth - 1) * ArraySize(quadIndices)); + for (int col = 0; col < gradientImageWidth - 1; col++) { + for (const uint16_t index : quadIndices) { + indices.push_back((uint16_t)(col * 2 + index)); + } + } + + return graphicsPlugin.MakeSimpleMesh(indices, vertices); + } + } // namespace + + TEST_CASE("GradientFormatsLinearVsNonLinear", "[composition][interactive]") + { + const GlobalData& globalData = GetGlobalData(); + if (!globalData.IsUsingGraphicsPlugin()) { + // Nothing to check - no graphics plugin means no swapchain + SKIP("Cannot test swapchains without a graphics plugin"); + } + + CompositionHelper compositionHelper("Linear vs Non-Linear"); + InteractiveLayerManager interactiveLayerManager(compositionHelper, "gradient_linearity.png", + "Both gradients should match both each other" + " and the example image, except for banding artifacts," + " and should appear perceptually linear." + " Banding may introduce small color artifacts, but" + " the gradients should be broadly the same color."); + + XrSession session = compositionHelper.GetSession(); + + const XrSpace viewSpace = compositionHelper.CreateReferenceSpace(XR_REFERENCE_SPACE_TYPE_VIEW, Pose::Identity); + + // Enumerate formats + std::vector imageFormatArray; + { + uint32_t countOutput = 0; + XRC_CHECK_THROW_XRCMD_UNQUALIFIED_SUCCESS(xrEnumerateSwapchainFormats(session, 0, &countOutput, nullptr)); + if (countOutput > 0) { + imageFormatArray.resize(countOutput, XRC_INVALID_IMAGE_FORMAT); + XRC_CHECK_THROW_XRCMD_UNQUALIFIED_SUCCESS( + xrEnumerateSwapchainFormats(session, countOutput, &countOutput, imageFormatArray.data())); + } + } + + int64_t defaultFormat = globalData.graphicsPlugin->GetSRGBA8Format(); + SwapchainCreateTestParameters defaultTestParameters; + REQUIRE(globalData.graphicsPlugin->GetSwapchainCreateTestParameters(defaultFormat, &defaultTestParameters)); + + compositionHelper.GetInteractionManager().AttachActionSets(); + compositionHelper.BeginSession(); + + auto doInteractiveRenderTest = [&](int64_t projFormat, const SwapchainCreateTestParameters& projTestParameters, // + int64_t quadFormat, const SwapchainCreateTestParameters& quadTestParameters) { + // Set up composition projection layer and swapchains (one swapchain per view). + std::vector swapchains; + XrCompositionLayerProjection* const projLayer = compositionHelper.CreateProjectionLayer(viewSpace); + { + const std::vector viewProperties = compositionHelper.EnumerateConfigurationViews(); + XrSwapchainCreateInfo createInfo = MakeDefaultSwapchainCreateInfo(projFormat, projTestParameters); + for (uint32_t j = 0; j < projLayer->viewCount; j++) { + // this may need to be rounded up for compressed formats (maybe compensated for with a subimage?) + createInfo.width = viewProperties[j].recommendedImageRectWidth; + createInfo.height = viewProperties[j].recommendedImageRectHeight; + const XrSwapchain swapchain = compositionHelper.CreateSwapchain(createInfo); + const_cast(projLayer->views[j].subImage) = compositionHelper.MakeDefaultSubImage(swapchain, 0); + swapchains.push_back(swapchain); + } + } + + // Set up swapchain for composition quad layer + XrSwapchain gradientQuadSwapchain; + { + XrSwapchainCreateInfo createInfo = MakeDefaultSwapchainCreateInfo(quadFormat, quadTestParameters); + // this may need to be rounded up for compressed formats (maybe compensated for with a subimage?) + createInfo.width = gradientImageWidth; + createInfo.height = gradientImageHeight; + gradientQuadSwapchain = compositionHelper.CreateSwapchain(createInfo); + } + + const float labelYOffset = gradientHeight + (labelHeight / 2) + labelMargin; + + XrCompositionLayerQuad* const projFormatLabelQuad = MakeFormatLabel( + compositionHelper, viewSpace, "Projection: " + projTestParameters.imageFormatName, {0, labelYOffset, quadZ}); + interactiveLayerManager.AddLayer(projFormatLabelQuad); + + XrCompositionLayerQuad* const quadFormatLabelQuad = + MakeFormatLabel(compositionHelper, viewSpace, "Quad: " + quadTestParameters.imageFormatName, {0, -labelYOffset, quadZ}); + interactiveLayerManager.AddLayer(quadFormatLabelQuad); + + XrCompositionLayerQuad* gradientQuad = compositionHelper.CreateQuadLayer(gradientQuadSwapchain, viewSpace, gradientWidth, + XrPosef{Quat::Identity, {0, -yOffset, quadZ}}); + interactiveLayerManager.AddLayer(gradientQuad); + + // Each mesh should only write non-zero to the color components that the **other** format supports, which is why the below looks backwards. + // We could do an intersection of the two format components, but e.g. writing all white to a format with fewer channels + // may expose errors that writing something more limited would not. + MeshHandle quadMesh = MakeGradientMesh(*globalData.graphicsPlugin, projTestParameters.colorComponents); + MeshHandle projMesh = MakeGradientMesh(*globalData.graphicsPlugin, quadTestParameters.colorComponents); + + auto updateLayers = [&](const XrFrameState& frameState) { + auto viewData = compositionHelper.LocateViews(viewSpace, frameState.predictedDisplayTime); + const auto& viewState = std::get(viewData); + + std::vector layers; + if (viewState.viewStateFlags & XR_VIEW_STATE_POSITION_VALID_BIT && + viewState.viewStateFlags & XR_VIEW_STATE_ORIENTATION_VALID_BIT) { + const auto& views = std::get>(viewData); + + const uint32_t imageArrayIndex = 0; + // Render the gradient into the quad's swapchain + compositionHelper.AcquireWaitReleaseImage(gradientQuadSwapchain, [&](const XrSwapchainImageBaseHeader* swapchainImage) { + GetGlobalData().graphicsPlugin->ClearImageSlice(swapchainImage, imageArrayIndex, Colors::Black); + + XrCompositionLayerProjectionView view = {XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW}; + const float quadRenderDistance = 1.0; + const float halfAngleX = std::atan2(gradientWidth / 2, quadRenderDistance); + const float halfAngleY = std::atan2(gradientHeight / 2, quadRenderDistance); + view.fov = {-halfAngleX, halfAngleX, halfAngleY, -halfAngleY}; + view.pose = Pose::Identity; + view.subImage = compositionHelper.MakeDefaultSubImage(gradientQuadSwapchain, imageArrayIndex); + + auto quadMeshList = {MeshDrawable{quadMesh, {Quat::Identity, {0, 0, -quadRenderDistance}}}}; + GetGlobalData().graphicsPlugin->RenderView(view, swapchainImage, RenderParams().Draw(quadMeshList)); + }); + + auto projMeshList = {MeshDrawable{projMesh, {Quat::Identity, {0, yOffset, quadZ}}}}; + // 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, imageArrayIndex, Colors::Black); + const_cast(projLayer->views[view].fov) = views[view].fov; + const_cast(projLayer->views[view].pose) = views[view].pose; + GetGlobalData().graphicsPlugin->RenderView(projLayer->views[view], swapchainImage, + RenderParams().Draw(projMeshList)); + }); + } + + layers.push_back({reinterpret_cast(projLayer)}); + } + return interactiveLayerManager.EndFrame(frameState, layers); + }; + + RenderLoop(session, updateLayers).Loop(); + }; + + for (int64_t imageFormat : imageFormatArray) { + + SwapchainCreateTestParameters tp; + REQUIRE(globalData.graphicsPlugin->GetSwapchainCreateTestParameters(imageFormat, &tp)); + + if (!tp.supportsRendering) { + // currently, we render to the format, + // but we could generate the image + // or render to another format and copy. + continue; + } + if (!tp.colorFormat) { + // we are testing by visual inspection + continue; + } + if (tp.colorIntegerRange != SwapchainFormat::ColorIntegerRange::NoIntegerColor) { + // unsure whether and how non-normalized + // integer formats map to the screen + continue; + } + + DYNAMIC_SECTION(tp.imageFormatName) + { + CAPTURE(tp.imageFormatName); + + SECTION("Custom projection layer format") + { + INFO("Formats: projection: " << tp.imageFormatName << ", quad: " << defaultTestParameters.imageFormatName + << " (default)"); + doInteractiveRenderTest(imageFormat, tp, defaultFormat, defaultTestParameters); + } + if (imageFormat == defaultFormat) { + // compare proj to quad, but no point doing it twice + continue; + } + SECTION("Custom quad layer format") + { + INFO("Formats: projection: " << defaultTestParameters.imageFormatName << " (default), quad: " << tp.imageFormatName); + doInteractiveRenderTest(defaultFormat, defaultTestParameters, imageFormat, tp); + } + } + + // SwapchainScoped will have xrDestroySwapchain + // now we need to flush + GetGlobalData().graphicsPlugin->ClearSwapchainCache(); + GetGlobalData().graphicsPlugin->Flush(); + } + } +} // namespace Conformance diff --git a/src/conformance/conformance_test/test_SessionState.cpp b/src/conformance/conformance_test/test_SessionState.cpp index 43a24ccc..da379cf4 100644 --- a/src/conformance/conformance_test/test_SessionState.cpp +++ b/src/conformance/conformance_test/test_SessionState.cpp @@ -16,8 +16,7 @@ #include "conformance_utils.h" #include "conformance_framework.h" -#include "matchers.h" -#include "utilities/utils.h" +#include "utilities/types_and_constants.h" #include "utilities/throw_helpers.h" #include @@ -26,168 +25,182 @@ #include #include -#include #include -#include -#include -#include #include -#include - -#define AS_LIST(name, val) name, -constexpr XrViewConfigurationType KnownViewTypes[] = {XR_LIST_ENUM_XrViewConfigurationType(AS_LIST)}; -#undef AS_LIST namespace Conformance { + static bool tryReadEvent(XrInstance instance, XrEventDataBuffer* evt) + { + *evt = {XR_TYPE_EVENT_DATA_BUFFER}; + XrResult res; + XRC_CHECK_THROW_XRCMD(res = xrPollEvent(instance, evt)); + return res == XR_SUCCESS; + } - TEST_CASE("SessionState", "") + static bool tryGetNextSessionState(XrInstance instance, XrEventDataSessionStateChanged* evt) { - AutoBasicSession session(AutoBasicSession::createSession); - 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; - XRC_CHECK_THROW_XRCMD(res = xrPollEvent(session.GetInstance(), evt)); - return res == XR_SUCCESS; - }; - - auto tryGetNextSessionState = [&](XrEventDataSessionStateChanged* evt) { - XrEventDataBuffer buffer; - while (tryReadEvent(&buffer)) { - if (buffer.type == XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED) { - *evt = *reinterpret_cast(&buffer); - return true; - } + XrEventDataBuffer buffer; + while (tryReadEvent(instance, &buffer)) { + if (buffer.type == XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED) { + *evt = *reinterpret_cast(&buffer); + return true; } + } + return false; + } - return false; - }; + static bool waitForNextSessionState(XrInstance instance, XrEventDataSessionStateChanged* evt, std::chrono::nanoseconds duration = 1s) + { + CountdownTimer countdown(duration); + while (!countdown.IsTimeUp()) { + if (tryGetNextSessionState(instance, evt)) { + return true; + } - auto waitForNextSessionState = [&](XrEventDataSessionStateChanged* evt, std::chrono::nanoseconds duration = 1s) { - CountdownTimer countdown(duration); - while (!countdown.IsTimeUp()) { - if (tryGetNextSessionState(evt)) { - return true; - } + std::this_thread::sleep_for(5ms); + } - std::this_thread::sleep_for(5ms); - } + return false; + } - return false; - }; + static void submitFrame(XrSession session) + { + XrFrameState frameState{XR_TYPE_FRAME_STATE}; + XRC_CHECK_THROW_XRCMD(xrWaitFrame(session, nullptr, &frameState)); + XRC_CHECK_THROW_XRCMD(xrBeginFrame(session, nullptr)); + + XrFrameEndInfo frameEndInfo{XR_TYPE_FRAME_END_INFO}; + frameEndInfo.displayTime = frameState.predictedDisplayTime; + frameEndInfo.environmentBlendMode = GetGlobalData().GetOptions().environmentBlendModeValue; + XRC_CHECK_THROW_XRCMD(xrEndFrame(session, &frameEndInfo)); + } + + static void submitFramesUntilSessionState(XrInstance instance, XrSession session, XrSessionState expectedSessionState, + std::chrono::nanoseconds duration = 30s) + { + CAPTURE(expectedSessionState); + + CountdownTimer countdown(duration); + while (!countdown.IsTimeUp()) { + XrEventDataSessionStateChanged evt; + if (tryGetNextSessionState(instance, &evt)) { + REQUIRE(evt.state == expectedSessionState); + return; + } + submitFrame(session); + } - XrEventDataSessionStateChanged evt{}; - REQUIRE(waitForNextSessionState(&evt) == true); - REQUIRE_MSG(evt.state == XR_SESSION_STATE_IDLE, "Unexpected session state " << evt.state); + FAIL("Failed to reach expected session state"); + } - REQUIRE(waitForNextSessionState(&evt) == true); - REQUIRE_MSG(evt.state == XR_SESSION_STATE_READY, "Unexpected session state " << evt.state); + TEST_CASE("SessionState", "") + { + AutoBasicInstance instance; - // Ensure unsupported view configuration types fail. + SECTION("Cycle through all states") { - // Get the list of supported view configurations - uint32_t viewCount = 0; - REQUIRE(XR_SUCCESS == xrEnumerateViewConfigurations(instance, systemId, 0, &viewCount, nullptr)); - std::vector runtimeViewTypes(viewCount); - REQUIRE(XR_SUCCESS == xrEnumerateViewConfigurations(instance, systemId, viewCount, &viewCount, runtimeViewTypes.data())); + AutoBasicSession session(AutoBasicSession::createSession, instance); - for (XrViewConfigurationType viewType : KnownViewTypes) { - CAPTURE(viewType); + REQUIRE(session != XR_NULL_HANDLE_CPP); - // Is this enum valid, check against enabled extensions. - bool valid = IsViewConfigurationTypeEnumValid(viewType); + XrEventDataSessionStateChanged evt{}; + REQUIRE(waitForNextSessionState(instance, &evt) == true); + REQUIRE_MSG(evt.state == XR_SESSION_STATE_IDLE, "Unexpected session state " << evt.state); - if (!IsViewConfigurationTypeEnumValid(viewType)) { - INFO("Must not enumerate invalid view configuration type"); - CHECK_THAT(runtimeViewTypes, !Catch::Matchers::VectorContains(viewType)); - } + REQUIRE(waitForNextSessionState(instance, &evt) == true); + REQUIRE_MSG(evt.state == XR_SESSION_STATE_READY, "Unexpected session state " << evt.state); - // 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; - } + XrSessionBeginInfo beginInfo{XR_TYPE_SESSION_BEGIN_INFO}; + beginInfo.primaryViewConfigurationType = GetGlobalData().GetOptions().viewConfigurationValue; + REQUIRE(XR_SUCCESS == xrBeginSession(session, &beginInfo)); - XrSessionBeginInfo beginInfo{XR_TYPE_SESSION_BEGIN_INFO}; - beginInfo.primaryViewConfigurationType = viewType; - XrResult result = xrBeginSession(session, &beginInfo); - REQUIRE_THAT(result, In({XR_ERROR_VALIDATION_FAILURE, XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED})); - if (!valid && result == XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED) { - WARN( - "On receiving an 'invalid' enum value " - << viewType - << ", the runtime returned as XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED instead of XR_ERROR_VALIDATION_FAILURE, which may make it harder for apps to reason about the error."); - } - else if (valid && result == XR_ERROR_VALIDATION_FAILURE) { - WARN( - "On receiving a 'valid' but not supported enum value " - << viewType - << ", the runtime returned as XR_ERROR_VALIDATION_FAILURE instead of XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED, which may make it harder for apps to reason about the error."); - } - } - } + submitFramesUntilSessionState(instance, session, XR_SESSION_STATE_SYNCHRONIZED); + submitFramesUntilSessionState(instance, session, XR_SESSION_STATE_VISIBLE); + submitFramesUntilSessionState(instance, session, XR_SESSION_STATE_FOCUSED); - XrSessionBeginInfo beginInfo{XR_TYPE_SESSION_BEGIN_INFO}; - beginInfo.primaryViewConfigurationType = GetGlobalData().GetOptions().viewConfigurationValue; - XRC_CHECK_THROW_XRCMD(xrBeginSession(session, &beginInfo)); + // Runtime should only allow ending a session in the STOPPING state. + REQUIRE(XR_ERROR_SESSION_NOT_STOPPING == xrEndSession(session)); - if (GetGlobalData().IsUsingGraphicsPlugin()) { - // Runtime should not transition from READY to SYNCHRONIZED until one or more frames have been submitted. - // The exception is if the runtime is transitioning to STOPPING, which should not happen - // during conformance testing. This will wait 1 second before assuming no such incorrect event will come. - REQUIRE_MSG(tryGetNextSessionState(&evt) == false, "Premature progression from READY to SYNCHRONIZED state"); + REQUIRE(XR_SUCCESS == xrRequestExitSession(session)); + + submitFramesUntilSessionState(instance, session, XR_SESSION_STATE_VISIBLE); + submitFramesUntilSessionState(instance, session, XR_SESSION_STATE_SYNCHRONIZED); + submitFramesUntilSessionState(instance, session, XR_SESSION_STATE_STOPPING); + + // Runtime should not transition from STOPPING to IDLE until the session has been ended. + // This will wait 1 second before assuming no such incorrect event will come. + REQUIRE_MSG(waitForNextSessionState(instance, &evt) == false, "Premature progression from STOPPING to IDLE state"); + + REQUIRE(XR_SUCCESS == xrEndSession(session)); + + submitFramesUntilSessionState(instance, session, XR_SESSION_STATE_IDLE); + + // https://registry.khronos.org/OpenXR/specs/1.1/html/xrspec.html#session-lifecycle + // If the runtime determines that its use of this XR session has + // concluded, it will transition the session state from + // XR_SESSION_STATE_IDLE to XR_SESSION_STATE_EXITING. + submitFramesUntilSessionState(instance, session, XR_SESSION_STATE_EXITING); + + // When the application receives the XR_SESSION_STATE_EXITING + // event, it releases the resources related to the session and + // calls xrDestroySession. } - auto submitFrame = [&]() { - XrFrameState frameState{XR_TYPE_FRAME_STATE}; - XRC_CHECK_THROW_XRCMD(xrWaitFrame(session, nullptr, &frameState)); - XRC_CHECK_THROW_XRCMD(xrBeginFrame(session, nullptr)); - - XrFrameEndInfo frameEndInfo{XR_TYPE_FRAME_END_INFO}; - frameEndInfo.displayTime = frameState.predictedDisplayTime; - frameEndInfo.environmentBlendMode = GetGlobalData().GetOptions().environmentBlendModeValue; - XRC_CHECK_THROW_XRCMD(xrEndFrame(session, &frameEndInfo)); - }; - - auto submitFramesUntilSessionState = [&](XrSessionState expectedSessionState, std::chrono::nanoseconds duration = 30s) { - CAPTURE(expectedSessionState); - - CountdownTimer countdown(duration); - while (!countdown.IsTimeUp()) { - if (tryGetNextSessionState(&evt)) { - REQUIRE(evt.state == expectedSessionState); - return; - } - submitFrame(); + SECTION("xrRequestExitSession Session Not Running") + { + SECTION("Session not running before starting") + { + AutoBasicSession session(AutoBasicSession::createSession, instance); + + REQUIRE(XR_ERROR_SESSION_NOT_RUNNING == xrRequestExitSession(session)); } - FAIL("Failed to reach expected session state"); - }; + SECTION("Session not running after ending") + { + // A session is considered running after a successful call to + // xrBeginSession and remains running until any call is made to + // xrEndSession. Certain functions are only valid to call when + // a session is running, such as xrWaitFrame, or else the + // XR_ERROR_SESSION_NOT_RUNNING error must be returned by the + // runtime. - submitFramesUntilSessionState(XR_SESSION_STATE_SYNCHRONIZED); - submitFramesUntilSessionState(XR_SESSION_STATE_VISIBLE); - submitFramesUntilSessionState(XR_SESSION_STATE_FOCUSED); + // If session is not running when xrRequestExitSession is + // called, XR_ERROR_SESSION_NOT_RUNNING must be returned. - // Runtime should only allow ending a session in the STOPPING state. - REQUIRE(XR_ERROR_SESSION_NOT_STOPPING == xrEndSession(session)); + AutoBasicSession session( + AutoBasicSession::beginSession | AutoBasicSession::createSpaces | AutoBasicSession::createSwapchains, instance); + REQUIRE(XR_SUCCESS == xrRequestExitSession(session)); - XRC_CHECK_THROW_XRCMD(xrRequestExitSession(session)); + FrameIterator frameIterator(&session); + frameIterator.RunToSessionState(XR_SESSION_STATE_STOPPING); + REQUIRE(XR_SUCCESS == xrEndSession(session)); - submitFramesUntilSessionState(XR_SESSION_STATE_VISIBLE); - submitFramesUntilSessionState(XR_SESSION_STATE_SYNCHRONIZED); - submitFramesUntilSessionState(XR_SESSION_STATE_STOPPING); + // Actually test what we want to test! + REQUIRE(XR_ERROR_SESSION_NOT_RUNNING == xrRequestExitSession(session)); + } + } - // Runtime should not transition from STOPPING to IDLE until the session has been ended. - REQUIRE_MSG(tryGetNextSessionState(&evt) == false, "Premature progression from STOPPING to IDLE state"); + // Runtime should not transition from READY to SYNCHRONIZED until one + // or more frames have been submitted. + // The exception is if the runtime is transitioning to STOPPING, which + // should not happen during conformance testing. + if (GetGlobalData().IsUsingGraphicsPlugin()) { + SECTION("Advance without frame submission") + { + AutoBasicSession session(AutoBasicSession::createSession, instance); - XRC_CHECK_THROW_XRCMD(xrEndSession(session)); + FrameIterator frameIterator(&session); + frameIterator.RunToSessionState(XR_SESSION_STATE_READY); - submitFramesUntilSessionState(XR_SESSION_STATE_IDLE); - submitFramesUntilSessionState(XR_SESSION_STATE_EXITING); + XrSessionBeginInfo beginInfo{XR_TYPE_SESSION_BEGIN_INFO}; + beginInfo.primaryViewConfigurationType = GetGlobalData().GetOptions().viewConfigurationValue; + REQUIRE(XR_SUCCESS == xrBeginSession(session, &beginInfo)); + + // This will wait 1 second before assuming no such incorrect event will come. + XrEventDataSessionStateChanged evt; + REQUIRE_MSG(waitForNextSessionState(instance, &evt) == false, "Premature progression from READY to SYNCHRONIZED state"); + } + } } } // namespace Conformance diff --git a/src/conformance/conformance_test/test_ViewConfigurations.cpp b/src/conformance/conformance_test/test_ViewConfigurations.cpp index 14d80dc4..46de14bc 100644 --- a/src/conformance/conformance_test/test_ViewConfigurations.cpp +++ b/src/conformance/conformance_test/test_ViewConfigurations.cpp @@ -19,6 +19,8 @@ #include "matchers.h" #include +#include +#include #include #include @@ -29,7 +31,6 @@ namespace Conformance { - TEST_CASE("ViewConfigurations", "") { // XrResult xrEnumerateViewConfigurations(XrInstance instance, XrSystemId systemId, uint32_t viewConfigurationTypeCapacityInput, @@ -71,6 +72,59 @@ namespace Conformance } } + // Ensure unsupported view configuration types fail. + { +#define AS_LIST(name, val) name, + constexpr XrViewConfigurationType KnownViewTypes[] = {XR_LIST_ENUM_XrViewConfigurationType(AS_LIST)}; +#undef AS_LIST + + XrSystemId systemId = instance.systemId; + + // Get the list of supported view configurations + uint32_t viewCount = 0; + REQUIRE(XR_SUCCESS == xrEnumerateViewConfigurations(instance, systemId, 0, &viewCount, nullptr)); + std::vector runtimeViewTypes(viewCount); + REQUIRE(XR_SUCCESS == xrEnumerateViewConfigurations(instance, systemId, viewCount, &viewCount, runtimeViewTypes.data())); + + AutoBasicSession session(AutoBasicSession::createSession, instance); + FrameIterator frameIterator(&session); + frameIterator.RunToSessionState(XR_SESSION_STATE_READY); + + for (XrViewConfigurationType viewType : KnownViewTypes) { + CAPTURE(viewType); + + // Is this enum valid, check against enabled extensions. + bool valid = IsViewConfigurationTypeEnumValid(viewType); + + if (!IsViewConfigurationTypeEnumValid(viewType)) { + INFO("Must not enumerate invalid view configuration type"); + CHECK_THAT(runtimeViewTypes, !Catch::Matchers::VectorContains(viewType)); + } + + // 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; + } + + XrSessionBeginInfo beginInfo{XR_TYPE_SESSION_BEGIN_INFO}; + beginInfo.primaryViewConfigurationType = viewType; + XrResult result = xrBeginSession(session, &beginInfo); + REQUIRE_THAT(result, In({XR_ERROR_VALIDATION_FAILURE, XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED})); + if (!valid && result == XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED) { + WARN( + "On receiving an 'invalid' enum value " + << viewType + << ", the runtime returned as XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED instead of XR_ERROR_VALIDATION_FAILURE, which may make it harder for apps to reason about the error."); + } + else if (valid && result == XR_ERROR_VALIDATION_FAILURE) { + WARN( + "On receiving a 'valid' but not supported enum value " + << viewType + << ", the runtime returned as XR_ERROR_VALIDATION_FAILURE instead of XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED, which may make it harder for apps to reason about the error."); + } + } + } + // xrGetViewConfigurationProperties { // XrResult xrGetViewConfigurationProperties(XrInstance instance, XrSystemId systemId, XrViewConfigurationType diff --git a/src/conformance/conformance_test/test_XR_FB_space_warp.cpp b/src/conformance/conformance_test/test_XR_FB_space_warp.cpp index 11d9e275..945bfc53 100644 --- a/src/conformance/conformance_test/test_XR_FB_space_warp.cpp +++ b/src/conformance/conformance_test/test_XR_FB_space_warp.cpp @@ -95,28 +95,43 @@ namespace Conformance float maxDepth; float nearZ; float farZ; + XrResult result; }; constexpr float minimum_useful_z = 0.01f; std::vector varyingInfoTestArray{ - SpaceWarpVaryingInfo{0, 0.0f, 1.0f, minimum_useful_z, 100.0f}, - SpaceWarpVaryingInfo{0, 0.5f, 0.6f, minimum_useful_z, 100.0f}, - SpaceWarpVaryingInfo{0, 0.0f, 1.0f, minimum_useful_z, std::numeric_limits::infinity()}, - SpaceWarpVaryingInfo{0, 0.0f, 1.0f, 100.0f, minimum_useful_z}, - SpaceWarpVaryingInfo{0, 0.0f, 1.0f, std::numeric_limits::infinity(), minimum_useful_z}, - SpaceWarpVaryingInfo{0, 0.0f, 1.0f, std::numeric_limits::max(), minimum_useful_z}, - SpaceWarpVaryingInfo{0, 0.0f, 1.0f, std::numeric_limits::max(), minimum_useful_z}, - SpaceWarpVaryingInfo{XR_COMPOSITION_LAYER_SPACE_WARP_INFO_FRAME_SKIP_BIT_FB, 0.0f, 1.0f, minimum_useful_z, 100.0f}, - SpaceWarpVaryingInfo{XR_COMPOSITION_LAYER_SPACE_WARP_INFO_FRAME_SKIP_BIT_FB, 0.5f, 0.6f, minimum_useful_z, 100.0f}, + SpaceWarpVaryingInfo{0, 0.0f, 1.0f, minimum_useful_z, 100.0f, XR_SUCCESS}, + SpaceWarpVaryingInfo{0, 0.5f, 0.6f, minimum_useful_z, 100.0f, XR_SUCCESS}, + SpaceWarpVaryingInfo{0, 0.0f, 1.0f, minimum_useful_z, std::numeric_limits::infinity(), XR_SUCCESS}, + SpaceWarpVaryingInfo{0, 0.0f, 1.0f, 100.0f, minimum_useful_z, XR_SUCCESS}, + SpaceWarpVaryingInfo{0, 0.0f, 1.0f, std::numeric_limits::infinity(), minimum_useful_z, XR_SUCCESS}, + SpaceWarpVaryingInfo{0, 0.0f, 1.0f, std::numeric_limits::max(), minimum_useful_z, XR_SUCCESS}, + SpaceWarpVaryingInfo{0, 0.0f, 1.0f, std::numeric_limits::max(), minimum_useful_z, XR_SUCCESS}, + SpaceWarpVaryingInfo{XR_COMPOSITION_LAYER_SPACE_WARP_INFO_FRAME_SKIP_BIT_FB, 0.0f, 1.0f, minimum_useful_z, 100.0f, + XR_SUCCESS}, + SpaceWarpVaryingInfo{XR_COMPOSITION_LAYER_SPACE_WARP_INFO_FRAME_SKIP_BIT_FB, 0.5f, 0.6f, minimum_useful_z, 100.0f, + XR_SUCCESS}, SpaceWarpVaryingInfo{XR_COMPOSITION_LAYER_SPACE_WARP_INFO_FRAME_SKIP_BIT_FB, 0.0f, 1.0f, minimum_useful_z, - std::numeric_limits::infinity()}, - SpaceWarpVaryingInfo{XR_COMPOSITION_LAYER_SPACE_WARP_INFO_FRAME_SKIP_BIT_FB, 0.0f, 1.0f, 100.0f, minimum_useful_z}, + std::numeric_limits::infinity(), XR_SUCCESS}, + SpaceWarpVaryingInfo{XR_COMPOSITION_LAYER_SPACE_WARP_INFO_FRAME_SKIP_BIT_FB, 0.0f, 1.0f, 100.0f, minimum_useful_z, + XR_SUCCESS}, SpaceWarpVaryingInfo{XR_COMPOSITION_LAYER_SPACE_WARP_INFO_FRAME_SKIP_BIT_FB, 0.0f, 1.0f, - std::numeric_limits::infinity(), minimum_useful_z}, + std::numeric_limits::infinity(), minimum_useful_z, XR_SUCCESS}, SpaceWarpVaryingInfo{XR_COMPOSITION_LAYER_SPACE_WARP_INFO_FRAME_SKIP_BIT_FB, 0.0f, 1.0f, std::numeric_limits::max(), - minimum_useful_z}, + minimum_useful_z, XR_SUCCESS}, SpaceWarpVaryingInfo{XR_COMPOSITION_LAYER_SPACE_WARP_INFO_FRAME_SKIP_BIT_FB, 0.0f, 1.0f, std::numeric_limits::max(), - minimum_useful_z}}; + minimum_useful_z, XR_SUCCESS}, + SpaceWarpVaryingInfo{0, 0.0f, 1.0f, minimum_useful_z, minimum_useful_z, XR_ERROR_VALIDATION_FAILURE}, + SpaceWarpVaryingInfo{0, 0.0f, 1.0f, 100.0f, 100.0f, XR_ERROR_VALIDATION_FAILURE}, + SpaceWarpVaryingInfo{0, 0.0f, 1.0f, std::numeric_limits::infinity(), std::numeric_limits::infinity(), + XR_ERROR_VALIDATION_FAILURE}, + SpaceWarpVaryingInfo{XR_COMPOSITION_LAYER_SPACE_WARP_INFO_FRAME_SKIP_BIT_FB, 0.0f, 1.0f, minimum_useful_z, minimum_useful_z, + XR_ERROR_VALIDATION_FAILURE}, + SpaceWarpVaryingInfo{XR_COMPOSITION_LAYER_SPACE_WARP_INFO_FRAME_SKIP_BIT_FB, 0.0f, 1.0f, 100.0f, 100.0f, + XR_ERROR_VALIDATION_FAILURE}, + SpaceWarpVaryingInfo{XR_COMPOSITION_LAYER_SPACE_WARP_INFO_FRAME_SKIP_BIT_FB, 0.0f, 1.0f, + std::numeric_limits::infinity(), std::numeric_limits::infinity(), + XR_ERROR_VALIDATION_FAILURE}}; for (const SpaceWarpVaryingInfo& varyingInfo : varyingInfoTestArray) { REQUIRE(frameIterator.PrepareSubmitFrame() == FrameIterator::RunResult::Success); @@ -160,7 +175,7 @@ namespace Conformance // xrEndFrame requires the XR_FB_space_warp extension to be // enabled or else it must return XR_ERROR_LAYER_INVALID. XrResult result = xrEndFrame(session.GetSession(), &frameIterator.frameEndInfo); - CHECK(result == XR_SUCCESS); + CHECK(result == varyingInfo.result); } } diff --git a/src/conformance/conformance_test/test_actions.cpp b/src/conformance/conformance_test/test_actions.cpp index 344efb63..b4bc19a1 100644 --- a/src/conformance/conformance_test/test_actions.cpp +++ b/src/conformance/conformance_test/test_actions.cpp @@ -34,7 +34,9 @@ #include #include #include +#include #include +#include #include #include @@ -1047,19 +1049,18 @@ namespace Conformance XrBoundSourcesForActionEnumerateInfo info{XR_TYPE_BOUND_SOURCES_FOR_ACTION_ENUMERATE_INFO}; info.action = selectAction; uint32_t sourceCountOutput; - XrPath buffer; - REQUIRE_RESULT(xrEnumerateBoundSourcesForAction(session, &info, 0, &sourceCountOutput, &buffer), - XR_ERROR_ACTIONSET_NOT_ATTACHED); - } - SECTION("Get localized source name") - { - XrInputSourceLocalizedNameGetInfo getInfo{XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO}; - getInfo.whichComponents = XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT; - getInfo.sourcePath = StringToPath(instance, "/user/hand/left/input/select/click"); - uint32_t sourceCountOutput; - char buffer; - REQUIRE_RESULT(xrGetInputSourceLocalizedName(session, &getInfo, 0, &sourceCountOutput, &buffer), + REQUIRE_RESULT(xrEnumerateBoundSourcesForAction(session, &info, 0, &sourceCountOutput, nullptr), XR_ERROR_ACTIONSET_NOT_ATTACHED); + + REQUIRE_RESULT(xrAttachSessionActionSets(session, &attachInfo), XR_SUCCESS); + + std::vector boundSourcesPaths = REQUIRE_TWO_CALL(XrPath, {}, xrEnumerateBoundSourcesForAction, session, &info); + sourceCountOutput = static_cast(boundSourcesPaths.size()); + + // should not get a null path, not really much else we can assert here. + REQUIRE_THAT(boundSourcesPaths, !Catch::Matchers::VectorContains(XrPath{})); + // Can we assert that we don't enumerate duplicates? Would be weird to return duplicates but may not be strictly forbidden. + REQUIRE_THAT(boundSourcesPaths, VectorHasOnlyUniqueElements{}); } } SECTION("Unattached action sets") @@ -3925,65 +3926,94 @@ namespace Conformance REQUIRE(enumerateResult.size() > 0); + // should not get a null path, not really much else we can assert here. + REQUIRE_THAT(enumerateResult, !Catch::Matchers::VectorContains(XrPath{})); + // Can we assert that we don't enumerate duplicates? Would be weird to return duplicates but may not be strictly forbidden. + REQUIRE_THAT(enumerateResult, VectorHasOnlyUniqueElements{}); + XrInputSourceLocalizedNameGetInfo getInfo{XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO}; + getInfo.whichComponents = XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT; getInfo.sourcePath = enumerateResult[0]; - SECTION("xrGetInputSourceLocalizedName") + + uint32_t sourceCountOutput; + SECTION("Invalid components") + { + getInfo.whichComponents = 0; + REQUIRE_RESULT(xrGetInputSourceLocalizedName(session, &getInfo, 0, &sourceCountOutput, nullptr), + XR_ERROR_VALIDATION_FAILURE); + } + SECTION("Invalid path") { - getInfo.whichComponents = XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT; - std::vector localizedStringResult = REQUIRE_TWO_CALL(char, {}, xrGetInputSourceLocalizedName, session, &getInfo); - CHECK(localizedStringResult.size() > 1); // more than null terminator - CHECK_THAT(localizedStringResult, NullTerminatedVec()); - - getInfo.whichComponents = XR_INPUT_SOURCE_LOCALIZED_NAME_INTERACTION_PROFILE_BIT; - localizedStringResult = REQUIRE_TWO_CALL(char, {}, xrGetInputSourceLocalizedName, session, &getInfo); - CHECK(localizedStringResult.size() > 1); // more than null terminator - CHECK_THAT(localizedStringResult, NullTerminatedVec()); - - getInfo.whichComponents = XR_INPUT_SOURCE_LOCALIZED_NAME_COMPONENT_BIT; - localizedStringResult = REQUIRE_TWO_CALL(char, {}, xrGetInputSourceLocalizedName, session, &getInfo); - CHECK(localizedStringResult.size() > 1); // more than null terminator - CHECK_THAT(localizedStringResult, NullTerminatedVec()); - - getInfo.whichComponents = - XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT | XR_INPUT_SOURCE_LOCALIZED_NAME_INTERACTION_PROFILE_BIT; - localizedStringResult = REQUIRE_TWO_CALL(char, {}, xrGetInputSourceLocalizedName, session, &getInfo); - CHECK(localizedStringResult.size() > 1); // more than null terminator - CHECK_THAT(localizedStringResult, NullTerminatedVec()); - - getInfo.whichComponents = XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT | XR_INPUT_SOURCE_LOCALIZED_NAME_COMPONENT_BIT; - localizedStringResult = REQUIRE_TWO_CALL(char, {}, xrGetInputSourceLocalizedName, session, &getInfo); - CHECK(localizedStringResult.size() > 1); // more than null terminator - CHECK_THAT(localizedStringResult, NullTerminatedVec()); - - getInfo.whichComponents = - XR_INPUT_SOURCE_LOCALIZED_NAME_INTERACTION_PROFILE_BIT | XR_INPUT_SOURCE_LOCALIZED_NAME_COMPONENT_BIT; - localizedStringResult = REQUIRE_TWO_CALL(char, {}, xrGetInputSourceLocalizedName, session, &getInfo); - CHECK(localizedStringResult.size() > 1); // more than null terminator - CHECK_THAT(localizedStringResult, NullTerminatedVec()); - - 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, session, &getInfo); - CHECK(localizedStringResult.size() > 1); // more than null terminator - CHECK_THAT(localizedStringResult, NullTerminatedVec()); + getInfo.sourcePath = XR_NULL_PATH; + REQUIRE_RESULT(xrGetInputSourceLocalizedName(session, &getInfo, 0, &sourceCountOutput, nullptr), XR_ERROR_PATH_INVALID); + getInfo.sourcePath = (XrPath)0x1234; + REQUIRE_RESULT(xrGetInputSourceLocalizedName(session, &getInfo, 0, &sourceCountOutput, nullptr), XR_ERROR_PATH_INVALID); + } - uint32_t sourceCountOutput; - char buffer; - SECTION("Invalid components") - { - getInfo.whichComponents = 0; - REQUIRE_RESULT(xrGetInputSourceLocalizedName(session, &getInfo, 0, &sourceCountOutput, &buffer), - XR_ERROR_VALIDATION_FAILURE); - } - SECTION("Invalid path") - { - getInfo.sourcePath = XR_NULL_PATH; - REQUIRE_RESULT(xrGetInputSourceLocalizedName(session, &getInfo, 0, &sourceCountOutput, &buffer), - XR_ERROR_PATH_INVALID); - getInfo.sourcePath = (XrPath)0x1234; - REQUIRE_RESULT(xrGetInputSourceLocalizedName(session, &getInfo, 0, &sourceCountOutput, &buffer), - XR_ERROR_PATH_INVALID); + SECTION("xrGetInputSourceLocalizedName-on-each") + { + for (size_t i = 0; i < enumerateResult.size(); ++i) { + getInfo.sourcePath = enumerateResult[i]; + CAPTURE(i); + + auto s = PathToString(instance, getInfo.sourcePath); + CHECK(!s.empty()); + CAPTURE(s); + std::vector localizedStringResult; + + { + CAPTURE(XrInputSourceLocalizedNameFlagsRefCPP(getInfo.whichComponents) = + XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT); + localizedStringResult = REQUIRE_TWO_CALL(char, {}, xrGetInputSourceLocalizedName, session, &getInfo); + CHECK(localizedStringResult.size() > 1); // more than null terminator + CHECK_THAT(localizedStringResult, NullTerminatedVec()); + } + { + CAPTURE(XrInputSourceLocalizedNameFlagsRefCPP(getInfo.whichComponents) = + XR_INPUT_SOURCE_LOCALIZED_NAME_INTERACTION_PROFILE_BIT); + localizedStringResult = REQUIRE_TWO_CALL(char, {}, xrGetInputSourceLocalizedName, session, &getInfo); + CHECK(localizedStringResult.size() > 1); // more than null terminator + CHECK_THAT(localizedStringResult, NullTerminatedVec()); + } + { + CAPTURE(XrInputSourceLocalizedNameFlagsRefCPP(getInfo.whichComponents) = + XR_INPUT_SOURCE_LOCALIZED_NAME_COMPONENT_BIT); + localizedStringResult = REQUIRE_TWO_CALL(char, {}, xrGetInputSourceLocalizedName, session, &getInfo); + CHECK(localizedStringResult.size() > 1); // more than null terminator + CHECK_THAT(localizedStringResult, NullTerminatedVec()); + } + { + CAPTURE(XrInputSourceLocalizedNameFlagsRefCPP(getInfo.whichComponents) = + XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT | + XR_INPUT_SOURCE_LOCALIZED_NAME_INTERACTION_PROFILE_BIT); + localizedStringResult = REQUIRE_TWO_CALL(char, {}, xrGetInputSourceLocalizedName, session, &getInfo); + CHECK(localizedStringResult.size() > 1); // more than null terminator + CHECK_THAT(localizedStringResult, NullTerminatedVec()); + } + { + CAPTURE(XrInputSourceLocalizedNameFlagsRefCPP(getInfo.whichComponents) = + XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT | XR_INPUT_SOURCE_LOCALIZED_NAME_COMPONENT_BIT); + localizedStringResult = REQUIRE_TWO_CALL(char, {}, xrGetInputSourceLocalizedName, session, &getInfo); + CHECK(localizedStringResult.size() > 1); // more than null terminator + CHECK_THAT(localizedStringResult, NullTerminatedVec()); + } + { + CAPTURE(XrInputSourceLocalizedNameFlagsRefCPP(getInfo.whichComponents) = + XR_INPUT_SOURCE_LOCALIZED_NAME_INTERACTION_PROFILE_BIT | + XR_INPUT_SOURCE_LOCALIZED_NAME_COMPONENT_BIT); + localizedStringResult = REQUIRE_TWO_CALL(char, {}, xrGetInputSourceLocalizedName, session, &getInfo); + CHECK(localizedStringResult.size() > 1); // more than null terminator + CHECK_THAT(localizedStringResult, NullTerminatedVec()); + } + { + CAPTURE(XrInputSourceLocalizedNameFlagsRefCPP(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, session, &getInfo); + CHECK(localizedStringResult.size() > 1); // more than null terminator + CHECK_THAT(localizedStringResult, NullTerminatedVec()); + } } } } diff --git a/src/conformance/conformance_test/test_xrLocateViews.cpp b/src/conformance/conformance_test/test_xrLocateViews.cpp index 2fbc527e..8466527c 100644 --- a/src/conformance/conformance_test/test_xrLocateViews.cpp +++ b/src/conformance/conformance_test/test_xrLocateViews.cpp @@ -33,84 +33,95 @@ namespace Conformance { GlobalData& globalData = GetGlobalData(); - // Get a session started. - AutoBasicSession session(AutoBasicSession::createInstance | AutoBasicSession::createSession | AutoBasicSession::beginSession | - AutoBasicSession::createSwapchains | AutoBasicSession::createSpaces); - - // Get frames iterating to the point of app focused state. This will draw frames along the way. - FrameIterator frameIterator(&session); - frameIterator.RunToSessionState(XR_SESSION_STATE_FOCUSED); - - // Render one frame to get a predicted display time for the xrLocateViews calls. - FrameIterator::RunResult runResult = frameIterator.SubmitFrame(); - REQUIRE(runResult == FrameIterator::RunResult::Success); - - XrResult result; + // Get a session, but do not start it yet, we might pick a different view config type. + // Swapchain will be sized based on default view config type, but that is OK. + AutoBasicSession session(AutoBasicSession::createInstance | AutoBasicSession::createSession | AutoBasicSession::createSwapchains | + AutoBasicSession::createSpaces); XrViewLocateInfo locateInfo{XR_TYPE_VIEW_LOCATE_INFO}; locateInfo.space = session.spaceVector.front(); - XrTime time = frameIterator.frameState.predictedDisplayTime; - CHECK(time != 0); - locateInfo.displayTime = time; - locateInfo.viewConfigurationType = globalData.GetOptions().viewConfigurationValue; - - XrViewState viewState{XR_TYPE_VIEW_STATE}; - uint32_t viewCount = (uint32_t)session.viewConfigurationViewVector.size(); + auto viewConfigDependentSetup = [&](XrViewConfigurationType configType) { + locateInfo.viewConfigurationType = configType; + session.viewConfigurationType = configType; + { + CAPTURE(configType); + session.BeginSession(); + } - CAPTURE(viewCount); - SECTION("valid inputs") - { - std::vector views(viewCount, {XR_TYPE_VIEW}); - uint32_t viewCountOut = 0; + // Get frames iterating to the point of app focused state. This will draw frames along the way. + FrameIterator frameIterator(&session); + frameIterator.RunToSessionState(XR_SESSION_STATE_FOCUSED); - CAPTURE(locateInfo.displayTime); - CHECK(XR_SUCCESS == xrLocateViews(session, &locateInfo, &viewState, viewCount, &viewCountOut, views.data())); + // Render one frame to get a predicted display time for the xrLocateViews calls. + // FrameIterator::RunResult runResult = frameIterator.SubmitFrame(); + // REQUIRE(runResult == FrameIterator::RunResult::Success); - CHECK(viewCountOut == viewCount); - } - SECTION("invalid inputs") + const XrTime time = frameIterator.frameState.predictedDisplayTime; + REQUIRE(time != 0); + locateInfo.displayTime = time; + }; + SECTION("Selected view config") { - std::vector views(viewCount, {XR_TYPE_VIEW}); - uint32_t viewCountOut = 0; + viewConfigDependentSetup(globalData.GetOptions().viewConfigurationValue); - OPTIONAL_INVALID_HANDLE_VALIDATION_SECTION - { - // Exercise NULL session handle. - CHECK(xrLocateViews(XR_NULL_HANDLE_CPP, &locateInfo, &viewState, viewCount, &viewCountOut, views.data()) == - XR_ERROR_HANDLE_INVALID); - - // Exercise invalid session handle. - CHECK(xrLocateViews(GlobalData().invalidSession, &locateInfo, &viewState, viewCount, &viewCountOut, views.data()) == - XR_ERROR_HANDLE_INVALID); - } + XrViewState viewState{XR_TYPE_VIEW_STATE}; + uint32_t viewCount = (uint32_t)session.viewConfigurationViewVector.size(); - SECTION("Exercise 0 as an invalid time") + CAPTURE(viewCount); + SECTION("valid inputs") { - locateInfo.displayTime = 0; - CAPTURE(locateInfo.displayTime); - CHECK(XR_ERROR_TIME_INVALID == xrLocateViews(session, &locateInfo, &viewState, viewCount, &viewCountOut, views.data())); - } + std::vector views(viewCount, {XR_TYPE_VIEW}); + uint32_t viewCountOut = 0; - SECTION("Exercise negative values as an invalid time") - { - locateInfo.displayTime = (XrTime)-42; CAPTURE(locateInfo.displayTime); - CHECK(XR_ERROR_TIME_INVALID == xrLocateViews(session, &locateInfo, &viewState, viewCount, &viewCountOut, views.data())); - } + CHECK(XR_SUCCESS == xrLocateViews(session, &locateInfo, &viewState, viewCount, &viewCountOut, views.data())); - OPTIONAL_INVALID_TYPE_VALIDATION_SECTION + CHECK(viewCountOut == viewCount); + } + SECTION("invalid inputs") { - std::vector invalidViews(viewCount, {XR_TYPE_UNKNOWN}); - REQUIRE(xrLocateViews(session, &locateInfo, &viewState, viewCount, &viewCountOut, invalidViews.data()) == - XR_ERROR_VALIDATION_FAILURE); + std::vector views(viewCount, {XR_TYPE_VIEW}); + uint32_t viewCountOut = 0; + + OPTIONAL_INVALID_HANDLE_VALIDATION_SECTION + { + // Exercise NULL session handle. + CHECK(xrLocateViews(XR_NULL_HANDLE_CPP, &locateInfo, &viewState, viewCount, &viewCountOut, views.data()) == + XR_ERROR_HANDLE_INVALID); + + // Exercise invalid session handle. + CHECK(xrLocateViews(GlobalData().invalidSession, &locateInfo, &viewState, viewCount, &viewCountOut, views.data()) == + XR_ERROR_HANDLE_INVALID); + } + + SECTION("Exercise 0 as an invalid time") + { + locateInfo.displayTime = 0; + CAPTURE(locateInfo.displayTime); + CHECK(XR_ERROR_TIME_INVALID == xrLocateViews(session, &locateInfo, &viewState, viewCount, &viewCountOut, views.data())); + } + + SECTION("Exercise negative values as an invalid time") + { + locateInfo.displayTime = (XrTime)-42; + CAPTURE(locateInfo.displayTime); + CHECK(XR_ERROR_TIME_INVALID == xrLocateViews(session, &locateInfo, &viewState, viewCount, &viewCountOut, views.data())); + } + + OPTIONAL_INVALID_TYPE_VALIDATION_SECTION + { + std::vector invalidViews(viewCount, {XR_TYPE_UNKNOWN}); + REQUIRE(xrLocateViews(session, &locateInfo, &viewState, viewCount, &viewCountOut, invalidViews.data()) == + XR_ERROR_VALIDATION_FAILURE); + } } } SECTION("all known view types") { // Ensure unsupported view configuration types fail and supported types pass - XrInstance instance = session.GetInstance(); - XrSystemId systemId = session.GetSystemId(); + const XrInstance instance = session.GetInstance(); + const XrSystemId systemId = session.GetSystemId(); // Get the list of supported view configurations uint32_t viewConfigCount = 0; @@ -119,61 +130,65 @@ namespace Conformance REQUIRE(XR_SUCCESS == xrEnumerateViewConfigurations(instance, systemId, viewConfigCount, &viewConfigCount, runtimeViewTypes.data())); - CAPTURE(locateInfo.displayTime); - + XrViewState viewState{XR_TYPE_VIEW_STATE}; for (auto viewTypeAndName : KnownViewTypes) { - XrViewConfigurationType viewType = viewTypeAndName.first; - CAPTURE(viewType); - CAPTURE(viewTypeAndName.second); + DYNAMIC_SECTION("View type " << viewTypeAndName.second) + { + XrViewConfigurationType viewType = viewTypeAndName.first; + CAPTURE(viewType); + CAPTURE(viewTypeAndName.second); + + // Is this enum valid, check against enabled extensions. + bool valid = IsViewConfigurationTypeEnumValid(viewType); + + const bool isSupportedType = Catch::Matchers::VectorContains(viewType).match(runtimeViewTypes); + CAPTURE(valid); + CAPTURE(isSupportedType); + + if (!valid) { + INFO("Not a valid view configuration type given the enabled extensions"); + CHECK_MSG(!isSupportedType, "Cannot support invalid view configuration type"); + } - // Is this enum valid, check against enabled extensions. - bool valid = IsViewConfigurationTypeEnumValid(viewType); + if (isSupportedType) { - const bool isSupportedType = Catch::Matchers::VectorContains(viewType).match(runtimeViewTypes); - CAPTURE(valid); - CAPTURE(isSupportedType); + // Supported but we don't have the corresponding view count immediately at hand + // So, we look it up. + uint32_t expectedViewCount = 0; + REQUIRE(XR_SUCCESS == xrEnumerateViewConfigurationViews(session.GetInstance(), session.GetSystemId(), viewType, 0, + &expectedViewCount, nullptr)); - if (!valid) { - INFO("Not a valid view configuration type given the enabled extensions"); - CHECK_MSG(!isSupportedType, "Cannot support invalid view configuration type"); - } + viewConfigDependentSetup(viewType); - locateInfo.viewConfigurationType = viewType; - if (isSupportedType) { - // Supported but we don't have the corresponding view count immediately at hand - // So, we look it up. - uint32_t expectedViewCount = 0; - REQUIRE(XR_SUCCESS == xrEnumerateViewConfigurationViews(session.GetInstance(), session.GetSystemId(), viewType, 0, - &expectedViewCount, nullptr)); - INFO("Calling xrLocateViews with the noted viewType, which is claimed to be supported"); - uint32_t viewCountOut = 0; - std::vector views(expectedViewCount, {XR_TYPE_VIEW}); - CHECK(XR_SUCCESS == xrLocateViews(session, &locateInfo, &viewState, expectedViewCount, &viewCountOut, views.data())); - } - else { - // Not a supported type, so call should fail, regardless of the array size. - INFO("Calling xrLocateViews with the noted viewType, which is claimed to be not supported"); - uint32_t viewCountOut = 0; - std::vector views(viewCount, {XR_TYPE_VIEW}); - - result = xrLocateViews(session, &locateInfo, &viewState, viewCount, &viewCountOut, views.data()); - REQUIRE_THAT(result, In({XR_ERROR_VALIDATION_FAILURE, XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED})); - if (!valid && result == XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED) { - WARN( - "Runtime accepted an invalid enum value as unsupported, which makes it harder for apps to reason about the error."); + CAPTURE(locateInfo.displayTime); + + INFO("Calling xrLocateViews with the noted viewType, which is claimed to be supported"); + uint32_t viewCountOut = 0; + std::vector views(expectedViewCount, {XR_TYPE_VIEW}); + CHECK(XR_SUCCESS == + xrLocateViews(session, &locateInfo, &viewState, expectedViewCount, &viewCountOut, views.data())); } - else if (valid && result == XR_ERROR_VALIDATION_FAILURE) { - WARN( - "Runtime accepted an valid but unsupported enum value as unsupported, which makes it harder for apps to reason about the error."); + else { + // Not a supported type, so call should fail, regardless of the array size. + INFO("Calling xrLocateViews with the noted viewType, which is claimed to be not supported"); + uint32_t viewCount = static_cast(session.viewConfigurationViewVector.size()); + uint32_t viewCountOut = 0; + std::vector views(viewCount, {XR_TYPE_VIEW}); + + XrResult result = xrLocateViews(session, &locateInfo, &viewState, viewCount, &viewCountOut, views.data()); + REQUIRE_THAT(result, In({XR_ERROR_VALIDATION_FAILURE, XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED})); + if (!valid && result == XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED) { + WARN( + "Runtime accepted an invalid enum value as unsupported, which makes it harder for apps to reason about the error."); + } + else if (valid && result == XR_ERROR_VALIDATION_FAILURE) { + WARN( + "Runtime accepted an valid but unsupported enum value as unsupported, which makes it harder for apps to reason about the error."); + } } } } } - - // Leave - result = xrRequestExitSession(session); - CHECK(result == XR_SUCCESS); - - frameIterator.RunToSessionState(XR_SESSION_STATE_STOPPING); } + } // namespace Conformance diff --git a/src/conformance/conformance_test/test_xrRequestExitSession.cpp b/src/conformance/conformance_test/test_xrRequestExitSession.cpp deleted file mode 100644 index a9e36175..00000000 --- a/src/conformance/conformance_test/test_xrRequestExitSession.cpp +++ /dev/null @@ -1,36 +0,0 @@ -// 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 "conformance_utils.h" - -#include -#include - -namespace Conformance -{ - TEST_CASE("xrRequestExitSession", "") - { - AutoBasicSession session(AutoBasicSession::createSession); - - SECTION("Session Not Running") - { - REQUIRE(XR_ERROR_SESSION_NOT_RUNNING == xrRequestExitSession(session)); - } - - // Successful case is tested in test_SessionState.cpp - } - -} // namespace Conformance diff --git a/src/conformance/framework/CMakeLists.txt b/src/conformance/framework/CMakeLists.txt index 8adc3a50..9f89b954 100644 --- a/src/conformance/framework/CMakeLists.txt +++ b/src/conformance/framework/CMakeLists.txt @@ -50,12 +50,16 @@ run_xr_xml_generate( ${PROJECT_SOURCE_DIR}/src/scripts/template_function_info.cpp ) run_xr_xml_generate( - conformance_generator.py interaction_info_generated.cpp + conformance_generator.py + interaction_info_generated.cpp "${PROJECT_SOURCE_DIR}/src/scripts/template_interaction_info_generated.cpp" + "${PROJECT_SOURCE_DIR}/src/scripts/interaction_profile_processor.py" ) run_xr_xml_generate( - conformance_generator.py interaction_info_generated.h + conformance_generator.py + interaction_info_generated.h "${PROJECT_SOURCE_DIR}/src/scripts/template_interaction_info_generated.h" + "${PROJECT_SOURCE_DIR}/src/scripts/interaction_profile_processor.py" ) add_library( diff --git a/src/conformance/framework/conformance_framework.cpp b/src/conformance/framework/conformance_framework.cpp index 6738032e..fce1ff4c 100644 --- a/src/conformance/framework/conformance_framework.cpp +++ b/src/conformance/framework/conformance_framework.cpp @@ -133,12 +133,46 @@ namespace Conformance AppendSprintf(reportString, "Handle invalidation tested: %s\n", globalData.options.invalidHandleValidation ? "yes" : "no"); AppendSprintf(reportString, "Type invalidation tested: %s\n", globalData.options.invalidTypeValidation ? "yes" : "no"); AppendSprintf(reportString, "Non-disconnectable devices: %s\n", globalData.options.nonDisconnectableDevices ? "yes" : "no"); - AppendSprintf(reportString, "Test Success Count: %d\n", (int)testSuccessCount); - AppendSprintf(reportString, "Test Failure Count: %d\n", (int)testFailureCount); + AppendSprintf(reportString, "Test Success Count: %zu\n", TestSuccessCount()); + AppendSprintf(reportString, "Test Failure Count: %zu\n", TestFailureCount()); + if (TestFailureCount() > 0) { + AppendSprintf(reportString, "Tests with failures:\n"); + for (auto const& pair : results) { + const auto& name = pair.first; + const auto& score = pair.second; + if (score.testFailureCount > 0) { + AppendSprintf(reportString, " %s\n", name.c_str()); + } + } + } + if (!unmatchedTestSpecs.empty()) { + AppendSprintf(reportString, "Unmatched Test Specs:\n"); + for (auto const& spec : unmatchedTestSpecs) { + AppendSprintf(reportString, " %s\n", spec.c_str()); + } + } return reportString; } + uint64_t ConformanceReport::TestSuccessCount() const + { + uint64_t success{}; + for (auto const& pair : results) { + success += pair.second.testSuccessCount; + } + return success; + } + + uint64_t ConformanceReport::TestFailureCount() const + { + uint64_t failure{}; + for (auto const& pair : results) { + failure += pair.second.testFailureCount; + } + return failure; + } + bool GlobalData::Initialize() { // NOTE: Runs *after* population of command-line options. @@ -301,49 +335,6 @@ namespace Conformance } } } - // Fill out the functions in functionInfoMap. - // Keep trying all functions, only failing out at end if one of them failed. - bool functionMapInitialized = true; - const FunctionInfoMap& functionInfoMap = GetFunctionInfoMap(); - for (auto& functionInfo : functionInfoMap) { - // We need to poke the address pointer into map entries. - result = xrGetInstanceProcAddr(autoInstance, functionInfo.first.c_str(), - &const_cast(functionInfo.second).functionPtr); - - if (XR_SUCCEEDED(result)) { - // This doesn't actually prove the pointer is correct. However, we will exercise that later. - if (functionInfo.second.functionPtr == nullptr) { - ReportF("GlobalData::Initialize: xrGetInstanceProcAddr for '%s' failed to return valid addr.", - functionInfo.first.c_str()); - functionMapInitialized = false; - } - } - else { - if (!ValidateResultAllowed("xrGetInstanceProcAddr", result)) { - ReportF("GlobalData::Initialize: xrGetInstanceProcAddr for '%s' returned invalid XrResult.", - functionInfo.first.c_str()); - functionMapInitialized = false; - } - - // If we could not get a pointer to this function, then it should be because we didn't - // enable the extension required by the function. - if (result != XR_ERROR_FUNCTION_UNSUPPORTED) { - ReportF("GlobalData::Initialize: xrGetInstanceProcAddr for '%s' failed with result: %s.", functionInfo.first.c_str(), - ResultToString(result)); - functionMapInitialized = false; - } - - // At this point, result is XR_ERROR_FUNCTION_UNSUPPORTED. - // Verify that the extension was *not* enabled. - // functionInfo.second.requiredExtension - // To do. - } - } - - if (!functionMapInitialized) { - ReportF("GlobalData::Initialize: xrGetInstanceProcAddr failed for one or more functions."); - return false; - } // Find XrSystemId (for later use and to ensure device is connected/available for whatever that means in a given runtime) XrSystemId systemId = XR_NULL_SYSTEM_ID; diff --git a/src/conformance/framework/conformance_framework.h b/src/conformance/framework/conformance_framework.h index 6febb8d0..55faa770 100644 --- a/src/conformance/framework/conformance_framework.h +++ b/src/conformance/framework/conformance_framework.h @@ -167,7 +167,7 @@ namespace Conformance /// or 0 when auto skip is disabled. std::chrono::milliseconds autoSkipTimeout{0}; - /// Options include "stereo" "mono". See enum XrViewConfigurationType. + /// Options include "stereo" "mono" "foveatedInset" "firstPersonObserver". See enum XrViewConfigurationType. /// Default is stereo. std::string viewConfiguration{"Stereo"}; XrViewConfigurationType viewConfigurationValue{XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO}; @@ -310,10 +310,21 @@ namespace Conformance std::string GetReportString() const; public: + class Score + { + public: + uint64_t testSuccessCount{}; + uint64_t testFailureCount{}; + }; + /// The total successful test case runs across all test cases. + uint64_t TestSuccessCount() const; + + /// The total failed test case runs across all test cases. + uint64_t TestFailureCount() const; + XrVersion apiVersion{XR_CURRENT_API_VERSION}; - uint64_t testSuccessCount{}; - uint64_t testFailureCount{}; - bool unmatchedTestSpecs{false}; + std::unordered_map results; + std::vector unmatchedTestSpecs; Catch::Totals totals{}; TimedSubmissionResults timedSubmission; std::vector> swapchainFormats; diff --git a/src/conformance/framework/conformance_utils.cpp b/src/conformance/framework/conformance_utils.cpp index 99fa2cd2..412f57dd 100644 --- a/src/conformance/framework/conformance_utils.cpp +++ b/src/conformance/framework/conformance_utils.cpp @@ -440,12 +440,13 @@ namespace Conformance //////////////////////////////////////////////////////////////////////////////////////////////// // AutoBasicSession //////////////////////////////////////////////////////////////////////////////////////////////// - AutoBasicSession::AutoBasicSession(int optionFlags_, XrInstance instance_) : optionFlags(optionFlags_) + AutoBasicSession::AutoBasicSession(int optionFlags_, XrInstance instance_, XrViewConfigurationType viewConfigType_) + : optionFlags(optionFlags_) { - Init(optionFlags_, instance_); + Init(optionFlags_, instance_, viewConfigType_); } - void AutoBasicSession::Init(int optionFlags_, XrInstance instance_) + void AutoBasicSession::Init(int optionFlags_, XrInstance instance_, XrViewConfigurationType viewConfigType_) { GlobalData& globalData = GetGlobalData(); @@ -467,6 +468,8 @@ namespace Conformance instance = instance_; optionFlags = optionFlags_; + viewConfigurationType = + viewConfigType_ == XR_VIEW_CONFIGURATION_TYPE_MAX_ENUM ? globalData.options.viewConfigurationValue : viewConfigType_; if ((optionFlags & createInstance) == 0) { // cannot proceed further without an instance @@ -503,16 +506,13 @@ namespace Conformance XRC_CHECK_THROW_XRCMD(xrStringToPath(instance, "/user/hand/left", &handSubactionArray[0])); XRC_CHECK_THROW_XRCMD(xrStringToPath(instance, "/user/hand/right", &handSubactionArray[1])); - // Note that while we are enumerating this, normally our testing is done via a pre-chosen one (globalData.options.viewConfigurationValue). - XRC_CHECK_THROW_XRCMD(doTwoCallInPlace(viewConfigurationTypeVector, xrEnumerateViewConfigurations, instance, systemId)); - - // We use globalData.options.viewConfigurationValue as the type we enumerate with, despite that the runtime may support others. + // We use viewConfigurationType as the type we enumerate with, despite that the runtime may support others. XRC_CHECK_THROW_XRCMD(doTwoCallInPlaceWithEmptyElement(viewConfigurationViewVector, {XR_TYPE_VIEW_CONFIGURATION_VIEW}, xrEnumerateViewConfigurationViews, instance, systemId, - globalData.options.viewConfigurationValue)); + viewConfigurationType)); - XRC_CHECK_THROW_XRCMD(doTwoCallInPlace(environmentBlendModeVector, xrEnumerateEnvironmentBlendModes, instance, systemId, - globalData.options.viewConfigurationValue)); + XRC_CHECK_THROW_XRCMD( + doTwoCallInPlace(environmentBlendModeVector, xrEnumerateEnvironmentBlendModes, instance, systemId, viewConfigurationType)); if ((optionFlags & createSwapchains) && globalData.IsUsingGraphicsPlugin()) { auto graphicsPlugin = globalData.GetGraphicsPlugin(); @@ -604,8 +604,13 @@ namespace Conformance XrSessionBeginInfo sessionBeginInfo{XR_TYPE_SESSION_BEGIN_INFO, globalData.GetPlatformPlugin()->PopulateNextFieldForStruct(XR_TYPE_SESSION_BEGIN_INFO), - globalData.options.viewConfigurationValue}; + viewConfigurationType}; XRC_CHECK_THROW_XRCMD(xrBeginSession(session, &sessionBeginInfo)); + + // We potentially changed the view configuration so we need to update the viewConfigurationViewVector + XRC_CHECK_THROW_XRCMD(doTwoCallInPlaceWithEmptyElement(viewConfigurationViewVector, {XR_TYPE_VIEW_CONFIGURATION_VIEW}, + xrEnumerateViewConfigurationViews, instance, systemId, + viewConfigurationType)); } AutoBasicSession::~AutoBasicSession() @@ -626,9 +631,11 @@ namespace Conformance actionSet = XR_NULL_HANDLE; // Let parent session destroy this. actionVector.clear(); // Let parent session destroy this. spaceTypeVector.clear(); // Let parent session destroy this. - viewConfigurationTypeVector.clear(); + spaceVector.clear(); viewConfigurationViewVector.clear(); environmentBlendModeVector.clear(); + viewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_MAX_ENUM; + sessionState = XR_SESSION_STATE_UNKNOWN; if (session != XR_NULL_HANDLE) { xrDestroySession(session); @@ -675,7 +682,6 @@ namespace Conformance XRC_CHECK_THROW(autoBasicSession); XRC_CHECK_THROW(autoBasicSession->GetInstance()); XRC_CHECK_THROW(autoBasicSession->GetSession()); - XRC_CHECK_THROW(!autoBasicSession->viewConfigurationTypeVector.empty()); XRC_CHECK_THROW(!autoBasicSession->environmentBlendModeVector.empty()); } @@ -762,7 +768,7 @@ namespace Conformance return RunResult::Error; XrViewLocateInfo viewLocateInfo{XR_TYPE_VIEW_LOCATE_INFO}; - viewLocateInfo.viewConfigurationType = GetGlobalData().options.viewConfigurationValue; + viewLocateInfo.viewConfigurationType = autoBasicSession->viewConfigurationType; viewLocateInfo.displayTime = frameState.predictedDisplayTime; viewLocateInfo.space = autoBasicSession->spaceVector[0]; XrViewState viewState{XR_TYPE_VIEW_STATE}; @@ -906,7 +912,7 @@ namespace Conformance GlobalData& globalData = GetGlobalData(); XrSessionBeginInfo sessionBeginInfo{ XR_TYPE_SESSION_BEGIN_INFO, globalData.GetPlatformPlugin()->PopulateNextFieldForStruct(XR_TYPE_SESSION_BEGIN_INFO), - globalData.options.viewConfigurationValue}; + autoBasicSession->viewConfigurationType}; REQUIRE(xrBeginSession(autoBasicSession->GetSession(), &sessionBeginInfo) == XR_SUCCESS); } diff --git a/src/conformance/framework/conformance_utils.h b/src/conformance/framework/conformance_utils.h index 43340daf..1f4d6d06 100644 --- a/src/conformance/framework/conformance_utils.h +++ b/src/conformance/framework/conformance_utils.h @@ -582,15 +582,17 @@ namespace Conformance }; /// If instance is valid then we inherit it instead of create one ourselves. - AutoBasicSession(int optionFlags = 0, XrInstance instance = XR_NULL_HANDLE); + AutoBasicSession(int optionFlags = 0, XrInstance instance = XR_NULL_HANDLE, + XrViewConfigurationType viewConfigType_ = XR_VIEW_CONFIGURATION_TYPE_MAX_ENUM); /// Calls Shutdown if not shut down already. ~AutoBasicSession(); /// If instance is valid then we inherit it instead of create one ourselves. - void Init(int optionFlags, XrInstance instance = XR_NULL_HANDLE); + void Init(int optionFlags, XrInstance instance = XR_NULL_HANDLE, + XrViewConfigurationType viewConfigType_ = XR_VIEW_CONFIGURATION_TYPE_MAX_ENUM); - /// Begin the session. + /// Begin the session with specific view configuration type void BeginSession(); /// Restores the class instance to a pre-initialized state. @@ -666,9 +668,9 @@ namespace Conformance // Enumerated types. std::vector swapchainFormatVector; std::vector spaceTypeVector; - std::vector viewConfigurationTypeVector; std::vector viewConfigurationViewVector; std::vector environmentBlendModeVector; + XrViewConfigurationType viewConfigurationType{XR_VIEW_CONFIGURATION_TYPE_MAX_ENUM}; }; /// Output operator for the `XrSession` handle in a @ref AutoBasicSession @@ -685,17 +687,13 @@ namespace Conformance /// Identifies conformance-related information about individual OpenXR functions. struct FunctionInfo { - PFN_xrVoidFunction functionPtr; bool nullInstanceOk; const char* requiredExtension; std::vector validResults; - FunctionInfo(PFN_xrVoidFunction functionPtr = nullptr, bool nullInstanceOk = false, const char* requiredExtension = nullptr, + FunctionInfo(bool nullInstanceOk = false, const char* requiredExtension = nullptr, std::vector validResults = std::vector()) - : functionPtr(functionPtr) - , nullInstanceOk(nullInstanceOk) - , requiredExtension(requiredExtension) - , validResults(std::move(validResults)) + : nullInstanceOk(nullInstanceOk), requiredExtension(requiredExtension), validResults(std::move(validResults)) { } }; diff --git a/src/conformance/framework/cts_tinygltf.cpp b/src/conformance/framework/cts_tinygltf.cpp index 6150d106..8e893f7e 100644 --- a/src/conformance/framework/cts_tinygltf.cpp +++ b/src/conformance/framework/cts_tinygltf.cpp @@ -9,9 +9,4 @@ #pragma warning(disable : 4189) // local variable is initialized but not referenced #endif // defined(_MSC_VER) -#if defined(__GNUC__) || defined(__clang__) -#pragma GCC diagnostic ignored "-Wunused-variable" -#pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" -#endif - #include "cts_tinygltf.h" diff --git a/src/conformance/framework/graphics_plugin_metal.cpp b/src/conformance/framework/graphics_plugin_metal.cpp index 4f261f03..ae18e64d 100644 --- a/src/conformance/framework/graphics_plugin_metal.cpp +++ b/src/conformance/framework/graphics_plugin_metal.cpp @@ -427,76 +427,78 @@ namespace Conformance static const SwapchainFormatDataMap& GetSwapchainFormatData() { + using namespace SwapchainFormat; + // Add SwapchainCreateTestParameters for other Vulkan formats if they are supported by a runtime static SwapchainFormatDataMap map{{ - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA8Unorm).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA8Unorm_sRGB).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBGRA8Unorm).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBGRA8Unorm_sRGB).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA8Unorm).rgba().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA8Unorm_sRGB).rgba().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBGRA8Unorm).rgba().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBGRA8Unorm_sRGB).rgba().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG8Unorm).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG8Unorm_sRGB).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG8Unorm).rg().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG8Unorm_sRGB).rg().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR8Unorm).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR8Unorm_sRGB).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR8Unorm).r().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR8Unorm_sRGB).r().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR8Snorm).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG8Snorm).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA8Snorm).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR8Snorm).r().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG8Snorm).rg().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA8Snorm).rgba().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR8Uint).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG8Uint).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA8Uint).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR8Sint).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG8Sint).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA8Sint).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR8Uint).r().Int(ColorIntegerRange::u8).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG8Uint).rg().Int(ColorIntegerRange::u8).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA8Uint).rgba().Int(ColorIntegerRange::u8).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR8Sint).r().Int(ColorIntegerRange::s8).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG8Sint).rg().Int(ColorIntegerRange::s8).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA8Sint).rgba().Int(ColorIntegerRange::s8).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR16Unorm).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG16Unorm).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA16Unorm).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR16Snorm).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG16Snorm).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA16Snorm).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR16Unorm).r().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG16Unorm).rg().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA16Unorm).rgba().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR16Snorm).r().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG16Snorm).rg().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA16Snorm).rgba().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR16Uint).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG16Uint).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA16Uint).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR16Uint).r().Int(ColorIntegerRange::u16).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG16Uint).rg().Int(ColorIntegerRange::u16).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA16Uint).rgba().Int(ColorIntegerRange::u16).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR16Sint).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG16Sint).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA16Sint).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR16Sint).r().Int(ColorIntegerRange::s16).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG16Sint).rg().Int(ColorIntegerRange::s16).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA16Sint).rgba().Int(ColorIntegerRange::s16).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR16Float).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG16Float).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA16Float).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR16Float).r().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG16Float).rg().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA16Float).rgba().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR32Sint).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG32Sint).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA32Sint).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR32Sint).r().Int(ColorIntegerRange::s32).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG32Sint).rg().Int(ColorIntegerRange::s32).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA32Sint).rgba().Int(ColorIntegerRange::s32).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR32Uint).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG32Uint).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA32Uint).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR32Uint).r().Int(ColorIntegerRange::u32).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG32Uint).rg().Int(ColorIntegerRange::u32).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA32Uint).rgba().Int(ColorIntegerRange::u32).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR32Float).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG32Float).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA32Float).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR32Float).r().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG32Float).rg().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA32Float).rgba().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatB5G6R5Unorm).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatA1BGR5Unorm).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBGR5A1Unorm).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatB5G6R5Unorm).rgb().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatA1BGR5Unorm).rgba().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBGR5A1Unorm).rgba().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatABGR4Unorm).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatABGR4Unorm).rgba().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGB10A2Unorm).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBGR10A2Unorm).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGB10A2Unorm).rgba().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBGR10A2Unorm).rgba().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGB10A2Uint).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGB10A2Uint).rgba().Int(ColorIntegerRange::uRGB10A2).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG11B10Float).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGB9E5Float).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA16Float).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG11B10Float).rgb().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGB9E5Float).rgb().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA16Float).rgba().ToPair(), XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatDepth16Unorm).Depth().ToPair(), XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatDepth24Unorm_Stencil8).DepthStencil().ToPair(), @@ -504,77 +506,77 @@ namespace Conformance XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatDepth32Float).Depth().ToPair(), XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatDepth32Float_Stencil8).DepthStencil().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatETC2_RGB8).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatETC2_RGB8A1).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatETC2_RGB8_sRGB).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatETC2_RGB8A1_sRGB).Compressed().ToPair(), - - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatEAC_R11Unorm).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatEAC_RG11Unorm).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatEAC_R11Snorm).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatEAC_RG11Snorm).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatEAC_RGBA8).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatEAC_RGBA8_sRGB).Compressed().ToPair(), - - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_4x4_sRGB).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_5x4_sRGB).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_5x5_sRGB).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_6x5_sRGB).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_6x6_sRGB).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_8x5_sRGB).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_8x6_sRGB).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_8x8_sRGB).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x5_sRGB).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x6_sRGB).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x10_sRGB).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x10_sRGB).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_12x10_sRGB).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_12x12_sRGB).Compressed().ToPair(), - - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_4x4_LDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_5x4_LDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_5x5_LDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_6x5_LDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_6x6_LDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_8x5_LDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_8x6_LDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_8x8_LDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x5_LDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x6_LDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x10_LDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x10_LDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_12x10_LDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_12x12_LDR).Compressed().ToPair(), - - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_4x4_HDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_5x4_HDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_5x5_HDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_6x5_HDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_6x6_HDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_8x5_HDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_8x6_HDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_8x8_HDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x5_HDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x6_HDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x10_HDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x10_HDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_12x10_HDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_12x12_HDR).Compressed().ToPair(), - - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBC1_RGBA).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBC1_RGBA_sRGB).Compressed().ToPair(), - - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBC2_RGBA).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBC2_RGBA_sRGB).Compressed().ToPair(), - - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBC3_RGBA).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBC3_RGBA_sRGB).Compressed().ToPair(), - - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBC6H_RGBFloat).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBC6H_RGBUfloat).Compressed().ToPair(), - - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBC7_RGBAUnorm).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBC7_RGBAUnorm_sRGB).Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatETC2_RGB8).rgb().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatETC2_RGB8A1).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatETC2_RGB8_sRGB).rgb().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatETC2_RGB8A1_sRGB).rgba().Compressed().ToPair(), + + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatEAC_R11Unorm).r().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatEAC_RG11Unorm).rg().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatEAC_R11Snorm).r().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatEAC_RG11Snorm).rg().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatEAC_RGBA8).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatEAC_RGBA8_sRGB).rgba().Compressed().ToPair(), + + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_4x4_sRGB).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_5x4_sRGB).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_5x5_sRGB).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_6x5_sRGB).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_6x6_sRGB).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_8x5_sRGB).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_8x6_sRGB).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_8x8_sRGB).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x5_sRGB).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x6_sRGB).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x10_sRGB).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x10_sRGB).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_12x10_sRGB).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_12x12_sRGB).rgba().Compressed().ToPair(), + + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_4x4_LDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_5x4_LDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_5x5_LDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_6x5_LDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_6x6_LDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_8x5_LDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_8x6_LDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_8x8_LDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x5_LDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x6_LDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x10_LDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x10_LDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_12x10_LDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_12x12_LDR).rgba().Compressed().ToPair(), + + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_4x4_HDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_5x4_HDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_5x5_HDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_6x5_HDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_6x6_HDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_8x5_HDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_8x6_HDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_8x8_HDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x5_HDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x6_HDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x10_HDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x10_HDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_12x10_HDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_12x12_HDR).rgba().Compressed().ToPair(), + + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBC1_RGBA).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBC1_RGBA_sRGB).rgba().Compressed().ToPair(), + + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBC2_RGBA).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBC2_RGBA_sRGB).rgba().Compressed().ToPair(), + + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBC3_RGBA).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBC3_RGBA_sRGB).rgba().Compressed().ToPair(), + + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBC6H_RGBFloat).rgb().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBC6H_RGBUfloat).rgb().Compressed().ToPair(), + + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBC7_RGBAUnorm).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBC7_RGBAUnorm_sRGB).rgba().Compressed().ToPair(), }}; return map; } @@ -941,36 +943,35 @@ namespace Conformance using NS::StringEncoding::UTF8StringEncoding; const char* shaderSrc = R"( - #include +#include using namespace metal; - struct VertexBuffer { - float4 position; - float4 color; - }; + struct VertexBuffer + { + float4 position; + float4 color; + }; - struct v2f - { - float4 position [[position]]; - half4 color; - }; + struct v2f + { + float4 position [[position]]; + half4 color; + }; - v2f vertex vertexMain( uint vertexId [[vertex_id]], - uint instanceId [[instance_id]], - device const VertexBuffer* vertexBuffer [[buffer(0)]], - device const float4x4* matricesBuffer [[buffer(1)]] ) - { - v2f o; - float4 pos = vertexBuffer[vertexId].position; - o.position = matricesBuffer[instanceId] * pos; - o.color = half4(vertexBuffer[vertexId].color); - return o; - } + v2f vertex vertexMain(uint vertexId [[vertex_id]], uint instanceId [[instance_id]], + device const VertexBuffer* vertexBuffer [[buffer(0)]], device const float4x4* matricesBuffer [[buffer(1)]]) + { + v2f o; + float4 pos = vertexBuffer[vertexId].position; + o.position = matricesBuffer[instanceId] * pos; + o.color = half4(vertexBuffer[vertexId].color); + return o; + } - half4 fragment fragmentMain( v2f in [[stage_in]] ) - { - return in.color; - } + half4 fragment fragmentMain(v2f in [[stage_in]]) + { + return in.color; + } )"; NS::Error* pError = nullptr; diff --git a/src/conformance/framework/graphics_plugin_opengl.cpp b/src/conformance/framework/graphics_plugin_opengl.cpp index 5a510f10..95f4d5d2 100644 --- a/src/conformance/framework/graphics_plugin_opengl.cpp +++ b/src/conformance/framework/graphics_plugin_opengl.cpp @@ -93,54 +93,56 @@ namespace Conformance #define WORKAROUND NotMutable() static const SwapchainFormatDataMap& GetSwapchainFormatData() { + using namespace SwapchainFormat; + static SwapchainFormatDataMap map{ - XRC_SWAPCHAIN_FORMAT(GL_RGBA8).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RGBA16).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RGB10_A2).WORKAROUND.ToPair(), - - XRC_SWAPCHAIN_FORMAT(GL_R8).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_R16).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RG8).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RG16).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RGB10_A2UI).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_R16F).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RG16F).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RGB16F).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RGBA16F).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_R32F).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RG32F).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RGBA32F).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_R11F_G11F_B10F).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_R8_SNORM).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RG8_SNORM).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RGB8_SNORM).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RGBA8_SNORM).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_R8I).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_R8UI).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_R16I).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_R16UI).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_R32I).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_R32UI).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RG8I).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RG8UI).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RG16I).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RG16UI).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RG32I).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RG32UI).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RGBA8I).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RGBA8UI).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RGBA16I).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RGBA16UI).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RGBA32I).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RGBA32UI).WORKAROUND.ToPair(), - - XRC_SWAPCHAIN_FORMAT(GL_RGBA4).WORKAROUND.NotMutable().NoUnorderedAccess().ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RGB5_A1).WORKAROUND.NotMutable().NoUnorderedAccess().ToPair(), - - XRC_SWAPCHAIN_FORMAT(GL_SRGB8).WORKAROUND.NoUnorderedAccess().ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_SRGB8_ALPHA8).WORKAROUND.NoUnorderedAccess().ToPair(), - - XRC_SWAPCHAIN_FORMAT(GL_RGB565).WORKAROUND.NotMutable().NoUnorderedAccess().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RGBA8).WORKAROUND.rgba().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RGBA16).WORKAROUND.rgba().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RGB10_A2).WORKAROUND.rgba().ToPair(), + + XRC_SWAPCHAIN_FORMAT(GL_R8).WORKAROUND.r().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_R16).WORKAROUND.r().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RG8).WORKAROUND.rg().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RG16).WORKAROUND.rg().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RGB10_A2UI).WORKAROUND.rgba().Int(uRGB10A2).ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_R16F).WORKAROUND.r().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RG16F).WORKAROUND.rg().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RGB16F).WORKAROUND.rgb().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RGBA16F).WORKAROUND.rgba().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_R32F).WORKAROUND.r().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RG32F).WORKAROUND.rg().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RGBA32F).WORKAROUND.rgba().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_R11F_G11F_B10F).WORKAROUND.rgb().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_R8_SNORM).WORKAROUND.r().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RG8_SNORM).WORKAROUND.rg().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RGB8_SNORM).WORKAROUND.rgb().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RGBA8_SNORM).WORKAROUND.rgba().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_R8I).WORKAROUND.r().Int(s8).ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_R8UI).WORKAROUND.r().Int(u8).ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_R16I).WORKAROUND.r().Int(s16).ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_R16UI).WORKAROUND.r().Int(u16).ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_R32I).WORKAROUND.r().Int(s32).ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_R32UI).WORKAROUND.r().Int(u32).ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RG8I).WORKAROUND.rg().Int(s8).ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RG8UI).WORKAROUND.rg().Int(u8).ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RG16I).WORKAROUND.rg().Int(s16).ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RG16UI).WORKAROUND.rg().Int(u16).ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RG32I).WORKAROUND.rg().Int(s32).ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RG32UI).WORKAROUND.rg().Int(u32).ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RGBA8I).WORKAROUND.rgba().Int(s8).ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RGBA8UI).WORKAROUND.rgba().Int(u8).ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RGBA16I).WORKAROUND.rgba().Int(s16).ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RGBA16UI).WORKAROUND.rgba().Int(u16).ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RGBA32I).WORKAROUND.rgba().Int(s32).ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RGBA32UI).WORKAROUND.rgba().Int(u32).ToPair(), + + XRC_SWAPCHAIN_FORMAT(GL_RGBA4).WORKAROUND.rgba().NotMutable().NoUnorderedAccess().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RGB5_A1).WORKAROUND.rgba().NotMutable().NoUnorderedAccess().ToPair(), + + XRC_SWAPCHAIN_FORMAT(GL_SRGB8).WORKAROUND.rgb().NoUnorderedAccess().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_SRGB8_ALPHA8).WORKAROUND.rgba().NoUnorderedAccess().ToPair(), + + XRC_SWAPCHAIN_FORMAT(GL_RGB565).WORKAROUND.rgb().NotMutable().NoUnorderedAccess().ToPair(), XRC_SWAPCHAIN_FORMAT(GL_DEPTH_COMPONENT16).WORKAROUND.Depth().ToPair(), XRC_SWAPCHAIN_FORMAT(GL_DEPTH_COMPONENT24).WORKAROUND.Depth().ToPair(), @@ -150,25 +152,25 @@ namespace Conformance XRC_SWAPCHAIN_FORMAT(GL_DEPTH32F_STENCIL8).WORKAROUND.DepthStencil().ToPair(), XRC_SWAPCHAIN_FORMAT(GL_STENCIL_INDEX8).WORKAROUND.Stencil().ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RED_RGTC1).WORKAROUND.Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SIGNED_RED_RGTC1).WORKAROUND.Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RG_RGTC2).WORKAROUND.Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SIGNED_RG_RGTC2).WORKAROUND.Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA_BPTC_UNORM).WORKAROUND.Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM).WORKAROUND.Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT).WORKAROUND.Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT).WORKAROUND.Compressed().ToPair(), - - XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGB8_ETC2).WORKAROUND.Compressed().NotMutable().ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ETC2).WORKAROUND.Compressed().NotMutable().ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2).WORKAROUND.Compressed().NotMutable().ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2).WORKAROUND.Compressed().NotMutable().ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA8_ETC2_EAC).WORKAROUND.Compressed().NotMutable().ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC).WORKAROUND.Compressed().NotMutable().ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_R11_EAC).WORKAROUND.Compressed().NotMutable().ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SIGNED_R11_EAC).WORKAROUND.Compressed().NotMutable().ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RG11_EAC).WORKAROUND.Compressed().NotMutable().ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SIGNED_RG11_EAC).WORKAROUND.Compressed().NotMutable().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RED_RGTC1).WORKAROUND.r().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SIGNED_RED_RGTC1).WORKAROUND.r().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RG_RGTC2).WORKAROUND.rg().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SIGNED_RG_RGTC2).WORKAROUND.rg().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA_BPTC_UNORM).WORKAROUND.rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM).WORKAROUND.rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT).WORKAROUND.rgb().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT).WORKAROUND.rgb().Compressed().ToPair(), + + XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGB8_ETC2).WORKAROUND.rgb().Compressed().NotMutable().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ETC2).WORKAROUND.rgb().Compressed().NotMutable().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2).WORKAROUND.rgba().Compressed().NotMutable().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2).WORKAROUND.rgba().Compressed().NotMutable().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA8_ETC2_EAC).WORKAROUND.rgba().Compressed().NotMutable().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC).WORKAROUND.rgba().Compressed().NotMutable().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_R11_EAC).WORKAROUND.r().Compressed().NotMutable().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SIGNED_R11_EAC).WORKAROUND.r().Compressed().NotMutable().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RG11_EAC).WORKAROUND.rg().Compressed().NotMutable().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SIGNED_RG11_EAC).WORKAROUND.rg().Compressed().NotMutable().ToPair(), }; return map; } diff --git a/src/conformance/framework/graphics_plugin_opengles.cpp b/src/conformance/framework/graphics_plugin_opengles.cpp index 10bb7335..e6255159 100644 --- a/src/conformance/framework/graphics_plugin_opengles.cpp +++ b/src/conformance/framework/graphics_plugin_opengles.cpp @@ -620,204 +620,243 @@ namespace Conformance static const SwapchainFormatDataMap& GetSwapchainFormatData() { + using namespace SwapchainFormat; + using C = SwapchainFormat::RawColorComponents; + static SwapchainFormatDataMap map{ // // 8 bits per component // - XRC_SWAPCHAIN_FORMAT(GL_R8).WORKAROUND.ToPair(), // 1-component, 8-bit unsigned normalized - XRC_SWAPCHAIN_FORMAT(GL_RG8).WORKAROUND.ToPair(), // 2-component, 8-bit unsigned normalized - XRC_SWAPCHAIN_FORMAT(GL_RGB8).WORKAROUND.ToPair(), // 3-component, 8-bit unsigned normalized - XRC_SWAPCHAIN_FORMAT(GL_RGBA8).WORKAROUND.ToPair(), // 4-component, 8-bit unsigned normalized - XRC_SWAPCHAIN_FORMAT(GL_R8_SNORM).WORKAROUND.ToPair(), // 1-component, 8-bit signed normalized - XRC_SWAPCHAIN_FORMAT(GL_RG8_SNORM).WORKAROUND.ToPair(), // 2-component, 8-bit signed normalized - XRC_SWAPCHAIN_FORMAT(GL_RGB8_SNORM).WORKAROUND.ToPair(), // 3-component, 8-bit signed normalized - XRC_SWAPCHAIN_FORMAT(GL_RGBA8_SNORM).WORKAROUND.ToPair(), // 4-component, 8-bit signed normalized - XRC_SWAPCHAIN_FORMAT(GL_R8UI).WORKAROUND.ToPair(), // 1-component, 8-bit unsigned integer - XRC_SWAPCHAIN_FORMAT(GL_RG8UI).WORKAROUND.ToPair(), // 2-component, 8-bit unsigned integer - XRC_SWAPCHAIN_FORMAT(GL_RGB8UI).WORKAROUND.ToPair(), // 3-component, 8-bit unsigned integer - XRC_SWAPCHAIN_FORMAT(GL_RGBA8UI).WORKAROUND.ToPair(), // 4-component, 8-bit unsigned integer - XRC_SWAPCHAIN_FORMAT(GL_R8I).WORKAROUND.ToPair(), // 1-component, 8-bit signed integer - XRC_SWAPCHAIN_FORMAT(GL_RG8I).WORKAROUND.ToPair(), // 2-component, 8-bit signed integer - XRC_SWAPCHAIN_FORMAT(GL_RGB8I).WORKAROUND.ToPair(), // 3-component, 8-bit signed integer - XRC_SWAPCHAIN_FORMAT(GL_RGBA8I).WORKAROUND.ToPair(), // 4-component, 8-bit signed integer - XRC_SWAPCHAIN_FORMAT(GL_SR8).WORKAROUND.ToPair(), // 1-component, 8-bit sRGB - XRC_SWAPCHAIN_FORMAT(GL_SRG8).WORKAROUND.ToPair(), // 2-component, 8-bit sRGB - XRC_SWAPCHAIN_FORMAT(GL_SRGB8).WORKAROUND.ToPair(), // 3-component, 8-bit sRGB - XRC_SWAPCHAIN_FORMAT(GL_SRGB8_ALPHA8).WORKAROUND.ToPair(), // 4-component, 8-bit sRGB + XRC_SWAPCHAIN_FORMAT(GL_R8).WORKAROUND.r().ToPair(), // 1-component, 8-bit unsigned normalized + XRC_SWAPCHAIN_FORMAT(GL_RG8).WORKAROUND.rg().ToPair(), // 2-component, 8-bit unsigned normalized + XRC_SWAPCHAIN_FORMAT(GL_RGB8).WORKAROUND.rgb().ToPair(), // 3-component, 8-bit unsigned normalized + XRC_SWAPCHAIN_FORMAT(GL_RGBA8).WORKAROUND.rgba().ToPair(), // 4-component, 8-bit unsigned normalized + XRC_SWAPCHAIN_FORMAT(GL_R8_SNORM).WORKAROUND.r().ToPair(), // 1-component, 8-bit signed normalized + XRC_SWAPCHAIN_FORMAT(GL_RG8_SNORM).WORKAROUND.rg().ToPair(), // 2-component, 8-bit signed normalized + XRC_SWAPCHAIN_FORMAT(GL_RGB8_SNORM).WORKAROUND.rgb().ToPair(), // 3-component, 8-bit signed normalized + XRC_SWAPCHAIN_FORMAT(GL_RGBA8_SNORM).WORKAROUND.rgba().ToPair(), // 4-component, 8-bit signed normalized + XRC_SWAPCHAIN_FORMAT(GL_R8UI).WORKAROUND.r().Int(u8).ToPair(), // 1-component, 8-bit unsigned integer + XRC_SWAPCHAIN_FORMAT(GL_RG8UI).WORKAROUND.rg().Int(u8).ToPair(), // 2-component, 8-bit unsigned integer + XRC_SWAPCHAIN_FORMAT(GL_RGB8UI).WORKAROUND.rgb().Int(u8).ToPair(), // 3-component, 8-bit unsigned integer + XRC_SWAPCHAIN_FORMAT(GL_RGBA8UI).WORKAROUND.rgba().Int(u8).ToPair(), // 4-component, 8-bit unsigned integer + XRC_SWAPCHAIN_FORMAT(GL_R8I).WORKAROUND.r().Int(s8).ToPair(), // 1-component, 8-bit signed integer + XRC_SWAPCHAIN_FORMAT(GL_RG8I).WORKAROUND.rg().Int(s8).ToPair(), // 2-component, 8-bit signed integer + XRC_SWAPCHAIN_FORMAT(GL_RGB8I).WORKAROUND.rgb().Int(s8).ToPair(), // 3-component, 8-bit signed integer + XRC_SWAPCHAIN_FORMAT(GL_RGBA8I).WORKAROUND.rgba().Int(s8).ToPair(), // 4-component, 8-bit signed integer + XRC_SWAPCHAIN_FORMAT(GL_SR8).WORKAROUND.r().ToPair(), // 1-component, 8-bit sRGB + XRC_SWAPCHAIN_FORMAT(GL_SRG8).WORKAROUND.rg().ToPair(), // 2-component, 8-bit sRGB + XRC_SWAPCHAIN_FORMAT(GL_SRGB8).WORKAROUND.rgb().ToPair(), // 3-component, 8-bit sRGB + XRC_SWAPCHAIN_FORMAT(GL_SRGB8_ALPHA8).WORKAROUND.rgba().ToPair(), // 4-component, 8-bit sRGB // // 16 bits per component // - XRC_SWAPCHAIN_FORMAT(GL_R16).WORKAROUND.ToPair(), // 1-component, 16-bit unsigned normalized - XRC_SWAPCHAIN_FORMAT(GL_RG16).WORKAROUND.ToPair(), // 2-component, 16-bit unsigned normalized - XRC_SWAPCHAIN_FORMAT(GL_RGB16).WORKAROUND.ToPair(), // 3-component, 16-bit unsigned normalized - XRC_SWAPCHAIN_FORMAT(GL_RGBA16).WORKAROUND.ToPair(), // 4-component, 16-bit unsigned normalized - XRC_SWAPCHAIN_FORMAT(GL_R16_SNORM).WORKAROUND.ToPair(), // 1-component, 16-bit signed normalized - XRC_SWAPCHAIN_FORMAT(GL_RG16_SNORM).WORKAROUND.ToPair(), // 2-component, 16-bit signed normalized - XRC_SWAPCHAIN_FORMAT(GL_RGB16_SNORM).WORKAROUND.ToPair(), // 3-component, 16-bit signed normalized - XRC_SWAPCHAIN_FORMAT(GL_RGBA16_SNORM).WORKAROUND.ToPair(), // 4-component, 16-bit signed normalized - XRC_SWAPCHAIN_FORMAT(GL_R16UI).WORKAROUND.ToPair(), // 1-component, 16-bit unsigned integer - XRC_SWAPCHAIN_FORMAT(GL_RG16UI).WORKAROUND.ToPair(), // 2-component, 16-bit unsigned integer - XRC_SWAPCHAIN_FORMAT(GL_RGB16UI).WORKAROUND.ToPair(), // 3-component, 16-bit unsigned integer - XRC_SWAPCHAIN_FORMAT(GL_RGBA16UI).WORKAROUND.ToPair(), // 4-component, 16-bit unsigned integer - XRC_SWAPCHAIN_FORMAT(GL_R16I).WORKAROUND.ToPair(), // 1-component, 16-bit signed integer - XRC_SWAPCHAIN_FORMAT(GL_RG16I).WORKAROUND.ToPair(), // 2-component, 16-bit signed integer - XRC_SWAPCHAIN_FORMAT(GL_RGB16I).WORKAROUND.ToPair(), // 3-component, 16-bit signed integer - XRC_SWAPCHAIN_FORMAT(GL_RGBA16I).WORKAROUND.ToPair(), // 4-component, 16-bit signed integer - XRC_SWAPCHAIN_FORMAT(GL_R16F).WORKAROUND.ToPair(), // 1-component, 16-bit floating-point - XRC_SWAPCHAIN_FORMAT(GL_RG16F).WORKAROUND.ToPair(), // 2-component, 16-bit floating-point - XRC_SWAPCHAIN_FORMAT(GL_RGB16F).WORKAROUND.ToPair(), // 3-component, 16-bit floating-point - XRC_SWAPCHAIN_FORMAT(GL_RGBA16F).WORKAROUND.ToPair(), // 4-component, 16-bit floating-point + XRC_SWAPCHAIN_FORMAT(GL_R16).WORKAROUND.r().ToPair(), // 1-component, 16-bit unsigned normalized + XRC_SWAPCHAIN_FORMAT(GL_RG16).WORKAROUND.rg().ToPair(), // 2-component, 16-bit unsigned normalized + XRC_SWAPCHAIN_FORMAT(GL_RGB16).WORKAROUND.rgb().ToPair(), // 3-component, 16-bit unsigned normalized + XRC_SWAPCHAIN_FORMAT(GL_RGBA16).WORKAROUND.rgba().ToPair(), // 4-component, 16-bit unsigned normalized + XRC_SWAPCHAIN_FORMAT(GL_R16_SNORM).WORKAROUND.r().ToPair(), // 1-component, 16-bit signed normalized + XRC_SWAPCHAIN_FORMAT(GL_RG16_SNORM).WORKAROUND.rg().ToPair(), // 2-component, 16-bit signed normalized + XRC_SWAPCHAIN_FORMAT(GL_RGB16_SNORM).WORKAROUND.rgb().ToPair(), // 3-component, 16-bit signed normalized + XRC_SWAPCHAIN_FORMAT(GL_RGBA16_SNORM).WORKAROUND.rgba().ToPair(), // 4-component, 16-bit signed normalized + XRC_SWAPCHAIN_FORMAT(GL_R16UI).WORKAROUND.r().Int(u16).ToPair(), // 1-component, 16-bit unsigned integer + XRC_SWAPCHAIN_FORMAT(GL_RG16UI).WORKAROUND.rg().Int(u16).ToPair(), // 2-component, 16-bit unsigned integer + XRC_SWAPCHAIN_FORMAT(GL_RGB16UI).WORKAROUND.rgb().Int(u16).ToPair(), // 3-component, 16-bit unsigned integer + XRC_SWAPCHAIN_FORMAT(GL_RGBA16UI).WORKAROUND.rgba().Int(u16).ToPair(), // 4-component, 16-bit unsigned integer + XRC_SWAPCHAIN_FORMAT(GL_R16I).WORKAROUND.r().Int(s16).ToPair(), // 1-component, 16-bit signed integer + XRC_SWAPCHAIN_FORMAT(GL_RG16I).WORKAROUND.rg().Int(s16).ToPair(), // 2-component, 16-bit signed integer + XRC_SWAPCHAIN_FORMAT(GL_RGB16I).WORKAROUND.rgb().Int(s16).ToPair(), // 3-component, 16-bit signed integer + XRC_SWAPCHAIN_FORMAT(GL_RGBA16I).WORKAROUND.rgba().Int(s16).ToPair(), // 4-component, 16-bit signed integer + XRC_SWAPCHAIN_FORMAT(GL_R16F).WORKAROUND.r().ToPair(), // 1-component, 16-bit floating-point + XRC_SWAPCHAIN_FORMAT(GL_RG16F).WORKAROUND.rg().ToPair(), // 2-component, 16-bit floating-point + XRC_SWAPCHAIN_FORMAT(GL_RGB16F).WORKAROUND.rgb().ToPair(), // 3-component, 16-bit floating-point + XRC_SWAPCHAIN_FORMAT(GL_RGBA16F).WORKAROUND.rgba().ToPair(), // 4-component, 16-bit floating-point // // 32 bits per component // - XRC_SWAPCHAIN_FORMAT(GL_R32UI).WORKAROUND.ToPair(), // 1-component, 32-bit unsigned integer - XRC_SWAPCHAIN_FORMAT(GL_RG32UI).WORKAROUND.ToPair(), // 2-component, 32-bit unsigned integer - XRC_SWAPCHAIN_FORMAT(GL_RGB32UI).WORKAROUND.ToPair(), // 3-component, 32-bit unsigned integer - XRC_SWAPCHAIN_FORMAT(GL_RGBA32UI).WORKAROUND.ToPair(), // 4-component, 32-bit unsigned integer - XRC_SWAPCHAIN_FORMAT(GL_R32I).WORKAROUND.ToPair(), // 1-component, 32-bit signed integer - XRC_SWAPCHAIN_FORMAT(GL_RG32I).WORKAROUND.ToPair(), // 2-component, 32-bit signed integer - XRC_SWAPCHAIN_FORMAT(GL_RGB32I).WORKAROUND.ToPair(), // 3-component, 32-bit signed integer - XRC_SWAPCHAIN_FORMAT(GL_RGBA32I).WORKAROUND.ToPair(), // 4-component, 32-bit signed integer - XRC_SWAPCHAIN_FORMAT(GL_R32F).WORKAROUND.ToPair(), // 1-component, 32-bit floating-point - XRC_SWAPCHAIN_FORMAT(GL_RG32F).WORKAROUND.ToPair(), // 2-component, 32-bit floating-point - XRC_SWAPCHAIN_FORMAT(GL_RGB32F).WORKAROUND.ToPair(), // 3-component, 32-bit floating-point - XRC_SWAPCHAIN_FORMAT(GL_RGBA32F).WORKAROUND.ToPair(), // 4-component, 32-bit floating-point + XRC_SWAPCHAIN_FORMAT(GL_R32UI).WORKAROUND.r().Int(u32).ToPair(), // 1-component, 32-bit unsigned integer + XRC_SWAPCHAIN_FORMAT(GL_RG32UI).WORKAROUND.rg().Int(u32).ToPair(), // 2-component, 32-bit unsigned integer + XRC_SWAPCHAIN_FORMAT(GL_RGB32UI).WORKAROUND.rgb().Int(u32).ToPair(), // 3-component, 32-bit unsigned integer + XRC_SWAPCHAIN_FORMAT(GL_RGBA32UI).WORKAROUND.rgba().Int(u32).ToPair(), // 4-component, 32-bit unsigned integer + XRC_SWAPCHAIN_FORMAT(GL_R32I).WORKAROUND.r().Int(s32).ToPair(), // 1-component, 32-bit signed integer + XRC_SWAPCHAIN_FORMAT(GL_RG32I).WORKAROUND.rg().Int(s32).ToPair(), // 2-component, 32-bit signed integer + XRC_SWAPCHAIN_FORMAT(GL_RGB32I).WORKAROUND.rgb().Int(s32).ToPair(), // 3-component, 32-bit signed integer + XRC_SWAPCHAIN_FORMAT(GL_RGBA32I).WORKAROUND.rgba().Int(s32).ToPair(), // 4-component, 32-bit signed integer + XRC_SWAPCHAIN_FORMAT(GL_R32F).WORKAROUND.r().ToPair(), // 1-component, 32-bit floating-point + XRC_SWAPCHAIN_FORMAT(GL_RG32F).WORKAROUND.rg().ToPair(), // 2-component, 32-bit floating-point + XRC_SWAPCHAIN_FORMAT(GL_RGB32F).WORKAROUND.rgb().ToPair(), // 3-component, 32-bit floating-point + XRC_SWAPCHAIN_FORMAT(GL_RGBA32F).WORKAROUND.rgba().ToPair(), // 4-component, 32-bit floating-point // // Packed // - XRC_SWAPCHAIN_FORMAT(GL_RGB5).WORKAROUND.ToPair(), // 3-component 5:5:5, unsigned normalized - XRC_SWAPCHAIN_FORMAT(GL_RGB565).WORKAROUND.ToPair(), // 3-component 5:6:5, unsigned normalized - XRC_SWAPCHAIN_FORMAT(GL_RGB10).WORKAROUND.ToPair(), // 3-component 10:10:10, unsigned normalized - XRC_SWAPCHAIN_FORMAT(GL_RGBA4).WORKAROUND.ToPair(), // 4-component 4:4:4:4, unsigned normalized - XRC_SWAPCHAIN_FORMAT(GL_RGB5_A1).WORKAROUND.ToPair(), // 4-component 5:5:5:1, unsigned normalized - XRC_SWAPCHAIN_FORMAT(GL_RGB10_A2).WORKAROUND.ToPair(), // 4-component 10:10:10:2, unsigned normalized - XRC_SWAPCHAIN_FORMAT(GL_RGB10_A2UI).WORKAROUND.ToPair(), // 4-component 10:10:10:2, unsigned integer - XRC_SWAPCHAIN_FORMAT(GL_R11F_G11F_B10F).WORKAROUND.ToPair(), // 3-component 11:11:10, floating-point - XRC_SWAPCHAIN_FORMAT(GL_RGB9_E5).WORKAROUND.ToPair(), // 3-component/exp 9:9:9/5, floating-point + XRC_SWAPCHAIN_FORMAT(GL_RGB5).WORKAROUND.rgb().ToPair(), // 3-component 5:5:5, unsigned normalized + XRC_SWAPCHAIN_FORMAT(GL_RGB565).WORKAROUND.rgb().ToPair(), // 3-component 5:6:5, unsigned normalized + XRC_SWAPCHAIN_FORMAT(GL_RGB10).WORKAROUND.rgb().ToPair(), // 3-component 10:10:10, unsigned normalized + XRC_SWAPCHAIN_FORMAT(GL_RGBA4).WORKAROUND.rgba().ToPair(), // 4-component 4:4:4:4, unsigned normalized + XRC_SWAPCHAIN_FORMAT(GL_RGB5_A1).WORKAROUND.rgba().ToPair(), // 4-component 5:5:5:1, unsigned normalized + XRC_SWAPCHAIN_FORMAT(GL_RGB10_A2).WORKAROUND.rgba().ToPair(), // 4-component 10:10:10:2, unsigned normalized + XRC_SWAPCHAIN_FORMAT(GL_RGB10_A2UI).WORKAROUND.rgba().Int(uRGB10A2).ToPair(), // 4-component 10:10:10:2, unsigned integer + XRC_SWAPCHAIN_FORMAT(GL_R11F_G11F_B10F).WORKAROUND.rgb().ToPair(), // 3-component 11:11:10, floating-point + XRC_SWAPCHAIN_FORMAT(GL_RGB9_E5).WORKAROUND.rgb().ToPair(), // 3-component/exp 9:9:9/5, floating-point // // S3TC/DXT/BC // XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGB_S3TC_DXT1_EXT) + .rgb() .Compressed() .NotMutable() .ToPair(), // line through 3D space, 4x4 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) + .rgba() .Compressed() .NotMutable() .ToPair(), // line through 3D space plus 1-bit alpha, 4x4 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA_S3TC_DXT5_EXT) + .rgba() .Compressed() .NotMutable() .ToPair(), // line through 3D space plus line through 1D space, 4x4 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA_S3TC_DXT3_EXT) + .rgba() .Compressed() .NotMutable() .ToPair(), // line through 3D space plus 4-bit alpha, 4x4 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB_S3TC_DXT1_EXT) + .rgb() .Compressed() .NotMutable() .ToPair(), // line through 3D space, 4x4 blocks, sRGB XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT) + .rgba() .Compressed() .NotMutable() .ToPair(), // line through 3D space plus 1-bit alpha, 4x4 blocks, sRGB XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT) + .rgba() .Compressed() .NotMutable() .ToPair(), // line through 3D space plus line through 1D space, 4x4 blocks, sRGB XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT) + .rgba() .Compressed() .NotMutable() .ToPair(), // line through 3D space plus 4-bit alpha, 4x4 blocks, sRGB XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_LUMINANCE_LATC1_EXT) + .r() .Compressed() .NotMutable() .ToPair(), // line through 1D space, 4x4 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT) + .Color(C(r | a)) .Compressed() .NotMutable() .ToPair(), // two lines through 1D space, 4x4 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT) + .r() .Compressed() .NotMutable() .ToPair(), // line through 1D space, 4x4 blocks, signed normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT) + .Color(C(r | a)) .Compressed() .NotMutable() .ToPair(), // two lines through 1D space, 4x4 blocks, signed normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RED_RGTC1) + .r() .Compressed() .NotMutable() .ToPair(), // line through 1D space, 4x4 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RG_RGTC2) + .rg() .Compressed() .NotMutable() .ToPair(), // two lines through 1D space, 4x4 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SIGNED_RED_RGTC1) + .r() .Compressed() .NotMutable() .ToPair(), // line through 1D space, 4x4 blocks, signed normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SIGNED_RG_RGTC2) + .rg() .Compressed() .NotMutable() .ToPair(), // two lines through 1D space, 4x4 blocks, signed normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT) + .rgb() .Compressed() .NotMutable() .ToPair(), // 3-component, 4x4 blocks, unsigned floating-point XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT) + .rgb() .Compressed() .NotMutable() .ToPair(), // 3-component, 4x4 blocks, signed floating-point XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA_BPTC_UNORM) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component, 4x4 blocks, unsigned normalized - XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM).Compressed().NotMutable().ToPair(), // 4-component, 4x4 blocks, sRGB + XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM) + .rgba() + .Compressed() + .NotMutable() + .ToPair(), // 4-component, 4x4 blocks, sRGB // // ETC // - XRC_SWAPCHAIN_FORMAT(GL_ETC1_RGB8_OES).Compressed().NotMutable().ToPair(), // 3-component ETC1, 4x4 blocks, unsigned normalized + XRC_SWAPCHAIN_FORMAT(GL_ETC1_RGB8_OES) + .rgb() + .Compressed() + .NotMutable() + .ToPair(), // 3-component ETC1, 4x4 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGB8_ETC2) + .rgb() .Compressed() .NotMutable() .ToPair(), // 3-component ETC2, 4x4 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ETC2 with 1-bit alpha, 4x4 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA8_ETC2_EAC) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ETC2, 4x4 blocks, unsigned normalized - XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ETC2).Compressed().NotMutable().ToPair(), // 3-component ETC2, 4x4 blocks, sRGB + XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ETC2).rgb().Compressed().NotMutable().ToPair(), // 3-component ETC2, 4x4 blocks, sRGB XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ETC2 with 1-bit alpha, 4x4 blocks, sRGB XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ETC2, 4x4 blocks, sRGB XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_R11_EAC) + .r() .Compressed() .NotMutable() .ToPair(), // 1-component ETC, 4x4 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RG11_EAC) + .rg() .Compressed() .NotMutable() .ToPair(), // 2-component ETC, 4x4 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SIGNED_R11_EAC) + .r() .Compressed() .NotMutable() .ToPair(), // 1-component ETC, 4x4 blocks, signed normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SIGNED_RG11_EAC) + .rg() .Compressed() .NotMutable() .ToPair(), // 2-component ETC, 4x4 blocks, signed normalized @@ -826,114 +865,142 @@ namespace Conformance // ASTC // XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA_ASTC_4x4_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 4x4 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA_ASTC_5x4_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 5x4 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA_ASTC_5x5_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 5x5 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA_ASTC_6x5_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 6x5 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA_ASTC_6x6_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 6x6 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA_ASTC_8x5_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 8x5 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA_ASTC_8x6_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 8x6 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA_ASTC_8x8_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 8x8 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA_ASTC_10x5_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 10x5 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA_ASTC_10x6_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 10x6 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA_ASTC_10x8_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 10x8 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA_ASTC_10x10_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 10x10 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA_ASTC_12x10_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 12x10 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA_ASTC_12x12_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 12x12 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 4x4 blocks, sRGB XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 5x4 blocks, sRGB XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 5x5 blocks, sRGB XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 6x5 blocks, sRGB XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 6x6 blocks, sRGB XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 8x5 blocks, sRGB XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 8x6 blocks, sRGB XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 8x8 blocks, sRGB XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 10x5 blocks, sRGB XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 10x6 blocks, sRGB XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 10x8 blocks, sRGB XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 10x10 blocks, sRGB XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 12x10 blocks, sRGB XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 12x12 blocks, sRGB diff --git a/src/conformance/framework/graphics_plugin_vulkan.cpp b/src/conformance/framework/graphics_plugin_vulkan.cpp index 987a5c68..657abf20 100644 --- a/src/conformance/framework/graphics_plugin_vulkan.cpp +++ b/src/conformance/framework/graphics_plugin_vulkan.cpp @@ -1429,97 +1429,98 @@ namespace Conformance static const SwapchainFormatDataMap& GetSwapchainFormatData() { + using namespace SwapchainFormat; // Add SwapchainCreateTestParameters for other Vulkan formats if they are supported by a runtime static SwapchainFormatDataMap map{{ - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8B8A8_UNORM).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8B8A8_SRGB).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_B8G8R8A8_UNORM).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_B8G8R8A8_SRGB).ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8B8_UNORM).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8B8_SRGB).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_B8G8R8_UNORM).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_B8G8R8_SRGB).ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8_UNORM).ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8_UNORM).ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8_SNORM).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8_SNORM).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8B8_SNORM).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8B8A8_SNORM).ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8_UINT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8_UINT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8B8_UINT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8B8A8_UINT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8_SINT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8_SINT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8B8_SINT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8_UNORM).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8B8A8_SINT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8_SRGB).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8_SRGB).ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16_UNORM).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16_UNORM).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16_UNORM).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16A16_UNORM).ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16_SNORM).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16_SNORM).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16_SNORM).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16A16_SNORM).ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16_UINT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16_UINT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16_UINT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16A16_UINT).ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16_SINT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16_SINT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16_SINT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16A16_SINT).ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16_SFLOAT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16_SFLOAT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16_SFLOAT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16A16_SFLOAT).ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32_SINT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32G32_SINT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32G32B32_SINT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32G32B32A32_SINT).ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32_UINT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32G32_UINT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32G32B32_UINT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32G32B32A32_UINT).ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32_SFLOAT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32G32_SFLOAT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32G32B32_SFLOAT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32G32B32A32_SFLOAT).ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R5G5B5A1_UNORM_PACK16).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R5G6B5_UNORM_PACK16).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_A2B10G10R10_UNORM_PACK32).ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R4G4B4A4_UNORM_PACK16).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_A1R5G5B5_UNORM_PACK16).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_A2R10G10B10_UNORM_PACK32).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_A2R10G10B10_UINT_PACK32).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_A2B10G10R10_UNORM_PACK32).ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_A2B10G10R10_UINT_PACK32).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8B8A8_UNORM).rgba().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8B8A8_SRGB).rgba().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_B8G8R8A8_UNORM).rgba().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_B8G8R8A8_SRGB).rgba().ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8B8_UNORM).rgb().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8B8_SRGB).rgb().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_B8G8R8_UNORM).rgb().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_B8G8R8_SRGB).rgb().ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8_UNORM).rg().ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8_UNORM).r().ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8_SNORM).r().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8_SNORM).rg().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8B8_SNORM).rgb().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8B8A8_SNORM).rgba().ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8_UINT).r().Int(u8).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8_UINT).rg().Int(u8).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8B8_UINT).rgb().Int(u8).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8B8A8_UINT).rgba().Int(u8).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8_SINT).r().Int(s8).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8_SINT).rg().Int(s8).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8B8_SINT).rgb().Int(s8).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8_UNORM).r().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8B8A8_SINT).rgba().Int(s8).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8_SRGB).r().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8_SRGB).rg().ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16_UNORM).r().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16_UNORM).rg().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16_UNORM).rgb().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16A16_UNORM).rgba().ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16_SNORM).r().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16_SNORM).rg().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16_SNORM).rgb().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16A16_SNORM).rgba().ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16_UINT).r().Int(u16).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16_UINT).rg().Int(u16).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16_UINT).rgb().Int(u16).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16A16_UINT).rgba().Int(u16).ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16_SINT).r().Int(s16).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16_SINT).rg().Int(s16).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16_SINT).rgb().Int(s16).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16A16_SINT).rgba().Int(s16).ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16_SFLOAT).r().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16_SFLOAT).rg().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16_SFLOAT).rgb().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16A16_SFLOAT).rgba().ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32_SINT).r().Int(s32).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32G32_SINT).rg().Int(s32).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32G32B32_SINT).rgb().Int(s32).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32G32B32A32_SINT).rgba().Int(s32).ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32_UINT).r().Int(u32).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32G32_UINT).rg().Int(u32).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32G32B32_UINT).rgb().Int(u32).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32G32B32A32_UINT).rgba().Int(u32).ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32_SFLOAT).r().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32G32_SFLOAT).rg().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32G32B32_SFLOAT).rgb().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32G32B32A32_SFLOAT).rgba().ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R5G5B5A1_UNORM_PACK16).rgba().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R5G6B5_UNORM_PACK16).rgb().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_A2B10G10R10_UNORM_PACK32).rgba().ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R4G4B4A4_UNORM_PACK16).rgba().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_A1R5G5B5_UNORM_PACK16).rgba().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_A2R10G10B10_UNORM_PACK32).rgba().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_A2R10G10B10_UINT_PACK32).rgba().Int(uRGB10A2).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_A2B10G10R10_UNORM_PACK32).rgba().ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_A2B10G10R10_UINT_PACK32).rgba().Int(uRGB10A2).ToPair(), // Runtimes with D3D11 back-ends map VK_FORMAT_B10G11R11_UFLOAT_PACK32 to DXGI_FORMAT_R11G11B10_FLOAT and that format doesn't have a TYPELESS equivalent. - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_B10G11R11_UFLOAT_PACK32).NotMutable().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_E5B9G9R9_UFLOAT_PACK32).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_B10G11R11_UFLOAT_PACK32).rgb().NotMutable().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_E5B9G9R9_UFLOAT_PACK32).rgb().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16A16_SFLOAT).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16A16_SFLOAT).rgba().ToPair(), XRC_SWAPCHAIN_FORMAT(VK_FORMAT_D16_UNORM).Depth().ToPair(), XRC_SWAPCHAIN_FORMAT(VK_FORMAT_D24_UNORM_S8_UINT).DepthStencil().ToPair(), @@ -1530,62 +1531,62 @@ namespace Conformance XRC_SWAPCHAIN_FORMAT(VK_FORMAT_D32_SFLOAT).Depth().ToPair(), XRC_SWAPCHAIN_FORMAT(VK_FORMAT_D32_SFLOAT_S8_UINT).DepthStencil().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK).Compressed().ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_EAC_R11_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_EAC_R11G11_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_EAC_R11_SNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_EAC_R11G11_SNORM_BLOCK).Compressed().ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_4x4_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_5x4_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_5x5_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_6x5_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_6x6_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_8x5_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_8x6_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_8x8_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_10x5_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_10x6_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_10x8_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_10x10_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_12x10_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_12x12_UNORM_BLOCK).Compressed().ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_4x4_SRGB_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_5x4_SRGB_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_5x5_SRGB_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_6x5_SRGB_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_6x6_SRGB_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_8x5_SRGB_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_8x6_SRGB_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_8x8_SRGB_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_10x5_SRGB_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_10x6_SRGB_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_10x8_SRGB_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_10x10_SRGB_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_12x10_SRGB_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_12x12_SRGB_BLOCK).Compressed().ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_BC1_RGBA_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_BC1_RGBA_SRGB_BLOCK).Compressed().ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_BC2_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_BC2_SRGB_BLOCK).Compressed().ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_BC3_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_BC3_SRGB_BLOCK).Compressed().ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_BC6H_UFLOAT_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_BC6H_SFLOAT_BLOCK).Compressed().ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_BC7_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_BC7_SRGB_BLOCK).Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK).rgb().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK).rgb().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK).rgba().Compressed().ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_EAC_R11_UNORM_BLOCK).r().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_EAC_R11G11_UNORM_BLOCK).rg().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_EAC_R11_SNORM_BLOCK).r().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_EAC_R11G11_SNORM_BLOCK).rg().Compressed().ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_4x4_UNORM_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_5x4_UNORM_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_5x5_UNORM_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_6x5_UNORM_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_6x6_UNORM_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_8x5_UNORM_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_8x6_UNORM_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_8x8_UNORM_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_10x5_UNORM_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_10x6_UNORM_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_10x8_UNORM_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_10x10_UNORM_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_12x10_UNORM_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_12x12_UNORM_BLOCK).rgba().Compressed().ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_4x4_SRGB_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_5x4_SRGB_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_5x5_SRGB_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_6x5_SRGB_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_6x6_SRGB_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_8x5_SRGB_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_8x6_SRGB_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_8x8_SRGB_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_10x5_SRGB_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_10x6_SRGB_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_10x8_SRGB_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_10x10_SRGB_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_12x10_SRGB_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_12x12_SRGB_BLOCK).rgba().Compressed().ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_BC1_RGBA_UNORM_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_BC1_RGBA_SRGB_BLOCK).rgba().Compressed().ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_BC2_UNORM_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_BC2_SRGB_BLOCK).rgba().Compressed().ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_BC3_UNORM_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_BC3_SRGB_BLOCK).rgba().Compressed().ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_BC6H_UFLOAT_BLOCK).rgb().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_BC6H_SFLOAT_BLOCK).rgb().Compressed().ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_BC7_UNORM_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_BC7_SRGB_BLOCK).rgba().Compressed().ToPair(), }}; return map; } diff --git a/src/conformance/framework/input_testinputdevice.cpp b/src/conformance/framework/input_testinputdevice.cpp index 6aabb4e1..0ea05a63 100644 --- a/src/conformance/framework/input_testinputdevice.cpp +++ b/src/conformance/framework/input_testinputdevice.cpp @@ -76,6 +76,7 @@ namespace Conformance m_actionSet = actionSet; m_actionMap = actionMap; m_firstBooleanAction = firstBooleanAction; // will be used for testing active controller + m_firstTrackerAction = XR_NULL_HANDLE; // will be used for testing active controller if m_firstBooleanAction is not set m_shouldDestroyActionSet = false; // actions and action sets are handled by the test, so do not destroy } @@ -129,6 +130,9 @@ namespace Conformance if (m_firstBooleanAction == XR_NULL_PATH && inputSourceData.Type == XR_ACTION_TYPE_BOOLEAN_INPUT) { m_firstBooleanAction = action; } + if (m_firstTrackerAction == XR_NULL_PATH && inputSourceData.Type == XR_ACTION_TYPE_POSE_INPUT) { + m_firstTrackerAction = action; + } const XrPath bindingPath = StringToPath(instance, inputSourceData.Path); m_actionMap.insert({bindingPath, action}); @@ -182,6 +186,10 @@ namespace Conformance XrActionSet detectionActionSet{waitCondition.detectionActionSet}; XrAction detectionBoolAction{waitCondition.detectionBoolAction}; + // The selected controller has no boolean actions so... + XRC_CHECK_THROW(detectionBoolAction != XR_NULL_HANDLE || m_firstBooleanAction != XR_NULL_HANDLE || + m_firstTrackerAction != XR_NULL_HANDLE); + // Checks the isActive on a boolean action to determine if a controller is on auto findController = [&]() -> ControllerState { XrActiveActionSet activeActionSets[] = { @@ -201,15 +209,28 @@ namespace Conformance } } - XrActionStateBoolean booleanActionData{XR_TYPE_ACTION_STATE_BOOLEAN}; - XrActionStateGetInfo getInfo{XR_TYPE_ACTION_STATE_GET_INFO}; - getInfo.action = detectionBoolAction == XR_NULL_HANDLE ? m_firstBooleanAction : detectionBoolAction; - const XrResult res = xrGetActionStateBoolean(m_session, &getInfo, &booleanActionData); - if (res != XR_SUCCESS) { - XRC_THROW_XRRESULT(res, xrGetActionStateBoolean); + if (detectionBoolAction != XR_NULL_HANDLE || m_firstBooleanAction != XR_NULL_HANDLE) { + XrActionStateBoolean booleanActionData{XR_TYPE_ACTION_STATE_BOOLEAN}; + XrActionStateGetInfo getInfo{XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.action = detectionBoolAction != XR_NULL_HANDLE ? detectionBoolAction : m_firstBooleanAction; + const XrResult res = xrGetActionStateBoolean(m_session, &getInfo, &booleanActionData); + if (res != XR_SUCCESS) { + XRC_THROW_XRRESULT(res, xrGetActionStateBoolean); + } + + return booleanActionData.isActive ? ControllerState::Active : ControllerState::Inactive; } + else { + XrActionStatePose trackerActionData{XR_TYPE_ACTION_STATE_POSE}; + XrActionStateGetInfo getInfo{XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.action = m_firstTrackerAction; + const XrResult res = xrGetActionStatePose(m_session, &getInfo, &trackerActionData); + if (res != XR_SUCCESS) { + XRC_THROW_XRRESULT(res, xrGetActionStatePose); + } - return booleanActionData.isActive ? ControllerState::Active : ControllerState::Inactive; + return trackerActionData.isActive ? ControllerState::Active : ControllerState::Inactive; + } }; const ControllerState desiredControllerState = state ? ControllerState::Active : ControllerState::Inactive; @@ -532,6 +553,7 @@ namespace Conformance std::map m_actionMap; XrAction m_firstBooleanAction{XR_NULL_PATH}; // Used to detect controller state + XrAction m_firstTrackerAction{XR_NULL_PATH}; // Used to detect controller state bool m_shouldDestroyActionSet = true; // Don't destroy the action set if the test provided one }; diff --git a/src/conformance/framework/xml_test_environment.cpp b/src/conformance/framework/xml_test_environment.cpp index af10edf2..802076bf 100644 --- a/src/conformance/framework/xml_test_environment.cpp +++ b/src/conformance/framework/xml_test_environment.cpp @@ -31,8 +31,8 @@ namespace Conformance .writeAttribute("patch", XR_VERSION_PATCH(cr.apiVersion)) .writeText(to_hex(cr.apiVersion)); xml.scopedElement(CTS_XML_NS_PREFIX_QUALIFIER "results") - .writeAttribute("testSuccessCount", cr.testSuccessCount) - .writeAttribute("testFailureCount", cr.testFailureCount); + .writeAttribute("testSuccessCount", cr.TestSuccessCount()) + .writeAttribute("testFailureCount", cr.TestFailureCount()); if (cr.timedSubmission.IsValid()) { const auto& timing = cr.timedSubmission; using ms = std::chrono::duration; diff --git a/src/conformance/usage/Makefile b/src/conformance/usage/Makefile index 17ebd9eb..067ef105 100644 --- a/src/conformance/usage/Makefile +++ b/src/conformance/usage/Makefile @@ -85,7 +85,7 @@ reflow: ################################################ # Meta builds for releases. Also copies into approximately the right layout for the OpenXR-Registry repo. -REGISTRYOUTDIR = $(GENDIR)/out/registry-release/cts-usage +REGISTRYOUTDIR := $(GENDIR)/out/registry-release/conformance $(REGISTRYOUTDIR)/cts_usage.pdf: $(CTSUSAGEPDF) $(ECHO) "[hexapdf] $(call MAKE_RELATIVE,$@)" diff --git a/src/conformance/usage/asciidoctor-targets.mk b/src/conformance/usage/asciidoctor-targets.mk index 7c81eda0..c14319d8 100644 --- a/src/conformance/usage/asciidoctor-targets.mk +++ b/src/conformance/usage/asciidoctor-targets.mk @@ -14,7 +14,7 @@ ADOCOPTS = --doctype book \ $(ADOC_FAILURE_LEVEL) \ $(ATTRIBOPTS) -SPECREVISION = 1.1.41 +SPECREVISION = 1.1.42 ifneq (,$(strip $(RELEASE))) # No dates or internal commit hashes in release builds for reproducibility diff --git a/src/conformance/usage/selftests.adoc b/src/conformance/usage/selftests.adoc index 222f33f5..bece57e9 100644 --- a/src/conformance/usage/selftests.adoc +++ b/src/conformance/usage/selftests.adoc @@ -14,8 +14,8 @@ with tests that build on them, submission of a CTS results package does not require them. Currently, the only self-tests tagged in this way are for the PBR/glTF rendering subsystem. -They synchronously load very large, artificial test assets, originally from -the "glTF-Sample-Models" repository, to test specific details of the +They (asynchronously) load very large, artificial test assets, originally +from the "glTF-Sample-Models" repository, to test specific details of the renderer. To run the self-tests, commands similar to the following can be used: @@ -50,3 +50,60 @@ conformance_cli "[self_test][interactive]" -G opengl --apiVersion 1.1 --reporter ---- ==== +.Corresponding ADB commands to launch on Android for OpenXR 1.0 +[%collapsible] +==== +Omit any graphics API binding extensions your runtime does not support. +These commands do not match one-to-one with the desktop examples due to +different graphics API availability on Android. + +[source,sh] +---- +adb shell am start-activity -S -n org.khronos.openxr.cts/android.app.NativeActivity --esa args "[self_test][interactive]" -e graphicsPlugin vulkan -e apiVersion 1.0 -e xmlFilename interactive_self_test_vulkan_1_0.xml + +# Wait until tests complete, then retrieve results with +adb pull /sdcard/Android/data/org.khronos.openxr.cts/files/interactive_self_test_vulkan_1_0.xml + + +adb shell am start-activity -S -n org.khronos.openxr.cts/android.app.NativeActivity --esa args "[self_test][interactive]" -e graphicsPlugin vulkan2 -e apiVersion 1.0 -e xmlFilename interactive_self_test_vulkan2_1_0.xml + +# Wait until tests complete, then retrieve results with +adb pull /sdcard/Android/data/org.khronos.openxr.cts/files/interactive_self_test_vulkan2_1_0.xml + + +adb shell am start-activity -S -n org.khronos.openxr.cts/android.app.NativeActivity --esa args "[self_test][interactive]" -e graphicsPlugin opengles -e apiVersion 1.0 -e xmlFilename interactive_self_test_opengles_1_0.xml + +# Wait until tests complete, then retrieve results with +adb pull /sdcard/Android/data/org.khronos.openxr.cts/files/interactive_self_test_opengles_1_0.xml + +---- +==== + +.Corresponding ADB commands to launch on Android for OpenXR 1.1 +[%collapsible] +==== +Omit any graphics API binding extensions your runtime does not support. +These commands do not match one-to-one with the desktop examples due to +different graphics API availability on Android. + +[source,sh] +---- +adb shell am start-activity -S -n org.khronos.openxr.cts/android.app.NativeActivity --esa args "[self_test][interactive]" -e graphicsPlugin vulkan -e apiVersion 1.1 -e xmlFilename interactive_self_test_vulkan_1_1.xml + +# Wait until tests complete, then retrieve results with +adb pull /sdcard/Android/data/org.khronos.openxr.cts/files/interactive_self_test_vulkan_1_1.xml + + +adb shell am start-activity -S -n org.khronos.openxr.cts/android.app.NativeActivity --esa args "[self_test][interactive]" -e graphicsPlugin vulkan2 -e apiVersion 1.1 -e xmlFilename interactive_self_test_vulkan2_1_1.xml + +# Wait until tests complete, then retrieve results with +adb pull /sdcard/Android/data/org.khronos.openxr.cts/files/interactive_self_test_vulkan2_1_1.xml + + +adb shell am start-activity -S -n org.khronos.openxr.cts/android.app.NativeActivity --esa args "[self_test][interactive]" -e graphicsPlugin opengles -e apiVersion 1.1 -e xmlFilename interactive_self_test_opengles_1_1.xml + +# Wait until tests complete, then retrieve results with +adb pull /sdcard/Android/data/org.khronos.openxr.cts/files/interactive_self_test_opengles_1_1.xml + +---- +==== diff --git a/src/conformance/usage/submissions_criteria.adoc b/src/conformance/usage/submissions_criteria.adoc index 415bc112..532e22e5 100644 --- a/src/conformance/usage/submissions_criteria.adoc +++ b/src/conformance/usage/submissions_criteria.adoc @@ -33,14 +33,14 @@ or [source,xml] ---- - + ---- or [source,xml] ---- - + ---- With the results of the entire run summarized in `testsuite` tag (listing @@ -59,8 +59,11 @@ the number of top level test cases): ---- -**Any error or failure when testing core or KHR extension functionality -means your runtime is not conformant.** +[IMPORTANT] +==== +Any error or failure when testing **core or KHR extension** functionality +means your runtime is **not conformant**. Any warnings **may** indicate non-conformance and **must** be explained in the submission package. +==== diff --git a/src/conformance/usage/submissions_package.adoc b/src/conformance/usage/submissions_package.adoc index 6093b7b4..cecb382a 100644 --- a/src/conformance/usage/submissions_package.adoc +++ b/src/conformance/usage/submissions_package.adoc @@ -7,10 +7,10 @@ The submission package **must** include each of the following: -. <> +. <> . The console output produced by the CTS runs above. -. <> -. <> +. <> +. <> When you submit for OpenXR 1.1 conformance, you **must** also submit for each earlier OpenXR minor version that your runtime supports in the same @@ -95,15 +95,14 @@ Can we drop this requirement? ==== Each test suite run starts by printing test configuration data, and ends by -printing a ``Report'' showing details of the runtime and environment +printing a "Report" showing details of the runtime and environment (extensions, etc) used in that run. A few tests produce console output in-between that does not show up in the result XML. It is important to have this data for the interpretation of the results. [[submissions-package-build-info]] -=== Information on the build of conformance used in generating the -results +=== Information on the build of conformance used in generating the results Files containing the result of the commands `git status` and `git log` from the CTS directory: diff --git a/src/conformance/usage/submissions_testing_steps.adoc b/src/conformance/usage/submissions_testing_steps.adoc index a58a6b6f..52d8ba12 100644 --- a/src/conformance/usage/submissions_testing_steps.adoc +++ b/src/conformance/usage/submissions_testing_steps.adoc @@ -354,6 +354,12 @@ For any supported interaction profiles that are valid for `/user/gamepad` rather than `/user/hand/left` and `/user/hand/right`, further filter the tests by specifying the tag `[gamepad]`. +To clarify, if you support providing input for all components of one of the +following interaction profiles, specify them as well. + +* `/interaction_profiles/khr/simple_controller` +* `/interaction_profiles/ext/hand_interaction_ext` +* `/interaction_profiles/ext/eye_gaze_interaction` .Example command lines for OpenXR 1.0 [%collapsible] @@ -362,9 +368,14 @@ Select the interaction profiles to test based on the preceding description. [source,sh] ---- -# Generic profile +## Generic: Simple controller conformance_cli "[actions][interactive]" -G d3d11 -I "khr/simple_controller" --apiVersion 1.0 --reporter ctsxml::out=interactive_action_simple_controller_1_0.xml +## Generic: Hand interaction (whether via hand tracking or controller) +conformance_cli "[actions][interactive]" -G d3d11 -I "ext/hand_interaction_ext" --apiVersion 1.0 --reporter ctsxml::out=interactive_action_ext_hand_interaction_interaction_1_0.xml + +## Generic: Eye gaze +conformance_cli "[actions][interactive]" -G d3d11 -I "ext/eye_gaze_interaction" --apiVersion 1.0 --reporter ctsxml::out=interactive_action_ext_eye_gaze_interaction_1_0.xml # Sample device-associated profiles conformance_cli "[actions][interactive]" -G d3d11 -I "microsoft/motion_controller" --apiVersion 1.0 --reporter ctsxml::out=interactive_action_microsoft_motion_controller_1_0.xml @@ -382,9 +393,14 @@ conformance_cli "[actions][interactive]" -G d3d11 -I "htc/vive_controller" --api Select the interaction profiles to test based on the preceding description. [source,sh] ---- -# Generic profile +## Generic: Simple controller conformance_cli "[actions][interactive]" -G d3d11 -I "khr/simple_controller" --apiVersion 1.1 --reporter ctsxml::out=interactive_action_simple_controller_1_1.xml +## Generic: Hand interaction (whether via hand tracking or controller) +conformance_cli "[actions][interactive]" -G d3d11 -I "ext/hand_interaction_ext" --apiVersion 1.1 --reporter ctsxml::out=interactive_action_ext_hand_interaction_interaction_1_1.xml + +## Generic: Eye gaze +conformance_cli "[actions][interactive]" -G d3d11 -I "ext/eye_gaze_interaction" --apiVersion 1.1 --reporter ctsxml::out=interactive_action_ext_eye_gaze_interaction_1_1.xml # Sample device-associated profiles conformance_cli "[actions][interactive]" -G d3d11 -I "microsoft/motion_controller" --apiVersion 1.1 --reporter ctsxml::out=interactive_action_microsoft_motion_controller_1_1.xml @@ -423,13 +439,27 @@ Select the interaction profiles to test based on the preceding description. [source,sh] ---- -## Generic profile +## Generic: Simple controller adb shell am start-activity -S -n org.khronos.openxr.cts/android.app.NativeActivity --esa args "[actions][interactive],-I,khr/simple_controller" -e apiVersion 1.0 -e xmlFilename interactive_action_simple_controller_1_0.xml # Wait until tests complete, then retrieve results with adb pull /sdcard/Android/data/org.khronos.openxr.cts/files/interactive_action_simple_controller_1_0.xml +## Generic: Hand interaction (whether via hand tracking or controller) +adb shell am start-activity -S -n org.khronos.openxr.cts/android.app.NativeActivity --esa args "[actions][interactive],-I,ext/hand_interaction_ext" -e apiVersion 1.0 -e xmlFilename interactive_action_ext_hand_interaction_1_0.xml + +# Wait until tests complete, then retrieve results with +adb pull /sdcard/Android/data/org.khronos.openxr.cts/files/interactive_action_ext_hand_interaction_1_0.xml + + +## Generic: Eye gaze interaction +adb shell am start-activity -S -n org.khronos.openxr.cts/android.app.NativeActivity --esa args "[actions][interactive],-I,ext/eye_gaze_interaction" -e apiVersion 1.0 -e xmlFilename interactive_action_ext_eye_gaze_interaction_1_0.xml + +# Wait until tests complete, then retrieve results with +adb pull /sdcard/Android/data/org.khronos.openxr.cts/files/interactive_action_ext_eye_gaze_interaction_1_0.xml + + ## Sample device-associated profile adb shell am start-activity -S -n org.khronos.openxr.cts/android.app.NativeActivity --esa args "[actions][interactive],-I,oculus/touch_controller" -e apiVersion 1.0 -e xmlFilename interactive_action_oculus_touch_controller_1_0.xml @@ -451,13 +481,27 @@ adb pull /sdcard/Android/data/org.khronos.openxr.cts/files/interactive_action_mi ==== [source,sh] ---- -## Generic profile +## Generic: Simple controller adb shell am start-activity -S -n org.khronos.openxr.cts/android.app.NativeActivity --esa args "[actions][interactive],-I,khr/simple_controller" -e apiVersion 1.1 -e xmlFilename interactive_action_simple_controller_1_1.xml # Wait until tests complete, then retrieve results with adb pull /sdcard/Android/data/org.khronos.openxr.cts/files/interactive_action_simple_controller_1_1.xml +## Generic: Hand interaction (whether via hand tracking or controller) +adb shell am start-activity -S -n org.khronos.openxr.cts/android.app.NativeActivity --esa args "[actions][interactive],-I,ext/hand_interaction_ext" -e apiVersion 1.1 -e xmlFilename interactive_action_ext_hand_interaction_1_1.xml + +# Wait until tests complete, then retrieve results with +adb pull /sdcard/Android/data/org.khronos.openxr.cts/files/interactive_action_ext_hand_interaction_1_1.xml + + +## Generic: Eye gaze interaction +adb shell am start-activity -S -n org.khronos.openxr.cts/android.app.NativeActivity --esa args "[actions][interactive],-I,ext/eye_gaze_interaction" -e apiVersion 1.1 -e xmlFilename interactive_action_ext_eye_gaze_interaction_1_1.xml + +# Wait until tests complete, then retrieve results with +adb pull /sdcard/Android/data/org.khronos.openxr.cts/files/interactive_action_ext_eye_gaze_interaction_1_1.xml + + ## Sample device-associated profile adb shell am start-activity -S -n org.khronos.openxr.cts/android.app.NativeActivity --esa args "[actions][interactive],-I,oculus/touch_controller" -e apiVersion 1.1 -e xmlFilename interactive_action_oculus_touch_controller_1_1.xml diff --git a/src/conformance/utilities/CMakeLists.txt b/src/conformance/utilities/CMakeLists.txt index aa539975..9a5e4ea1 100644 --- a/src/conformance/utilities/CMakeLists.txt +++ b/src/conformance/utilities/CMakeLists.txt @@ -59,6 +59,7 @@ add_library( string_utils.cpp stringification.cpp swapchain_format_data.cpp + swapchain_parameters.cpp throw_helpers.cpp types_and_constants.cpp utils.cpp diff --git a/src/conformance/utilities/bitmask_to_string.h b/src/conformance/utilities/bitmask_to_string.h index 06dec935..1b69bc85 100644 --- a/src/conformance/utilities/bitmask_to_string.h +++ b/src/conformance/utilities/bitmask_to_string.h @@ -22,7 +22,8 @@ namespace Conformance _(XrSwapchainUsageFlags) \ _(XrCompositionLayerFlags) \ _(XrViewStateFlags) \ - _(XrSpaceLocationFlags) + _(XrSpaceLocationFlags) \ + _(XrInputSourceLocalizedNameFlags) namespace detail { diff --git a/src/conformance/utilities/d3d_common.cpp b/src/conformance/utilities/d3d_common.cpp index e4268e5d..82876409 100644 --- a/src/conformance/utilities/d3d_common.cpp +++ b/src/conformance/utilities/d3d_common.cpp @@ -90,141 +90,152 @@ namespace Conformance SwapchainTestMap& GetDxgiSwapchainTestMap() { + using namespace SwapchainFormat; + static SwapchainTestMap dxgiSwapchainTestMap{ - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32B32A32_TYPELESS).Typeless().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32B32A32_FLOAT).ExpectedFormat(DXGI_FORMAT_R32G32B32A32_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32B32A32_UINT).ExpectedFormat(DXGI_FORMAT_R32G32B32A32_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32B32A32_SINT).ExpectedFormat(DXGI_FORMAT_R32G32B32A32_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32B32A32_TYPELESS).rgba().Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32B32A32_FLOAT).rgba().ExpectedFormat(DXGI_FORMAT_R32G32B32A32_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32B32A32_UINT).rgba().Int(u32).ExpectedFormat(DXGI_FORMAT_R32G32B32A32_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32B32A32_SINT).rgba().Int(s32).ExpectedFormat(DXGI_FORMAT_R32G32B32A32_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32B32_TYPELESS).Typeless().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32B32_FLOAT).ExpectedFormat(DXGI_FORMAT_R32G32B32_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32B32_UINT).ExpectedFormat(DXGI_FORMAT_R32G32B32_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32B32_SINT).ExpectedFormat(DXGI_FORMAT_R32G32B32_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32B32_TYPELESS).rgb().Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32B32_FLOAT).rgb().ExpectedFormat(DXGI_FORMAT_R32G32B32_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32B32_UINT).rgb().Int(u32).ExpectedFormat(DXGI_FORMAT_R32G32B32_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32B32_SINT).rgb().Int(s32).ExpectedFormat(DXGI_FORMAT_R32G32B32_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16B16A16_TYPELESS).Typeless().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16B16A16_FLOAT).ExpectedFormat(DXGI_FORMAT_R16G16B16A16_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16B16A16_UINT).ExpectedFormat(DXGI_FORMAT_R16G16B16A16_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16B16A16_SINT).ExpectedFormat(DXGI_FORMAT_R16G16B16A16_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16B16A16_UNORM).ExpectedFormat(DXGI_FORMAT_R16G16B16A16_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16B16A16_SNORM).ExpectedFormat(DXGI_FORMAT_R16G16B16A16_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16B16A16_TYPELESS).rgba().Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16B16A16_FLOAT).rgba().ExpectedFormat(DXGI_FORMAT_R16G16B16A16_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16B16A16_UINT).rgba().Int(u16).ExpectedFormat(DXGI_FORMAT_R16G16B16A16_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16B16A16_SINT).rgba().Int(s16).ExpectedFormat(DXGI_FORMAT_R16G16B16A16_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16B16A16_UNORM).rgba().ExpectedFormat(DXGI_FORMAT_R16G16B16A16_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16B16A16_SNORM).rgba().ExpectedFormat(DXGI_FORMAT_R16G16B16A16_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32_TYPELESS).Typeless().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32_FLOAT).ExpectedFormat(DXGI_FORMAT_R32G32_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32_UINT).ExpectedFormat(DXGI_FORMAT_R32G32_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32_SINT).ExpectedFormat(DXGI_FORMAT_R32G32_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32_TYPELESS).rg().Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32_FLOAT).rg().ExpectedFormat(DXGI_FORMAT_R32G32_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32_UINT).rg().Int(u32).ExpectedFormat(DXGI_FORMAT_R32G32_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32_SINT).rg().Int(s32).ExpectedFormat(DXGI_FORMAT_R32G32_TYPELESS).Build(), // 32bit channel, 8bit channel, 24bit ignored. All typeless. - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G8X24_TYPELESS).Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G8X24_TYPELESS).rg().Typeless().Build(), // 32bit float depth, 8 bit uint stencil, 24bit ignored. XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_D32_FLOAT_S8X24_UINT).ExpectedFormat(DXGI_FORMAT_R32G8X24_TYPELESS).DepthStencil().Build(), // 32bit float red, 8bit ignored, 24bit ignored. Not typeless because used parts are typed? - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS).Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS).r().Typeless().Build(), // typeless unused 32bit component, 8bit uint green, and 24bit unused. Not typeless because used parts are typed? - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_X32_TYPELESS_G8X24_UINT).ExpectedFormat(DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS).Build(), - - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R10G10B10A2_TYPELESS).Typeless().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R10G10B10A2_UNORM).ExpectedFormat(DXGI_FORMAT_R10G10B10A2_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R10G10B10A2_UINT).ExpectedFormat(DXGI_FORMAT_R10G10B10A2_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_X32_TYPELESS_G8X24_UINT) + .Color(g) + .Int(u8) + .ExpectedFormat(DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS) + .Build(), + + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R10G10B10A2_TYPELESS).rgba().Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R10G10B10A2_UNORM).rgba().ExpectedFormat(DXGI_FORMAT_R10G10B10A2_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R10G10B10A2_UINT) + .rgba() + .Int(uRGB10A2) + .ExpectedFormat(DXGI_FORMAT_R10G10B10A2_TYPELESS) + .Build(), // This doesn't have a typeless equivalent, so it's created as-is by the runtime. - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R11G11B10_FLOAT).NotMutable().Build(), - - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8B8A8_TYPELESS).Typeless().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8B8A8_UNORM).ExpectedFormat(DXGI_FORMAT_R8G8B8A8_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8B8A8_UNORM_SRGB).ExpectedFormat(DXGI_FORMAT_R8G8B8A8_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8B8A8_UINT).ExpectedFormat(DXGI_FORMAT_R8G8B8A8_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8B8A8_SINT).ExpectedFormat(DXGI_FORMAT_R8G8B8A8_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8B8A8_SNORM).ExpectedFormat(DXGI_FORMAT_R8G8B8A8_TYPELESS).Build(), - - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16_TYPELESS).Typeless().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16_FLOAT).ExpectedFormat(DXGI_FORMAT_R16G16_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16_UINT).ExpectedFormat(DXGI_FORMAT_R16G16_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16_SINT).ExpectedFormat(DXGI_FORMAT_R16G16_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16_UNORM).ExpectedFormat(DXGI_FORMAT_R16G16_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16_SNORM).ExpectedFormat(DXGI_FORMAT_R16G16_TYPELESS).Build(), - - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32_TYPELESS).Typeless().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32_FLOAT).ExpectedFormat(DXGI_FORMAT_R32_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R11G11B10_FLOAT).rgb().NotMutable().Build(), + + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8B8A8_TYPELESS).rgba().Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8B8A8_UNORM).rgba().ExpectedFormat(DXGI_FORMAT_R8G8B8A8_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8B8A8_UNORM_SRGB).rgba().ExpectedFormat(DXGI_FORMAT_R8G8B8A8_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8B8A8_UINT).rgba().Int(u8).ExpectedFormat(DXGI_FORMAT_R8G8B8A8_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8B8A8_SINT).rgba().Int(s8).ExpectedFormat(DXGI_FORMAT_R8G8B8A8_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8B8A8_SNORM).rgba().ExpectedFormat(DXGI_FORMAT_R8G8B8A8_TYPELESS).Build(), + + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16_TYPELESS).rg().Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16_FLOAT).rg().ExpectedFormat(DXGI_FORMAT_R16G16_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16_UINT).rg().Int(u16).ExpectedFormat(DXGI_FORMAT_R16G16_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16_SINT).rg().Int(s16).ExpectedFormat(DXGI_FORMAT_R16G16_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16_UNORM).rg().ExpectedFormat(DXGI_FORMAT_R16G16_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16_SNORM).rg().ExpectedFormat(DXGI_FORMAT_R16G16_TYPELESS).Build(), + + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32_TYPELESS).r().Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32_FLOAT).r().ExpectedFormat(DXGI_FORMAT_R32_TYPELESS).Build(), XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_D32_FLOAT).ExpectedFormat(DXGI_FORMAT_R32_TYPELESS).Depth().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32_UINT).ExpectedFormat(DXGI_FORMAT_R32_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32_SINT).ExpectedFormat(DXGI_FORMAT_R32_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32_UINT).r().Int(u32).ExpectedFormat(DXGI_FORMAT_R32_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32_SINT).r().Int(s32).ExpectedFormat(DXGI_FORMAT_R32_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R24G8_TYPELESS).Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R24G8_TYPELESS).rg().Typeless().Build(), XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_D24_UNORM_S8_UINT).ExpectedFormat(DXGI_FORMAT_R24G8_TYPELESS).Depth().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R24_UNORM_X8_TYPELESS).ExpectedFormat(DXGI_FORMAT_R24G8_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_X24_TYPELESS_G8_UINT).ExpectedFormat(DXGI_FORMAT_R24G8_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R24_UNORM_X8_TYPELESS).r().ExpectedFormat(DXGI_FORMAT_R24G8_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_X24_TYPELESS_G8_UINT).Color(g).Int(u8).ExpectedFormat(DXGI_FORMAT_R24G8_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8_TYPELESS).Typeless().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8_UINT).ExpectedFormat(DXGI_FORMAT_R8G8_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8_SINT).ExpectedFormat(DXGI_FORMAT_R8G8_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8_UNORM).ExpectedFormat(DXGI_FORMAT_R8G8_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8_SNORM).ExpectedFormat(DXGI_FORMAT_R8G8_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8_TYPELESS).rg().Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8_UINT).rg().Int(u8).ExpectedFormat(DXGI_FORMAT_R8G8_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8_SINT).rg().Int(s8).ExpectedFormat(DXGI_FORMAT_R8G8_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8_UNORM).rg().ExpectedFormat(DXGI_FORMAT_R8G8_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8_SNORM).rg().ExpectedFormat(DXGI_FORMAT_R8G8_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16_TYPELESS).Typeless().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16_FLOAT).ExpectedFormat(DXGI_FORMAT_R16_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16_TYPELESS).r().Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16_FLOAT).r().ExpectedFormat(DXGI_FORMAT_R16_TYPELESS).Build(), XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_D16_UNORM).ExpectedFormat(DXGI_FORMAT_R16_TYPELESS).Depth().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16_UINT).ExpectedFormat(DXGI_FORMAT_R16_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16_SINT).ExpectedFormat(DXGI_FORMAT_R16_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16_UNORM).ExpectedFormat(DXGI_FORMAT_R16_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16_SNORM).ExpectedFormat(DXGI_FORMAT_R16_TYPELESS).Build(), - - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8_TYPELESS).Typeless().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8_UINT).ExpectedFormat(DXGI_FORMAT_R8_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8_SINT).ExpectedFormat(DXGI_FORMAT_R8_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8_UNORM).ExpectedFormat(DXGI_FORMAT_R8_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8_SNORM).ExpectedFormat(DXGI_FORMAT_R8_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_A8_UNORM).ExpectedFormat(DXGI_FORMAT_R8_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16_UINT).r().Int(u16).ExpectedFormat(DXGI_FORMAT_R16_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16_SINT).r().Int(s16).ExpectedFormat(DXGI_FORMAT_R16_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16_UNORM).r().ExpectedFormat(DXGI_FORMAT_R16_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16_SNORM).r().ExpectedFormat(DXGI_FORMAT_R16_TYPELESS).Build(), + + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8_TYPELESS).r().Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8_UINT).r().Int(u8).ExpectedFormat(DXGI_FORMAT_R8_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8_SINT).r().Int(s8).ExpectedFormat(DXGI_FORMAT_R8_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8_UNORM).r().ExpectedFormat(DXGI_FORMAT_R8_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8_SNORM).r().ExpectedFormat(DXGI_FORMAT_R8_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_A8_UNORM).Color(a).ExpectedFormat(DXGI_FORMAT_R8_TYPELESS).Build(), // These don't have typeless equivalents, so they are created as-is by the runtime. - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R1_UNORM).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R9G9B9E5_SHAREDEXP).NotMutable().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8_B8G8_UNORM).NotMutable().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_G8R8_G8B8_UNORM).NotMutable().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R1_UNORM).r().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R9G9B9E5_SHAREDEXP).rgb().NotMutable().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8_B8G8_UNORM).rgb().NotMutable().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_G8R8_G8B8_UNORM).rgb().NotMutable().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC1_TYPELESS).Compressed().Typeless().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC1_UNORM).Compressed().ExpectedFormat(DXGI_FORMAT_BC1_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC1_UNORM_SRGB).Compressed().ExpectedFormat(DXGI_FORMAT_BC1_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC1_TYPELESS).rgba().Compressed().Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC1_UNORM).rgba().Compressed().ExpectedFormat(DXGI_FORMAT_BC1_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC1_UNORM_SRGB).rgba().Compressed().ExpectedFormat(DXGI_FORMAT_BC1_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC2_TYPELESS).Compressed().Typeless().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC2_UNORM).Compressed().ExpectedFormat(DXGI_FORMAT_BC2_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC2_UNORM_SRGB).Compressed().ExpectedFormat(DXGI_FORMAT_BC2_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC2_TYPELESS).rgba().Compressed().Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC2_UNORM).rgba().Compressed().ExpectedFormat(DXGI_FORMAT_BC2_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC2_UNORM_SRGB).rgba().Compressed().ExpectedFormat(DXGI_FORMAT_BC2_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC3_TYPELESS).Compressed().Typeless().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC3_UNORM).Compressed().ExpectedFormat(DXGI_FORMAT_BC3_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC3_UNORM_SRGB).Compressed().ExpectedFormat(DXGI_FORMAT_BC3_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC3_TYPELESS).rgba().Compressed().Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC3_UNORM).rgba().Compressed().ExpectedFormat(DXGI_FORMAT_BC3_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC3_UNORM_SRGB).rgba().Compressed().ExpectedFormat(DXGI_FORMAT_BC3_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC4_TYPELESS).Compressed().Typeless().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC4_UNORM).Compressed().ExpectedFormat(DXGI_FORMAT_BC4_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC4_SNORM).Compressed().ExpectedFormat(DXGI_FORMAT_BC4_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC4_TYPELESS).r().Compressed().Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC4_UNORM).r().Compressed().ExpectedFormat(DXGI_FORMAT_BC4_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC4_SNORM).r().Compressed().ExpectedFormat(DXGI_FORMAT_BC4_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC5_TYPELESS).Compressed().Typeless().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC5_UNORM).Compressed().ExpectedFormat(DXGI_FORMAT_BC5_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC5_SNORM).Compressed().ExpectedFormat(DXGI_FORMAT_BC5_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC5_TYPELESS).rg().Compressed().Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC5_UNORM).rg().Compressed().ExpectedFormat(DXGI_FORMAT_BC5_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC5_SNORM).rg().Compressed().ExpectedFormat(DXGI_FORMAT_BC5_TYPELESS).Build(), // These don't have typeless equivalents, so they are created as-is by the runtime. - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_B5G6R5_UNORM).NotMutable().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_B5G5R5A1_UNORM).NotMutable().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM).NotMutable().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_B5G6R5_UNORM).rgb().NotMutable().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_B5G5R5A1_UNORM).rgba().NotMutable().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM).rgba().NotMutable().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_B8G8R8A8_TYPELESS).Typeless().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_B8G8R8A8_UNORM).ExpectedFormat(DXGI_FORMAT_B8G8R8A8_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_B8G8R8A8_UNORM_SRGB).ExpectedFormat(DXGI_FORMAT_B8G8R8A8_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_B8G8R8A8_TYPELESS).rgba().Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_B8G8R8A8_UNORM).rgba().ExpectedFormat(DXGI_FORMAT_B8G8R8A8_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_B8G8R8A8_UNORM_SRGB).rgba().ExpectedFormat(DXGI_FORMAT_B8G8R8A8_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_B8G8R8X8_TYPELESS).Typeless().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_B8G8R8X8_UNORM).ExpectedFormat(DXGI_FORMAT_B8G8R8X8_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_B8G8R8X8_UNORM_SRGB).ExpectedFormat(DXGI_FORMAT_B8G8R8X8_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_B8G8R8X8_TYPELESS).rgb().Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_B8G8R8X8_UNORM).rgb().ExpectedFormat(DXGI_FORMAT_B8G8R8X8_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_B8G8R8X8_UNORM_SRGB).rgb().ExpectedFormat(DXGI_FORMAT_B8G8R8X8_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC6H_TYPELESS).Compressed().Typeless().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC6H_UF16).Compressed().ExpectedFormat(DXGI_FORMAT_BC6H_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC6H_SF16).Compressed().ExpectedFormat(DXGI_FORMAT_BC6H_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC6H_TYPELESS).rgb().Compressed().Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC6H_UF16).rgb().Compressed().ExpectedFormat(DXGI_FORMAT_BC6H_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC6H_SF16).rgb().Compressed().ExpectedFormat(DXGI_FORMAT_BC6H_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC7_TYPELESS).Compressed().Typeless().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC7_UNORM).Compressed().ExpectedFormat(DXGI_FORMAT_BC7_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC7_UNORM_SRGB).Compressed().ExpectedFormat(DXGI_FORMAT_BC7_TYPELESS).Build(), + // The alpha channel is optional for these + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC7_TYPELESS).rgb().Compressed().Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC7_UNORM).rgb().Compressed().ExpectedFormat(DXGI_FORMAT_BC7_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC7_UNORM_SRGB).rgb().Compressed().ExpectedFormat(DXGI_FORMAT_BC7_TYPELESS).Build(), // This doesn't have a typeless equivalent, so it's created as-is by the runtime. - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_B4G4R4A4_UNORM).NotMutable().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_B4G4R4A4_UNORM).rgba().NotMutable().Build(), }; diff --git a/src/conformance/utilities/swapchain_format_data.cpp b/src/conformance/utilities/swapchain_format_data.cpp index 49ffcb8f..59be12ae 100644 --- a/src/conformance/utilities/swapchain_format_data.cpp +++ b/src/conformance/utilities/swapchain_format_data.cpp @@ -246,6 +246,13 @@ namespace Conformance SwapchainCreateTestParameters SwapchainFormatData::ToTestParameters() const { + if (!m_colorFormat && !m_depthFormat && !m_stencilFormat) { + std::ostringstream oss; + oss << "Format " << m_imageFormatName << " (" << to_hex(m_imageFormat) << ")" + << "has no known aspect (color, depth, stencil);" + " if this is correct, code may need to be updated."; + throw std::logic_error(oss.str()); + } span mipCountVector{kArrayOf1.begin(), kArrayOf1.end()}; if (m_colorFormat && !m_compressedFormat) { @@ -253,16 +260,17 @@ namespace Conformance } span arrayCountVector{kArrayOf1And2.begin(), kArrayOf1And2.end()}; + namespace FormatFlags = SwapchainFormat::Flags; return SwapchainCreateTestParameters{ - std::string{m_imageFormatName}, // - m_isTypeless ? SwapchainFormatMutability::MUTABLE : SwapchainFormatMutability::IMMUTABLE, // - m_supportsMutableFormat ? SwapchainFormatSupportsMutability::MUT_SUPPORT - : SwapchainFormatSupportsMutability::NO_MUT_SUPPORT, // - m_colorFormat ? SwapchainFormatIsColor::COLOR : SwapchainFormatIsColor::NON_COLOR, - m_compressedFormat ? SwapchainFormatIsCompressed::COMPRESSED : SwapchainFormatIsCompressed::UNCOMPRESSED, - m_compressedFormat ? SwapchainFormatSupportsRendering::NO_RENDERING_SUPPORT - : SwapchainFormatSupportsRendering::RENDERING_SUPPORT, + std::string{m_imageFormatName}, // + m_isTypeless ? FormatFlags::Mutability::MUTABLE : FormatFlags::Mutability::IMMUTABLE, // + m_supportsMutableFormat ? FormatFlags::SupportsMutability::MUT_SUPPORT : FormatFlags::SupportsMutability::NO_MUT_SUPPORT, // + m_colorFormat ? FormatFlags::IsColor::COLOR : FormatFlags::IsColor::NON_COLOR, + m_compressedFormat ? FormatFlags::IsCompressed::COMPRESSED : FormatFlags::IsCompressed::UNCOMPRESSED, + m_compressedFormat ? FormatFlags::SupportsRendering::NO_RENDERING_SUPPORT : FormatFlags::SupportsRendering::RENDERING_SUPPORT, m_expectedCreatedImageFormat, + m_colorComponents, + m_integerRange, {m_usageFlagsVector.begin(), m_usageFlagsVector.end()}, {m_createFlagsVector.begin(), m_createFlagsVector.end()}, {arrayCountVector.begin(), arrayCountVector.end()}, diff --git a/src/conformance/utilities/swapchain_format_data.h b/src/conformance/utilities/swapchain_format_data.h index 46bac4a0..18be78a6 100644 --- a/src/conformance/utilities/swapchain_format_data.h +++ b/src/conformance/utilities/swapchain_format_data.h @@ -30,7 +30,6 @@ namespace Conformance { using nonstd::span; - /// Minimal data structure storing details about a swapchain image format. /// /// May eventually replace @ref SwapchainImageTestParam @@ -145,7 +144,10 @@ namespace Conformance bool m_supportsMutableFormat = true; /// Whether the format is a color-specific format - bool m_colorFormat = true; + bool m_colorFormat = false; + + /// Which color components the format resolves to (including alpha) + SwapchainFormat::RawColorComponents m_colorComponents = SwapchainFormat::RawColorComponents::Unknown; /// Whether the format can be use as a depth buffer: implies not color bool m_depthFormat = false; @@ -156,6 +158,9 @@ namespace Conformance /// Whether the format is a compressed format (and thus cannot be rendered to) bool m_compressedFormat = false; + /// The signed-ness and integer range of this format. + SwapchainFormat::ColorIntegerRange m_integerRange = SwapchainFormat::ColorIntegerRange::NoIntegerColor; + /// XrSwapchainUsageFlags to exercise for this format. /// Defaults to all combinations, including 0, of the core flags. /// @todo Stop making so many copies of this, generate it from the other data instead @@ -188,6 +193,60 @@ namespace Conformance using Self = SwapchainCreateTestParametersBuilder; + /// Mark this as supporting color buffer usage with the channels/components @param components + /// + /// Also sets some default usage flags. + Self& Color(SwapchainFormat::RawColorComponents components) + { + m_data.m_colorFormat = true; + m_data.m_colorComponents = components; + UpdateDefaultUsageFlagVector(); + return *this; + } + + /// Mark this as supporting color buffer usage with the channels R + /// + /// Also sets some default usage flags. + Self& r() + { + using C = SwapchainFormat::RawColorComponents; + return Color(C::r); + } + + /// Mark this as supporting color buffer usage with the channels R, G + /// + /// Also sets some default usage flags. + Self& rg() + { + using C = SwapchainFormat::RawColorComponents; + return Color((C)(C::r | C::g)); + } + + /// Mark this as supporting color buffer usage with the channels R, G, B + /// + /// Also sets some default usage flags. + Self& rgb() + { + using C = SwapchainFormat::RawColorComponents; + return Color((C)(C::r | C::g | C::b)); + } + + /// Mark this as supporting color buffer usage with the channels R, G, B, A + /// + /// Also sets some default usage flags. + Self& rgba() + { + using C = SwapchainFormat::RawColorComponents; + return Color((C)(C::r | C::g | C::b | C::a)); + } + + /// Mark this as a color integer format with the color/alpha depth specified + Self& Int(SwapchainFormat::ColorIntegerRange range) + { + m_data.m_integerRange = range; + return *this; + } + /// Mark this as not supporting "unordered access" Self& NoUnorderedAccess() { @@ -214,7 +273,7 @@ namespace Conformance Self& Depth() { m_data.m_depthFormat = true; - NotColor(); + UpdateDefaultUsageFlagVector(); return *this; } @@ -224,7 +283,7 @@ namespace Conformance Self& Stencil() { m_data.m_stencilFormat = true; - NotColor(); + UpdateDefaultUsageFlagVector(); return *this; } @@ -237,7 +296,7 @@ namespace Conformance { m_data.m_stencilFormat = true; m_data.m_depthFormat = true; - NotColor(); + UpdateDefaultUsageFlagVector(); return *this; } @@ -292,12 +351,6 @@ namespace Conformance std::string ToString() const; private: - void NotColor() - { - m_data.m_colorFormat = false; - UpdateDefaultUsageFlagVector(); - } - void UpdateDefaultUsageFlagVector(); SwapchainFormatData m_data; }; diff --git a/src/conformance/utilities/swapchain_parameters.cpp b/src/conformance/utilities/swapchain_parameters.cpp new file mode 100644 index 00000000..8f93d0df --- /dev/null +++ b/src/conformance/utilities/swapchain_parameters.cpp @@ -0,0 +1,63 @@ +// 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 "swapchain_parameters.h" + +namespace Conformance +{ + namespace SwapchainFormat + { + uint8_t ColorIntegerRangeBits(ColorIntegerRange colorIntegerRange) + { + switch (colorIntegerRange) { + case ColorIntegerRange::NoIntegerColor: + throw std::logic_error("ColorIntegerRangeBits only valid for integer colors"); + case ColorIntegerRange::u8: + case ColorIntegerRange::s8: + return 8; + case ColorIntegerRange::u16: + case ColorIntegerRange::s16: + return 16; + case ColorIntegerRange::u32: + case ColorIntegerRange::s32: + return 32; + case ColorIntegerRange::uRGB10A2: + return 10; + default: + throw std::logic_error("Missing case in implementation of ColorIntegerRangeBits"); + } + } + + bool ColorIntegerRangeIsSigned(ColorIntegerRange colorIntegerRange) + { + switch (colorIntegerRange) { + case ColorIntegerRange::NoIntegerColor: + throw std::logic_error("ColorIntegerRangeIsSigned only valid for integer colors"); + case ColorIntegerRange::u8: + case ColorIntegerRange::u16: + case ColorIntegerRange::u32: + case ColorIntegerRange::uRGB10A2: + return false; + case ColorIntegerRange::s8: + case ColorIntegerRange::s16: + case ColorIntegerRange::s32: + return true; + default: + throw std::logic_error("Missing case in implementation of ColorIntegerRangeIsSigned"); + } + } + } // namespace SwapchainFormat +} // namespace Conformance diff --git a/src/conformance/utilities/swapchain_parameters.h b/src/conformance/utilities/swapchain_parameters.h index a16db1c7..b9c61dd6 100644 --- a/src/conformance/utilities/swapchain_parameters.h +++ b/src/conformance/utilities/swapchain_parameters.h @@ -22,31 +22,66 @@ namespace Conformance { - enum SwapchainFormatMutability + namespace SwapchainFormat { - IMMUTABLE, - MUTABLE, - }; - enum SwapchainFormatSupportsMutability - { - NO_MUT_SUPPORT, - MUT_SUPPORT, - }; - enum SwapchainFormatIsColor - { - NON_COLOR, - COLOR, - }; - enum SwapchainFormatIsCompressed - { - UNCOMPRESSED, - COMPRESSED, - }; - enum SwapchainFormatSupportsRendering - { - NO_RENDERING_SUPPORT, - RENDERING_SUPPORT, - }; + namespace Flags + { + enum Mutability + { + IMMUTABLE, + MUTABLE, + }; + enum SupportsMutability + { + NO_MUT_SUPPORT, + MUT_SUPPORT, + }; + enum IsColor + { + NON_COLOR, + COLOR, + }; + enum IsCompressed + { + UNCOMPRESSED, + COMPRESSED, + }; + enum SupportsRendering + { + NO_RENDERING_SUPPORT, + RENDERING_SUPPORT, + }; + } // namespace Flags + + /// The components defined by the texture during sampling + /// (i.e. not just returned as the default value) + enum RawColorComponents : uint8_t + { + Unknown = 0, + r = 1 << 0, + g = 1 << 1, + b = 1 << 2, + a = 1 << 3, + }; + + /// Textures whose output is interpreted as an integer + /// and not mapped to a fixed- or floating-point value. + /// This does not yet account for integer aspects of + /// non-color formats, but could be renamed and extended. + enum ColorIntegerRange + { + NoIntegerColor = 0, + u8, + s8, + u16, + s16, + u32, + s32, + uRGB10A2, + }; + uint8_t ColorIntegerRangeBits(ColorIntegerRange colorIntegerRange); + bool ColorIntegerRangeIsSigned(ColorIntegerRange colorIntegerRange); + } // namespace SwapchainFormat /// Defines XrSwapchainCreateInfo test parameters for a single given image format. /// Sometimes values are zeroed, for the case that use of them is invalid or unsupportable. @@ -58,23 +93,29 @@ namespace Conformance std::string imageFormatName; /// Whether the image format is a mutable (a.k.a. typeless) type. - SwapchainFormatMutability mutableFormat; + SwapchainFormat::Flags::Mutability mutableFormat; /// Whether the image format supports creation with XR_SWAPCHAIN_USAGE_MUTABLE_FORMAT_BIT. - SwapchainFormatSupportsMutability supportsMutableFormat; + SwapchainFormat::Flags::SupportsMutability supportsMutableFormat; /// Whether the format is a color-specific format, as opposed to a depth-specific format. - SwapchainFormatIsColor colorFormat; + SwapchainFormat::Flags::IsColor colorFormat; /// Whether the format is a compressed format. - SwapchainFormatIsCompressed compressedFormat; + SwapchainFormat::Flags::IsCompressed compressedFormat; /// Whether the image format can be rendered to. - SwapchainFormatSupportsRendering supportsRendering; + SwapchainFormat::Flags::SupportsRendering supportsRendering; /// The graphics-specific created image format returned by xrCreateSwapchain, may be different from imageFormat in some cases. int64_t expectedCreatedImageFormat; + /// The color components that, when sampled, will not just be set to default values. + SwapchainFormat::RawColorComponents colorComponents; + + /// For integer (not floating point or normalised) color images, the bit depth of each color/alpha component. + SwapchainFormat::ColorIntegerRange colorIntegerRange; + /// XrSwapchainUsageFlags to exercise for this format. std::vector usageFlagsVector; diff --git a/src/external/stb/stb_image.h b/src/external/stb/stb_image.h index d60371b9..9eedabed 100644 --- a/src/external/stb/stb_image.h +++ b/src/external/stb/stb_image.h @@ -1,4 +1,4 @@ -/* stb_image - v2.27 - public domain image loader - http://nothings.org/stb +/* stb_image - v2.30 - public domain image loader - http://nothings.org/stb no warranty implied; use at your own risk Do this: @@ -48,6 +48,9 @@ LICENSE RECENT REVISION HISTORY: + 2.30 (2024-05-31) avoid erroneous gcc warning + 2.29 (2023-05-xx) optimizations + 2.28 (2023-01-29) many error fixes, security errors, just tons of stuff 2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes 2.26 (2020-07-13) many minor fixes 2.25 (2020-02-02) fix warnings @@ -108,7 +111,7 @@ RECENT REVISION HISTORY: Cass Everitt Ryamond Barbiero github:grim210 Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus - Josh Tobin Matthew Gregan github:poppolopoppo + Josh Tobin Neil Bickford Matthew Gregan github:poppolopoppo Julian Raschke Gregory Mullen Christian Floisand github:darealshinji Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007 Brad Weinberger Matvey Cherevko github:mosra @@ -140,7 +143,7 @@ RECENT REVISION HISTORY: // // ... x = width, y = height, n = # 8-bit components per pixel ... // // ... replace '0' with '1'..'4' to force that many components per pixel // // ... but 'n' will always be the number that it would have been if you said 0 -// stbi_image_free(data) +// stbi_image_free(data); // // Standard parameters: // int *x -- outputs image width in pixels @@ -635,7 +638,7 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const ch #endif #endif -#ifdef _MSC_VER +#if defined(_MSC_VER) || defined(__SYMBIAN32__) typedef unsigned short stbi__uint16; typedef signed short stbi__int16; typedef unsigned int stbi__uint32; @@ -1063,6 +1066,23 @@ static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) } #endif +// returns 1 if the sum of two signed ints is valid (between -2^31 and 2^31-1 inclusive), 0 on overflow. +static int stbi__addints_valid(int a, int b) +{ + if ((a >= 0) != (b >= 0)) return 1; // a and b have different signs, so no overflow + if (a < 0 && b < 0) return a >= INT_MIN - b; // same as a + b >= INT_MIN; INT_MIN - b cannot overflow since b < 0. + return a <= INT_MAX - b; +} + +// returns 1 if the product of two ints fits in a signed short, 0 on overflow. +static int stbi__mul2shorts_valid(int a, int b) +{ + if (b == 0 || b == -1) return 1; // multiplication by 0 is always 0; check for -1 so SHRT_MIN/b doesn't overflow + if ((a >= 0) == (b >= 0)) return a <= SHRT_MAX/b; // product is positive, so similar to mul2sizes_valid + if (b < 0) return a <= SHRT_MIN / b; // same as a * b >= SHRT_MIN + return a >= SHRT_MIN / b; +} + // stbi__err - error // stbi__errpf - error returning pointer to float // stbi__errpuc - error returning pointer to unsigned char @@ -1985,9 +2005,12 @@ static int stbi__build_huffman(stbi__huffman *h, int *count) int i,j,k=0; unsigned int code; // build size list for each symbol (from JPEG spec) - for (i=0; i < 16; ++i) - for (j=0; j < count[i]; ++j) + for (i=0; i < 16; ++i) { + for (j=0; j < count[i]; ++j) { h->size[k++] = (stbi_uc) (i+1); + if(k >= 257) return stbi__err("bad size list","Corrupt JPEG"); + } + } h->size[k] = 0; // compute actual symbols (from jpeg spec) @@ -2112,6 +2135,8 @@ stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) // convert the huffman code to the symbol id c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; + if(c < 0 || c >= 256) // symbol id out of bounds! + return -1; STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); // convert the id to a symbol @@ -2130,6 +2155,7 @@ stbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n) unsigned int k; int sgn; if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative) k = stbi_lrot(j->code_buffer, n); @@ -2144,6 +2170,7 @@ stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) { unsigned int k; if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing k = stbi_lrot(j->code_buffer, n); j->code_buffer = k & ~stbi__bmask[n]; k &= stbi__bmask[n]; @@ -2155,6 +2182,7 @@ stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) { unsigned int k; if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); + if (j->code_bits < 1) return 0; // ran out of bits from stream, return 0s intead of continuing k = j->code_buffer; j->code_buffer <<= 1; --j->code_bits; @@ -2192,8 +2220,10 @@ static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman memset(data,0,64*sizeof(data[0])); diff = t ? stbi__extend_receive(j, t) : 0; + if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta","Corrupt JPEG"); dc = j->img_comp[b].dc_pred + diff; j->img_comp[b].dc_pred = dc; + if (!stbi__mul2shorts_valid(dc, dequant[0])) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); data[0] = (short) (dc * dequant[0]); // decode AC components, see JPEG spec @@ -2207,6 +2237,7 @@ static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman if (r) { // fast-AC path k += (r >> 4) & 15; // run s = r & 15; // combined length + if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available"); j->code_buffer <<= s; j->code_bits -= s; // decode into unzigzag'd location @@ -2246,8 +2277,10 @@ static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__ if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); diff = t ? stbi__extend_receive(j, t) : 0; + if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta", "Corrupt JPEG"); dc = j->img_comp[b].dc_pred + diff; j->img_comp[b].dc_pred = dc; + if (!stbi__mul2shorts_valid(dc, 1 << j->succ_low)) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); data[0] = (short) (dc * (1 << j->succ_low)); } else { // refinement scan for DC coefficient @@ -2282,6 +2315,7 @@ static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__ if (r) { // fast-AC path k += (r >> 4) & 15; // run s = r & 15; // combined length + if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available"); j->code_buffer <<= s; j->code_bits -= s; zig = stbi__jpeg_dezigzag[k++]; @@ -3102,6 +3136,7 @@ static int stbi__process_marker(stbi__jpeg *z, int m) sizes[i] = stbi__get8(z->s); n += sizes[i]; } + if(n > 256) return stbi__err("bad DHT header","Corrupt JPEG"); // Loop over i < n would write past end of values! L -= 17; if (tc == 0) { if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; @@ -3351,6 +3386,28 @@ static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) return 1; } +static stbi_uc stbi__skip_jpeg_junk_at_end(stbi__jpeg *j) +{ + // some JPEGs have junk at end, skip over it but if we find what looks + // like a valid marker, resume there + while (!stbi__at_eof(j->s)) { + stbi_uc x = stbi__get8(j->s); + while (x == 0xff) { // might be a marker + if (stbi__at_eof(j->s)) return STBI__MARKER_none; + x = stbi__get8(j->s); + if (x != 0x00 && x != 0xff) { + // not a stuffed zero or lead-in to another marker, looks + // like an actual marker, return it + return x; + } + // stuffed zero has x=0 now which ends the loop, meaning we go + // back to regular scan loop. + // repeated 0xff keeps trying to read the next byte of the marker. + } + } + return STBI__MARKER_none; +} + // decode image to YCbCr format static int stbi__decode_jpeg_image(stbi__jpeg *j) { @@ -3367,25 +3424,22 @@ static int stbi__decode_jpeg_image(stbi__jpeg *j) if (!stbi__process_scan_header(j)) return 0; if (!stbi__parse_entropy_coded_data(j)) return 0; if (j->marker == STBI__MARKER_none ) { - // handle 0s at the end of image data from IP Kamera 9060 - while (!stbi__at_eof(j->s)) { - int x = stbi__get8(j->s); - if (x == 255) { - j->marker = stbi__get8(j->s); - break; - } - } + j->marker = stbi__skip_jpeg_junk_at_end(j); // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 } + m = stbi__get_marker(j); + if (STBI__RESTART(m)) + m = stbi__get_marker(j); } else if (stbi__DNL(m)) { int Ld = stbi__get16be(j->s); stbi__uint32 NL = stbi__get16be(j->s); if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); + m = stbi__get_marker(j); } else { - if (!stbi__process_marker(j, m)) return 0; + if (!stbi__process_marker(j, m)) return 1; + m = stbi__get_marker(j); } - m = stbi__get_marker(j); } if (j->progressive) stbi__jpeg_finish(j); @@ -3976,6 +4030,7 @@ static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int re unsigned char* result; stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); if (!j) return stbi__errpuc("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); STBI_NOTUSED(ri); j->s = s; stbi__setup_jpeg(j); @@ -3989,6 +4044,7 @@ static int stbi__jpeg_test(stbi__context *s) int r; stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); if (!j) return stbi__err("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); j->s = s; stbi__setup_jpeg(j); r = stbi__decode_jpeg_header(j, STBI__SCAN_type); @@ -4014,6 +4070,7 @@ static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) int result; stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); if (!j) return stbi__err("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); j->s = s; result = stbi__jpeg_info_raw(j, x, y, comp); STBI_FREE(j); @@ -4121,6 +4178,7 @@ typedef struct { stbi_uc *zbuffer, *zbuffer_end; int num_bits; + int hit_zeof_once; stbi__uint32 code_buffer; char *zout; @@ -4187,9 +4245,20 @@ stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) int b,s; if (a->num_bits < 16) { if (stbi__zeof(a)) { - return -1; /* report error for unexpected end of data. */ + if (!a->hit_zeof_once) { + // This is the first time we hit eof, insert 16 extra padding btis + // to allow us to keep going; if we actually consume any of them + // though, that is invalid data. This is caught later. + a->hit_zeof_once = 1; + a->num_bits += 16; // add 16 implicit zero bits + } else { + // We already inserted our extra 16 padding bits and are again + // out, this stream is actually prematurely terminated. + return -1; + } + } else { + stbi__fill_bits(a); } - stbi__fill_bits(a); } b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; if (b) { @@ -4254,17 +4323,25 @@ static int stbi__parse_huffman_block(stbi__zbuf *a) int len,dist; if (z == 256) { a->zout = zout; + if (a->hit_zeof_once && a->num_bits < 16) { + // The first time we hit zeof, we inserted 16 extra zero bits into our bit + // buffer so the decoder can just do its speculative decoding. But if we + // actually consumed any of those bits (which is the case when num_bits < 16), + // the stream actually read past the end so it is malformed. + return stbi__err("unexpected end","Corrupt PNG"); + } return 1; } + if (z >= 286) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, length codes 286 and 287 must not appear in compressed data z -= 257; len = stbi__zlength_base[z]; if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); z = stbi__zhuffman_decode(a, &a->z_distance); - if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); + if (z < 0 || z >= 30) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, distance codes 30 and 31 must not appear in compressed data dist = stbi__zdist_base[z]; if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); - if (zout + len > a->zout_end) { + if (len > a->zout_end - zout) { if (!stbi__zexpand(a, zout, len)) return 0; zout = a->zout; } @@ -4408,6 +4485,7 @@ static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) if (!stbi__parse_zlib_header(a)) return 0; a->num_bits = 0; a->code_buffer = 0; + a->hit_zeof_once = 0; do { final = stbi__zreceive(a,1); type = stbi__zreceive(a,2); @@ -4563,9 +4641,8 @@ enum { STBI__F_up=2, STBI__F_avg=3, STBI__F_paeth=4, - // synthetic filters used for first scanline to avoid needing a dummy row of 0s - STBI__F_avg_first, - STBI__F_paeth_first + // synthetic filter used for first scanline to avoid needing a dummy row of 0s + STBI__F_avg_first }; static stbi_uc first_row_filter[5] = @@ -4574,29 +4651,56 @@ static stbi_uc first_row_filter[5] = STBI__F_sub, STBI__F_none, STBI__F_avg_first, - STBI__F_paeth_first + STBI__F_sub // Paeth with b=c=0 turns out to be equivalent to sub }; static int stbi__paeth(int a, int b, int c) { - int p = a + b - c; - int pa = abs(p-a); - int pb = abs(p-b); - int pc = abs(p-c); - if (pa <= pb && pa <= pc) return a; - if (pb <= pc) return b; - return c; + // This formulation looks very different from the reference in the PNG spec, but is + // actually equivalent and has favorable data dependencies and admits straightforward + // generation of branch-free code, which helps performance significantly. + int thresh = c*3 - (a + b); + int lo = a < b ? a : b; + int hi = a < b ? b : a; + int t0 = (hi <= thresh) ? lo : c; + int t1 = (thresh <= lo) ? hi : t0; + return t1; } static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; +// adds an extra all-255 alpha channel +// dest == src is legal +// img_n must be 1 or 3 +static void stbi__create_png_alpha_expand8(stbi_uc *dest, stbi_uc *src, stbi__uint32 x, int img_n) +{ + int i; + // must process data backwards since we allow dest==src + if (img_n == 1) { + for (i=x-1; i >= 0; --i) { + dest[i*2+1] = 255; + dest[i*2+0] = src[i]; + } + } else { + STBI_ASSERT(img_n == 3); + for (i=x-1; i >= 0; --i) { + dest[i*4+3] = 255; + dest[i*4+2] = src[i*3+2]; + dest[i*4+1] = src[i*3+1]; + dest[i*4+0] = src[i*3+0]; + } + } +} + // create the png data from post-deflated data static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) { - int bytes = (depth == 16? 2 : 1); + int bytes = (depth == 16 ? 2 : 1); stbi__context *s = a->s; stbi__uint32 i,j,stride = x*out_n*bytes; stbi__uint32 img_len, img_width_bytes; + stbi_uc *filter_buf; + int all_ok = 1; int k; int img_n = s->img_n; // copy it into a local for later @@ -4608,8 +4712,11 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into if (!a->out) return stbi__err("outofmem", "Out of memory"); + // note: error exits here don't need to clean up a->out individually, + // stbi__do_png always does on error. if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); img_width_bytes = (((img_n * x * depth) + 7) >> 3); + if (!stbi__mad2sizes_valid(img_width_bytes, y, img_width_bytes)) return stbi__err("too large", "Corrupt PNG"); img_len = (img_width_bytes + 1) * y; // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, @@ -4617,189 +4724,137 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r // so just check for raw_len < img_len always. if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); + // Allocate two scan lines worth of filter workspace buffer. + filter_buf = (stbi_uc *) stbi__malloc_mad2(img_width_bytes, 2, 0); + if (!filter_buf) return stbi__err("outofmem", "Out of memory"); + + // Filtering for low-bit-depth images + if (depth < 8) { + filter_bytes = 1; + width = img_width_bytes; + } + for (j=0; j < y; ++j) { - stbi_uc *cur = a->out + stride*j; - stbi_uc *prior; + // cur/prior filter buffers alternate + stbi_uc *cur = filter_buf + (j & 1)*img_width_bytes; + stbi_uc *prior = filter_buf + (~j & 1)*img_width_bytes; + stbi_uc *dest = a->out + stride*j; + int nk = width * filter_bytes; int filter = *raw++; - if (filter > 4) - return stbi__err("invalid filter","Corrupt PNG"); - - if (depth < 8) { - if (img_width_bytes > x) return stbi__err("invalid width","Corrupt PNG"); - cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place - filter_bytes = 1; - width = img_width_bytes; + // check filter type + if (filter > 4) { + all_ok = stbi__err("invalid filter","Corrupt PNG"); + break; } - prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above // if first row, use special filter that doesn't sample previous row if (j == 0) filter = first_row_filter[filter]; - // handle first byte explicitly - for (k=0; k < filter_bytes; ++k) { - switch (filter) { - case STBI__F_none : cur[k] = raw[k]; break; - case STBI__F_sub : cur[k] = raw[k]; break; - case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; - case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; - case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; - case STBI__F_avg_first : cur[k] = raw[k]; break; - case STBI__F_paeth_first: cur[k] = raw[k]; break; - } - } - - if (depth == 8) { - if (img_n != out_n) - cur[img_n] = 255; // first pixel - raw += img_n; - cur += out_n; - prior += out_n; - } else if (depth == 16) { - if (img_n != out_n) { - cur[filter_bytes] = 255; // first pixel top byte - cur[filter_bytes+1] = 255; // first pixel bottom byte - } - raw += filter_bytes; - cur += output_bytes; - prior += output_bytes; - } else { - raw += 1; - cur += 1; - prior += 1; + // perform actual filtering + switch (filter) { + case STBI__F_none: + memcpy(cur, raw, nk); + break; + case STBI__F_sub: + memcpy(cur, raw, filter_bytes); + for (k = filter_bytes; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); + break; + case STBI__F_up: + for (k = 0; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + prior[k]); + break; + case STBI__F_avg: + for (k = 0; k < filter_bytes; ++k) + cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); + for (k = filter_bytes; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); + break; + case STBI__F_paeth: + for (k = 0; k < filter_bytes; ++k) + cur[k] = STBI__BYTECAST(raw[k] + prior[k]); // prior[k] == stbi__paeth(0,prior[k],0) + for (k = filter_bytes; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes], prior[k], prior[k-filter_bytes])); + break; + case STBI__F_avg_first: + memcpy(cur, raw, filter_bytes); + for (k = filter_bytes; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); + break; } - // this is a little gross, so that we don't switch per-pixel or per-component - if (depth < 8 || img_n == out_n) { - int nk = (width - 1)*filter_bytes; - #define STBI__CASE(f) \ - case f: \ - for (k=0; k < nk; ++k) - switch (filter) { - // "none" filter turns into a memcpy here; make that explicit. - case STBI__F_none: memcpy(cur, raw, nk); break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; - } - #undef STBI__CASE - raw += nk; - } else { - STBI_ASSERT(img_n+1 == out_n); - #define STBI__CASE(f) \ - case f: \ - for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ - for (k=0; k < filter_bytes; ++k) - switch (filter) { - STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; - } - #undef STBI__CASE - - // the loop above sets the high byte of the pixels' alpha, but for - // 16 bit png files we also need the low byte set. we'll do that here. - if (depth == 16) { - cur = a->out + stride*j; // start at the beginning of the row again - for (i=0; i < x; ++i,cur+=output_bytes) { - cur[filter_bytes+1] = 255; - } - } - } - } + raw += nk; - // we make a separate pass to expand bits to pixels; for performance, - // this could run two scanlines behind the above code, so it won't - // intefere with filtering but will still be in the cache. - if (depth < 8) { - for (j=0; j < y; ++j) { - stbi_uc *cur = a->out + stride*j; - stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; - // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit - // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop + // expand decoded bits in cur to dest, also adding an extra alpha channel if desired + if (depth < 8) { stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range + stbi_uc *in = cur; + stbi_uc *out = dest; + stbi_uc inb = 0; + stbi__uint32 nsmp = x*img_n; - // note that the final byte might overshoot and write more data than desired. - // we can allocate enough data that this never writes out of memory, but it - // could also overwrite the next scanline. can it overwrite non-empty data - // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. - // so we need to explicitly clamp the final ones - + // expand bits to bytes first if (depth == 4) { - for (k=x*img_n; k >= 2; k-=2, ++in) { - *cur++ = scale * ((*in >> 4) ); - *cur++ = scale * ((*in ) & 0x0f); + for (i=0; i < nsmp; ++i) { + if ((i & 1) == 0) inb = *in++; + *out++ = scale * (inb >> 4); + inb <<= 4; } - if (k > 0) *cur++ = scale * ((*in >> 4) ); } else if (depth == 2) { - for (k=x*img_n; k >= 4; k-=4, ++in) { - *cur++ = scale * ((*in >> 6) ); - *cur++ = scale * ((*in >> 4) & 0x03); - *cur++ = scale * ((*in >> 2) & 0x03); - *cur++ = scale * ((*in ) & 0x03); + for (i=0; i < nsmp; ++i) { + if ((i & 3) == 0) inb = *in++; + *out++ = scale * (inb >> 6); + inb <<= 2; } - if (k > 0) *cur++ = scale * ((*in >> 6) ); - if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); - if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); - } else if (depth == 1) { - for (k=x*img_n; k >= 8; k-=8, ++in) { - *cur++ = scale * ((*in >> 7) ); - *cur++ = scale * ((*in >> 6) & 0x01); - *cur++ = scale * ((*in >> 5) & 0x01); - *cur++ = scale * ((*in >> 4) & 0x01); - *cur++ = scale * ((*in >> 3) & 0x01); - *cur++ = scale * ((*in >> 2) & 0x01); - *cur++ = scale * ((*in >> 1) & 0x01); - *cur++ = scale * ((*in ) & 0x01); + } else { + STBI_ASSERT(depth == 1); + for (i=0; i < nsmp; ++i) { + if ((i & 7) == 0) inb = *in++; + *out++ = scale * (inb >> 7); + inb <<= 1; } - if (k > 0) *cur++ = scale * ((*in >> 7) ); - if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); - if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); - if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); - if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); - if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); - if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); } - if (img_n != out_n) { - int q; - // insert alpha = 255 - cur = a->out + stride*j; + + // insert alpha=255 values if desired + if (img_n != out_n) + stbi__create_png_alpha_expand8(dest, dest, x, img_n); + } else if (depth == 8) { + if (img_n == out_n) + memcpy(dest, cur, x*img_n); + else + stbi__create_png_alpha_expand8(dest, cur, x, img_n); + } else if (depth == 16) { + // convert the image data from big-endian to platform-native + stbi__uint16 *dest16 = (stbi__uint16*)dest; + stbi__uint32 nsmp = x*img_n; + + if (img_n == out_n) { + for (i = 0; i < nsmp; ++i, ++dest16, cur += 2) + *dest16 = (cur[0] << 8) | cur[1]; + } else { + STBI_ASSERT(img_n+1 == out_n); if (img_n == 1) { - for (q=x-1; q >= 0; --q) { - cur[q*2+1] = 255; - cur[q*2+0] = cur[q]; + for (i = 0; i < x; ++i, dest16 += 2, cur += 2) { + dest16[0] = (cur[0] << 8) | cur[1]; + dest16[1] = 0xffff; } } else { STBI_ASSERT(img_n == 3); - for (q=x-1; q >= 0; --q) { - cur[q*4+3] = 255; - cur[q*4+2] = cur[q*3+2]; - cur[q*4+1] = cur[q*3+1]; - cur[q*4+0] = cur[q*3+0]; + for (i = 0; i < x; ++i, dest16 += 4, cur += 6) { + dest16[0] = (cur[0] << 8) | cur[1]; + dest16[1] = (cur[2] << 8) | cur[3]; + dest16[2] = (cur[4] << 8) | cur[5]; + dest16[3] = 0xffff; } } } } - } else if (depth == 16) { - // force the image data from big-endian to platform-native. - // this is done in a separate pass due to the decoding relying - // on the data being untouched, but could probably be done - // per-line during decode if care is taken. - stbi_uc *cur = a->out; - stbi__uint16 *cur16 = (stbi__uint16*)cur; - - for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { - *cur16 = (cur[0] << 8) | cur[1]; - } } + STBI_FREE(filter_buf); + if (!all_ok) return 0; + return 1; } @@ -4955,7 +5010,7 @@ STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set; static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set; -STBIDEF void stbi__unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply) +STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply) { stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply; stbi__unpremultiply_on_load_set = 1; @@ -5064,14 +5119,13 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) if (!pal_img_n) { s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); - if (scan == STBI__SCAN_header) return 1; } else { // if paletted, then pal_n is our final components, and // img_n is # components to decompress/filter. s->img_n = 1; if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); - // if SCAN_header, have to scan to see if we have a tRNS } + // even with SCAN_header, have to scan to see if we have a tRNS break; } @@ -5103,10 +5157,14 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); has_trans = 1; + // non-paletted with tRNS = constant alpha. if header-scanning, we can stop now. + if (scan == STBI__SCAN_header) { ++s->img_n; return 1; } if (z->depth == 16) { - for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is + for (k = 0; k < s->img_n && k < 3; ++k) // extra loop test to suppress false GCC warning + tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is } else { - for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger + for (k = 0; k < s->img_n && k < 3; ++k) + tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger } } break; @@ -5115,7 +5173,13 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) case STBI__PNG_TYPE('I','D','A','T'): { if (first) return stbi__err("first not IHDR", "Corrupt PNG"); if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); - if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } + if (scan == STBI__SCAN_header) { + // header scan definitely stops at first IDAT + if (pal_img_n) + s->img_n = pal_img_n; + return 1; + } + if (c.length > (1u << 30)) return stbi__err("IDAT size limit", "IDAT section larger than 2^30 bytes"); if ((int)(ioff + c.length) < (int)ioff) return 0; if (ioff + c.length > idata_limit) { stbi__uint32 idata_limit_old = idata_limit; @@ -5498,8 +5562,22 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req psize = (info.offset - info.extra_read - info.hsz) >> 2; } if (psize == 0) { - if (info.offset != s->callback_already_read + (s->img_buffer - s->img_buffer_original)) { - return stbi__errpuc("bad offset", "Corrupt BMP"); + // accept some number of extra bytes after the header, but if the offset points either to before + // the header ends or implies a large amount of extra data, reject the file as malformed + int bytes_read_so_far = s->callback_already_read + (int)(s->img_buffer - s->img_buffer_original); + int header_limit = 1024; // max we actually read is below 256 bytes currently. + int extra_data_limit = 256*4; // what ordinarily goes here is a palette; 256 entries*4 bytes is its max size. + if (bytes_read_so_far <= 0 || bytes_read_so_far > header_limit) { + return stbi__errpuc("bad header", "Corrupt BMP"); + } + // we established that bytes_read_so_far is positive and sensible. + // the first half of this test rejects offsets that are either too small positives, or + // negative, and guarantees that info.offset >= bytes_read_so_far > 0. this in turn + // ensures the number computed in the second half of the test can't overflow. + if (info.offset < bytes_read_so_far || info.offset - bytes_read_so_far > extra_data_limit) { + return stbi__errpuc("bad offset", "Corrupt BMP"); + } else { + stbi__skip(s, info.offset - bytes_read_so_far); } } @@ -7187,12 +7265,12 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re // Run value = stbi__get8(s); count -= 128; - if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } for (z = 0; z < count; ++z) scanline[i++ * 4 + k] = value; } else { // Dump - if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } for (z = 0; z < count; ++z) scanline[i++ * 4 + k] = stbi__get8(s); } @@ -7446,10 +7524,17 @@ static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0); if (!out) return stbi__errpuc("outofmem", "Out of memory"); - stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8)); + if (!stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8))) { + STBI_FREE(out); + return stbi__errpuc("bad PNM", "PNM file truncated"); + } if (req_comp && req_comp != s->img_n) { - out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + if (ri->bits_per_channel == 16) { + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, s->img_n, req_comp, s->img_x, s->img_y); + } else { + out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + } if (out == NULL) return out; // stbi__convert_format frees input on failure } return out; @@ -7486,6 +7571,8 @@ static int stbi__pnm_getinteger(stbi__context *s, char *c) while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { value = value*10 + (*c - '0'); *c = (char) stbi__get8(s); + if((value > 214748364) || (value == 214748364 && *c > '7')) + return stbi__err("integer parse overflow", "Parsing an integer in the PPM header overflowed a 32-bit int"); } return value; @@ -7516,9 +7603,13 @@ static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) stbi__pnm_skip_whitespace(s, &c); *x = stbi__pnm_getinteger(s, &c); // read width + if(*x == 0) + return stbi__err("invalid width", "PPM image header had zero or overflowing width"); stbi__pnm_skip_whitespace(s, &c); *y = stbi__pnm_getinteger(s, &c); // read height + if (*y == 0) + return stbi__err("invalid width", "PPM image header had zero or overflowing width"); stbi__pnm_skip_whitespace(s, &c); maxv = stbi__pnm_getinteger(s, &c); // read max value diff --git a/src/external/stb/stb_truetype.h b/src/external/stb/stb_truetype.h index bbf2284b..90a5c2e2 100644 --- a/src/external/stb/stb_truetype.h +++ b/src/external/stb/stb_truetype.h @@ -54,7 +54,7 @@ // Hou Qiming Derek Vinyard // Rob Loach Cort Stratton // Kenney Phillis Jr. Brian Costabile -// Ken Voskuil (kaesve) +// Ken Voskuil (kaesve) Yakov Galka // // VERSION HISTORY // @@ -4604,6 +4604,8 @@ STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float sc scale_y = -scale_y; { + // distance from singular values (in the same units as the pixel grid) + const float eps = 1./1024, eps2 = eps*eps; int x,y,i,j; float *precompute; stbtt_vertex *verts; @@ -4616,15 +4618,15 @@ STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float sc float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; float x1 = verts[j].x*scale_x, y1 = verts[j].y*scale_y; float dist = (float) STBTT_sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0)); - precompute[i] = (dist == 0) ? 0.0f : 1.0f / dist; + precompute[i] = (dist < eps) ? 0.0f : 1.0f / dist; } else if (verts[i].type == STBTT_vcurve) { float x2 = verts[j].x *scale_x, y2 = verts[j].y *scale_y; float x1 = verts[i].cx*scale_x, y1 = verts[i].cy*scale_y; float x0 = verts[i].x *scale_x, y0 = verts[i].y *scale_y; float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; float len2 = bx*bx + by*by; - if (len2 != 0.0f) - precompute[i] = 1.0f / (bx*bx + by*by); + if (len2 >= eps2) + precompute[i] = 1.0f / len2; else precompute[i] = 0.0f; } else @@ -4689,8 +4691,8 @@ STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float sc float a = 3*(ax*bx + ay*by); float b = 2*(ax*ax + ay*ay) + (mx*bx+my*by); float c = mx*ax+my*ay; - if (a == 0.0) { // if a is 0, it's linear - if (b != 0.0) { + if (STBTT_fabs(a) < eps2) { // if a is 0, it's linear + if (STBTT_fabs(b) >= eps2) { res[num++] = -c/b; } } else { diff --git a/src/external/tinygltf/README.md b/src/external/tinygltf/README.md index 557734a1..5e706d9c 100644 --- a/src/external/tinygltf/README.md +++ b/src/external/tinygltf/README.md @@ -9,7 +9,7 @@ If you are looking for old, C++03 version, please use `devel-picojson` branch (b ## Status Currently TinyGLTF is stable and maintenance mode. No drastic changes and feature additions planned. - + - v2.9.0 Various fixes and improvements. Filesystem callback API change. - v2.8.0 Add URICallbacks for custom URI handling in Buffer and Image. PR#397 - v2.7.0 Change WriteImageDataFunction user callback function signature. PR#393 - v2.6.0 Support serializing sparse accessor(Thanks to @fynv). @@ -26,8 +26,6 @@ Currently TinyGLTF is stable and maintenance mode. No drastic changes and featur ## Builds -[![Build Status](https://travis-ci.org/syoyo/tinygltf.svg?branch=devel)](https://travis-ci.org/syoyo/tinygltf) - [![Build status](https://ci.appveyor.com/api/projects/status/warngenu9wjjhlm8?svg=true)](https://ci.appveyor.com/project/syoyo/tinygltf) ![C/C++ CI](https://github.com/syoyo/tinygltf/workflows/C/C++%20CI/badge.svg) @@ -109,6 +107,7 @@ WASI build example is located in [wasm](wasm) . * [SanityEngine](https://github.com/DethRaid/SanityEngine) - A C++/D3D12 renderer focused on the personal and professional development of its developer * [Open3D](http://www.open3d.org/) - A Modern Library for 3D Data Processing * [Supernova Engine](https://github.com/supernovaengine/supernova) - Game engine for 2D and 3D projects with Lua or C++ in data oriented design. +* [Wicked Engine](https://github.com/turanszkij/WickedEngine) - 3D engine with modern graphics * Your projects here! (Please send PR) ## TODOs @@ -197,6 +196,7 @@ if (!ret) { * `TINYGLTF_USE_RAPIDJSON` : Use RapidJSON as a JSON parser/serializer. RapidJSON files are not included in TinyGLTF repo. Please set an include path to RapidJSON if you enable this feature. * `TINYGLTF_USE_CPP14` : Use C++14 feature(requires C++14 compiler). This may give better performance than C++11. + ## CMake options You can add tinygltf using `add_subdirectory` feature. diff --git a/src/external/tinygltf/tiny_gltf.h b/src/external/tinygltf/tiny_gltf.h index 0f69ad27..6d8c3ffb 100644 --- a/src/external/tinygltf/tiny_gltf.h +++ b/src/external/tinygltf/tiny_gltf.h @@ -25,32 +25,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -// Version: -// - v2.8.1 Missed serialization texture sampler name fixed. PR#399. -// - v2.8.0 Add URICallbacks for custom URI handling in Buffer and Image. PR#397. -// - v2.7.0 Change WriteImageDataFunction user callback function signature. PR#393. -// - v2.6.3 Fix GLB file with empty BIN chunk was not handled. PR#382 and PR#383. -// - v2.6.2 Fix out-of-bounds access of accessors. PR#379. -// - v2.6.1 Better GLB validation check when loading. -// - v2.6.0 Support serializing sparse accessor(Thanks to @fynv). -// Disable expanding file path for security(no use of awkward `wordexp` anymore). -// - v2.5.0 Add SetPreserveImageChannels() option to load image data as is. -// - v2.4.3 Fix null object output when material has all default -// parameters. -// - v2.4.2 Decode percent-encoded URI. -// - v2.4.1 Fix some glTF object class does not have `extensions` and/or -// `extras` property. -// - v2.4.0 Experimental RapidJSON and C++14 support(Thanks to @jrkoone). -// - v2.3.1 Set default value of minFilter and magFilter in Sampler to -1. -// - v2.3.0 Modified Material representation according to glTF 2.0 schema -// (and introduced TextureInfo class) -// Change the behavior of `Value::IsNumber`. It return true either the -// value is int or real. -// - v2.2.0 Add loading 16bit PNG support. Add Sparse accessor support(Thanks -// to @Ybalrid) -// - v2.1.0 Add draco compression. -// - v2.0.1 Add comparison feature(Thanks to @Selmar). -// - v2.0.0 glTF 2.0!. +// Version: - v2.9.* +// See https://github.com/syoyo/tinygltf/releases for release history. // // Tiny glTF loader is using following third party libraries: // @@ -67,20 +43,19 @@ #include #include #include +#include #include #include #include +#include #include -//Auto-detect C++14 standard version -#if !defined(TINYGLTF_USE_CPP14) && defined(__cplusplus) && (__cplusplus >= 201402L) +// Auto-detect C++14 standard version +#if !defined(TINYGLTF_USE_CPP14) && defined(__cplusplus) && \ + (__cplusplus >= 201402L) #define TINYGLTF_USE_CPP14 #endif -#ifndef TINYGLTF_USE_CPP14 -#include -#endif - #ifdef __ANDROID__ #ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS #include @@ -222,6 +197,11 @@ typedef enum { OBJECT_TYPE } Type; +typedef enum { + Permissive, + Strict +} ParseStrictness; + static inline int32_t GetComponentSizeInBytes(uint32_t componentType) { if (componentType == TINYGLTF_COMPONENT_TYPE_BYTE) { return 1; @@ -284,11 +264,7 @@ class Value { typedef std::vector Array; typedef std::map Object; - Value() - : type_(NULL_TYPE), - int_value_(0), - real_value_(0.0), - boolean_value_(false) {} + Value() = default; explicit Value(bool b) : type_(BOOL_TYPE) { boolean_value_ = b; } explicit Value(int i) : type_(INT_TYPE) { @@ -301,9 +277,7 @@ class Value { } explicit Value(std::string &&s) : type_(STRING_TYPE), string_value_(std::move(s)) {} - explicit Value(const char *s) : type_(STRING_TYPE) { - string_value_ = s; - } + explicit Value(const char *s) : type_(STRING_TYPE) { string_value_ = s; } explicit Value(const unsigned char *p, size_t n) : type_(BINARY_TYPE) { binary_value_.resize(n); memcpy(binary_value_.data(), p, n); @@ -365,12 +339,11 @@ class Value { T &Get(); // Lookup value from an array - const Value &Get(int idx) const { + const Value &Get(size_t idx) const { static Value null_value; assert(IsArray()); - assert(idx >= 0); - return (static_cast(idx) < array_value_.size()) - ? array_value_[static_cast(idx)] + return (idx < array_value_.size()) + ? array_value_[idx] : null_value; } @@ -549,28 +522,30 @@ typedef std::map ParameterMap; typedef std::map ExtensionMap; struct AnimationChannel { - int sampler; // required - int target_node; // optional index of the node to target (alternative + int sampler{-1}; // required + int target_node{-1}; // optional index of the node to target (alternative // target should be provided by extension) std::string target_path; // required with standard values of ["translation", // "rotation", "scale", "weights"] Value extras; ExtensionMap extensions; + Value target_extras; ExtensionMap target_extensions; // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. std::string extras_json_string; std::string extensions_json_string; + std::string target_extras_json_string; std::string target_extensions_json_string; - AnimationChannel() : sampler(-1), target_node(-1) {} + AnimationChannel() = default; DEFAULT_METHODS(AnimationChannel) bool operator==(const AnimationChannel &) const; }; struct AnimationSampler { - int input; // required - int output; // required + int input{-1}; // required + int output{-1}; // required std::string interpolation; // "LINEAR", "STEP","CUBICSPLINE" or user defined // string. default "LINEAR" Value extras; @@ -580,7 +555,7 @@ struct AnimationSampler { std::string extras_json_string; std::string extensions_json_string; - AnimationSampler() : input(-1), output(-1), interpolation("LINEAR") {} + AnimationSampler() : interpolation("LINEAR") {} DEFAULT_METHODS(AnimationSampler) bool operator==(const AnimationSampler &) const; }; @@ -603,9 +578,9 @@ struct Animation { struct Skin { std::string name; - int inverseBindMatrices; // required here but not in the spec - int skeleton; // The index of the node used as a skeleton root - std::vector joints; // Indices of skeleton nodes + int inverseBindMatrices{-1}; // required here but not in the spec + int skeleton{-1}; // The index of the node used as a skeleton root + std::vector joints; // Indices of skeleton nodes Value extras; ExtensionMap extensions; @@ -614,10 +589,7 @@ struct Skin { std::string extras_json_string; std::string extensions_json_string; - Skin() { - inverseBindMatrices = -1; - skeleton = -1; - } + Skin() = default; DEFAULT_METHODS(Skin) bool operator==(const Skin &) const; }; @@ -648,25 +620,21 @@ struct Sampler { std::string extras_json_string; std::string extensions_json_string; - Sampler() - : minFilter(-1), - magFilter(-1), - wrapS(TINYGLTF_TEXTURE_WRAP_REPEAT), - wrapT(TINYGLTF_TEXTURE_WRAP_REPEAT) {} + Sampler() = default; DEFAULT_METHODS(Sampler) bool operator==(const Sampler &) const; }; struct Image { std::string name; - int width; - int height; - int component; - int bits; // bit depth per channel. 8(byte), 16 or 32. - int pixel_type; // pixel type(TINYGLTF_COMPONENT_TYPE_***). usually - // UBYTE(bits = 8) or USHORT(bits = 16) + int width{-1}; + int height{-1}; + int component{-1}; + int bits{-1}; // bit depth per channel. 8(byte), 16 or 32. + int pixel_type{-1}; // pixel type(TINYGLTF_COMPONENT_TYPE_***). usually + // UBYTE(bits = 8) or USHORT(bits = 16) std::vector image; - int bufferView; // (required if no uri) + int bufferView{-1}; // (required if no uri) std::string mimeType; // (required if no uri) ["image/jpeg", "image/png", // "image/bmp", "image/gif"] std::string uri; // (required if no mimeType) uri is not decoded(e.g. @@ -681,19 +649,10 @@ struct Image { // When this flag is true, data is stored to `image` in as-is format(e.g. jpeg // compressed for "image/jpeg" mime) This feature is good if you use custom // image loader function. (e.g. delayed decoding of images for faster glTF - // parsing) Default parser for Image does not provide as-is loading feature at - // the moment. (You can manipulate this by providing your own LoadImageData - // function) - bool as_is; - - Image() : as_is(false) { - bufferView = -1; - width = -1; - height = -1; - component = -1; - bits = -1; - pixel_type = -1; - } + // parsing). + bool as_is{false}; + + Image() = default; DEFAULT_METHODS(Image) bool operator==(const Image &) const; @@ -702,8 +661,8 @@ struct Image { struct Texture { std::string name; - int sampler; - int source; + int sampler{-1}; + int source{-1}; Value extras; ExtensionMap extensions; @@ -711,16 +670,16 @@ struct Texture { std::string extras_json_string; std::string extensions_json_string; - Texture() : sampler(-1), source(-1) {} + Texture() = default; DEFAULT_METHODS(Texture) bool operator==(const Texture &) const; }; struct TextureInfo { - int index = -1; // required. - int texCoord; // The set index of texture's TEXCOORD attribute used for - // texture coordinate mapping. + int index{-1}; // required. + int texCoord{0}; // The set index of texture's TEXCOORD attribute used for + // texture coordinate mapping. Value extras; ExtensionMap extensions; @@ -729,17 +688,18 @@ struct TextureInfo { std::string extras_json_string; std::string extensions_json_string; - TextureInfo() : index(-1), texCoord(0) {} + TextureInfo() = default; DEFAULT_METHODS(TextureInfo) bool operator==(const TextureInfo &) const; }; struct NormalTextureInfo { - int index = -1; // required - int texCoord; // The set index of texture's TEXCOORD attribute used for - // texture coordinate mapping. - double scale; // scaledNormal = normalize(( - // * 2.0 - 1.0) * vec3(, , 1.0)) + int index{-1}; // required + int texCoord{0}; // The set index of texture's TEXCOORD attribute used for + // texture coordinate mapping. + double scale{ + 1.0}; // scaledNormal = normalize(( + // * 2.0 - 1.0) * vec3(, , 1.0)) Value extras; ExtensionMap extensions; @@ -748,17 +708,17 @@ struct NormalTextureInfo { std::string extras_json_string; std::string extensions_json_string; - NormalTextureInfo() : index(-1), texCoord(0), scale(1.0) {} + NormalTextureInfo() = default; DEFAULT_METHODS(NormalTextureInfo) bool operator==(const NormalTextureInfo &) const; }; struct OcclusionTextureInfo { - int index = -1; // required - int texCoord; // The set index of texture's TEXCOORD attribute used for + int index{-1}; // required + int texCoord{0}; // The set index of texture's TEXCOORD attribute used for // texture coordinate mapping. - double strength; // occludedColor = lerp(color, color * , ) + double strength{1.0}; // occludedColor = lerp(color, color * , ) Value extras; ExtensionMap extensions; @@ -767,17 +727,17 @@ struct OcclusionTextureInfo { std::string extras_json_string; std::string extensions_json_string; - OcclusionTextureInfo() : index(-1), texCoord(0), strength(1.0) {} + OcclusionTextureInfo() = default; DEFAULT_METHODS(OcclusionTextureInfo) bool operator==(const OcclusionTextureInfo &) const; }; // pbrMetallicRoughness class defined in glTF 2.0 spec. struct PbrMetallicRoughness { - std::vector baseColorFactor; // len = 4. default [1,1,1,1] + std::vector baseColorFactor{1.0, 1.0, 1.0, 1.0}; // len = 4. default [1,1,1,1] TextureInfo baseColorTexture; - double metallicFactor; // default 1 - double roughnessFactor; // default 1 + double metallicFactor{1.0}; // default 1 + double roughnessFactor{1.0}; // default 1 TextureInfo metallicRoughnessTexture; Value extras; @@ -787,11 +747,9 @@ struct PbrMetallicRoughness { std::string extras_json_string; std::string extensions_json_string; - PbrMetallicRoughness() - : baseColorFactor(std::vector{1.0, 1.0, 1.0, 1.0}), - metallicFactor(1.0), - roughnessFactor(1.0) {} + PbrMetallicRoughness() = default; DEFAULT_METHODS(PbrMetallicRoughness) + bool operator==(const PbrMetallicRoughness &) const; }; @@ -801,10 +759,11 @@ struct PbrMetallicRoughness { struct Material { std::string name; - std::vector emissiveFactor; // length 3. default [0, 0, 0] - std::string alphaMode; // default "OPAQUE" - double alphaCutoff; // default 0.5 - bool doubleSided; // default false; + std::vector emissiveFactor{0.0, 0.0, 0.0}; // length 3. default [0, 0, 0] + std::string alphaMode{"OPAQUE"}; // default "OPAQUE" + double alphaCutoff{0.5}; // default 0.5 + bool doubleSided{false}; // default false + std::vector lods; // level of detail materials (MSFT_lod) PbrMetallicRoughness pbrMetallicRoughness; @@ -824,7 +783,7 @@ struct Material { std::string extras_json_string; std::string extensions_json_string; - Material() : alphaMode("OPAQUE"), alphaCutoff(0.5), doubleSided(false) {} + Material() = default; DEFAULT_METHODS(Material) bool operator==(const Material &) const; @@ -848,26 +807,20 @@ struct BufferView { bool dracoDecoded{false}; // Flag indicating this has been draco decoded - BufferView() - : buffer(-1), - byteOffset(0), - byteLength(0), - byteStride(0), - target(0), - dracoDecoded(false) {} + BufferView() = default; DEFAULT_METHODS(BufferView) bool operator==(const BufferView &) const; }; struct Accessor { - int bufferView; // optional in spec but required here since sparse accessor - // are not supported + int bufferView{-1}; // optional in spec but required here since sparse + // accessor are not supported std::string name; - size_t byteOffset; - bool normalized; // optional. - int componentType; // (required) One of TINYGLTF_COMPONENT_TYPE_*** - size_t count; // required - int type; // (required) One of TINYGLTF_TYPE_*** .. + size_t byteOffset{0}; + bool normalized{false}; // optional. + int componentType{-1}; // (required) One of TINYGLTF_COMPONENT_TYPE_*** + size_t count{0}; // required + int type{-1}; // (required) One of TINYGLTF_TYPE_*** .. Value extras; ExtensionMap extensions; @@ -880,19 +833,33 @@ struct Accessor { std::vector maxValues; // optional. integer value is promoted to double - struct { + struct Sparse { int count; bool isSparse; struct { - int byteOffset; + size_t byteOffset; int bufferView; int componentType; // a TINYGLTF_COMPONENT_TYPE_ value + Value extras; + ExtensionMap extensions; + std::string extras_json_string; + std::string extensions_json_string; } indices; struct { int bufferView; - int byteOffset; + size_t byteOffset; + Value extras; + ExtensionMap extensions; + std::string extras_json_string; + std::string extensions_json_string; } values; - } sparse; + Value extras; + ExtensionMap extensions; + std::string extras_json_string; + std::string extensions_json_string; + }; + + Sparse sparse; /// /// Utility function to compute byteStride for a given bufferView object. @@ -914,8 +881,8 @@ struct Accessor { return componentSizeInBytes * numComponents; } else { - // Check if byteStride is a multiple of the size of the accessor's component - // type. + // Check if byteStride is a multiple of the size of the accessor's + // component type. int componentSizeInBytes = GetComponentSizeInBytes(static_cast(componentType)); if (componentSizeInBytes <= 0) { @@ -932,12 +899,8 @@ struct Accessor { } Accessor() - : bufferView(-1), - byteOffset(0), - normalized(false), - componentType(-1), - count(0), - type(-1) { + + { sparse.isSparse = false; } DEFAULT_METHODS(Accessor) @@ -945,17 +908,12 @@ struct Accessor { }; struct PerspectiveCamera { - double aspectRatio; // min > 0 - double yfov; // required. min > 0 - double zfar; // min > 0 - double znear; // required. min > 0 - - PerspectiveCamera() - : aspectRatio(0.0), - yfov(0.0), - zfar(0.0) // 0 = use infinite projection matrix - , - znear(0.0) {} + double aspectRatio{0.0}; // min > 0 + double yfov{0.0}; // required. min > 0 + double zfar{0.0}; // min > 0 + double znear{0.0}; // required. min > 0 + + PerspectiveCamera() = default; DEFAULT_METHODS(PerspectiveCamera) bool operator==(const PerspectiveCamera &) const; @@ -968,12 +926,12 @@ struct PerspectiveCamera { }; struct OrthographicCamera { - double xmag; // required. must not be zero. - double ymag; // required. must not be zero. - double zfar; // required. `zfar` must be greater than `znear`. - double znear; // required + double xmag{0.0}; // required. must not be zero. + double ymag{0.0}; // required. must not be zero. + double zfar{0.0}; // required. `zfar` must be greater than `znear`. + double znear{0.0}; // required - OrthographicCamera() : xmag(0.0), ymag(0.0), zfar(0.0), znear(0.0) {} + OrthographicCamera() = default; DEFAULT_METHODS(OrthographicCamera) bool operator==(const OrthographicCamera &) const; @@ -992,7 +950,7 @@ struct Camera { PerspectiveCamera perspective; OrthographicCamera orthographic; - Camera() {} + Camera() = default; DEFAULT_METHODS(Camera) bool operator==(const Camera &) const; @@ -1009,10 +967,10 @@ struct Primitive { // integer, where each integer // is the index of the accessor // containing an attribute. - int material; // The index of the material to apply to this primitive - // when rendering. - int indices; // The index of the accessor that contains the indices. - int mode; // one of TINYGLTF_MODE_*** + int material{-1}; // The index of the material to apply to this primitive + // when rendering. + int indices{-1}; // The index of the accessor that contains the indices. + int mode{-1}; // one of TINYGLTF_MODE_*** std::vector > targets; // array of morph targets, // where each target is a dict with attributes in ["POSITION, "NORMAL", // "TANGENT"] pointing @@ -1024,11 +982,7 @@ struct Primitive { std::string extras_json_string; std::string extensions_json_string; - Primitive() { - material = -1; - indices = -1; - mode = -1; - } + Primitive() = default; DEFAULT_METHODS(Primitive) bool operator==(const Primitive &) const; }; @@ -1051,17 +1005,20 @@ struct Mesh { class Node { public: - Node() : camera(-1), skin(-1), mesh(-1) {} + Node() = default; DEFAULT_METHODS(Node) bool operator==(const Node &) const; - int camera; // the index of the camera referenced by this node + int camera{-1}; // the index of the camera referenced by this node std::string name; - int skin; - int mesh; + int skin{-1}; + int mesh{-1}; + int light{-1}; // light source index (KHR_lights_punctual) + int emitter{-1}; // audio emitter index (KHR_audio) + std::vector lods; // level of detail nodes (MSFT_lod) std::vector children; std::vector rotation; // length must be 0 or 4 std::vector scale; // length must be 0 or 3 @@ -1115,6 +1072,7 @@ struct Asset { struct Scene { std::string name; std::vector nodes; + std::vector audioEmitters; // KHR_audio global emitters ExtensionMap extensions; Value extras; @@ -1129,10 +1087,10 @@ struct Scene { }; struct SpotLight { - double innerConeAngle; - double outerConeAngle; + double innerConeAngle{0.0}; + double outerConeAngle{0.7853981634}; - SpotLight() : innerConeAngle(0.0), outerConeAngle(0.7853981634) {} + SpotLight() = default; DEFAULT_METHODS(SpotLight) bool operator==(const SpotLight &) const; @@ -1152,7 +1110,7 @@ struct Light { double range{0.0}; // 0.0 = infinite SpotLight spot; - Light() : intensity(1.0), range(0.0) {} + Light() = default; DEFAULT_METHODS(Light) bool operator==(const Light &) const; @@ -1165,6 +1123,89 @@ struct Light { std::string extensions_json_string; }; +struct PositionalEmitter { + double coneInnerAngle{6.283185307179586}; + double coneOuterAngle{6.283185307179586}; + double coneOuterGain{0.0}; + double maxDistance{100.0}; + double refDistance{1.0}; + double rolloffFactor{1.0}; + + PositionalEmitter() = default; + DEFAULT_METHODS(PositionalEmitter) + bool operator==(const PositionalEmitter &) const; + + ExtensionMap extensions; + Value extras; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; +}; + +struct AudioEmitter { + std::string name; + double gain{1.0}; + bool loop{false}; + bool playing{false}; + std::string + type; // positional - Positional audio emitters. Using sound cones, the + // orientation is +Z having the same front side for a glTF asset. + // global - Global audio emitters are not affected by the position + // of audio listeners. coneInnerAngle, coneOuterAngle, + // coneOuterGain, distanceModel, maxDistance, refDistance, and + // rolloffFactor should all be ignored when set. + std::string + distanceModel; // linear - A linear distance model calculating the + // gain induced by the distance according to: 1.0 + // - rolloffFactor * (distance - refDistance) / + // (maxDistance - refDistance) + // inverse - (default) An inverse distance model + // calculating the gain induced by the distance according + // to: refDistance / (refDistance + rolloffFactor * + // (Math.max(distance, refDistance) - refDistance)) + // exponential - An exponential distance model calculating + // the gain induced by the distance according to: + // pow((Math.max(distance, refDistance) / refDistance, + // -rolloffFactor)) + PositionalEmitter positional; + int source{-1}; + + AudioEmitter() : type("global"), distanceModel("inverse") {} + DEFAULT_METHODS(AudioEmitter) + + bool operator==(const AudioEmitter &) const; + + ExtensionMap extensions; + Value extras; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; +}; + +struct AudioSource { + std::string name; + std::string uri; + int bufferView{-1}; // (required if no uri) + std::string + mimeType; // (required if no uri) The audio's MIME type. Required if + // bufferView is defined. Unless specified by another + // extension, the only supported mimeType is audio/mpeg. + + AudioSource() = default; + DEFAULT_METHODS(AudioSource) + + bool operator==(const AudioSource &) const; + + Value extras; + ExtensionMap extensions; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; +}; + class Model { public: Model() = default; @@ -1186,8 +1227,10 @@ class Model { std::vector cameras; std::vector scenes; std::vector lights; + std::vector audioEmitters; + std::vector audioSources; - int defaultScene = -1; + int defaultScene{-1}; std::vector extensionsUsed; std::vector extensionsRequired; @@ -1220,17 +1263,18 @@ enum SectionCheck { /// image URIs differently, for example. See /// https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#uris /// -typedef bool (*URIEncodeFunction)(const std::string &in_uri, - const std::string &object_type, - std::string *out_uri, void *user_data); +using URIEncodeFunction = std::function; /// /// URIDecodeFunction type. Signature for custom URI decoding of external /// resources such as .bin and image files. Used by tinygltf when computing /// filenames to write resources. /// -typedef bool (*URIDecodeFunction)(const std::string &in_uri, - std::string *out_uri, void *user_data); +using URIDecodeFunction = + std::function; // Declaration of default uri decode function bool URIDecode(const std::string &in_uri, std::string *out_uri, @@ -1247,68 +1291,36 @@ struct URICallbacks { }; /// -/// LoadImageDataFunction type. Signature for custom image loading callbacks. -/// -typedef bool (*LoadImageDataFunction)(Image *, const int, std::string *, - std::string *, int, int, - const unsigned char *, int, - void *user_pointer); - -/// -/// WriteImageDataFunction type. Signature for custom image writing callbacks. -/// The out_uri parameter becomes the URI written to the gltf and may reference -/// a file or contain a data URI. -/// -typedef bool (*WriteImageDataFunction)(const std::string *basepath, - const std::string *filename, - const Image *image, bool embedImages, - const URICallbacks *uri_cb, - std::string *out_uri, - void *user_pointer); - -#ifndef TINYGLTF_NO_STB_IMAGE -// Declaration of default image loader callback -bool LoadImageData(Image *image, const int image_idx, std::string *err, - std::string *warn, int req_width, int req_height, - const unsigned char *bytes, int size, void *); -#endif - -#ifndef TINYGLTF_NO_STB_IMAGE_WRITE -// Declaration of default image writer callback -bool WriteImageData(const std::string *basepath, const std::string *filename, - const Image *image, bool embedImages, - const URICallbacks *uri_cb, std::string *out_uri, void *); -#endif - -/// -/// FilExistsFunction type. Signature for custom filesystem callbacks. +/// FileExistsFunction type. Signature for custom filesystem callbacks. /// -typedef bool (*FileExistsFunction)(const std::string &abs_filename, void *); +using FileExistsFunction = std::function; /// /// ExpandFilePathFunction type. Signature for custom filesystem callbacks. /// -typedef std::string (*ExpandFilePathFunction)(const std::string &, void *); +using ExpandFilePathFunction = + std::function; /// /// ReadWholeFileFunction type. Signature for custom filesystem callbacks. /// -typedef bool (*ReadWholeFileFunction)(std::vector *, - std::string *, const std::string &, - void *); +using ReadWholeFileFunction = std::function *, std::string *, const std::string &, void *)>; /// /// WriteWholeFileFunction type. Signature for custom filesystem callbacks. /// -typedef bool (*WriteWholeFileFunction)(std::string *, const std::string &, - const std::vector &, - void *); +using WriteWholeFileFunction = + std::function &, void *)>; /// /// GetFileSizeFunction type. Signature for custom filesystem callbacks. /// -typedef bool (*GetFileSizeFunction)(size_t *filesize_out, std::string *err, const std::string &abs_filename, - void *userdata); +using GetFileSizeFunction = + std::function; /// /// A structure containing all required filesystem callbacks and a pointer to @@ -1319,7 +1331,8 @@ struct FsCallbacks { ExpandFilePathFunction ExpandFilePath; ReadWholeFileFunction ReadWholeFile; WriteWholeFileFunction WriteWholeFile; - GetFileSizeFunction GetFileSizeInBytes; // To avoid GetFileSize Win32 API, add `InBytes` suffix. + GetFileSizeFunction GetFileSizeInBytes; // To avoid GetFileSize Win32 API, + // add `InBytes` suffix. void *user_data; // An argument that is passed to all fs callbacks }; @@ -1344,8 +1357,42 @@ bool ReadWholeFile(std::vector *out, std::string *err, bool WriteWholeFile(std::string *err, const std::string &filepath, const std::vector &contents, void *); -bool GetFileSizeInBytes(size_t *filesize_out, std::string *err, const std::string &filepath, - void *); +bool GetFileSizeInBytes(size_t *filesize_out, std::string *err, + const std::string &filepath, void *); +#endif + +/// +/// LoadImageDataFunction type. Signature for custom image loading callbacks. +/// +using LoadImageDataFunction = std::function; + +/// +/// WriteImageDataFunction type. Signature for custom image writing callbacks. +/// The out_uri parameter becomes the URI written to the gltf and may reference +/// a file or contain a data URI. +/// +using WriteImageDataFunction = std::function; + +#ifndef TINYGLTF_NO_STB_IMAGE +// Declaration of default image loader callback +bool LoadImageData(Image *image, const int image_idx, std::string *err, + std::string *warn, int req_width, int req_height, + const unsigned char *bytes, int size, void *); +#endif + +#ifndef TINYGLTF_NO_STB_IMAGE_WRITE +// Declaration of default image writer callback +bool WriteImageData(const std::string *basepath, const std::string *filename, + const Image *image, bool embedImages, + const FsCallbacks* fs_cb, const URICallbacks *uri_cb, + std::string *out_uri, void *); #endif /// @@ -1358,13 +1405,13 @@ class TinyGLTF { #pragma clang diagnostic ignored "-Wc++98-compat" #endif - TinyGLTF() : bin_data_(nullptr), bin_size_(0), is_binary_(false) {} + TinyGLTF() = default; #ifdef __clang__ #pragma clang diagnostic pop #endif - ~TinyGLTF() {} + ~TinyGLTF() = default; /// /// Loads glTF ASCII asset from a file. @@ -1425,7 +1472,13 @@ class TinyGLTF { bool prettyPrint, bool writeBinary); /// - /// Set callback to use for loading image data + /// Sets the parsing strictness. + /// + void SetParseStrictness(ParseStrictness strictness); + + /// + /// Set callback to use for loading image data. Passing the nullptr is akin to + /// calling RemoveImageLoader(). /// void SetImageLoader(LoadImageDataFunction LoadImageData, void *user_data); @@ -1440,14 +1493,18 @@ class TinyGLTF { void SetImageWriter(WriteImageDataFunction WriteImageData, void *user_data); /// - /// Set callbacks to use for URI encoding and decoding and their user data + /// Set callbacks to use for URI encoding and decoding and their user data. + /// Returns false if there is an error with the callbacks. If err is not + /// nullptr, explanation will be written there. /// - void SetURICallbacks(URICallbacks callbacks); + bool SetURICallbacks(URICallbacks callbacks, std::string* err = nullptr); /// - /// Set callbacks to use for filesystem (fs) access and their user data + /// Set callbacks to use for filesystem (fs) access and their user data. + /// Returns false if there is an error with the callbacks. If err is not + /// nullptr, explanation will be written there. /// - void SetFsCallbacks(FsCallbacks callbacks); + bool SetFsCallbacks(FsCallbacks callbacks, std::string* err = nullptr); /// /// Set serializing default values(default = false). @@ -1485,6 +1542,17 @@ class TinyGLTF { preserve_image_channels_ = onoff; } + bool GetPreserveImageChannels() const { return preserve_image_channels_; } + + /// + /// Specifiy whether image data is decoded/decompressed during load, or left as is + /// + void SetImagesAsIs(bool onoff) { + images_as_is_ = onoff; + } + + bool GetImagesAsIs() const { return images_as_is_; } + /// /// Set maximum allowed external file size in bytes. /// Default: 2GB @@ -1494,11 +1562,7 @@ class TinyGLTF { max_external_file_size_ = max_bytes; } - size_t GetMaxExternalFileSize() const { - return max_external_file_size_; - } - - bool GetPreserveImageChannels() const { return preserve_image_channels_; } + size_t GetMaxExternalFileSize() const { return max_external_file_size_; } private: /// @@ -1515,6 +1579,8 @@ class TinyGLTF { size_t bin_size_ = 0; bool is_binary_ = false; + ParseStrictness strictness_ = ParseStrictness::Strict; + bool serialize_default_values_ = false; ///< Serialize default values? bool store_original_json_for_extras_and_extensions_ = false; @@ -1522,7 +1588,10 @@ class TinyGLTF { bool preserve_image_channels_ = false; /// Default false(expand channels to /// RGBA) for backward compatibility. - size_t max_external_file_size_{size_t((std::numeric_limits::max)())}; // Default 2GB + bool images_as_is_ = false; /// Default false (decode/decompress images) + + size_t max_external_file_size_{ + size_t((std::numeric_limits::max)())}; // Default 2GB // Warning & error messages std::string warn_; @@ -1530,8 +1599,11 @@ class TinyGLTF { FsCallbacks fs = { #ifndef TINYGLTF_NO_FS - &tinygltf::FileExists, &tinygltf::ExpandFilePath, - &tinygltf::ReadWholeFile, &tinygltf::WriteWholeFile, &tinygltf::GetFileSizeInBytes, + &tinygltf::FileExists, + &tinygltf::ExpandFilePath, + &tinygltf::ReadWholeFile, + &tinygltf::WriteWholeFile, + &tinygltf::GetFileSizeInBytes, nullptr // Fs callback user data #else @@ -1578,11 +1650,12 @@ class TinyGLTF { #if defined(TINYGLTF_IMPLEMENTATION) || defined(__INTELLISENSE__) #include -//#include +// #include #ifndef TINYGLTF_NO_FS +#include // for is_directory check + #include #include -#include // for is_directory check #endif #include @@ -1723,7 +1796,7 @@ class TinyGLTF { #endif #elif !defined(__ANDROID__) && !defined(__OpenBSD__) -//#include +// #include #endif #if defined(__sparcv9) || defined(__powerpc__) @@ -1743,6 +1816,7 @@ namespace detail { // documents may be active at once. using json = rapidjson::GenericValue, rapidjson::CrtAllocator>; +using json_iterator = json::MemberIterator; using json_const_iterator = json::ConstMemberIterator; using json_const_array_iterator = json const *; using JsonDocument = @@ -1754,6 +1828,7 @@ rapidjson::CrtAllocator &GetAllocator() { return s_CrtAllocator; } // not thread safe. Only a single JsonDocument may be active at any one time, // meaning only a single gltf load/save can be active any one time. using json = rapidjson::Value; +using json_iterator = json::MemberIterator; using json_const_iterator = json::ConstMemberIterator; using json_const_array_iterator = json const *; rapidjson::Document *s_pActiveDocument = nullptr; @@ -1800,6 +1875,7 @@ struct JsonDocument : public rapidjson::Document { #else using nlohmann::json; +using json_iterator = json::iterator; using json_const_iterator = json::const_iterator; using json_const_array_iterator = json_const_iterator; using JsonDocument = json; @@ -1814,8 +1890,8 @@ void JsonParse(JsonDocument &doc, const char *str, size_t length, doc = detail::json::parse(str, str + length, nullptr, throwExc); #endif } -} // namespace -} +} // namespace detail +} // namespace tinygltf #ifdef __APPLE__ #include "TargetConditionals.h" @@ -1839,6 +1915,9 @@ struct LoadImageDataOption { // channels) default `false`(channels are expanded to RGBA for backward // compatibility). bool preserve_channels{false}; + // true: do not decode/decompress image data. + // default `false`: decode/decompress image data. + bool as_is{false}; }; // Equals function for Value, for recursivity @@ -1868,7 +1947,7 @@ static bool Equals(const tinygltf::Value &one, const tinygltf::Value &other) { } case ARRAY_TYPE: { if (one.Size() != other.Size()) return false; - for (int i = 0; i < int(one.Size()); ++i) + for (size_t i = 0; i < one.Size(); ++i) if (!Equals(one.Get(i), other.Get(i))) return false; return true; } @@ -1958,6 +2037,17 @@ bool Light::operator==(const Light &other) const { return Equals(this->color, other.color) && this->name == other.name && this->type == other.type; } +bool AudioEmitter::operator==(const AudioEmitter &other) const { + return this->name == other.name && + TINYGLTF_DOUBLE_EQUAL(this->gain, other.gain) && + this->loop == other.loop && this->playing == other.playing && + this->type == other.type && + this->distanceModel == other.distanceModel && + this->source == other.source; +} +bool AudioSource::operator==(const AudioSource &other) const { + return this->name == other.name && this->uri == other.uri; +} bool Material::operator==(const Material &other) const { return (this->pbrMetallicRoughness == other.pbrMetallicRoughness) && (this->normalTexture == other.normalTexture) && @@ -1997,6 +2087,7 @@ bool Node::operator==(const Node &other) const { return this->camera == other.camera && this->children == other.children && this->extensions == other.extensions && this->extras == other.extras && Equals(this->matrix, other.matrix) && this->mesh == other.mesh && + (this->light == other.light) && (this->emitter == other.emitter) && this->name == other.name && Equals(this->rotation, other.rotation) && Equals(this->scale, other.scale) && this->skin == other.skin && Equals(this->translation, other.translation) && @@ -2007,6 +2098,15 @@ bool SpotLight::operator==(const SpotLight &other) const { TINYGLTF_DOUBLE_EQUAL(this->innerConeAngle, other.innerConeAngle) && TINYGLTF_DOUBLE_EQUAL(this->outerConeAngle, other.outerConeAngle); } +bool PositionalEmitter::operator==(const PositionalEmitter &other) const { + return this->extensions == other.extensions && this->extras == other.extras && + TINYGLTF_DOUBLE_EQUAL(this->coneInnerAngle, other.coneInnerAngle) && + TINYGLTF_DOUBLE_EQUAL(this->coneOuterAngle, other.coneOuterAngle) && + TINYGLTF_DOUBLE_EQUAL(this->coneOuterGain, other.coneOuterGain) && + TINYGLTF_DOUBLE_EQUAL(this->maxDistance, other.maxDistance) && + TINYGLTF_DOUBLE_EQUAL(this->refDistance, other.refDistance) && + TINYGLTF_DOUBLE_EQUAL(this->rolloffFactor, other.rolloffFactor); +} bool OrthographicCamera::operator==(const OrthographicCamera &other) const { return this->extensions == other.extensions && this->extras == other.extras && TINYGLTF_DOUBLE_EQUAL(this->xmag, other.xmag) && @@ -2137,8 +2237,9 @@ static std::string FindFile(const std::vector &paths, } // https://github.com/syoyo/tinygltf/issues/416 - // Use strlen() since std::string's size/length reports the number of elements in the buffer, not the length of string(null-terminated) - // strip null-character in the middle of string. + // Use strlen() since std::string's size/length reports the number of elements + // in the buffer, not the length of string(null-terminated) strip + // null-character in the middle of string. size_t slength = strlen(filepath.c_str()); if (slength == 0) { return std::string(); @@ -2165,7 +2266,7 @@ static std::string GetFilePathExtension(const std::string &FileName) { static std::string GetBaseDir(const std::string &filepath) { if (filepath.find_last_of("/\\") != std::string::npos) - return filepath.substr(0, filepath.find_last_of("/\\")); + return filepath.substr(0, filepath.find_last_of("/\\") + 1); return ""; } @@ -2394,7 +2495,8 @@ bool URIDecode(const std::string &in_uri, std::string *out_uri, static bool LoadExternalFile(std::vector *out, std::string *err, std::string *warn, const std::string &filename, const std::string &basedir, bool required, - size_t reqBytes, bool checkSize, size_t maxFileSize, FsCallbacks *fs) { + size_t reqBytes, bool checkSize, + size_t maxFileSize, FsCallbacks *fs) { if (fs == nullptr || fs->FileExists == nullptr || fs->ExpandFilePath == nullptr || fs->ReadWholeFile == nullptr) { // This is a developer error, assert() ? @@ -2422,14 +2524,15 @@ static bool LoadExternalFile(std::vector *out, std::string *err, // Check file size if (fs->GetFileSizeInBytes) { - size_t file_size{0}; std::string _err; - bool ok = fs->GetFileSizeInBytes(&file_size, &_err, filepath, fs->user_data); + bool ok = + fs->GetFileSizeInBytes(&file_size, &_err, filepath, fs->user_data); if (!ok) { if (_err.size()) { if (failMsgOut) { - (*failMsgOut) += "Getting file size failed : " + filename + ", err = " + _err + "\n"; + (*failMsgOut) += "Getting file size failed : " + filename + + ", err = " + _err + "\n"; } } return false; @@ -2437,7 +2540,9 @@ static bool LoadExternalFile(std::vector *out, std::string *err, if (file_size > maxFileSize) { if (failMsgOut) { - (*failMsgOut) += "File size " + std::to_string(file_size) + " exceeds maximum allowed file size " + std::to_string(maxFileSize) + " : " + filepath + "\n"; + (*failMsgOut) += "File size " + std::to_string(file_size) + + " exceeds maximum allowed file size " + + std::to_string(maxFileSize) + " : " + filepath + "\n"; } return false; } @@ -2482,8 +2587,16 @@ static bool LoadExternalFile(std::vector *out, std::string *err, return true; } +void TinyGLTF::SetParseStrictness(ParseStrictness strictness) { + strictness_ = strictness; +} + void TinyGLTF::SetImageLoader(LoadImageDataFunction func, void *user_data) { - LoadImageData = func; + if (func == nullptr) { + RemoveImageLoader(); + return; + } + LoadImageData = std::move(func); load_image_user_data_ = user_data; user_image_loader_ = true; } @@ -2513,48 +2626,65 @@ bool LoadImageData(Image *image, const int image_idx, std::string *err, int w = 0, h = 0, comp = 0, req_comp = 0; - unsigned char *data = nullptr; + // Try to decode image header + if (!stbi_info_from_memory(bytes, size, &w, &h, &comp)) { + // On failure, if we load images as is, we just warn. + std::string* msgOut = option.as_is ? warn : err; + if (msgOut) { + (*msgOut) += + "Unknown image format. STB cannot decode image header for image[" + + std::to_string(image_idx) + "] name = \"" + image->name + "\".\n"; + } + if (!option.as_is) { + // If we decode images, error out. + return false; + } else { + // If we load images as is, we copy the image data, + // set all image properties to invalid, and report success. + image->width = image->height = image->component = -1; + image->bits = image->pixel_type = -1; + image->image.resize(static_cast(size)); + std::copy(bytes, bytes + size, image->image.begin()); + return true; + } + } - // preserve_channels true: Use channels stored in the image file. - // false: force 32-bit textures for common Vulkan compatibility. It appears - // that some GPU drivers do not support 24-bit images for Vulkan - req_comp = option.preserve_channels ? 0 : 4; int bits = 8; int pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE; - // It is possible that the image we want to load is a 16bit per channel image - // We are going to attempt to load it as 16bit per channel, and if it worked, - // set the image data accordingly. We are casting the returned pointer into - // unsigned char, because we are representing "bytes". But we are updating - // the Image metadata to signal that this image uses 2 bytes (16bits) per - // channel: if (stbi_is_16_bit_from_memory(bytes, size)) { - data = reinterpret_cast( - stbi_load_16_from_memory(bytes, size, &w, &h, &comp, req_comp)); - if (data) { - bits = 16; - pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT; - } - } - - // at this point, if data is still NULL, it means that the image wasn't - // 16bit per channel, we are going to load it as a normal 8bit per channel - // image as we used to do: - // if image cannot be decoded, ignore parsing and keep it by its path - // don't break in this case - // FIXME we should only enter this function if the image is embedded. If - // image->uri references - // an image file, it should be left as it is. Image loading should not be - // mandatory (to support other formats) - if (!data) data = stbi_load_from_memory(bytes, size, &w, &h, &comp, req_comp); - if (!data) { - // NOTE: you can use `warn` instead of `err` - if (err) { - (*err) += - "Unknown image format. STB cannot decode image data for image[" + - std::to_string(image_idx) + "] name = \"" + image->name + "\".\n"; + bits = 16; + pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT; + } + + // preserve_channels true: Use channels stored in the image file. + // false: force 32-bit textures for common Vulkan compatibility. It appears + // that some GPU drivers do not support 24-bit images for Vulkan + req_comp = (option.preserve_channels || option.as_is) ? 0 : 4; + + unsigned char* data = nullptr; + // Perform image decoding if requested + if (!option.as_is) { + // If the image is marked as 16 bit per channel, attempt to decode it as such first. + // If that fails, we are going to attempt to load it as 8 bit per channel image. + if (bits == 16) { + data = reinterpret_cast(stbi_load_16_from_memory(bytes, size, &w, &h, &comp, req_comp)); + } + // Load as 8 bit per channel data + if (!data) { + data = stbi_load_from_memory(bytes, size, &w, &h, &comp, req_comp); + if (!data) { + if (err) { + (*err) += + "Unknown image format. STB cannot decode image data for image[" + + std::to_string(image_idx) + "] name = \"" + image->name + "\".\n"; + } + return false; + } + // If we were succesful, mark as 8 bit + bits = 8; + pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE; } - return false; } if ((w < 1) || (h < 1)) { @@ -2600,16 +2730,26 @@ bool LoadImageData(Image *image, const int image_idx, std::string *err, image->component = comp; image->bits = bits; image->pixel_type = pixel_type; - image->image.resize(static_cast(w * h * comp) * size_t(bits / 8)); - std::copy(data, data + w * h * comp * (bits / 8), image->image.begin()); - stbi_image_free(data); + image->as_is = option.as_is; + if (option.as_is) { + // Store the original image data + image->image.resize(static_cast(size)); + std::copy(bytes, bytes + size, image->image.begin()); + } + else { + // Store the decoded image data + image->image.resize(static_cast(w * h * comp) * size_t(bits / 8)); + std::copy(data, data + w * h * comp * (bits / 8), image->image.begin()); + } + + stbi_image_free(data); return true; } #endif void TinyGLTF::SetImageWriter(WriteImageDataFunction func, void *user_data) { - WriteImageData = func; + WriteImageData = std::move(func); write_image_user_data_ = user_data; } @@ -2625,36 +2765,51 @@ static void WriteToMemory_stbi(void *context, void *data, int size) { bool WriteImageData(const std::string *basepath, const std::string *filename, const Image *image, bool embedImages, - const URICallbacks *uri_cb, std::string *out_uri, - void *fsPtr) { + const FsCallbacks* fs_cb, const URICallbacks *uri_cb, + std::string *out_uri, void *) { + // Early out on empty images, report the original uri if the image was not written. + if (image->image.empty()) { + *out_uri = *filename; + return true; + } + const std::string ext = GetFilePathExtension(*filename); // Write image to temporary buffer std::string header; std::vector data; + // If the image data is already encoded, take it as is + if (image->as_is) { + data = image->image; + } + if (ext == "png") { - if ((image->bits != 8) || - (image->pixel_type != TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE)) { - // Unsupported pixel format - return false; - } + if (!image->as_is) { + if ((image->bits != 8) || + (image->pixel_type != TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE)) { + // Unsupported pixel format + return false; + } - if (!stbi_write_png_to_func(WriteToMemory_stbi, &data, image->width, - image->height, image->component, - &image->image[0], 0)) { - return false; + if (!stbi_write_png_to_func(WriteToMemory_stbi, &data, image->width, + image->height, image->component, + &image->image[0], 0)) { + return false; + } } header = "data:image/png;base64,"; } else if (ext == "jpg") { - if (!stbi_write_jpg_to_func(WriteToMemory_stbi, &data, image->width, + if (!image->as_is && + !stbi_write_jpg_to_func(WriteToMemory_stbi, &data, image->width, image->height, image->component, &image->image[0], 100)) { return false; } header = "data:image/jpeg;base64,"; } else if (ext == "bmp") { - if (!stbi_write_bmp_to_func(WriteToMemory_stbi, &data, image->width, + if (!image->as_is && + !stbi_write_bmp_to_func(WriteToMemory_stbi, &data, image->width, image->height, image->component, &image->image[0])) { return false; @@ -2668,19 +2823,18 @@ bool WriteImageData(const std::string *basepath, const std::string *filename, if (embedImages) { // Embed base64-encoded image into URI if (data.size()) { - *out_uri = header + - base64_encode(&data[0], static_cast(data.size())); + *out_uri = header + base64_encode(&data[0], + static_cast(data.size())); } else { // Throw error? } } else { // Write image to disc - FsCallbacks *fs = reinterpret_cast(fsPtr); - if ((fs != nullptr) && (fs->WriteWholeFile != nullptr)) { + if ((fs_cb != nullptr) && (fs_cb->WriteWholeFile != nullptr)) { const std::string imagefilepath = JoinPath(*basepath, *filename); std::string writeError; - if (!fs->WriteWholeFile(&writeError, imagefilepath, data, - fs->user_data)) { + if (!fs_cb->WriteWholeFile(&writeError, imagefilepath, data, + fs_cb->user_data)) { // Could not write image file to disc; Throw error ? return false; } @@ -2700,14 +2854,36 @@ bool WriteImageData(const std::string *basepath, const std::string *filename, } #endif -void TinyGLTF::SetURICallbacks(URICallbacks callbacks) { - assert(callbacks.decode); +bool TinyGLTF::SetURICallbacks(URICallbacks callbacks, std::string* err) { + if (callbacks.decode == nullptr) { + if (err != nullptr) { + *err = "URI Callback require a non-null decode function."; + } + return false; + } + if (callbacks.decode) { - uri_cb = callbacks; + uri_cb = std::move(callbacks); } + return true; } -void TinyGLTF::SetFsCallbacks(FsCallbacks callbacks) { fs = callbacks; } +bool TinyGLTF::SetFsCallbacks(FsCallbacks callbacks, std::string *err) { + // If callbacks are defined at all, they must all be defined. + if (callbacks.FileExists == nullptr || callbacks.ExpandFilePath == nullptr || + callbacks.ReadWholeFile == nullptr || + callbacks.WriteWholeFile == nullptr || + callbacks.GetFileSizeInBytes == nullptr) { + if (err != nullptr) { + *err = + "FS Callbacks must be completely defined. At least one callback is " + "null."; + } + return false; + } + fs = std::move(callbacks); + return true; +} #ifdef _WIN32 static inline std::wstring UTF8ToWchar(const std::string &str) { @@ -2748,7 +2924,7 @@ bool FileExists(const std::string &abs_filename, void *) { } #else #ifdef _WIN32 -#if defined(_MSC_VER) || defined(__GLIBCXX__) || defined(_LIBCPP_VERSION) +#if defined(_MSC_VER) || defined(_LIBCPP_VERSION) // First check if a file is a directory. DWORD result = GetFileAttributesW(UTF8ToWchar(abs_filename).c_str()); @@ -2764,6 +2940,11 @@ bool FileExists(const std::string &abs_filename, void *) { if (err != 0) { return false; } +#elif defined(__GLIBCXX__) + FILE *fp = fopen(abs_filename.c_str(), "rb"); + if (!fp) { + return false; + } #else // TODO: is_directory check FILE *fp = nullptr; @@ -2859,7 +3040,7 @@ std::string ExpandFilePath(const std::string &filepath, void *) { } bool GetFileSizeInBytes(size_t *filesize_out, std::string *err, - const std::string &filepath, void *userdata) { + const std::string &filepath, void *userdata) { (void)userdata; #ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS @@ -2915,27 +3096,29 @@ bool GetFileSizeInBytes(size_t *filesize_out, std::string *err, } // For directory(and pipe?), peek() will fail(Posix gnustl/libc++ only) - int buf = f.peek(); + f.peek(); if (!f) { if (err) { - (*err) += "File read error. Maybe empty file or invalid file : " + filepath + "\n"; + (*err) += + "File read error. Maybe empty file or invalid file : " + filepath + + "\n"; } return false; } f.seekg(0, f.end); - size_t sz = static_cast(f.tellg()); + const auto sz = f.tellg(); - //std::cout << "sz = " << sz << "\n"; + // std::cout << "sz = " << sz << "\n"; f.seekg(0, f.beg); - if (int64_t(sz) < 0) { + if (sz < 0) { if (err) { (*err) += "Invalid file size : " + filepath + " (does the path point to a directory?)"; } return false; - } else if (sz == 0) { + } else if (sz == std::streamoff(0)) { if (err) { (*err) += "File is empty : " + filepath + "\n"; } @@ -2947,7 +3130,7 @@ bool GetFileSizeInBytes(size_t *filesize_out, std::string *err, return false; } - (*filesize_out) = sz; + (*filesize_out) = static_cast(sz); return true; #endif } @@ -2972,7 +3155,7 @@ bool ReadWholeFile(std::vector *out, std::string *err, } return false; } - out->resize(size); + out->resize(static_cast(size)); AAsset_read(asset, reinterpret_cast(&out->at(0)), size); AAsset_close(asset); return true; @@ -3008,27 +3191,29 @@ bool ReadWholeFile(std::vector *out, std::string *err, } // For directory(and pipe?), peek() will fail(Posix gnustl/libc++ only) - int buf = f.peek(); + f.peek(); if (!f) { if (err) { - (*err) += "File read error. Maybe empty file or invalid file : " + filepath + "\n"; + (*err) += + "File read error. Maybe empty file or invalid file : " + filepath + + "\n"; } return false; } f.seekg(0, f.end); - size_t sz = static_cast(f.tellg()); + const auto sz = f.tellg(); - //std::cout << "sz = " << sz << "\n"; + // std::cout << "sz = " << sz << "\n"; f.seekg(0, f.beg); - if (int64_t(sz) < 0) { + if (sz < 0) { if (err) { (*err) += "Invalid file size : " + filepath + " (does the path point to a directory?)"; } return false; - } else if (sz == 0) { + } else if (sz == std::streamoff(0)) { if (err) { (*err) += "File is empty : " + filepath + "\n"; } @@ -3053,7 +3238,7 @@ bool WriteWholeFile(std::string *err, const std::string &filepath, #ifdef _WIN32 #if defined(__GLIBCXX__) // mingw int file_descriptor = _wopen(UTF8ToWchar(filepath).c_str(), - _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY); + _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY, _S_IWRITE); __gnu_cxx::stdio_filebuf wfile_buf( file_descriptor, std::ios_base::out | std::ios_base::binary); std::ostream f(&wfile_buf); @@ -3102,8 +3287,9 @@ static std::string MimeToExt(const std::string &mimeType) { static bool UpdateImageObject(const Image &image, std::string &baseDir, int index, bool embedImages, + const FsCallbacks *fs_cb, const URICallbacks *uri_cb, - WriteImageDataFunction *WriteImageData, + const WriteImageDataFunction& WriteImageData, void *user_data, std::string *out_uri) { std::string filename; std::string ext; @@ -3129,13 +3315,14 @@ static bool UpdateImageObject(const Image &image, std::string &baseDir, filename = std::to_string(index) + "." + ext; } - // If callback is set and image data exists, modify image data object. If - // image data does not exist, this is not considered a failure and the - // original uri should be maintained. + // If callback is set, modify image data object. + // Note that the callback is also invoked for images without data. + // The default callback implementation simply returns true for + // empty images and sets the out URI to filename. bool imageWritten = false; - if (*WriteImageData != nullptr && !filename.empty() && !image.image.empty()) { - imageWritten = (*WriteImageData)(&baseDir, &filename, &image, embedImages, - uri_cb, out_uri, user_data); + if (WriteImageData != nullptr && !filename.empty()) { + imageWritten = WriteImageData(&baseDir, &filename, &image, embedImages, + fs_cb, uri_cb, out_uri, user_data); if (!imageWritten) { return false; } @@ -3398,7 +3585,22 @@ std::string GetKey(detail::json_const_iterator &it) { #endif } -bool FindMember(const detail::json &o, const char *member, detail::json_const_iterator &it) { +bool FindMember(const detail::json &o, const char *member, + detail::json_const_iterator &it) { +#ifdef TINYGLTF_USE_RAPIDJSON + if (!o.IsObject()) { + return false; + } + it = o.FindMember(member); + return it != o.MemberEnd(); +#else + it = o.find(member); + return it != o.end(); +#endif +} + +bool FindMember(detail::json &o, const char *member, + detail::json_iterator &it) { #ifdef TINYGLTF_USE_RAPIDJSON if (!o.IsObject()) { return false; @@ -3411,6 +3613,22 @@ bool FindMember(const detail::json &o, const char *member, detail::json_const_it #endif } +void Erase(detail::json &o, detail::json_iterator &it) { +#ifdef TINYGLTF_USE_RAPIDJSON + o.EraseMember(it); +#else + o.erase(it); +#endif +} + +bool IsEmpty(const detail::json &o) { +#ifdef TINYGLTF_USE_RAPIDJSON + return o.ObjectEmpty(); +#else + return o.empty(); +#endif +} + const detail::json &GetValue(detail::json_const_iterator &it) { #ifdef TINYGLTF_USE_RAPIDJSON return it->value; @@ -3419,6 +3637,14 @@ const detail::json &GetValue(detail::json_const_iterator &it) { #endif } +detail::json &GetValue(detail::json_iterator &it) { +#ifdef TINYGLTF_USE_RAPIDJSON + return it->value; +#else + return it.value(); +#endif +} + std::string JsonToString(const detail::json &o, int spacing = -1) { #ifdef TINYGLTF_USE_RAPIDJSON using namespace rapidjson; @@ -3443,7 +3669,7 @@ std::string JsonToString(const detail::json &o, int spacing = -1) { #endif } -} // namespace +} // namespace detail static bool ParseJsonAsValue(Value *ret, const detail::json &o) { Value val{}; @@ -3552,7 +3778,8 @@ static bool ParseExtrasProperty(Value *ret, const detail::json &o) { return ParseJsonAsValue(ret, detail::GetValue(it)); } -static bool ParseBooleanProperty(bool *ret, std::string *err, const detail::json &o, +static bool ParseBooleanProperty(bool *ret, std::string *err, + const detail::json &o, const std::string &property, const bool required, const std::string &parent_node = "") { @@ -3601,7 +3828,8 @@ static bool ParseBooleanProperty(bool *ret, std::string *err, const detail::json return true; } -static bool ParseIntegerProperty(int *ret, std::string *err, const detail::json &o, +static bool ParseIntegerProperty(int *ret, std::string *err, + const detail::json &o, const std::string &property, const bool required, const std::string &parent_node = "") { @@ -3637,7 +3865,8 @@ static bool ParseIntegerProperty(int *ret, std::string *err, const detail::json return true; } -static bool ParseUnsignedProperty(size_t *ret, std::string *err, const detail::json &o, +static bool ParseUnsignedProperty(size_t *ret, std::string *err, + const detail::json &o, const std::string &property, const bool required, const std::string &parent_node = "") { @@ -3690,7 +3919,8 @@ static bool ParseUnsignedProperty(size_t *ret, std::string *err, const detail::j return true; } -static bool ParseNumberProperty(double *ret, std::string *err, const detail::json &o, +static bool ParseNumberProperty(double *ret, std::string *err, + const detail::json &o, const std::string &property, const bool required, const std::string &parent_node = "") { @@ -3729,8 +3959,8 @@ static bool ParseNumberProperty(double *ret, std::string *err, const detail::jso } static bool ParseNumberArrayProperty(std::vector *ret, std::string *err, - const detail::json &o, const std::string &property, - bool required, + const detail::json &o, + const std::string &property, bool required, const std::string &parent_node = "") { detail::json_const_iterator it; if (!detail::FindMember(o, property.c_str(), it)) { @@ -3965,8 +4195,8 @@ static bool ParseJSONProperty(std::map *ret, } static bool ParseParameterProperty(Parameter *param, std::string *err, - const detail::json &o, const std::string &prop, - bool required) { + const detail::json &o, + const std::string &prop, bool required) { // A parameter value can either be a string or an array of either a boolean or // a number. Booleans of any kind aren't supported here. Granted, it // complicates the Parameter structure and breaks it semantically in the sense @@ -4011,7 +4241,8 @@ static bool ParseExtensionsProperty(ExtensionMap *ret, std::string *err, return false; } ExtensionMap extensions; - detail::json_const_iterator extIt = detail::ObjectBegin(obj); // it.value().begin(); + detail::json_const_iterator extIt = + detail::ObjectBegin(obj); // it.value().begin(); detail::json_const_iterator extEnd = detail::ObjectEnd(obj); for (; extIt != extEnd; ++extIt) { auto &itObj = detail::GetValue(extIt); @@ -4031,42 +4262,49 @@ static bool ParseExtensionsProperty(ExtensionMap *ret, std::string *err, return true; } -static bool ParseAsset(Asset *asset, std::string *err, const detail::json &o, - bool store_original_json_for_extras_and_extensions) { - ParseStringProperty(&asset->version, err, o, "version", true, "Asset"); - ParseStringProperty(&asset->generator, err, o, "generator", false, "Asset"); - ParseStringProperty(&asset->minVersion, err, o, "minVersion", false, "Asset"); - ParseStringProperty(&asset->copyright, err, o, "copyright", false, "Asset"); - - ParseExtensionsProperty(&asset->extensions, err, o); - - // Unity exporter version is added as extra here - ParseExtrasProperty(&(asset->extras), o); +template +static bool ParseExtrasAndExtensions(GltfType *target, std::string *err, + const detail::json &o, + bool store_json_strings) { + ParseExtensionsProperty(&target->extensions, err, o); + ParseExtrasProperty(&target->extras, o); - if (store_original_json_for_extras_and_extensions) { + if (store_json_strings) { { detail::json_const_iterator it; if (detail::FindMember(o, "extensions", it)) { - asset->extensions_json_string = detail::JsonToString(detail::GetValue(it)); + target->extensions_json_string = + detail::JsonToString(detail::GetValue(it)); } } { detail::json_const_iterator it; if (detail::FindMember(o, "extras", it)) { - asset->extras_json_string = detail::JsonToString(detail::GetValue(it)); + target->extras_json_string = detail::JsonToString(detail::GetValue(it)); } } } + return true; +} +static bool ParseAsset(Asset *asset, std::string *err, const detail::json &o, + bool store_original_json_for_extras_and_extensions) { + ParseStringProperty(&asset->version, err, o, "version", true, "Asset"); + ParseStringProperty(&asset->generator, err, o, "generator", false, "Asset"); + ParseStringProperty(&asset->minVersion, err, o, "minVersion", false, "Asset"); + ParseStringProperty(&asset->copyright, err, o, "copyright", false, "Asset"); + + ParseExtrasAndExtensions(asset, err, o, + store_original_json_for_extras_and_extensions); return true; } static bool ParseImage(Image *image, const int image_idx, std::string *err, std::string *warn, const detail::json &o, bool store_original_json_for_extras_and_extensions, - const std::string &basedir, const size_t max_file_size, FsCallbacks *fs, - const URICallbacks *uri_cb, - LoadImageDataFunction *LoadImageData = nullptr, + const std::string &basedir, const size_t max_file_size, + FsCallbacks *fs, const URICallbacks *uri_cb, + const LoadImageDataFunction& LoadImageData = nullptr, void *load_image_user_data = nullptr) { // A glTF image must either reference a bufferView or an image uri @@ -4098,23 +4336,8 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err, return false; } - ParseExtensionsProperty(&image->extensions, err, o); - ParseExtrasProperty(&image->extras, o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator eit; - if (detail::FindMember(o, "extensions", eit)) { - image->extensions_json_string = detail::JsonToString(detail::GetValue(eit)); - } - } - { - detail::json_const_iterator eit; - if (detail::FindMember(o, "extras", eit)) { - image->extras_json_string = detail::JsonToString(detail::GetValue(eit)); - } - } - } + ParseExtrasAndExtensions(image, err, o, + store_original_json_for_extras_and_extensions); if (hasBufferView) { int bufferView = -1; @@ -4171,7 +4394,7 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err, } } else { // Assume external file - // Keep texture path (for textures that cannot be decoded) + // Unconditionally keep the external URI of the image image->uri = uri; #ifdef TINYGLTF_NO_EXTERNAL_IMAGE return true; @@ -4190,7 +4413,8 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err, if (!LoadExternalFile(&img, err, warn, decoded_uri, basedir, /* required */ false, /* required bytes */ 0, - /* checksize */ false, /* max file size */ max_file_size, fs)) { + /* checksize */ false, + /* max file size */ max_file_size, fs)) { if (warn) { (*warn) += "Failed to load external 'uri' for image[" + std::to_string(image_idx) + "] name = \"" + decoded_uri + @@ -4211,17 +4435,19 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err, #endif } - if (*LoadImageData == nullptr) { + if (LoadImageData == nullptr) { if (err) { (*err) += "No LoadImageData callback specified.\n"; } return false; } - return (*LoadImageData)(image, image_idx, err, warn, 0, 0, &img.at(0), - static_cast(img.size()), load_image_user_data); + + return LoadImageData(image, image_idx, err, warn, 0, 0, &img.at(0), + static_cast(img.size()), load_image_user_data); } -static bool ParseTexture(Texture *texture, std::string *err, const detail::json &o, +static bool ParseTexture(Texture *texture, std::string *err, + const detail::json &o, bool store_original_json_for_extras_and_extensions, const std::string &basedir) { (void)basedir; @@ -4234,23 +4460,8 @@ static bool ParseTexture(Texture *texture, std::string *err, const detail::json texture->sampler = sampler; texture->source = source; - ParseExtensionsProperty(&texture->extensions, err, o); - ParseExtrasProperty(&texture->extras, o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - texture->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - texture->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } + ParseExtrasAndExtensions(texture, err, o, + store_original_json_for_extras_and_extensions); ParseStringProperty(&texture->name, err, o, "name", false); @@ -4271,23 +4482,8 @@ static bool ParseTextureInfo( ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false); - ParseExtensionsProperty(&texinfo->extensions, err, o); - ParseExtrasProperty(&texinfo->extras, o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - texinfo->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - texinfo->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } + ParseExtrasAndExtensions(texinfo, err, o, + store_original_json_for_extras_and_extensions); return true; } @@ -4307,23 +4503,8 @@ static bool ParseNormalTextureInfo( ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false); ParseNumberProperty(&texinfo->scale, err, o, "scale", false); - ParseExtensionsProperty(&texinfo->extensions, err, o); - ParseExtrasProperty(&texinfo->extras, o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - texinfo->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - texinfo->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } + ParseExtrasAndExtensions(texinfo, err, o, + store_original_json_for_extras_and_extensions); return true; } @@ -4343,23 +4524,8 @@ static bool ParseOcclusionTextureInfo( ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false); ParseNumberProperty(&texinfo->strength, err, o, "strength", false); - ParseExtensionsProperty(&texinfo->extensions, err, o); - ParseExtrasProperty(&texinfo->extras, o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - texinfo->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - texinfo->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } + ParseExtrasAndExtensions(texinfo, err, o, + store_original_json_for_extras_and_extensions); return true; } @@ -4367,7 +4533,8 @@ static bool ParseOcclusionTextureInfo( static bool ParseBuffer(Buffer *buffer, std::string *err, const detail::json &o, bool store_original_json_for_extras_and_extensions, FsCallbacks *fs, const URICallbacks *uri_cb, - const std::string &basedir, const size_t max_buffer_size, bool is_binary = false, + const std::string &basedir, + const size_t max_buffer_size, bool is_binary = false, const unsigned char *bin_data = nullptr, size_t bin_size = 0) { size_t byteLength; @@ -4419,7 +4586,8 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const detail::json &o, } if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, decoded_uri, basedir, /* required */ true, - byteLength, /* checkSize */ true, /* max_file_size */max_buffer_size, fs)) { + byteLength, /* checkSize */ true, + /* max_file_size */ max_buffer_size, fs)) { return false; } } @@ -4428,7 +4596,8 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const detail::json &o, if ((bin_size == 0) || (bin_data == nullptr)) { if (err) { - (*err) += "Invalid binary data in `Buffer', or GLB with empty BIN chunk.\n"; + (*err) += + "Invalid binary data in `Buffer', or GLB with empty BIN chunk.\n"; } return false; } @@ -4467,7 +4636,8 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const detail::json &o, } if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, decoded_uri, basedir, /* required */ true, byteLength, - /* checkSize */ true, /* max file size */max_buffer_size, fs)) { + /* checkSize */ true, + /* max file size */ max_buffer_size, fs)) { return false; } } @@ -4475,23 +4645,8 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const detail::json &o, ParseStringProperty(&buffer->name, err, o, "name", false); - ParseExtensionsProperty(&buffer->extensions, err, o); - ParseExtrasProperty(&buffer->extras, o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - buffer->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - buffer->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } + ParseExtrasAndExtensions(buffer, err, o, + store_original_json_for_extras_and_extensions); return true; } @@ -4547,23 +4702,8 @@ static bool ParseBufferView( ParseStringProperty(&bufferView->name, err, o, "name", false); - ParseExtensionsProperty(&bufferView->extensions, err, o); - ParseExtrasProperty(&bufferView->extras, o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - bufferView->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - bufferView->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } + ParseExtrasAndExtensions(bufferView, err, o, + store_original_json_for_extras_and_extensions); bufferView->buffer = buffer; bufferView->byteOffset = byteOffset; @@ -4572,15 +4712,19 @@ static bool ParseBufferView( return true; } -static bool ParseSparseAccessor(Accessor *accessor, std::string *err, - const detail::json &o) { - accessor->sparse.isSparse = true; +static bool ParseSparseAccessor( + Accessor::Sparse *sparse, std::string *err, const detail::json &o, + bool store_original_json_for_extras_and_extensions) { + sparse->isSparse = true; int count = 0; if (!ParseIntegerProperty(&count, err, o, "count", true, "SparseAccessor")) { return false; } + ParseExtrasAndExtensions(sparse, err, o, + store_original_json_for_extras_and_extensions); + detail::json_const_iterator indices_iterator; detail::json_const_iterator values_iterator; if (!detail::FindMember(o, "indices", indices_iterator)) { @@ -4596,37 +4740,45 @@ static bool ParseSparseAccessor(Accessor *accessor, std::string *err, const detail::json &indices_obj = detail::GetValue(indices_iterator); const detail::json &values_obj = detail::GetValue(values_iterator); - int indices_buffer_view = 0, indices_byte_offset = 0, component_type = 0; + int indices_buffer_view = 0, component_type = 0; + size_t indices_byte_offset = 0; if (!ParseIntegerProperty(&indices_buffer_view, err, indices_obj, "bufferView", true, "SparseAccessor")) { return false; } - ParseIntegerProperty(&indices_byte_offset, err, indices_obj, "byteOffset", + ParseUnsignedProperty(&indices_byte_offset, err, indices_obj, "byteOffset", false); if (!ParseIntegerProperty(&component_type, err, indices_obj, "componentType", true, "SparseAccessor")) { return false; } - int values_buffer_view = 0, values_byte_offset = 0; + int values_buffer_view = 0; + size_t values_byte_offset = 0; if (!ParseIntegerProperty(&values_buffer_view, err, values_obj, "bufferView", true, "SparseAccessor")) { return false; } - ParseIntegerProperty(&values_byte_offset, err, values_obj, "byteOffset", + ParseUnsignedProperty(&values_byte_offset, err, values_obj, "byteOffset", false); - accessor->sparse.count = count; - accessor->sparse.indices.bufferView = indices_buffer_view; - accessor->sparse.indices.byteOffset = indices_byte_offset; - accessor->sparse.indices.componentType = component_type; - accessor->sparse.values.bufferView = values_buffer_view; - accessor->sparse.values.byteOffset = values_byte_offset; + sparse->count = count; + sparse->indices.bufferView = indices_buffer_view; + sparse->indices.byteOffset = indices_byte_offset; + sparse->indices.componentType = component_type; + ParseExtrasAndExtensions(&sparse->indices, err, indices_obj, + store_original_json_for_extras_and_extensions); + + sparse->values.bufferView = values_buffer_view; + sparse->values.byteOffset = values_byte_offset; + ParseExtrasAndExtensions(&sparse->values, err, values_obj, + store_original_json_for_extras_and_extensions); return true; } -static bool ParseAccessor(Accessor *accessor, std::string *err, const detail::json &o, +static bool ParseAccessor(Accessor *accessor, std::string *err, + const detail::json &o, bool store_original_json_for_extras_and_extensions) { int bufferView = -1; ParseIntegerProperty(&bufferView, err, o, "bufferView", false, "Accessor"); @@ -4706,29 +4858,16 @@ static bool ParseAccessor(Accessor *accessor, std::string *err, const detail::js } } - ParseExtensionsProperty(&(accessor->extensions), err, o); - ParseExtrasProperty(&(accessor->extras), o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - accessor->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - accessor->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } + ParseExtrasAndExtensions(accessor, err, o, + store_original_json_for_extras_and_extensions); // check if accessor has a "sparse" object: detail::json_const_iterator iterator; if (detail::FindMember(o, "sparse", iterator)) { // here this accessor has a "sparse" subobject - return ParseSparseAccessor(accessor, err, detail::GetValue(iterator)); + return ParseSparseAccessor(&accessor->sparse, err, + detail::GetValue(iterator), + store_original_json_for_extras_and_extensions); } return true; @@ -4828,8 +4967,9 @@ static bool GetAttributeForAllPoints(uint32_t componentType, draco::Mesh *mesh, } static bool ParseDracoExtension(Primitive *primitive, Model *model, - std::string *err, - const Value &dracoExtensionValue) { + std::string *err, std::string *warn, + const Value &dracoExtensionValue, + ParseStrictness strictness) { (void)err; auto bufferViewValue = dracoExtensionValue.Get("bufferView"); if (!bufferViewValue.IsInt()) return false; @@ -4861,6 +5001,33 @@ static bool ParseDracoExtension(Primitive *primitive, Model *model, // create new bufferView for indices if (primitive->indices >= 0) { + if (strictness == ParseStrictness::Permissive) { + const draco::PointIndex::ValueType numPoint = mesh->num_points(); + // handle the situation where the stored component type does not match the + // required type for the actual number of stored points + int supposedComponentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE; + if (numPoint < static_cast( + std::numeric_limits::max())) { + supposedComponentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE; + } else if ( + numPoint < static_cast( + std::numeric_limits::max())) { + supposedComponentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT; + } else { + supposedComponentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT; + } + + if (supposedComponentType > model->accessors[primitive->indices].componentType) { + if (warn) { + (*warn) += + "GLTF component type " + std::to_string(model->accessors[primitive->indices].componentType) + + " is not sufficient for number of stored points," + " treating as " + std::to_string(supposedComponentType) + "\n"; + } + model->accessors[primitive->indices].componentType = supposedComponentType; + } + } + int32_t componentSize = GetComponentSizeInBytes( model->accessors[primitive->indices].componentType); Buffer decodedIndexBuffer; @@ -4926,9 +5093,11 @@ static bool ParseDracoExtension(Primitive *primitive, Model *model, } #endif -static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err, +static bool ParsePrimitive(Primitive *primitive, Model *model, + std::string *err, std::string *warn, const detail::json &o, - bool store_original_json_for_extras_and_extensions) { + bool store_original_json_for_extras_and_extensions, + ParseStrictness strictness) { int material = -1; ParseIntegerProperty(&material, err, o, "material", false); primitive->material = material; @@ -4950,7 +5119,8 @@ static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err, if (detail::FindMember(o, "targets", targetsObject) && detail::IsArray(detail::GetValue(targetsObject))) { auto targetsObjectEnd = detail::ArrayEnd(detail::GetValue(targetsObject)); - for (detail::json_const_array_iterator i = detail::ArrayBegin(detail::GetValue(targetsObject)); + for (detail::json_const_array_iterator i = + detail::ArrayBegin(detail::GetValue(targetsObject)); i != targetsObjectEnd; ++i) { std::map targetAttribues; @@ -4969,51 +5139,44 @@ static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err, } } - ParseExtrasProperty(&(primitive->extras), o); - ParseExtensionsProperty(&primitive->extensions, err, o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - primitive->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - primitive->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } + ParseExtrasAndExtensions(primitive, err, o, + store_original_json_for_extras_and_extensions); #ifdef TINYGLTF_ENABLE_DRACO auto dracoExtension = primitive->extensions.find("KHR_draco_mesh_compression"); if (dracoExtension != primitive->extensions.end()) { - ParseDracoExtension(primitive, model, err, dracoExtension->second); + ParseDracoExtension(primitive, model, err, warn, dracoExtension->second, strictness); } #else (void)model; + (void)warn; + (void)strictness; #endif return true; } -static bool ParseMesh(Mesh *mesh, Model *model, std::string *err, const detail::json &o, - bool store_original_json_for_extras_and_extensions) { +static bool ParseMesh(Mesh *mesh, Model *model, + std::string *err, std::string *warn, + const detail::json &o, + bool store_original_json_for_extras_and_extensions, + ParseStrictness strictness) { ParseStringProperty(&mesh->name, err, o, "name", false); mesh->primitives.clear(); detail::json_const_iterator primObject; if (detail::FindMember(o, "primitives", primObject) && detail::IsArray(detail::GetValue(primObject))) { - detail::json_const_array_iterator primEnd = detail::ArrayEnd(detail::GetValue(primObject)); - for (detail::json_const_array_iterator i = detail::ArrayBegin(detail::GetValue(primObject)); + detail::json_const_array_iterator primEnd = + detail::ArrayEnd(detail::GetValue(primObject)); + for (detail::json_const_array_iterator i = + detail::ArrayBegin(detail::GetValue(primObject)); i != primEnd; ++i) { Primitive primitive; - if (ParsePrimitive(&primitive, model, err, *i, - store_original_json_for_extras_and_extensions)) { + if (ParsePrimitive(&primitive, model, err, warn, *i, + store_original_json_for_extras_and_extensions, + strictness)) { // Only add the primitive if the parsing succeeds. mesh->primitives.emplace_back(std::move(primitive)); } @@ -5023,23 +5186,8 @@ static bool ParseMesh(Mesh *mesh, Model *model, std::string *err, const detail:: // Should probably check if has targets and if dimensions fit ParseNumberArrayProperty(&mesh->weights, err, o, "weights", false); - ParseExtensionsProperty(&mesh->extensions, err, o); - ParseExtrasProperty(&(mesh->extras), o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - mesh->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - mesh->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } + ParseExtrasAndExtensions(mesh, err, o, + store_original_json_for_extras_and_extensions); return true; } @@ -5072,21 +5220,87 @@ static bool ParseNode(Node *node, std::string *err, const detail::json &o, ParseNumberArrayProperty(&node->weights, err, o, "weights", false); - ParseExtensionsProperty(&node->extensions, err, o); - ParseExtrasProperty(&(node->extras), o); + ParseExtrasAndExtensions(node, err, o, + store_original_json_for_extras_and_extensions); - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - node->extensions_json_string = detail::JsonToString(detail::GetValue(it)); + // KHR_lights_punctual: parse light source reference + int light = -1; + if (node->extensions.count("KHR_lights_punctual") != 0) { + auto const &light_ext = node->extensions["KHR_lights_punctual"]; + if (light_ext.Has("light")) { + light = light_ext.Get("light").GetNumberAsInt(); + } else { + if (err) { + *err += + "Node has extension KHR_lights_punctual, but does not reference " + "a light source.\n"; } + return false; } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - node->extras_json_string = detail::JsonToString(detail::GetValue(it)); + } + node->light = light; + + // KHR_audio: parse audio source reference + int emitter = -1; + if (node->extensions.count("KHR_audio") != 0) { + auto const &audio_ext = node->extensions["KHR_audio"]; + if (audio_ext.Has("emitter")) { + emitter = audio_ext.Get("emitter").GetNumberAsInt(); + } else { + if (err) { + *err += + "Node has extension KHR_audio, but does not reference " + "a audio emitter.\n"; + } + return false; + } + } + node->emitter = emitter; + + node->lods.clear(); + if (node->extensions.count("MSFT_lod") != 0) { + auto const &msft_lod_ext = node->extensions["MSFT_lod"]; + if (msft_lod_ext.Has("ids")) { + auto idsArr = msft_lod_ext.Get("ids"); + for (size_t i = 0; i < idsArr.ArrayLen(); ++i) { + node->lods.emplace_back(idsArr.Get(i).GetNumberAsInt()); + } + } else { + if (err) { + *err += + "Node has extension MSFT_lod, but does not reference " + "other nodes via their ids.\n"; + } + return false; + } + } + + return true; +} + +static bool ParseScene(Scene *scene, std::string *err, const detail::json &o, + bool store_original_json_for_extras_and_extensions) { + ParseStringProperty(&scene->name, err, o, "name", false); + ParseIntegerArrayProperty(&scene->nodes, err, o, "nodes", false); + + ParseExtrasAndExtensions(scene, err, o, + store_original_json_for_extras_and_extensions); + + // Parse KHR_audio global emitters + if (scene->extensions.count("KHR_audio") != 0) { + auto const &audio_ext = scene->extensions["KHR_audio"]; + if (audio_ext.Has("emitters")) { + auto emittersArr = audio_ext.Get("emitters"); + for (size_t i = 0; i < emittersArr.ArrayLen(); ++i) { + scene->audioEmitters.emplace_back(emittersArr.Get(i).GetNumberAsInt()); + } + } else { + if (err) { + *err += + "Node has extension KHR_audio, but does not reference " + "a audio emitter.\n"; } + return false; } } @@ -5126,7 +5340,8 @@ static bool ParsePbrMetallicRoughness( { detail::json_const_iterator it; if (detail::FindMember(o, "metallicRoughnessTexture", it)) { - ParseTextureInfo(&pbr->metallicRoughnessTexture, err, detail::GetValue(it), + ParseTextureInfo(&pbr->metallicRoughnessTexture, err, + detail::GetValue(it), store_original_json_for_extras_and_extensions); } } @@ -5134,35 +5349,30 @@ static bool ParsePbrMetallicRoughness( ParseNumberProperty(&pbr->metallicFactor, err, o, "metallicFactor", false); ParseNumberProperty(&pbr->roughnessFactor, err, o, "roughnessFactor", false); - ParseExtensionsProperty(&pbr->extensions, err, o); - ParseExtrasProperty(&pbr->extras, o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - pbr->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - pbr->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } + ParseExtrasAndExtensions(pbr, err, o, + store_original_json_for_extras_and_extensions); return true; } -static bool ParseMaterial(Material *material, std::string *err, const detail::json &o, - bool store_original_json_for_extras_and_extensions) { +static bool ParseMaterial(Material *material, std::string *err, std::string *warn, + const detail::json &o, + bool store_original_json_for_extras_and_extensions, + ParseStrictness strictness) { ParseStringProperty(&material->name, err, o, "name", /* required */ false); if (ParseNumberArrayProperty(&material->emissiveFactor, err, o, "emissiveFactor", /* required */ false)) { - if (material->emissiveFactor.size() != 3) { + if (strictness==ParseStrictness::Permissive && material->emissiveFactor.size() == 4) { + if (warn) { + (*warn) += + "Array length of `emissiveFactor` parameter in " + "material must be 3, but got 4\n"; + } + material->emissiveFactor.resize(3); + } + else if (material->emissiveFactor.size() != 3) { if (err) { (*err) += "Array length of `emissiveFactor` parameter in " @@ -5195,7 +5405,8 @@ static bool ParseMaterial(Material *material, std::string *err, const detail::js { detail::json_const_iterator it; if (detail::FindMember(o, "normalTexture", it)) { - ParseNormalTextureInfo(&material->normalTexture, err, detail::GetValue(it), + ParseNormalTextureInfo(&material->normalTexture, err, + detail::GetValue(it), store_original_json_for_extras_and_extensions); } } @@ -5203,7 +5414,8 @@ static bool ParseMaterial(Material *material, std::string *err, const detail::js { detail::json_const_iterator it; if (detail::FindMember(o, "occlusionTexture", it)) { - ParseOcclusionTextureInfo(&material->occlusionTexture, err, detail::GetValue(it), + ParseOcclusionTextureInfo(&material->occlusionTexture, err, + detail::GetValue(it), store_original_json_for_extras_and_extensions); } } @@ -5238,8 +5450,8 @@ static bool ParseMaterial(Material *material, std::string *err, const detail::js for (; itVal != itValEnd; ++itVal) { Parameter param; - if (ParseParameterProperty(¶m, err, values_object, detail::GetKey(itVal), - false)) { + if (ParseParameterProperty(¶m, err, values_object, + detail::GetKey(itVal), false)) { material->values.emplace(detail::GetKey(itVal), std::move(param)); } } @@ -5258,22 +5470,25 @@ static bool ParseMaterial(Material *material, std::string *err, const detail::js } } - material->extensions.clear(); - ParseExtensionsProperty(&material->extensions, err, o); - ParseExtrasProperty(&(material->extras), o); + material->extensions.clear(); // Note(agnat): Why? + ParseExtrasAndExtensions(material, err, o, + store_original_json_for_extras_and_extensions); - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator eit; - if (detail::FindMember(o, "extensions", eit)) { - material->extensions_json_string = detail::JsonToString(detail::GetValue(eit)); + material->lods.clear(); + if (material->extensions.count("MSFT_lod") != 0) { + auto const &msft_lod_ext = material->extensions["MSFT_lod"]; + if (msft_lod_ext.Has("ids")) { + auto idsArr = msft_lod_ext.Get("ids"); + for (size_t i = 0; i < idsArr.ArrayLen(); ++i) { + material->lods.emplace_back(idsArr.Get(i).GetNumberAsInt()); } - } - { - detail::json_const_iterator eit; - if (detail::FindMember(o, "extras", eit)) { - material->extras_json_string = detail::JsonToString(detail::GetValue(eit)); + } else { + if (err) { + *err += + "Material has extension MSFT_lod, but does not reference " + "other materials via their ids.\n"; } + return false; } } @@ -5294,7 +5509,8 @@ static bool ParseAnimationChannel( } detail::json_const_iterator targetIt; - if (detail::FindMember(o, "target", targetIt) && detail::IsObject(detail::GetValue(targetIt))) { + if (detail::FindMember(o, "target", targetIt) && + detail::IsObject(detail::GetValue(targetIt))) { const detail::json &target_object = detail::GetValue(targetIt); ParseIntegerProperty(&targetIndex, err, target_object, "node", false); @@ -5307,10 +5523,21 @@ static bool ParseAnimationChannel( return false; } ParseExtensionsProperty(&channel->target_extensions, err, target_object); + ParseExtrasProperty(&channel->target_extras, target_object); if (store_original_json_for_extras_and_extensions) { - detail::json_const_iterator it; - if (detail::FindMember(target_object, "extensions", it)) { - channel->target_extensions_json_string = detail::JsonToString(detail::GetValue(it)); + { + detail::json_const_iterator it; + if (detail::FindMember(target_object, "extensions", it)) { + channel->target_extensions_json_string = + detail::JsonToString(detail::GetValue(it)); + } + } + { + detail::json_const_iterator it; + if (detail::FindMember(target_object, "extras", it)) { + channel->target_extras_json_string = + detail::JsonToString(detail::GetValue(it)); + } } } } @@ -5318,23 +5545,8 @@ static bool ParseAnimationChannel( channel->sampler = samplerIndex; channel->target_node = targetIndex; - ParseExtensionsProperty(&channel->extensions, err, o); - ParseExtrasProperty(&(channel->extras), o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - channel->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - channel->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } + ParseExtrasAndExtensions(channel, err, o, + store_original_json_for_extras_and_extensions); return true; } @@ -5346,8 +5558,10 @@ static bool ParseAnimation(Animation *animation, std::string *err, detail::json_const_iterator channelsIt; if (detail::FindMember(o, "channels", channelsIt) && detail::IsArray(detail::GetValue(channelsIt))) { - detail::json_const_array_iterator channelEnd = detail::ArrayEnd(detail::GetValue(channelsIt)); - for (detail::json_const_array_iterator i = detail::ArrayBegin(detail::GetValue(channelsIt)); + detail::json_const_array_iterator channelEnd = + detail::ArrayEnd(detail::GetValue(channelsIt)); + for (detail::json_const_array_iterator i = + detail::ArrayBegin(detail::GetValue(channelsIt)); i != channelEnd; ++i) { AnimationChannel channel; if (ParseAnimationChannel( @@ -5362,7 +5576,8 @@ static bool ParseAnimation(Animation *animation, std::string *err, { detail::json_const_iterator samplerIt; - if (detail::FindMember(o, "samplers", samplerIt) && detail::IsArray(detail::GetValue(samplerIt))) { + if (detail::FindMember(o, "samplers", samplerIt) && + detail::IsArray(detail::GetValue(samplerIt))) { const detail::json &sampler_array = detail::GetValue(samplerIt); detail::json_const_array_iterator it = detail::ArrayBegin(sampler_array); @@ -5390,23 +5605,8 @@ static bool ParseAnimation(Animation *animation, std::string *err, } sampler.input = inputIndex; sampler.output = outputIndex; - ParseExtensionsProperty(&(sampler.extensions), err, o); - ParseExtrasProperty(&(sampler.extras), s); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator eit; - if (detail::FindMember(o, "extensions", eit)) { - sampler.extensions_json_string = detail::JsonToString(detail::GetValue(eit)); - } - } - { - detail::json_const_iterator eit; - if (detail::FindMember(o, "extras", eit)) { - sampler.extras_json_string = detail::JsonToString(detail::GetValue(eit)); - } - } - } + ParseExtrasAndExtensions(&sampler, err, s, + store_original_json_for_extras_and_extensions); animation->samplers.emplace_back(std::move(sampler)); } @@ -5415,28 +5615,14 @@ static bool ParseAnimation(Animation *animation, std::string *err, ParseStringProperty(&animation->name, err, o, "name", false); - ParseExtensionsProperty(&animation->extensions, err, o); - ParseExtrasProperty(&(animation->extras), o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - animation->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - animation->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } + ParseExtrasAndExtensions(animation, err, o, + store_original_json_for_extras_and_extensions); return true; } -static bool ParseSampler(Sampler *sampler, std::string *err, const detail::json &o, +static bool ParseSampler(Sampler *sampler, std::string *err, + const detail::json &o, bool store_original_json_for_extras_and_extensions) { ParseStringProperty(&sampler->name, err, o, "name", false); @@ -5461,23 +5647,8 @@ static bool ParseSampler(Sampler *sampler, std::string *err, const detail::json sampler->wrapT = wrapT; // sampler->wrapR = wrapR; - ParseExtensionsProperty(&(sampler->extensions), err, o); - ParseExtrasProperty(&(sampler->extras), o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - sampler->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - sampler->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } + ParseExtrasAndExtensions(sampler, err, o, + store_original_json_for_extras_and_extensions); return true; } @@ -5497,26 +5668,11 @@ static bool ParseSkin(Skin *skin, std::string *err, const detail::json &o, skin->skeleton = skeleton; int invBind = -1; - ParseIntegerProperty(&invBind, err, o, "inverseBindMatrices", true, "Skin"); + ParseIntegerProperty(&invBind, err, o, "inverseBindMatrices", false, "Skin"); skin->inverseBindMatrices = invBind; - ParseExtensionsProperty(&(skin->extensions), err, o); - ParseExtrasProperty(&(skin->extras), o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - skin->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - skin->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } + ParseExtrasAndExtensions(skin, err, o, + store_original_json_for_extras_and_extensions); return true; } @@ -5547,51 +5703,22 @@ static bool ParsePerspectiveCamera( camera->yfov = yfov; camera->znear = znear; - ParseExtensionsProperty(&camera->extensions, err, o); - ParseExtrasProperty(&(camera->extras), o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - camera->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - camera->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } + ParseExtrasAndExtensions(camera, err, o, + store_original_json_for_extras_and_extensions); // TODO(syoyo): Validate parameter values. return true; } -static bool ParseSpotLight(SpotLight *light, std::string *err, const detail::json &o, +static bool ParseSpotLight(SpotLight *light, std::string *err, + const detail::json &o, bool store_original_json_for_extras_and_extensions) { ParseNumberProperty(&light->innerConeAngle, err, o, "innerConeAngle", false); ParseNumberProperty(&light->outerConeAngle, err, o, "outerConeAngle", false); - ParseExtensionsProperty(&light->extensions, err, o); - ParseExtrasProperty(&light->extras, o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - light->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - light->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } + ParseExtrasAndExtensions(light, err, o, + store_original_json_for_extras_and_extensions); // TODO(syoyo): Validate parameter values. @@ -5622,23 +5749,8 @@ static bool ParseOrthographicCamera( return false; } - ParseExtensionsProperty(&camera->extensions, err, o); - ParseExtrasProperty(&(camera->extras), o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - camera->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - camera->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } + ParseExtrasAndExtensions(camera, err, o, + store_original_json_for_extras_and_extensions); camera->xmag = xmag; camera->ymag = ymag; @@ -5720,85 +5832,160 @@ static bool ParseCamera(Camera *camera, std::string *err, const detail::json &o, ParseStringProperty(&camera->name, err, o, "name", false); - ParseExtensionsProperty(&camera->extensions, err, o); - ParseExtrasProperty(&(camera->extras), o); + ParseExtrasAndExtensions(camera, err, o, + store_original_json_for_extras_and_extensions); - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - camera->extensions_json_string = detail::JsonToString(detail::GetValue(it)); + return true; +} + +static bool ParseLight(Light *light, std::string *err, const detail::json &o, + bool store_original_json_for_extras_and_extensions) { + if (!ParseStringProperty(&light->type, err, o, "type", true)) { + return false; + } + + if (light->type == "spot") { + detail::json_const_iterator spotIt; + if (!detail::FindMember(o, "spot", spotIt)) { + if (err) { + std::stringstream ss; + ss << "Spot light description not found." << std::endl; + (*err) += ss.str(); } + return false; } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - camera->extras_json_string = detail::JsonToString(detail::GetValue(it)); + + const detail::json &v = detail::GetValue(spotIt); + if (!detail::IsObject(v)) { + if (err) { + std::stringstream ss; + ss << "\"spot\" is not a JSON object." << std::endl; + (*err) += ss.str(); } + return false; + } + + if (!ParseSpotLight(&light->spot, err, v, + store_original_json_for_extras_and_extensions)) { + return false; } } + ParseStringProperty(&light->name, err, o, "name", false); + ParseNumberArrayProperty(&light->color, err, o, "color", false); + ParseNumberProperty(&light->range, err, o, "range", false); + ParseNumberProperty(&light->intensity, err, o, "intensity", false); + + ParseExtrasAndExtensions(light, err, o, + store_original_json_for_extras_and_extensions); + + return true; +} + +static bool ParsePositionalEmitter( + PositionalEmitter *positional, std::string *err, const detail::json &o, + bool store_original_json_for_extras_and_extensions) { + ParseNumberProperty(&positional->coneInnerAngle, err, o, "coneInnerAngle", + false); + ParseNumberProperty(&positional->coneOuterAngle, err, o, "coneOuterAngle", + false); + ParseNumberProperty(&positional->coneOuterGain, err, o, "coneOuterGain", + false); + ParseNumberProperty(&positional->maxDistance, err, o, "maxDistance", false); + ParseNumberProperty(&positional->refDistance, err, o, "refDistance", false); + ParseNumberProperty(&positional->rolloffFactor, err, o, "rolloffFactor", + false); + + ParseExtrasAndExtensions(positional, err, o, + store_original_json_for_extras_and_extensions); + return true; } -static bool ParseLight(Light *light, std::string *err, const detail::json &o, - bool store_original_json_for_extras_and_extensions) { - if (!ParseStringProperty(&light->type, err, o, "type", true)) { +static bool ParseAudioEmitter( + AudioEmitter *emitter, std::string *err, const detail::json &o, + bool store_original_json_for_extras_and_extensions) { + if (!ParseStringProperty(&emitter->type, err, o, "type", true)) { return false; } - if (light->type == "spot") { - detail::json_const_iterator spotIt; - if (!detail::FindMember(o, "spot", spotIt)) { + if (emitter->type == "positional") { + detail::json_const_iterator positionalIt; + if (!detail::FindMember(o, "positional", positionalIt)) { if (err) { std::stringstream ss; - ss << "Spot light description not found." << std::endl; + ss << "Positional emitter description not found." << std::endl; (*err) += ss.str(); } return false; } - const detail::json &v = detail::GetValue(spotIt); + const detail::json &v = detail::GetValue(positionalIt); if (!detail::IsObject(v)) { if (err) { std::stringstream ss; - ss << "\"spot\" is not a JSON object." << std::endl; + ss << "\"positional\" is not a JSON object." << std::endl; (*err) += ss.str(); } return false; } - if (!ParseSpotLight(&light->spot, err, v, - store_original_json_for_extras_and_extensions)) { + if (!ParsePositionalEmitter( + &emitter->positional, err, v, + store_original_json_for_extras_and_extensions)) { return false; } } - ParseStringProperty(&light->name, err, o, "name", false); - ParseNumberArrayProperty(&light->color, err, o, "color", false); - ParseNumberProperty(&light->range, err, o, "range", false); - ParseNumberProperty(&light->intensity, err, o, "intensity", false); - ParseExtensionsProperty(&light->extensions, err, o); - ParseExtrasProperty(&(light->extras), o); + ParseStringProperty(&emitter->name, err, o, "name", false); + ParseNumberProperty(&emitter->gain, err, o, "gain", false); + ParseBooleanProperty(&emitter->loop, err, o, "loop", false); + ParseBooleanProperty(&emitter->playing, err, o, "playing", false); + ParseStringProperty(&emitter->distanceModel, err, o, "distanceModel", false); + ParseIntegerProperty(&emitter->source, err, o, "source", true); - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - light->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - light->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } + ParseExtrasAndExtensions(emitter, err, o, + store_original_json_for_extras_and_extensions); + + return true; +} + +static bool ParseAudioSource( + AudioSource *source, std::string *err, const detail::json &o, + bool store_original_json_for_extras_and_extensions) { + ParseStringProperty(&source->name, err, o, "name", false); + ParseStringProperty(&source->uri, err, o, "uri", false); + + if (source->uri.empty()) { + ParseIntegerProperty(&source->bufferView, err, o, "bufferView", true); + ParseStringProperty(&source->mimeType, err, o, "mimeType", true); } + ParseExtrasAndExtensions(source, err, o, + store_original_json_for_extras_and_extensions); + return true; } +namespace detail { + +template +bool ForEachInArray(const detail::json &_v, const char *member, Callback &&cb) { + detail::json_const_iterator itm; + if (detail::FindMember(_v, member, itm) && + detail::IsArray(detail::GetValue(itm))) { + const detail::json &root = detail::GetValue(itm); + auto it = detail::ArrayBegin(root); + auto end = detail::ArrayEnd(root); + for (; it != end; ++it) { + if (!cb(*it)) return false; + } + } + return true; +}; + +} // end of namespace detail + bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, const char *json_str, unsigned int json_str_length, @@ -5850,7 +6037,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, { bool version_found = false; detail::json_const_iterator it; - if (detail::FindMember(v, "asset", it) && detail::IsObject(detail::GetValue(it))) { + if (detail::FindMember(v, "asset", it) && + detail::IsObject(detail::GetValue(it))) { auto &itObj = detail::GetValue(it); detail::json_const_iterator version_it; std::string versionStr; @@ -5872,9 +6060,11 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, // scene is not mandatory. // FIXME Maybe a better way to handle it than removing the code - auto IsArrayMemberPresent = [](const detail::json &_v, const char *name) -> bool { + auto IsArrayMemberPresent = [](const detail::json &_v, + const char *name) -> bool { detail::json_const_iterator it; - return detail::FindMember(_v, name, it) && detail::IsArray(detail::GetValue(it)); + return detail::FindMember(_v, name, it) && + detail::IsArray(detail::GetValue(it)); }; { @@ -5926,21 +6116,14 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, } } - model->buffers.clear(); - model->bufferViews.clear(); - model->accessors.clear(); - model->meshes.clear(); - model->cameras.clear(); - model->nodes.clear(); - model->extensionsUsed.clear(); - model->extensionsRequired.clear(); - model->extensions.clear(); - model->defaultScene = -1; + // Reset the model + (*model) = Model(); // 1. Parse Asset { detail::json_const_iterator it; - if (detail::FindMember(v, "asset", it) && detail::IsObject(detail::GetValue(it))) { + if (detail::FindMember(v, "asset", it) && + detail::IsObject(detail::GetValue(it))) { const detail::json &root = detail::GetValue(it); ParseAsset(&model->asset, err, root, @@ -5948,28 +6131,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, } } -#ifdef TINYGLTF_USE_CPP14 - auto ForEachInArray = [](const detail::json &_v, const char *member, - const auto &cb) -> bool -#else - // The std::function<> implementation can be less efficient because it will - // allocate heap when the size of the captured lambda is above 16 bytes with - // clang and gcc, but it does not require C++14. - auto ForEachInArray = [](const detail::json &_v, const char *member, - const std::function &cb) -> bool -#endif - { - detail::json_const_iterator itm; - if (detail::FindMember(_v, member, itm) && detail::IsArray(detail::GetValue(itm))) { - const detail::json &root = detail::GetValue(itm); - auto it = detail::ArrayBegin(root); - auto end = detail::ArrayEnd(root); - for (; it != end; ++it) { - if (!cb(*it)) return false; - } - } - return true; - }; + using detail::ForEachInArray; // 2. Parse extensionUsed { @@ -6002,7 +6164,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, Buffer buffer; if (!ParseBuffer(&buffer, err, o, store_original_json_for_extras_and_extensions_, &fs, - &uri_cb, base_dir, max_external_file_size_, is_binary_, bin_data_, bin_size_)) { + &uri_cb, base_dir, max_external_file_size_, is_binary_, + bin_data_, bin_size_)) { return false; } @@ -6072,8 +6235,9 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, return false; } Mesh mesh; - if (!ParseMesh(&mesh, model, err, o, - store_original_json_for_extras_and_extensions_)) { + if (!ParseMesh(&mesh, model, err, warn, o, + store_original_json_for_extras_and_extensions_, + strictness_)) { return false; } @@ -6101,20 +6265,22 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, return false; } - auto bufferView = + const auto bufferView = model->accessors[size_t(primitive.indices)].bufferView; - if (bufferView < 0 || size_t(bufferView) >= model->bufferViews.size()) { + if (bufferView < 0) { + // skip, bufferView could be null(-1) for certain extensions + } else if (size_t(bufferView) >= model->bufferViews.size()) { if (err) { (*err) += "accessor[" + std::to_string(primitive.indices) + "] invalid bufferView"; } return false; + } else { + model->bufferViews[size_t(bufferView)].target = + TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER; + // we could optionally check if accessors' bufferView type is Scalar, as + // it should be } - - model->bufferViews[size_t(bufferView)].target = - TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER; - // we could optionally check if accessors' bufferView type is Scalar, as - // it should be } for (auto &attribute : primitive.attributes) { @@ -6135,7 +6301,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, if (accessorsIndex < model->accessors.size()) { const auto bufferView = model->accessors[accessorsIndex].bufferView; // bufferView could be null(-1) for sparse morph target - if (bufferView >= 0 && bufferView < (int)model->bufferViews.size()) { + if (bufferView >= 0 && + bufferView < (int)model->bufferViews.size()) { model->bufferViews[size_t(bufferView)].target = TINYGLTF_TARGET_ARRAY_BUFFER; } @@ -6178,30 +6345,11 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, } return false; } - std::vector nodes; - ParseIntegerArrayProperty(&nodes, err, o, "nodes", false); Scene scene; - scene.nodes = std::move(nodes); - - ParseStringProperty(&scene.name, err, o, "name", false); - - ParseExtensionsProperty(&scene.extensions, err, o); - ParseExtrasProperty(&scene.extras, o); - - if (store_original_json_for_extras_and_extensions_) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - scene.extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - scene.extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } + if (!ParseScene(&scene, err, o, + store_original_json_for_extras_and_extensions_)) { + return false; } model->scenes.emplace_back(std::move(scene)); @@ -6217,7 +6365,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, { detail::json_const_iterator rootIt; int iVal; - if (detail::FindMember(v, "scene", rootIt) && detail::GetInt(detail::GetValue(rootIt), iVal)) { + if (detail::FindMember(v, "scene", rootIt) && + detail::GetInt(detail::GetValue(rootIt), iVal)) { model->defaultScene = iVal; } } @@ -6234,8 +6383,9 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, Material material; ParseStringProperty(&material.name, err, o, "name", false); - if (!ParseMaterial(&material, err, o, - store_original_json_for_extras_and_extensions_)) { + if (!ParseMaterial(&material, err, warn, o, + store_original_json_for_extras_and_extensions_, + strictness_)) { return false; } @@ -6258,6 +6408,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, load_image_user_data = load_image_user_data_; } else { load_image_option.preserve_channels = preserve_image_channels_; + load_image_option.as_is = images_as_is_; load_image_user_data = reinterpret_cast(&load_image_option); } @@ -6273,8 +6424,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, Image image; if (!ParseImage(&image, idx, err, warn, o, store_original_json_for_extras_and_extensions_, base_dir, - max_external_file_size_, &fs, &uri_cb, &this->LoadImageData, - load_image_user_data)) { + max_external_file_size_, &fs, &uri_cb, + this->LoadImageData, load_image_user_data)) { return false; } @@ -6303,7 +6454,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, } const Buffer &buffer = model->buffers[size_t(bufferView.buffer)]; - if (*LoadImageData == nullptr) { + if (LoadImageData == nullptr) { if (err) { (*err) += "No LoadImageData callback specified.\n"; } @@ -6449,13 +6600,15 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, } } - // 17. Parse Extensions - ParseExtensionsProperty(&model->extensions, err, v); + // 17. Parse Extras & Extensions + ParseExtrasAndExtensions(model, err, v, + store_original_json_for_extras_and_extensions_); // 18. Specific extension implementations { detail::json_const_iterator rootIt; - if (detail::FindMember(v, "extensions", rootIt) && detail::IsObject(detail::GetValue(rootIt))) { + if (detail::FindMember(v, "extensions", rootIt) && + detail::IsObject(detail::GetValue(rootIt))) { const detail::json &root = detail::GetValue(rootIt); detail::json_const_iterator it(detail::ObjectBegin(root)); @@ -6463,7 +6616,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, for (; it != itEnd; ++it) { // parse KHR_lights_punctual extension std::string key(detail::GetKey(it)); - if ((key == "KHR_lights_punctual") && detail::IsObject(detail::GetValue(it))) { + if ((key == "KHR_lights_punctual") && + detail::IsObject(detail::GetValue(it))) { const detail::json &object = detail::GetValue(it); detail::json_const_iterator itLight; if (detail::FindMember(object, "lights", itLight)) { @@ -6484,16 +6638,50 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, } } } - } - } - } + // parse KHR_audio extension + if ((key == "KHR_audio") && detail::IsObject(detail::GetValue(it))) { + const detail::json &object = detail::GetValue(it); + detail::json_const_iterator itKhrAudio; + if (detail::FindMember(object, "emitters", itKhrAudio)) { + const detail::json &emitters = detail::GetValue(itKhrAudio); + if (!detail::IsArray(emitters)) { + continue; + } + + auto arrayIt(detail::ArrayBegin(emitters)); + auto arrayItEnd(detail::ArrayEnd(emitters)); + for (; arrayIt != arrayItEnd; ++arrayIt) { + AudioEmitter emitter; + if (!ParseAudioEmitter( + &emitter, err, *arrayIt, + store_original_json_for_extras_and_extensions_)) { + return false; + } + model->audioEmitters.emplace_back(std::move(emitter)); + } + } - // 19. Parse Extras - ParseExtrasProperty(&model->extras, v); + if (detail::FindMember(object, "sources", itKhrAudio)) { + const detail::json &sources = detail::GetValue(itKhrAudio); + if (!detail::IsArray(sources)) { + continue; + } - if (store_original_json_for_extras_and_extensions_) { - model->extras_json_string = detail::JsonToString(v["extras"]); - model->extensions_json_string = detail::JsonToString(v["extensions"]); + auto arrayIt(detail::ArrayBegin(sources)); + auto arrayItEnd(detail::ArrayEnd(sources)); + for (; arrayIt != arrayItEnd; ++arrayIt) { + AudioSource source; + if (!ParseAudioSource( + &source, err, *arrayIt, + store_original_json_for_extras_and_extensions_)) { + return false; + } + model->audioSources.emplace_back(std::move(source)); + } + } + } + } + } } return true; @@ -6578,16 +6766,16 @@ bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err, return false; } - unsigned int version; // 4 bytes - unsigned int length; // 4 bytes + unsigned int version; // 4 bytes + unsigned int length; // 4 bytes unsigned int chunk0_length; // 4 bytes unsigned int chunk0_format; // 4 bytes; memcpy(&version, bytes + 4, 4); swap4(&version); - memcpy(&length, bytes + 8, 4); + memcpy(&length, bytes + 8, 4); // Total glb size, including header and all chunks. swap4(&length); - memcpy(&chunk0_length, bytes + 12, 4); // JSON data length + memcpy(&chunk0_length, bytes + 12, 4); // JSON data length swap4(&chunk0_length); memcpy(&chunk0_format, bytes + 16, 4); swap4(&chunk0_format); @@ -6602,13 +6790,16 @@ bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err, // Use 64bit uint to avoid integer overflow. uint64_t header_and_json_size = 20ull + uint64_t(chunk0_length); - if (header_and_json_size > std::numeric_limits::max()) { + if (header_and_json_size > (std::numeric_limits::max)()) { // Do not allow 4GB or more GLB data. - (*err) = "Invalid glTF binary. GLB data exceeds 4GB."; + if (err) { + (*err) = "Invalid glTF binary. GLB data exceeds 4GB."; + } + return false; } - if ((header_and_json_size > uint64_t(size)) || (chunk0_length < 1) || (length > size) || - (header_and_json_size > uint64_t(length)) || + if ((header_and_json_size > uint64_t(size)) || (chunk0_length < 1) || + (length > size) || (header_and_json_size > uint64_t(length)) || (chunk0_format != 0x4E4F534A)) { // 0x4E4F534A = JSON format. if (err) { (*err) = "Invalid glTF binary."; @@ -6623,77 +6814,105 @@ bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err, if (err) { (*err) = "JSON Chunk end does not aligned to a 4-byte boundary."; } + return false; } - //std::cout << "header_and_json_size = " << header_and_json_size << "\n"; - //std::cout << "length = " << length << "\n"; + // std::cout << "header_and_json_size = " << header_and_json_size << "\n"; + // std::cout << "length = " << length << "\n"; // Chunk1(BIN) data - // The spec says: When the binary buffer is empty or when it is stored by other means, this chunk SHOULD be omitted. - // So when header + JSON data == binary size, Chunk1 is omitted. + // The spec says: When the binary buffer is empty or when it is stored by + // other means, this chunk SHOULD be omitted. So when header + JSON data == + // binary size, Chunk1 is omitted. if (header_and_json_size == uint64_t(length)) { - bin_data_ = nullptr; bin_size_ = 0; } else { // Read Chunk1 info(BIN data) - // At least Chunk1 should have 12 bytes(8 bytes(header) + 4 bytes(bin payload could be 1~3 bytes, but need to be aligned to 4 bytes) - if ((header_and_json_size + 12ull) > uint64_t(length)) { + // + // issue-440: + // 'SHOULD' in glTF spec means 'RECOMMENDED', + // So there is a situation that Chunk1(BIN) is composed of zero-sized BIN data + // (chunksize(0) + binformat(BIN) = 8bytes). + // + if ((header_and_json_size + 8ull) > uint64_t(length)) { if (err) { - (*err) = "Insufficient storage space for Chunk1(BIN data). At least Chunk1 Must have 4 bytes or more bytes, but got " + std::to_string((header_and_json_size + 12ull) - uint64_t(length)) + ".\n"; + (*err) = + "Insufficient storage space for Chunk1(BIN data). At least Chunk1 " + "Must have 8 or more bytes, but got " + + std::to_string((header_and_json_size + 8ull) - uint64_t(length)) + + ".\n"; } return false; } - unsigned int chunk1_length; // 4 bytes - unsigned int chunk1_format; // 4 bytes; - memcpy(&chunk1_length, bytes + header_and_json_size, 4); // JSON data length + unsigned int chunk1_length{0}; // 4 bytes + unsigned int chunk1_format{0}; // 4 bytes; + memcpy(&chunk1_length, bytes + header_and_json_size, + 4); // Bin data length swap4(&chunk1_length); memcpy(&chunk1_format, bytes + header_and_json_size + 4, 4); swap4(&chunk1_format); - //std::cout << "chunk1_length = " << chunk1_length << "\n"; - - if (chunk1_length < 4) { + if (chunk1_format != 0x004e4942) { if (err) { - (*err) = "Insufficient Chunk1(BIN) data size."; + (*err) = "Invalid chunkType for Chunk1."; } return false; } - if ((chunk1_length % 4) != 0) { - if (err) { - (*err) = "BIN Chunk end does not aligned to a 4-byte boundary."; + if (chunk1_length == 0) { + + if (header_and_json_size + 8 > uint64_t(length)) { + if (err) { + (*err) = "BIN Chunk header location exceeds the GLB size."; + } + return false; } - return false; - } - if (uint64_t(chunk1_length) + header_and_json_size > uint64_t(length)) { - if (err) { - (*err) = "BIN Chunk data length exceeds the GLB size."; + bin_data_ = nullptr; + + } else { + + // When BIN chunk size is not zero, at least Chunk1 should have 12 bytes(8 bytes(header) + 4 bytes(bin + // payload could be 1~3 bytes, but need to be aligned to 4 bytes) + + if (chunk1_length < 4) { + if (err) { + (*err) = "Insufficient Chunk1(BIN) data size."; + } + return false; } - return false; - } - if (chunk1_format != 0x004e4942) { - if (err) { - (*err) = "Invalid type for chunk1 data."; + if ((chunk1_length % 4) != 0) { + if (strictness_==ParseStrictness::Permissive) { + if (warn) { + (*warn) += "BIN Chunk end is not aligned to a 4-byte boundary.\n"; + } + } + else { + if (err) { + (*err) = "BIN Chunk end is not aligned to a 4-byte boundary."; + } + return false; + } } - return false; - } - //std::cout << "chunk1_length = " << chunk1_length << "\n"; + // +8 chunk1 header size. + if (uint64_t(chunk1_length) + header_and_json_size + 8 > uint64_t(length)) { + if (err) { + (*err) = "BIN Chunk data length exceeds the GLB size."; + } + return false; + } - bin_data_ = bytes + header_and_json_size + - 8; // 4 bytes (bin_buffer_length) + 4 bytes(bin_buffer_format) + bin_data_ = bytes + header_and_json_size + + 8; // 4 bytes (bin_buffer_length) + 4 bytes(bin_buffer_format) + } bin_size_ = size_t(chunk1_length); } - // Extract JSON string. - std::string jsonString(reinterpret_cast(&bytes[20]), - chunk0_length); - is_binary_ = true; bool ret = LoadFromString(model, err, warn, @@ -6767,7 +6986,18 @@ void JsonAddMember(detail::json &o, const char *key, detail::json &&value) { if (!o.IsObject()) { o.SetObject(); } - o.AddMember(detail::json(key, detail::GetAllocator()), std::move(value), detail::GetAllocator()); + + // Issue 420. + // AddMember may create duplicated key, so use [] API when a key already + // exists. + // https://github.com/Tencent/rapidjson/issues/771#issuecomment-254386863 + detail::json_const_iterator it; + if (detail::FindMember(o, key, it)) { + o[key] = std::move(value); // replace + } else { + o.AddMember(detail::json(key, detail::GetAllocator()), std::move(value), + detail::GetAllocator()); + } #else o[key] = std::move(value); #endif @@ -6805,7 +7035,7 @@ void JsonReserveArray(detail::json &o, size_t s) { (void)(o); (void)(s); } -} // namespace +} // namespace detail // typedef std::pair json_object_pair; @@ -6820,8 +7050,10 @@ static void SerializeNumberProperty(const std::string &key, T number, #ifdef TINYGLTF_USE_RAPIDJSON template <> -void SerializeNumberProperty(const std::string &key, size_t number, detail::json &obj) { - detail::JsonAddMember(obj, key.c_str(), detail::json(static_cast(number))); +void SerializeNumberProperty(const std::string &key, size_t number, + detail::json &obj) { + detail::JsonAddMember(obj, key.c_str(), + detail::json(static_cast(number))); } #endif @@ -6840,8 +7072,10 @@ static void SerializeNumberArrayProperty(const std::string &key, } static void SerializeStringProperty(const std::string &key, - const std::string &value, detail::json &obj) { - detail::JsonAddMember(obj, key.c_str(), detail::JsonFromString(value.c_str())); + const std::string &value, + detail::json &obj) { + detail::JsonAddMember(obj, key.c_str(), + detail::JsonFromString(value.c_str())); } static void SerializeStringArrayProperty(const std::string &key, @@ -6919,10 +7153,10 @@ static bool ValueToJson(const Value &value, detail::json *ret) { obj = detail::json(value.Get()); break; case ARRAY_TYPE: { - for (unsigned int i = 0; i < value.ArrayLen(); ++i) { - Value elementValue = value.Get(int(i)); + for (size_t i = 0; i < value.ArrayLen(); ++i) { + Value elementValue = value.Get(i); detail::json elementJson; - if (ValueToJson(value.Get(int(i)), &elementJson)) + if (ValueToJson(value.Get(i), &elementJson)) obj.push_back(elementJson); } break; @@ -6976,7 +7210,7 @@ static bool SerializeGltfBufferData(const std::vector &data, #ifdef _WIN32 #if defined(__GLIBCXX__) // mingw int file_descriptor = _wopen(UTF8ToWchar(binFilename).c_str(), - _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY); + _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY, _S_IWRITE); __gnu_cxx::stdio_filebuf wfile_buf( file_descriptor, std::ios_base::out | std::ios_base::binary); std::ostream output(&wfile_buf); @@ -7034,7 +7268,8 @@ static void SerializeParameterMap(ParameterMap ¶m, detail::json &o) { } #endif -static void SerializeExtensionMap(const ExtensionMap &extensions, detail::json &o) { +static void SerializeExtensionMap(const ExtensionMap &extensions, + detail::json &o) { if (!extensions.size()) return; detail::json extMap; @@ -7060,12 +7295,22 @@ static void SerializeExtensionMap(const ExtensionMap &extensions, detail::json & detail::JsonAddMember(o, "extensions", std::move(extMap)); } +static void SerializeExtras(const Value &extras, detail::json &o) { + if (extras.Type() != NULL_TYPE) SerializeValue("extras", extras, o); +} + +template +void SerializeExtrasAndExtensions(const GltfType &obj, detail::json &o) { + SerializeExtensionMap(obj.extensions, o); + SerializeExtras(obj.extras, o); +} + static void SerializeGltfAccessor(const Accessor &accessor, detail::json &o) { if (accessor.bufferView >= 0) SerializeNumberProperty("bufferView", accessor.bufferView, o); if (accessor.byteOffset != 0) - SerializeNumberProperty("byteOffset", int(accessor.byteOffset), o); + SerializeNumberProperty("byteOffset", accessor.byteOffset, o); SerializeNumberProperty("componentType", accessor.componentType, o); SerializeNumberProperty("count", accessor.count, o); @@ -7126,29 +7371,34 @@ static void SerializeGltfAccessor(const Accessor &accessor, detail::json &o) { SerializeStringProperty("type", type, o); if (!accessor.name.empty()) SerializeStringProperty("name", accessor.name, o); - if (accessor.extras.Type() != NULL_TYPE) { - SerializeValue("extras", accessor.extras, o); - } + SerializeExtrasAndExtensions(accessor, o); // sparse - if (accessor.sparse.isSparse) - { - detail::json sparse; - SerializeNumberProperty("count", accessor.sparse.count, sparse); - { - detail::json indices; - SerializeNumberProperty("bufferView", accessor.sparse.indices.bufferView, indices); - SerializeNumberProperty("byteOffset", accessor.sparse.indices.byteOffset, indices); - SerializeNumberProperty("componentType", accessor.sparse.indices.componentType, indices); - detail::JsonAddMember(sparse, "indices", std::move(indices)); - } - { - detail::json values; - SerializeNumberProperty("bufferView", accessor.sparse.values.bufferView, values); - SerializeNumberProperty("byteOffset", accessor.sparse.values.byteOffset, values); - detail::JsonAddMember(sparse, "values", std::move(values)); - } - detail::JsonAddMember(o, "sparse", std::move(sparse)); + if (accessor.sparse.isSparse) { + detail::json sparse; + SerializeNumberProperty("count", accessor.sparse.count, sparse); + { + detail::json indices; + SerializeNumberProperty("bufferView", + accessor.sparse.indices.bufferView, indices); + SerializeNumberProperty("byteOffset", + accessor.sparse.indices.byteOffset, indices); + SerializeNumberProperty( + "componentType", accessor.sparse.indices.componentType, indices); + SerializeExtrasAndExtensions(accessor.sparse.indices, indices); + detail::JsonAddMember(sparse, "indices", std::move(indices)); + } + { + detail::json values; + SerializeNumberProperty("bufferView", + accessor.sparse.values.bufferView, values); + SerializeNumberProperty("byteOffset", + accessor.sparse.values.byteOffset, values); + SerializeExtrasAndExtensions(accessor.sparse.values, values); + detail::JsonAddMember(sparse, "values", std::move(values)); + } + SerializeExtrasAndExtensions(accessor.sparse, sparse); + detail::JsonAddMember(o, "sparse", std::move(sparse)); } } @@ -7165,15 +7415,12 @@ static void SerializeGltfAnimationChannel(const AnimationChannel &channel, SerializeStringProperty("path", channel.target_path, target); SerializeExtensionMap(channel.target_extensions, target); + SerializeExtras(channel.target_extras, target); detail::JsonAddMember(o, "target", std::move(target)); } - if (channel.extras.Type() != NULL_TYPE) { - SerializeValue("extras", channel.extras, o); - } - - SerializeExtensionMap(channel.extensions, o); + SerializeExtrasAndExtensions(channel, o); } static void SerializeGltfAnimationSampler(const AnimationSampler &sampler, @@ -7182,12 +7429,11 @@ static void SerializeGltfAnimationSampler(const AnimationSampler &sampler, SerializeNumberProperty("output", sampler.output, o); SerializeStringProperty("interpolation", sampler.interpolation, o); - if (sampler.extras.Type() != NULL_TYPE) { - SerializeValue("extras", sampler.extras, o); - } + SerializeExtrasAndExtensions(sampler, o); } -static void SerializeGltfAnimation(const Animation &animation, detail::json &o) { +static void SerializeGltfAnimation(const Animation &animation, + detail::json &o) { if (!animation.name.empty()) SerializeStringProperty("name", animation.name, o); @@ -7216,11 +7462,7 @@ static void SerializeGltfAnimation(const Animation &animation, detail::json &o) detail::JsonAddMember(o, "samplers", std::move(samplers)); } - if (animation.extras.Type() != NULL_TYPE) { - SerializeValue("extras", animation.extras, o); - } - - SerializeExtensionMap(animation.extensions, o); + SerializeExtrasAndExtensions(animation, o); } static void SerializeGltfAsset(const Asset &asset, detail::json &o) { @@ -7242,11 +7484,7 @@ static void SerializeGltfAsset(const Asset &asset, detail::json &o) { // TODO(syoyo): Do we need to check if `version` is greater or equal to 2.0? SerializeStringProperty("version", version, o); - if (asset.extras.Keys().size()) { - SerializeValue("extras", asset.extras, o); - } - - SerializeExtensionMap(asset.extensions, o); + SerializeExtrasAndExtensions(asset, o); } static void SerializeGltfBufferBin(const Buffer &buffer, detail::json &o, @@ -7256,9 +7494,7 @@ static void SerializeGltfBufferBin(const Buffer &buffer, detail::json &o, if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o); - if (buffer.extras.Type() != NULL_TYPE) { - SerializeValue("extras", buffer.extras, o); - } + SerializeExtrasAndExtensions(buffer, o); } static void SerializeGltfBuffer(const Buffer &buffer, detail::json &o) { @@ -7267,9 +7503,7 @@ static void SerializeGltfBuffer(const Buffer &buffer, detail::json &o) { if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o); - if (buffer.extras.Type() != NULL_TYPE) { - SerializeValue("extras", buffer.extras, o); - } + SerializeExtrasAndExtensions(buffer, o); } static bool SerializeGltfBuffer(const Buffer &buffer, detail::json &o, @@ -7281,13 +7515,12 @@ static bool SerializeGltfBuffer(const Buffer &buffer, detail::json &o, if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o); - if (buffer.extras.Type() != NULL_TYPE) { - SerializeValue("extras", buffer.extras, o); - } + SerializeExtrasAndExtensions(buffer, o); return true; } -static void SerializeGltfBufferView(const BufferView &bufferView, detail::json &o) { +static void SerializeGltfBufferView(const BufferView &bufferView, + detail::json &o) { SerializeNumberProperty("buffer", bufferView.buffer, o); SerializeNumberProperty("byteLength", bufferView.byteLength, o); @@ -7308,9 +7541,7 @@ static void SerializeGltfBufferView(const BufferView &bufferView, detail::json & SerializeStringProperty("name", bufferView.name, o); } - if (bufferView.extras.Type() != NULL_TYPE) { - SerializeValue("extras", bufferView.extras, o); - } + SerializeExtrasAndExtensions(bufferView, o); } static void SerializeGltfImage(const Image &image, const std::string &uri, @@ -7328,25 +7559,18 @@ static void SerializeGltfImage(const Image &image, const std::string &uri, SerializeStringProperty("name", image.name, o); } - if (image.extras.Type() != NULL_TYPE) { - SerializeValue("extras", image.extras, o); - } - - SerializeExtensionMap(image.extensions, o); + SerializeExtrasAndExtensions(image, o); } -static void SerializeGltfTextureInfo(const TextureInfo &texinfo, detail::json &o) { +static void SerializeGltfTextureInfo(const TextureInfo &texinfo, + detail::json &o) { SerializeNumberProperty("index", texinfo.index, o); if (texinfo.texCoord != 0) { SerializeNumberProperty("texCoord", texinfo.texCoord, o); } - if (texinfo.extras.Type() != NULL_TYPE) { - SerializeValue("extras", texinfo.extras, o); - } - - SerializeExtensionMap(texinfo.extensions, o); + SerializeExtrasAndExtensions(texinfo, o); } static void SerializeGltfNormalTextureInfo(const NormalTextureInfo &texinfo, @@ -7361,11 +7585,7 @@ static void SerializeGltfNormalTextureInfo(const NormalTextureInfo &texinfo, SerializeNumberProperty("scale", texinfo.scale, o); } - if (texinfo.extras.Type() != NULL_TYPE) { - SerializeValue("extras", texinfo.extras, o); - } - - SerializeExtensionMap(texinfo.extensions, o); + SerializeExtrasAndExtensions(texinfo, o); } static void SerializeGltfOcclusionTextureInfo( @@ -7380,11 +7600,7 @@ static void SerializeGltfOcclusionTextureInfo( SerializeNumberProperty("strength", texinfo.strength, o); } - if (texinfo.extras.Type() != NULL_TYPE) { - SerializeValue("extras", texinfo.extras, o); - } - - SerializeExtensionMap(texinfo.extensions, o); + SerializeExtrasAndExtensions(texinfo, o); } static void SerializeGltfPbrMetallicRoughness(const PbrMetallicRoughness &pbr, @@ -7415,11 +7631,7 @@ static void SerializeGltfPbrMetallicRoughness(const PbrMetallicRoughness &pbr, detail::JsonAddMember(o, "metallicRoughnessTexture", std::move(texinfo)); } - SerializeExtensionMap(pbr.extensions, o); - - if (pbr.extras.Type() != NULL_TYPE) { - SerializeValue("extras", pbr.extras, o); - } + SerializeExtrasAndExtensions(pbr, o); } static void SerializeGltfMaterial(const Material &material, detail::json &o) { @@ -7475,7 +7687,8 @@ static void SerializeGltfMaterial(const Material &material, detail::json &o) { // importers (and validators). // if (!detail::JsonIsNull(pbrMetallicRoughness)) { - detail::JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness)); + detail::JsonAddMember(o, "pbrMetallicRoughness", + std::move(pbrMetallicRoughness)); } } @@ -7487,14 +7700,39 @@ static void SerializeGltfMaterial(const Material &material, detail::json &o) { } SerializeParameterMap(material.additionalValues, o); -#else - #endif - SerializeExtensionMap(material.extensions, o); - - if (material.extras.Type() != NULL_TYPE) { - SerializeValue("extras", material.extras, o); + SerializeExtrasAndExtensions(material, o); + + // MSFT_lod + if (!material.lods.empty()) { + detail::json_iterator it; + if (!detail::FindMember(o, "extensions", it)) { + detail::json extensions; + detail::JsonSetObject(extensions); + detail::JsonAddMember(o, "extensions", std::move(extensions)); + detail::FindMember(o, "extensions", it); + } + auto &extensions = detail::GetValue(it); + if (!detail::FindMember(extensions, "MSFT_lod", it)) { + detail::json lod; + detail::JsonSetObject(lod); + detail::JsonAddMember(extensions, "MSFT_lod", std::move(lod)); + detail::FindMember(extensions, "MSFT_lod", it); + } + SerializeNumberArrayProperty("ids", material.lods, detail::GetValue(it)); + } else { + detail::json_iterator ext_it; + if (detail::FindMember(o, "extensions", ext_it)) { + auto &extensions = detail::GetValue(ext_it); + detail::json_iterator lp_it; + if (detail::FindMember(extensions, "MSFT_lod", lp_it)) { + detail::Erase(extensions, lp_it); + } + if (detail::IsEmpty(extensions)) { + detail::Erase(o, ext_it); + } + } } } @@ -7542,11 +7780,7 @@ static void SerializeGltfMesh(const Mesh &mesh, detail::json &o) { detail::JsonAddMember(primitive, "targets", std::move(targets)); } - SerializeExtensionMap(gltfPrimitive.extensions, primitive); - - if (gltfPrimitive.extras.Type() != NULL_TYPE) { - SerializeValue("extras", gltfPrimitive.extras, primitive); - } + SerializeExtrasAndExtensions(gltfPrimitive, primitive); detail::JsonPushBack(primitives, std::move(primitive)); } @@ -7561,19 +7795,13 @@ static void SerializeGltfMesh(const Mesh &mesh, detail::json &o) { SerializeStringProperty("name", mesh.name, o); } - SerializeExtensionMap(mesh.extensions, o); - if (mesh.extras.Type() != NULL_TYPE) { - SerializeValue("extras", mesh.extras, o); - } + SerializeExtrasAndExtensions(mesh, o); } static void SerializeSpotLight(const SpotLight &spot, detail::json &o) { SerializeNumberProperty("innerConeAngle", spot.innerConeAngle, o); SerializeNumberProperty("outerConeAngle", spot.outerConeAngle, o); - SerializeExtensionMap(spot.extensions, o); - if (spot.extras.Type() != NULL_TYPE) { - SerializeValue("extras", spot.extras, o); - } + SerializeExtrasAndExtensions(spot, o); } static void SerializeGltfLight(const Light &light, detail::json &o) { @@ -7589,10 +7817,61 @@ static void SerializeGltfLight(const Light &light, detail::json &o) { SerializeSpotLight(light.spot, spot); detail::JsonAddMember(o, "spot", std::move(spot)); } - SerializeExtensionMap(light.extensions, o); - if (light.extras.Type() != NULL_TYPE) { - SerializeValue("extras", light.extras, o); + SerializeExtrasAndExtensions(light, o); +} + +static void SerializeGltfPositionalEmitter(const PositionalEmitter &positional, + detail::json &o) { + if (!TINYGLTF_DOUBLE_EQUAL(positional.coneInnerAngle, 6.283185307179586)) + SerializeNumberProperty("coneInnerAngle", positional.coneInnerAngle, o); + if (!TINYGLTF_DOUBLE_EQUAL(positional.coneOuterAngle, 6.283185307179586)) + SerializeNumberProperty("coneOuterAngle", positional.coneOuterAngle, o); + if (positional.coneOuterGain > 0.0) + SerializeNumberProperty("coneOuterGain", positional.coneOuterGain, o); + if (!TINYGLTF_DOUBLE_EQUAL(positional.maxDistance, 100.0)) + SerializeNumberProperty("maxDistance", positional.maxDistance, o); + if (!TINYGLTF_DOUBLE_EQUAL(positional.refDistance, 1.0)) + SerializeNumberProperty("refDistance", positional.refDistance, o); + if (!TINYGLTF_DOUBLE_EQUAL(positional.rolloffFactor, 1.0)) + SerializeNumberProperty("rolloffFactor", positional.rolloffFactor, o); + + SerializeExtrasAndExtensions(positional, o); +} + +static void SerializeGltfAudioEmitter(const AudioEmitter &emitter, + detail::json &o) { + if (!emitter.name.empty()) SerializeStringProperty("name", emitter.name, o); + if (!TINYGLTF_DOUBLE_EQUAL(emitter.gain, 1.0)) + SerializeNumberProperty("gain", emitter.gain, o); + if (emitter.loop) SerializeNumberProperty("loop", emitter.loop, o); + if (emitter.playing) SerializeNumberProperty("playing", emitter.playing, o); + if (!emitter.type.empty()) SerializeStringProperty("type", emitter.type, o); + if (!emitter.distanceModel.empty()) + SerializeStringProperty("distanceModel", emitter.distanceModel, o); + if (emitter.type == "positional") { + detail::json positional; + SerializeGltfPositionalEmitter(emitter.positional, positional); + detail::JsonAddMember(o, "positional", std::move(positional)); + } + SerializeNumberProperty("source", emitter.source, o); + SerializeExtrasAndExtensions(emitter, o); +} + +static void SerializeGltfAudioSource(const AudioSource &source, + detail::json &o) { + std::string name; + std::string uri; + std::string mimeType; // (required if no uri) ["audio/mp3", "audio/ogg", + // "audio/wav", "audio/m4a"] + + if (!source.name.empty()) SerializeStringProperty("name", source.name, o); + if (source.uri.empty()) { + SerializeStringProperty("mimeType", source.mimeType, o); + SerializeNumberProperty("bufferView", source.bufferView, o); + } else { + SerializeStringProperty("uri", source.uri, o); } + SerializeExtrasAndExtensions(source, o); } static void SerializeGltfNode(const Node &node, detail::json &o) { @@ -7624,11 +7903,107 @@ static void SerializeGltfNode(const Node &node, detail::json &o) { SerializeNumberArrayProperty("weights", node.weights, o); } - if (node.extras.Type() != NULL_TYPE) { - SerializeValue("extras", node.extras, o); + SerializeExtrasAndExtensions(node, o); + + // Note(agnat): If the asset was loaded from disk, the node may already + // contain the KHR_lights_punctual extension. If it was constructed in + // memory it does not. In any case we update the JSON property using + // the value from the struct. Last, if the node does not have a light + // reference but the extension is still present, we remove it. + if (node.light != -1) { + detail::json_iterator it; + if (!detail::FindMember(o, "extensions", it)) { + detail::json extensions; + detail::JsonSetObject(extensions); + detail::JsonAddMember(o, "extensions", std::move(extensions)); + detail::FindMember(o, "extensions", it); + } + auto &extensions = detail::GetValue(it); + if (!detail::FindMember(extensions, "KHR_lights_punctual", it)) { + detail::json lights_punctual; + detail::JsonSetObject(lights_punctual); + detail::JsonAddMember(extensions, "KHR_lights_punctual", + std::move(lights_punctual)); + detail::FindMember(extensions, "KHR_lights_punctual", it); + } + SerializeNumberProperty("light", node.light, detail::GetValue(it)); + } else { + // node has no light ref (any longer)... so we clean up + detail::json_iterator ext_it; + if (detail::FindMember(o, "extensions", ext_it)) { + auto &extensions = detail::GetValue(ext_it); + detail::json_iterator lp_it; + if (detail::FindMember(extensions, "KHR_lights_punctual", lp_it)) { + detail::Erase(extensions, lp_it); + } + if (detail::IsEmpty(extensions)) { + detail::Erase(o, ext_it); + } + } + } + + // KHR_audio + if (node.emitter != -1) { + detail::json_iterator it; + if (!detail::FindMember(o, "extensions", it)) { + detail::json extensions; + detail::JsonSetObject(extensions); + detail::JsonAddMember(o, "extensions", std::move(extensions)); + detail::FindMember(o, "extensions", it); + } + auto &extensions = detail::GetValue(it); + if (!detail::FindMember(extensions, "KHR_audio", it)) { + detail::json audio; + detail::JsonSetObject(audio); + detail::JsonAddMember(extensions, "KHR_audio", std::move(audio)); + detail::FindMember(extensions, "KHR_audio", it); + } + SerializeNumberProperty("emitter", node.emitter, detail::GetValue(it)); + } else { + detail::json_iterator ext_it; + if (detail::FindMember(o, "extensions", ext_it)) { + auto &extensions = detail::GetValue(ext_it); + detail::json_iterator lp_it; + if (detail::FindMember(extensions, "KHR_audio", lp_it)) { + detail::Erase(extensions, lp_it); + } + if (detail::IsEmpty(extensions)) { + detail::Erase(o, ext_it); + } + } + } + + // MSFT_lod + if (!node.lods.empty()) { + detail::json_iterator it; + if (!detail::FindMember(o, "extensions", it)) { + detail::json extensions; + detail::JsonSetObject(extensions); + detail::JsonAddMember(o, "extensions", std::move(extensions)); + detail::FindMember(o, "extensions", it); + } + auto &extensions = detail::GetValue(it); + if (!detail::FindMember(extensions, "MSFT_lod", it)) { + detail::json lod; + detail::JsonSetObject(lod); + detail::JsonAddMember(extensions, "MSFT_lod", std::move(lod)); + detail::FindMember(extensions, "MSFT_lod", it); + } + SerializeNumberArrayProperty("ids", node.lods, detail::GetValue(it)); + } else { + detail::json_iterator ext_it; + if (detail::FindMember(o, "extensions", ext_it)) { + auto &extensions = detail::GetValue(ext_it); + detail::json_iterator lp_it; + if (detail::FindMember(extensions, "MSFT_lod", lp_it)) { + detail::Erase(extensions, lp_it); + } + if (detail::IsEmpty(extensions)) { + detail::Erase(o, ext_it); + } + } } - SerializeExtensionMap(node.extensions, o); if (!node.name.empty()) SerializeStringProperty("name", node.name, o); SerializeNumberArrayProperty("children", node.children, o); } @@ -7647,9 +8022,7 @@ static void SerializeGltfSampler(const Sampler &sampler, detail::json &o) { SerializeNumberProperty("wrapS", sampler.wrapS, o); SerializeNumberProperty("wrapT", sampler.wrapT, o); - if (sampler.extras.Type() != NULL_TYPE) { - SerializeValue("extras", sampler.extras, o); - } + SerializeExtrasAndExtensions(sampler, o); } static void SerializeGltfOrthographicCamera(const OrthographicCamera &camera, @@ -7659,9 +8032,7 @@ static void SerializeGltfOrthographicCamera(const OrthographicCamera &camera, SerializeNumberProperty("xmag", camera.xmag, o); SerializeNumberProperty("ymag", camera.ymag, o); - if (camera.extras.Type() != NULL_TYPE) { - SerializeValue("extras", camera.extras, o); - } + SerializeExtrasAndExtensions(camera, o); } static void SerializeGltfPerspectiveCamera(const PerspectiveCamera &camera, @@ -7676,9 +8047,7 @@ static void SerializeGltfPerspectiveCamera(const PerspectiveCamera &camera, SerializeNumberProperty("yfov", camera.yfov, o); } - if (camera.extras.Type() != NULL_TYPE) { - SerializeValue("extras", camera.extras, o); - } + SerializeExtrasAndExtensions(camera, o); } static void SerializeGltfCamera(const Camera &camera, detail::json &o) { @@ -7699,10 +8068,7 @@ static void SerializeGltfCamera(const Camera &camera, detail::json &o) { // ??? } - if (camera.extras.Type() != NULL_TYPE) { - SerializeValue("extras", camera.extras, o); - } - SerializeExtensionMap(camera.extensions, o); + SerializeExtrasAndExtensions(camera, o); } static void SerializeGltfScene(const Scene &scene, detail::json &o) { @@ -7711,10 +8077,39 @@ static void SerializeGltfScene(const Scene &scene, detail::json &o) { if (scene.name.size()) { SerializeStringProperty("name", scene.name, o); } - if (scene.extras.Type() != NULL_TYPE) { - SerializeValue("extras", scene.extras, o); + SerializeExtrasAndExtensions(scene, o); + + // KHR_audio + if (!scene.audioEmitters.empty()) { + detail::json_iterator it; + if (!detail::FindMember(o, "extensions", it)) { + detail::json extensions; + detail::JsonSetObject(extensions); + detail::JsonAddMember(o, "extensions", std::move(extensions)); + detail::FindMember(o, "extensions", it); + } + auto &extensions = detail::GetValue(it); + if (!detail::FindMember(extensions, "KHR_audio", it)) { + detail::json audio; + detail::JsonSetObject(audio); + detail::JsonAddMember(extensions, "KHR_audio", std::move(audio)); + detail::FindMember(o, "KHR_audio", it); + } + SerializeNumberArrayProperty("emitters", scene.audioEmitters, + detail::GetValue(it)); + } else { + detail::json_iterator ext_it; + if (detail::FindMember(o, "extensions", ext_it)) { + auto &extensions = detail::GetValue(ext_it); + detail::json_iterator lp_it; + if (detail::FindMember(extensions, "KHR_audio", lp_it)) { + detail::Erase(extensions, lp_it); + } + if (detail::IsEmpty(extensions)) { + detail::Erase(o, ext_it); + } + } } - SerializeExtensionMap(scene.extensions, o); } static void SerializeGltfSkin(const Skin &skin, detail::json &o) { @@ -7732,6 +8127,8 @@ static void SerializeGltfSkin(const Skin &skin, detail::json &o) { if (skin.name.size()) { SerializeStringProperty("name", skin.name, o); } + + SerializeExtrasAndExtensions(skin, o); } static void SerializeGltfTexture(const Texture &texture, detail::json &o) { @@ -7744,10 +8141,7 @@ static void SerializeGltfTexture(const Texture &texture, detail::json &o) { if (texture.name.size()) { SerializeStringProperty("name", texture.name, o); } - if (texture.extras.Type() != NULL_TYPE) { - SerializeValue("extras", texture.extras, o); - } - SerializeExtensionMap(texture.extensions, o); + SerializeExtrasAndExtensions(texture, o); } /// @@ -7845,6 +8239,16 @@ static void SerializeGltfModel(const Model *model, detail::json &o) { for (unsigned int i = 0; i < model->nodes.size(); ++i) { detail::json node; SerializeGltfNode(model->nodes[i], node); + + if (detail::JsonIsNull(node)) { + // Issue 457. + // `node` does not have any required parameters, + // so the result may be null(unmodified) when all node parameters + // have default value. + // + // null is not allowed thus we create an empty JSON object. + detail::JsonSetObject(node); + } detail::JsonPushBack(nodes, std::move(node)); } detail::JsonAddMember(o, "nodes", std::move(nodes)); @@ -7862,6 +8266,15 @@ static void SerializeGltfModel(const Model *model, detail::json &o) { for (unsigned int i = 0; i < model->scenes.size(); ++i) { detail::json currentScene; SerializeGltfScene(model->scenes[i], currentScene); + if (detail::JsonIsNull(currentScene)) { + // Issue 464. + // `scene` does not have any required parameters, + // so the result may be null(unmodified) when all scene parameters + // have default value. + // + // null is not allowed thus we create an empty JSON object. + detail::JsonSetObject(currentScene); + } detail::JsonPushBack(scenes, std::move(currentScene)); } detail::JsonAddMember(o, "scenes", std::move(scenes)); @@ -7915,8 +8328,8 @@ static void SerializeGltfModel(const Model *model, detail::json &o) { detail::JsonAddMember(o, "cameras", std::move(cameras)); } - // EXTENSIONS - SerializeExtensionMap(model->extensions, o); + // EXTRAS & EXTENSIONS + SerializeExtrasAndExtensions(*model, o); auto extensionsUsed = model->extensionsUsed; @@ -7940,7 +8353,8 @@ static void SerializeGltfModel(const Model *model, detail::json &o) { } } - detail::JsonAddMember(ext_j, "KHR_lights_punctual", std::move(khr_lights_cmn)); + detail::JsonAddMember(ext_j, "KHR_lights_punctual", + std::move(khr_lights_cmn)); detail::JsonAddMember(o, "extensions", std::move(ext_j)); @@ -7958,15 +8372,81 @@ static void SerializeGltfModel(const Model *model, detail::json &o) { } } + // KHR_audio + if (!model->audioEmitters.empty() || !model->audioSources.empty()) { + detail::json emitters; + detail::JsonReserveArray(emitters, model->audioEmitters.size()); + for (unsigned int i = 0; i < model->audioEmitters.size(); ++i) { + detail::json emitter; + SerializeGltfAudioEmitter(model->audioEmitters[i], emitter); + detail::JsonPushBack(emitters, std::move(emitter)); + } + detail::json khr_audio_cmn; + detail::JsonAddMember(khr_audio_cmn, "emitters", std::move(emitters)); + + detail::json sources; + detail::JsonReserveArray(sources, model->audioSources.size()); + for (unsigned int i = 0; i < model->audioSources.size(); ++i) { + detail::json source; + SerializeGltfAudioSource(model->audioSources[i], source); + detail::JsonPushBack(sources, std::move(source)); + } + detail::JsonAddMember(khr_audio_cmn, "sources", std::move(sources)); + + detail::json ext_j; + { + detail::json_const_iterator it; + if (detail::FindMember(o, "extensions", it)) { + detail::JsonAssign(ext_j, detail::GetValue(it)); + } + } + + detail::JsonAddMember(ext_j, "KHR_audio", std::move(khr_audio_cmn)); + + detail::JsonAddMember(o, "extensions", std::move(ext_j)); + + // Also add "KHR_audio" to `extensionsUsed` + { + auto has_khr_audio = std::find_if( + extensionsUsed.begin(), extensionsUsed.end(), + [](const std::string &s) { return (s.compare("KHR_audio") == 0); }); + + if (has_khr_audio == extensionsUsed.end()) { + extensionsUsed.push_back("KHR_audio"); + } + } + } + + // MSFT_lod + + // Look if there is a node that employs MSFT_lod + auto msft_lod_nodes_it = std::find_if( + model->nodes.begin(), model->nodes.end(), + [](const Node& node) { return !node.lods.empty(); }); + + // Look if there is a material that employs MSFT_lod + auto msft_lod_materials_it = std::find_if( + model->materials.begin(), model->materials.end(), + [](const Material& material) {return !material.lods.empty(); }); + + // If either a node or a material employ MSFT_lod, then we need + // to add MSFT_lod to the list of used extensions. + if (msft_lod_nodes_it != model->nodes.end() || msft_lod_materials_it != model->materials.end()) { + // First check if MSFT_lod is already registered as used extension + auto has_msft_lod = std::find_if( + extensionsUsed.begin(), extensionsUsed.end(), + [](const std::string &s) { return (s.compare("MSFT_lod") == 0); }); + + // If MSFT_lod is not registered yet, add it + if (has_msft_lod == extensionsUsed.end()) { + extensionsUsed.push_back("MSFT_lod"); + } + } + // Extensions used if (extensionsUsed.size()) { SerializeStringArrayProperty("extensionsUsed", extensionsUsed, o); } - - // EXTRAS - if (model->extras.Type() != NULL_TYPE) { - SerializeValue("extras", model->extras, o); - } } static bool WriteGltfStream(std::ostream &stream, const std::string &content) { @@ -7981,7 +8461,7 @@ static bool WriteGltfFile(const std::string &output, std::ofstream gltfFile(UTF8ToWchar(output).c_str()); #elif defined(__GLIBCXX__) int file_descriptor = _wopen(UTF8ToWchar(output).c_str(), - _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY); + _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY, _S_IWRITE); __gnu_cxx::stdio_filebuf wfile_buf( file_descriptor, std::ios_base::out | std::ios_base::binary); std::ostream gltfFile(&wfile_buf); @@ -8066,7 +8546,7 @@ static bool WriteBinaryGltfFile(const std::string &output, std::ofstream gltfFile(UTF8ToWchar(output).c_str(), std::ios::binary); #elif defined(__GLIBCXX__) int file_descriptor = _wopen(UTF8ToWchar(output).c_str(), - _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY); + _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY, _S_IWRITE); __gnu_cxx::stdio_filebuf wfile_buf( file_descriptor, std::ios_base::out | std::ios_base::binary); std::ostream gltfFile(&wfile_buf); @@ -8111,13 +8591,13 @@ bool TinyGLTF::WriteGltfSceneToStream(const Model *model, std::ostream &stream, for (unsigned int i = 0; i < model->images.size(); ++i) { detail::json image; - std::string dummystring = ""; + std::string dummystring; // UpdateImageObject need baseDir but only uses it if embeddedImages is // enabled, since we won't write separate images when writing to a stream // we std::string uri; if (!UpdateImageObject(model->images[i], dummystring, int(i), true, - &uri_cb, &this->WriteImageData, + &fs, &uri_cb, this->WriteImageData, this->write_image_user_data_, &uri)) { return false; } @@ -8128,9 +8608,11 @@ bool TinyGLTF::WriteGltfSceneToStream(const Model *model, std::ostream &stream, } if (writeBinary) { - return WriteBinaryGltfStream(stream, detail::JsonToString(output), binBuffer); + return WriteBinaryGltfStream(stream, detail::JsonToString(output), + binBuffer); } else { - return WriteGltfStream(stream, detail::JsonToString(output, prettyPrint ? 2 : -1)); + return WriteGltfStream(stream, + detail::JsonToString(output, prettyPrint ? 2 : -1)); } } @@ -8223,7 +8705,7 @@ bool TinyGLTF::WriteGltfSceneToFile(const Model *model, std::string uri; if (!UpdateImageObject(model->images[i], baseDir, int(i), embedImages, - &uri_cb, &this->WriteImageData, + &fs, &uri_cb, this->WriteImageData, this->write_image_user_data_, &uri)) { return false; } @@ -8234,9 +8716,11 @@ bool TinyGLTF::WriteGltfSceneToFile(const Model *model, } if (writeBinary) { - return WriteBinaryGltfFile(filename, detail::JsonToString(output), binBuffer); + return WriteBinaryGltfFile(filename, detail::JsonToString(output), + binBuffer); } else { - return WriteGltfFile(filename, detail::JsonToString(output, (prettyPrint ? 2 : -1))); + return WriteGltfFile(filename, + detail::JsonToString(output, (prettyPrint ? 2 : -1))); } } diff --git a/src/scripts/conformance_generator.py b/src/scripts/conformance_generator.py index b8a3dfff..d51815c2 100644 --- a/src/scripts/conformance_generator.py +++ b/src/scripts/conformance_generator.py @@ -79,15 +79,16 @@ def _compute_avail_symbols(self) -> List[Tuple[str, FrozenAvailability]]: temp = AvailabilitySymbols() for profile in self.interaction_profiles.interaction_profiles.values(): temp.add(profile.availability) - for component in profile.components.values(): - temp.add(component.availability) + for _, component in profile.yield_user_path_and_component_pairs(): + avail = profile.compute_component_availability(component) + temp.add(avail) return temp.make_frozen() # Write out all the information for the appropriate file, # and then call down to the base class to wrap everything up. # self the ConformanceLayerBaseGenerator object def endFile(self): - self.interaction_profiles.process_deferred() + self.interaction_profiles.finish_processing() avail_syms = self._compute_avail_symbols() sorted_cmds = self.core_commands + self.ext_commands file_data = self.template.render( diff --git a/src/scripts/interaction_profile_processor.py b/src/scripts/interaction_profile_processor.py index 6313248c..7dcaad49 100644 --- a/src/scripts/interaction_profile_processor.py +++ b/src/scripts/interaction_profile_processor.py @@ -19,23 +19,33 @@ def _format_conjunction(and_terms): if len(and_terms) > 1: - return f"({'+'.join(and_terms)})" + return f"({'+'.join(sorted(and_terms))})" assert and_terms return tuple(and_terms)[0] def _repr_conjunction(and_terms): - terms = ", ".join(f"'{t}'" for t in and_terms) + terms = ", ".join(f"'{t}'" for t in sorted(and_terms)) return "".join(("{", terms, "}")) FrozenAvailability = Tuple[Tuple[str, ...], ...] + class Availability: """ Information on when something is available. In 'disjunctive normal form' - an OR of ANDs. + + >>> Availability([{'a'}, {'b'}]) + [{'a'}, {'b'}] + + >>> Availability([{'a'}, {'a', 'b'}]) + [{'a'}] + + >>> Availability([{'a', 'b'}, {'a'}]) + [{'a'}] """ def __init__(self, conjunctions: Iterable[Set[str]]): @@ -54,16 +64,107 @@ def add(self, features: Set[str]) -> bool: Add a new set of features that would make this available. Return true if this feature set is redundant given what already exists. + + >>> Availability([{'a'}]).add({'a', 'b'}) + True + + >>> Availability([{'a'}]).add({'b'}) + False + + >>> a = Availability([{'a'}]) + >>> a.add({'b'}) + False + >>> a + [{'a'}, {'b'}] + + >>> a = Availability([{'a'}]) + >>> a.add({'a', 'b'}) + True + >>> a + [{'a'}] + + >>> a = Availability([{'a', 'b'}]) + >>> a.add({'a'}) + False + >>> a + [{'a'}] """ return self._add_impl(frozenset(features)) def merge(self, other: 'Availability') -> bool: - """Merge two availabilities.""" + """Merge two availabilities (by OR).""" redundant = [self._add_impl(condition) for condition in other.conjunctions] return all(redundant) + def merged(self, other: 'Availability') -> 'Availability': + """ + Return the results of merging without changing this object. + + + >>> Availability([{'a'}]).merged(Availability([{'b', 'c'}])) + [{'a'}, {'b', 'c'}] + + >>> Availability([{'a'}]).merged(Availability([{'a', 'b'}, {'b', 'c'}])) + [{'a'}, {'b', 'c'}] + + >>> Availability([{'a'}, {'b', 'c'}]).merged(Availability([{'a', 'b'}])) + [{'a'}, {'b', 'c'}] + """ + clone = deepcopy(self) + clone.merge(other) + return clone + + def anded(self, other: 'Availability') -> 'Availability': + """ + Return the boolean AND of this and another availability. + + >>> Availability([{'a'}]).anded(Availability([{'b', 'c'}])) + [{'a', 'b', 'c'}] + + + >>> Availability([{'a'}]).anded(Availability([{'a', 'b'}])) + [{'a', 'b'}] + + >>> # a+b+c drops out because a+b is sufficient + >>> Availability([{'a'}]).anded(Availability([{'a', 'b'}, {'b', 'c'}])) + [{'a', 'b'}] + + >>> Availability([{'a'}]).anded(Availability([{'b'}, {'c'}])) + [{'a', 'b'}, {'a', 'c'}] + + >>> Availability([{'a'}]).anded(Availability([{'b', 'c'}, {'d', 'e'}])) + [{'a', 'b', 'c'}, {'a', 'd', 'e'}] + + >>> Availability([{'a', 'b'}]).anded(Availability([{'c'}, {'d'}])) + [{'a', 'b', 'c'}, {'a', 'b', 'd'}] + + >>> Availability([{'a'}]).anded(Availability([])) + [] + + >>> Availability([]).anded(Availability([{'a'}])) + [] + """ + ret = Availability([]) + + for self_term in self.conjunctions: + for other_term in other.conjunctions: + ret._add_impl(self_term.union(other_term)) + + return ret + def test(self, present_features: Set[str]) -> bool: - """See if the provided features satisfy any of the conjunctions.""" + """ + See if the provided features satisfy any of the conjunctions. + + >>> Availability([{'a'}]).test({'b'}) + False + + >>> Availability([{'a'}]).test({'a'}) + True + + >>> Availability([]).test({'a'}) + False + """ for condition in self.conjunctions: if condition.issubset(present_features): return True @@ -76,27 +177,14 @@ def is_consistent(self) -> bool: if len(set(self.conjunctions)) < len(self.conjunctions): # got a dupe return False - for a, b in itertools.permutations(self.conjunctions): + for a, b in itertools.permutations(self.conjunctions, 2): if a.issubset(b): # One condition is a subset of another return False return True - def issubset(self, potential_superset: "Availability") -> bool: - for condition in self.conjunctions: - for other_condition in potential_superset.conjunctions: - if condition.issubset(other_condition): - return True - return False - - def issuperset(self, other: "Availability") -> bool: - for condition in self.conjunctions: - for other_condition in other.conjunctions: - if condition.issuperset(other_condition): - return True - return False - def make_frozen(self) -> FrozenAvailability: + """Convert to a tuple of term tuples.""" terms = [] for condition in self.conjunctions: terms.append(tuple(sorted(condition))) @@ -107,21 +195,34 @@ def as_normalized_symbol(self) -> str: for c in sorted(self.conjunctions)] return "_or_".join(conjs) + def cleaned(self, cleaner) -> 'Availability': + """Return an availability where each term has been filtered by your function.""" + ret = Availability([]) + for term in self.conjunctions: + ret.add(set(cleaner(term))) + return ret + def _add_impl(self, features: FrozenSet[str]) -> bool: if features in self.conjunctions: return True - # these we can't be sure which is right - redundant = any(features.issubset(c) or features.issuperset(c) for c in self.conjunctions) - self.conjunctions.append(features) - return redundant + # Do not add this term if we are a superset of any existing term + # e.g. A, A+B -> A + if any(features.issuperset(c) for c in self.conjunctions): + return True + + # Drop any terms that are a superset of the new term + remaining = [term for term in self.conjunctions if not term.issuperset(features)] + remaining.append(features) + self.conjunctions = remaining + return False def __str__(self): - conjs = [_format_conjunction(c) for c in self.conjunctions] + conjs = [_format_conjunction(c) for c in sorted(self.conjunctions)] return f'({" OR ".join(conjs)})' def __repr__(self) -> str: - conjs = [_repr_conjunction(c) for c in self.conjunctions] + conjs = [_repr_conjunction(c) for c in sorted(self.conjunctions)] return f"[{ ', '.join(conjs)}]" @classmethod @@ -144,6 +245,38 @@ def make_frozen(self) -> List[Tuple[str, FrozenAvailability]]: for key in sorted(self.syms.keys())] +def _version_cleaner(term: FrozenSet[str]) -> FrozenSet[str]: + versions = list(sorted((s for s in term if s.startswith("XR_VERSION_")))) + if not versions: + return term + + mutable_term = set(term) + # Saying the default version is redundant if we have more interesting dependencies. + if len(versions) == 1 and versions[0] == _DEFAULT_VER and len(term) > 1: + mutable_term.discard(_DEFAULT_VER) + + # keep the last one + redundant_versions = versions[:-1] + if redundant_versions: + mutable_term.difference_update(redundant_versions) + + return frozenset(mutable_term) + + +_DEFAULT_VER = 'XR_VERSION_1_0' + + +def _process_depends_string(s: Optional[str]) -> Availability: + if not s: + return Availability.create({_DEFAULT_VER}) + + terms = [{feat.strip() for feat in t.split("+")} for t in s.split(',')] + for t in terms: + if not any(feat.startswith('XR_VERSION_') for feat in t): + t.add(_DEFAULT_VER) + return Availability(terms) + + @dataclass class InteractionProfileComponent: """A component of an interaction profile""" @@ -200,16 +333,13 @@ def add_component(self, limit_to_user_path: Optional[str] = None, verbose: bool = _VERBOSE_INTERACTION_PROFILE_PROCESSING ) -> InteractionProfileComponent: - # if not avail.issuperset(self.availability): - # # the component availability does not include all the requirements of the profile availability? - # print(f"{self.name}: Add {subpath}: component availability {avail} is not a superset of profile availability {self.availability}") if limit_to_user_path: user_paths = {limit_to_user_path} else: - user_paths = deepcopy(self.valid_user_paths) - if subpath in self.components: - component = self.components[subpath] + user_paths = self.valid_user_paths + component = self.components.get(subpath) + if component: if action_type != component.action_type: raise RuntimeError(f"{self.name}: Add {subpath} again: Action type mismatch") if system != component.system: @@ -217,11 +347,9 @@ def add_component(self, if user_paths != component.valid_user_paths: raise RuntimeError(f"{self.name}: Add {subpath} again: Valid user paths mismatch") - assert len(avail.conjunctions) == 1 - # Just extend the availability - redundant = component.availability.merge(avail) - if redundant and verbose: - print(f"{self.name}{subpath}: Redundant availability {avail}") + # Just extend the availability. Not caring about return value (whether the avail is redundant) + component.availability.merge(avail) + return component ret = InteractionProfileComponent( @@ -235,14 +363,39 @@ def add_component(self, self.components[subpath] = ret return ret + def yield_user_path_and_component_pairs(self): + """ + Yield a user path and a component object. + + Outer iteration is over user paths. + """ + for user_path in sorted(self.valid_user_paths): + for subpath in sorted(self.components.keys()): + component = self.components[subpath] + if user_path in component.valid_user_paths: + yield user_path, component + + def generate_binding_paths(self): + """ + Yield a full binding path, availability, and the component object. + + Outer iteration is over user paths. + """ + for user_path, component in self.yield_user_path_and_component_pairs(): + yield (f"{user_path}{component.subpath}", + self.compute_component_availability(component), + component) + + def compute_component_availability(self, component: InteractionProfileComponent) -> Availability: + return self.availability.anded(component.availability).cleaned(_version_cleaner) + class InteractionProfileProcessor: """Encapsulates the logic to process interaction profiles in the XML.""" def __init__(self): self.interaction_profiles: Dict[str, InteractionProfile] = dict() - self.deferred_added_components = defaultdict(list) - self.availabilities: Set[FrozenAvailability] = set() + self.processed_features: List[et.Element] = [] self.finished = False self.verbose = _VERBOSE_INTERACTION_PROFILE_PROCESSING @@ -255,12 +408,12 @@ def process_feature(self, root: et.Element, interface: et.Element, emit: bool): root is the root of the registry XML tree. interface is either a `` tag or `` tag. - This may queue added components, if a feature requiring their profile - is not yet processed. Call process_deferred() to resolve this once all features - are processed. + This logs the features processed, in order, to perform additional passes + when finishing processing. Call `finish_processing()` once all features + are processed to perform these subsequent processing passes. """ if self.finished: - print("Hey, we think we are already finished!") + raise ValueError("Cannot process more features once we are finished!") interface_name = interface.get('name') assert interface_name @@ -268,11 +421,12 @@ def process_feature(self, root: et.Element, interface: et.Element, emit: bool): if self.verbose: print(f"Handling {interface.tag}: {interface_name} {emit}") + # Only grab interaction profiles in this first pass for require in interface.findall('./require[interaction_profile]'): - required_features = self._compute_deps_for_interaction_profile(interface, require) + avail = self._compute_deps_for_interaction_profile(interface, require) if self.verbose: - print(interface.tag, interface_name, required_features) + print(interface.tag, interface_name, avail) for include_ipp in require.findall('./interaction_profile'): # Path @@ -282,61 +436,58 @@ def process_feature(self, root: et.Element, interface: et.Element, emit: bool): if self.verbose: print(interface_name, name) - self._include_interaction_profile(root, name, required_features) + self._include_interaction_profile(root, name, avail) + self.processed_features.append(interface) + def _process_extending_bindings(self, interface: et.Element): + + interface_name = interface.get('name') + assert interface_name + + if self.verbose: + print(f"Handling {interface.tag}: {interface_name}: Pass 2, additional binding paths") for require in interface.findall('./require[extend]'): - required_features = self._compute_deps_for_interaction_profile(interface, require) + avail = self._compute_deps_for_interaction_profile(interface, require) for extend in require.findall('./extend[@interaction_profile_path]'): profile_name = extend.get("interaction_profile_path") assert profile_name - profile = self.interaction_profiles.get(profile_name) - if profile: - self._add_interaction_profile_components(profile, extend, required_features) - else: - self.deferred_added_components[profile_name].append((extend, required_features)) + profile = self.interaction_profiles[profile_name] + self._add_interaction_profile_components(profile, extend, avail) - def process_deferred(self): + def finish_processing(self): """ - Process components originating in features processed before their profile. + Perform the second pass over features and other finish-up work. - This is idempotent: you can call it multiple times safely. - However, you must have processed the profiles referred to by deferred components - first! So, just call it once from endFile() if you are using the common - Generator classes. + To guarantee correct processing, only call this once, after all + calls to `process_feature()` are complete. + Call it once from `endFile()` if you are using the common + `Generator` classes. """ - for profile_name, components in self.deferred_added_components.items(): - for extend, deps in components: - profile = self.interaction_profiles.get(profile_name) - assert profile - self._add_interaction_profile_components(profile, extend, deps) - self.deferred_added_components.clear() - - for profile in self.interaction_profiles.values(): - self.availabilities.add(profile.availability.make_frozen()) - for component in profile.components.values(): - self.availabilities.add(component.availability.make_frozen()) + # Second pass: extend binding paths (components) + for feature in self.processed_features: + self._process_extending_bindings(feature) + + self.processed_features.clear() self.finished = True - # for ip in self.interaction_profiles.values(): - # for component in ip.components: - def _include_interaction_profile(self, root: et.Element, name: str, required_features: Set[str]): + def _include_interaction_profile(self, root: et.Element, name: str, avail: Availability): if name in self.interaction_profiles: profile = self.interaction_profiles[name] - redundant = profile.availability.add(required_features) + redundant = profile.availability.merge(avail) if redundant and self.verbose: - print(f"{name}: Adding redundant set of required features! {required_features}") + print(f"{name}: Adding redundant availability! {avail}") # Add our new route of availability to all components defined inline, without # going through the XML again. for component in profile.components.values(): if component.integral: - redundant = component.availability.add(required_features) + redundant = component.availability.merge(avail) if redundant and self.verbose: - print(f"{name}{component.subpath}: Adding redundant set of required features!") - print(f" new: {required_features} existing availability: {component.availability}") + print(f"{name}{component.subpath}: Adding redundant availability!") + print(f" new: {avail} existing availability: {component.availability}") return @@ -348,37 +499,27 @@ def _include_interaction_profile(self, root: et.Element, name: str, required_fea title = profile_elt.get("title") assert title - avail = Availability.create(required_features) profile = InteractionProfile(valid_user_paths=user_paths, name=name, title=title, availability=avail) self.interaction_profiles[name] = profile - self._add_interaction_profile_components(profile, profile_elt, required_features, integral=True) + self._add_interaction_profile_components(profile, profile_elt, avail, integral=True) - def _compute_deps_for_interaction_profile(self, feature_elt: et.Element, require_elt: et.Element) -> Set[str]: + def _compute_deps_for_interaction_profile(self, feature_elt: et.Element, require_elt: et.Element) -> Availability: feature_name = feature_elt.get("name") assert feature_name - deps = {feature_name} - - # TODO finish fixing for schema update - if feature_elt.tag == "extension": - requiresCore = feature_elt.get("requiresCore", "1.0") - deps.add(f"XR_VERSION_{requiresCore.replace('.', '_')}") - - additional_extension = require_elt.get('extension') - if additional_extension is None: - additional_extension = require_elt.get('depends') - if additional_extension is not None: - deps.add(additional_extension) + deps = Availability.create({feature_name}) - return deps + require_depends = require_elt.get('depends') + if require_depends: + deps = deps.anded(_process_depends_string(require_depends)) - def _add_interaction_profile_components(self, profile: InteractionProfile, component_parent, required_features: Set[str], integral: bool = False): + return deps.cleaned(_version_cleaner) - avail = Availability.create(required_features) + def _add_interaction_profile_components(self, profile: InteractionProfile, component_parent, avail: Availability, integral: bool = False): for component in component_parent.findall("./component"): system = False if component.get("system") is not None: diff --git a/src/scripts/template_function_info.cpp b/src/scripts/template_function_info.cpp index bb02e710..07daa06a 100644 --- a/src/scripts/template_function_info.cpp +++ b/src/scripts/template_function_info.cpp @@ -13,7 +13,7 @@ static const FunctionInfoMap functionInfoMapInternal{ //# for cur_cmd in sorted_cmds //# if cur_cmd.ext_name != "XR_LOADER_VERSION_1_0" {/*{ cur_cmd.name | quote_string }*/, - FunctionInfo(PFN_xrVoidFunction(nullptr), + FunctionInfo( //# if cur_cmd.name in null_instance_ok true, /* null instance OK */ //# else diff --git a/src/scripts/template_interaction_info_generated.cpp b/src/scripts/template_interaction_info_generated.cpp index d9385aa2..e6edc388 100644 --- a/src/scripts/template_interaction_info_generated.cpp +++ b/src/scripts/template_interaction_info_generated.cpp @@ -9,12 +9,12 @@ namespace Conformance { -//# macro make_qualified_path_entry(user_path, component) +//# macro make_path_entry(binding_path, avail, component) InputSourcePathAvailData{ - /*{ (user_path + component.subpath) | quote_string }*/, + /*{ binding_path | quote_string }*/, /*{ component.action_type }*/, - InteractionProfileAvailability::Avail_/*{- component.availability.as_normalized_symbol() }*/ - //# if component.system + InteractionProfileAvailability::Avail_/*{- avail.as_normalized_symbol() }*/ + //#- if component.system , true //# endif } @@ -24,13 +24,14 @@ const std::vector& GetAllInteractionProfiles() // // Generated lists of component paths for interaction profiles, with metadata and availability expressions. // + //# for path, profile in interaction_profiles.items() - static const InputSourcePathAvailCollection /*{'c' + (path | replace("/", "_")) }*/{ -//# for component in profile.components.values() -//# for user_path in component.valid_user_paths - /*{ make_qualified_path_entry(user_path, component) | collapse_whitespace }*/, -//# endfor + // Interaction profile path: /*{ path }*/ + // Availability: /*{ profile.availability }*/ + static const InputSourcePathAvailCollection /*{'c' + (path | replace("/", "_") | replace("-", "_")) }*/{ +//# for binding_path, avail, component in profile.generate_binding_paths() + /*{ make_path_entry(binding_path, avail, component) | collapse_whitespace }*/, //# endfor }; @@ -46,12 +47,12 @@ const std::vector& GetAllInteractionProfiles() /*{ profile.name | quote_string }*/, /*{ profile.name | replace("/interaction_profiles/", "") | quote_string }*/, { - //# for user_path in profile.valid_user_paths + //# for user_path in profile.valid_user_paths | sort /*{ user_path | quote_string }*/, //# endfor }, InteractionProfileAvailability::Avail_/*{- profile.availability.as_normalized_symbol() -}*/, - /*{'c' + (path |replace("/", "_")) }*/ + /*{'c' + (path | replace("/", "_") | replace("-", "_")) }*/ }, //# endfor }; diff --git a/src/scripts/template_interaction_info_generated.h b/src/scripts/template_interaction_info_generated.h index 65ba0856..8f8fc467 100644 --- a/src/scripts/template_interaction_info_generated.h +++ b/src/scripts/template_interaction_info_generated.h @@ -61,7 +61,7 @@ static const std::array kInteract /// This is a generated list of all interaction profiles in the order returned by GetAllInteractionProfiles. enum class InteractionProfileIndex { //# for path, profile in interaction_profiles.items() - Profile_/*{ (path | replace("/interaction_profiles/", "") | replace("/", "_")) }*/, + Profile_/*{ (path | replace("/interaction_profiles/", "") | replace("/", "_") | replace("-", "_")) }*/, //# endfor };