diff --git a/CHANGELOG.CTS.md b/CHANGELOG.CTS.md index cef6b377..6ec26798 100644 --- a/CHANGELOG.CTS.md +++ b/CHANGELOG.CTS.md @@ -17,6 +17,55 @@ particular, since it is primarily software, pull requests may be integrated as they are accepted even between periodic updates. However, versions that are not signed tags on the `approved` branch are not valid for conformance submission. +## OpenXR CTS 1.0.33.0 (2024-01-18) + +- Conformance Tests + - Fix: Refactor Pbr::Model into an immutable Pbr::Model and Pbr::Instance that + holds the state for one drawn instance of the model. This corrects the known + issue in the self-tests mentioned in a previous changelog. + ([internal MR 3079](https://gitlab.khronos.org/openxr/openxr/merge_requests/3079), + [internal issue 2139](https://gitlab.khronos.org/openxr/openxr/issues/2139), + [internal MR 3141](https://gitlab.khronos.org/openxr/openxr/merge_requests/3141)) + - Fix: Avoid artificial error precedence requirement in test for + `XR_ERROR_GRAPHICS_DEVICE_INVALID`, by making sure to call the "check graphics + requirements" function if applicable. + ([internal MR 3093](https://gitlab.khronos.org/openxr/openxr/merge_requests/3093), + [internal issue 2155](https://gitlab.khronos.org/openxr/openxr/issues/2155)) + - Fix: Remove extra `xrSyncActions` call in `test_glTFRendering` to resolve + interaction issue. + ([internal MR 3107](https://gitlab.khronos.org/openxr/openxr/merge_requests/3107), + [internal issue 2163](https://gitlab.khronos.org/openxr/openxr/issues/2163)) + - Fix: Skip `XR_MSFT_controller_model` interactive test if extension is not + supported. + ([internal MR 3146](https://gitlab.khronos.org/openxr/openxr/merge_requests/3146), + [internal issue 2187](https://gitlab.khronos.org/openxr/openxr/issues/2187)) + - Improvement: Adjust `StringToPath` utility function to be easier to use. + ([internal MR 2076](https://gitlab.khronos.org/openxr/openxr/merge_requests/2076)) + - Improvement: Simplify how some tests refer to the main OpenXR handles. + ([internal MR 3023](https://gitlab.khronos.org/openxr/openxr/merge_requests/3023)) + - Improvement: Make `AutoBasicSession` use `EventReader` to support event + multiplexing. + ([internal MR 3023](https://gitlab.khronos.org/openxr/openxr/merge_requests/3023)) + - Improvement: Do not require system support for `XR_EXT_eye_gaze_interaction` + before running XrPath and interaction profile related tests. Paths are valid as + long as the extension is offered and enabled, regardless of whether there is + system support for eye tracking. + ([internal MR 3055](https://gitlab.khronos.org/openxr/openxr/merge_requests/3055)) + - Improvement: General code cleanup, warning fixes, clang-tidy fixes, and + refactoring to improve flexibility and maintainability. + ([internal MR 3082](https://gitlab.khronos.org/openxr/openxr/merge_requests/3082), + [internal MR 3023](https://gitlab.khronos.org/openxr/openxr/merge_requests/3023)) + - Improvement: On Android, log using the "FATAL" severity before triggering an + abort from the conformance layer. + ([internal MR 3087](https://gitlab.khronos.org/openxr/openxr/merge_requests/3087)) + - Improvement: Exclude loader negotiation functions (added to XML and ratified + spec in 1.0.33) from the list of functions automatically tested by the + conformance suite. + ([internal MR 3113](https://gitlab.khronos.org/openxr/openxr/merge_requests/3113)) + - New test: Check behavior for actions created without subaction paths, but + queried using subaction paths. + ([internal MR 3068](https://gitlab.khronos.org/openxr/openxr/merge_requests/3068)) + ## OpenXR CTS 1.0.32.1 (2023-12-14) A notable change in this release, is that the build system now checks for git diff --git a/changes/conformance/mr.3023.gl.md b/changes/conformance/mr.3023.gl.md deleted file mode 100644 index f2631350..00000000 --- a/changes/conformance/mr.3023.gl.md +++ /dev/null @@ -1,3 +0,0 @@ -- Improvement: Simplify how some tests refer to the main OpenXR handles. -- Improvement: Make `AutoBasicSession` use `EventReader` to support event multiplexing. -- Improvement: Miscellaneous code cleanups and refactoring to improve flexibility and maintainability. diff --git a/changes/conformance/mr.3087.gl.md b/changes/conformance/mr.3087.gl.md deleted file mode 100644 index fb9c451e..00000000 --- a/changes/conformance/mr.3087.gl.md +++ /dev/null @@ -1 +0,0 @@ -Improvement: On Android, log using the "FATAL" severity before triggering an abort from the conformance layer. diff --git a/specification/registry/xr.xml b/specification/registry/xr.xml index f4845c47..a3520581 100644 --- a/specification/registry/xr.xml +++ b/specification/registry/xr.xml @@ -1,4 +1,4 @@ - + diff --git a/src/conformance/conformance_layer/CMakeLists.txt b/src/conformance/conformance_layer/CMakeLists.txt index f7a0bbbc..8405eacf 100644 --- a/src/conformance/conformance_layer/CMakeLists.txt +++ b/src/conformance/conformance_layer/CMakeLists.txt @@ -45,7 +45,6 @@ add_library( XrApiLayer_runtime_conformance MODULE ${LOCAL_SOURCE} ${LOCAL_HEADERS} - XrApiLayer_conformance_layer.def ${PROJECT_BINARY_DIR}/src/xr_generated_dispatch_table.c ${CMAKE_CURRENT_BINARY_DIR}/gen_dispatch.cpp ${CMAKE_CURRENT_BINARY_DIR}/gen_dispatch.h @@ -107,17 +106,33 @@ if(ANDROID) ) endif() -if(APPLE) +# Dynamic Library: +# - Make build depend on the module definition/version script/export map +# - Add the linker flag (except windows) +if(WIN32) + target_sources( + XrApiLayer_runtime_conformance + PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/XrApiLayer_conformance_layer.def" + ) +elseif(APPLE) set_target_properties( XrApiLayer_runtime_conformance PROPERTIES LINK_FLAGS "-Wl,-exported_symbols_list,${CMAKE_CURRENT_SOURCE_DIR}/XrApiLayer_conformance_layer.expsym" ) -elseif(NOT WIN32) + target_sources( + XrApiLayer_runtime_conformance + PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/XrApiLayer_conformance_layer.expsym" + ) +else() set_target_properties( XrApiLayer_runtime_conformance - PROPERTIES LINK_FLAGS "-Wl,-Bsymbolic,--exclude-libs,ALL" + PROPERTIES LINK_FLAGS "-Wl,--version-script=\"${CMAKE_CURRENT_SOURCE_DIR}/XrApiLayer_conformance_layer.map\"" + ) + target_sources( + XrApiLayer_runtime_conformance + PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/XrApiLayer_conformance_layer.map" ) endif() diff --git a/src/conformance/conformance_layer/RuntimeFailure.h b/src/conformance/conformance_layer/RuntimeFailure.h index b228f652..fda7a8e3 100644 --- a/src/conformance/conformance_layer/RuntimeFailure.h +++ b/src/conformance/conformance_layer/RuntimeFailure.h @@ -43,14 +43,15 @@ void ValidateXrQuaternion(ConformanceHooksBase* conformanceHook, const XrQuatern void ValidateXrVector3f(ConformanceHooksBase* conformanceHook, const XrVector3f& v, const char* valueName, const char* xrFunctionName); // clang-format off -#define ENUM_CASE_BOOL(name, val) case name: return true; +#define ENUM_CASE_BOOL(name, val) case name: #define MAKE_IS_VALID_ENUM_VALUE(enumType, zeroIsValid) \ inline bool is_valid_enum_val(enumType e) { \ if (!zeroIsValid && e == 0) return false; \ - else if (e == 0x7FFFFFFF) return false; \ + if (e == 0x7FFFFFFF) return false; \ \ switch (e) { \ XR_LIST_ENUM_##enumType(ENUM_CASE_BOOL) \ + return true; \ default: return false; \ } \ } diff --git a/src/conformance/conformance_layer/XrApiLayer_conformance_layer.def b/src/conformance/conformance_layer/XrApiLayer_conformance_layer.def index 5ac07b3a..f2874f18 100644 --- a/src/conformance/conformance_layer/XrApiLayer_conformance_layer.def +++ b/src/conformance/conformance_layer/XrApiLayer_conformance_layer.def @@ -1,2 +1,6 @@ +; Copyright (c) 2019-2024, The Khronos Group Inc. +; +; SPDX-License-Identifier: Apache-2.0 + EXPORTS xrNegotiateLoaderApiLayerInterface \ No newline at end of file diff --git a/src/conformance/conformance_layer/XrApiLayer_conformance_layer.def.license b/src/conformance/conformance_layer/XrApiLayer_conformance_layer.def.license deleted file mode 100644 index cca73d04..00000000 --- a/src/conformance/conformance_layer/XrApiLayer_conformance_layer.def.license +++ /dev/null @@ -1,3 +0,0 @@ -SPDX-FileCopyrightText: 2019-2024, The Khronos Group Inc. - -SPDX-License-Identifier: Apache-2.0 diff --git a/src/conformance/conformance_layer/XrApiLayer_conformance_layer.map b/src/conformance/conformance_layer/XrApiLayer_conformance_layer.map new file mode 100644 index 00000000..5fc8ab24 --- /dev/null +++ b/src/conformance/conformance_layer/XrApiLayer_conformance_layer.map @@ -0,0 +1,12 @@ +/* +Copyright (c) 2019-2024, The Khronos Group Inc. + +SPDX-License-Identifier: Apache-2.0 +*/ + +{ + global: + xrNegotiateLoaderApiLayerInterface; + local: + *; +}; diff --git a/src/conformance/conformance_test/test_LayerComposition.cpp b/src/conformance/conformance_test/test_LayerComposition.cpp index 24155a96..7996ee6b 100644 --- a/src/conformance/conformance_test/test_LayerComposition.cpp +++ b/src/conformance/conformance_test/test_LayerComposition.cpp @@ -19,7 +19,7 @@ #include "conformance_framework.h" #include "utilities/throw_helpers.h" #include "utilities/types_and_constants.h" -#include "utilities/utils.h" +#include "utilities/xrduration_literals.h" #include #include @@ -309,8 +309,7 @@ namespace Conformance interactionManager.AttachActionSets(); compositionHelper.BeginSession(); - const XrSpace localSpace = - compositionHelper.CreateReferenceSpace(XR_REFERENCE_SPACE_TYPE_LOCAL, XrPosef{Quat::Identity, {0, 0, 0}}); + const XrSpace localSpace = compositionHelper.CreateReferenceSpace(XR_REFERENCE_SPACE_TYPE_LOCAL, XrPosefCPP()); const std::vector viewProperties = compositionHelper.EnumerateConfigurationViews(); @@ -382,8 +381,7 @@ namespace Conformance interactionManager.AttachActionSets(); compositionHelper.BeginSession(); - const XrSpace localSpace = - compositionHelper.CreateReferenceSpace(XR_REFERENCE_SPACE_TYPE_LOCAL, XrPosef{Quat::Identity, {0, 0, 0}}); + const XrSpace localSpace = compositionHelper.CreateReferenceSpace(XR_REFERENCE_SPACE_TYPE_LOCAL, XrPosefCPP()); const std::vector viewProperties = compositionHelper.EnumerateConfigurationViews(); @@ -524,7 +522,7 @@ namespace Conformance XrActionSpaceCreateInfo spaceCreateInfo{XR_TYPE_ACTION_SPACE_CREATE_INFO}; spaceCreateInfo.action = gripPoseAction; spaceCreateInfo.subactionPath = subactionPaths[i]; - spaceCreateInfo.poseInActionSpace = {{0, 0, 0, 1}, {0, 0, 0}}; + spaceCreateInfo.poseInActionSpace = XrPosefCPP(); XRC_CHECK_THROW_XRCMD(xrCreateActionSpace(session, &spaceCreateInfo, &space)); } else { diff --git a/src/conformance/conformance_test/test_XR_EXT_eye_gaze_interaction.cpp b/src/conformance/conformance_test/test_XR_EXT_eye_gaze_interaction.cpp index e68e6b4e..04a662a5 100644 --- a/src/conformance/conformance_test/test_XR_EXT_eye_gaze_interaction.cpp +++ b/src/conformance/conformance_test/test_XR_EXT_eye_gaze_interaction.cpp @@ -43,7 +43,7 @@ namespace Conformance MakeSystemPropertiesBoolChecker(XrSystemEyeGazeInteractionPropertiesEXT{XR_TYPE_SYSTEM_EYE_GAZE_INTERACTION_PROPERTIES_EXT}, &XrSystemEyeGazeInteractionPropertiesEXT::supportsEyeGazeInteraction); - TEST_CASE("XR_EXT_eye_gaze_interaction", "[scenario][interactive][no_auto][XR_EXT_eye_gaze_interaction]") + TEST_CASE("XR_EXT_eye_gaze_interaction-system_support_optional", "[XR_EXT_eye_gaze_interaction]") { GlobalData& globalData = GetGlobalData(); if (!globalData.IsInstanceExtensionSupported(XR_EXT_EYE_GAZE_INTERACTION_EXTENSION_NAME)) { @@ -70,12 +70,7 @@ namespace Conformance SECTION("Extension enabled") { - AutoBasicInstance instance({XR_EXT_EYE_GAZE_INTERACTION_EXTENSION_NAME}, AutoBasicInstance::createSystemId); - XrSystemId systemId = instance.systemId; - if (!SystemSupportsEyeGazeInteraction(instance, systemId)) { - // This runtime does support eye gaze, but this headset does not which is fine. - SKIP("System does not support eye gaze interaction"); - } + AutoBasicInstance instance({XR_EXT_EYE_GAZE_INTERACTION_EXTENSION_NAME}); SECTION("Create and destroy eye gaze actions") { @@ -206,14 +201,7 @@ namespace Conformance // Verify that eye gaze interaction input can be combined with other input sources. // Use Simple Controller profile as opposed to vendor-specific inputs for broader coverage. - AutoBasicInstance instance({XR_EXT_EYE_GAZE_INTERACTION_EXTENSION_NAME}, AutoBasicInstance::createSystemId); - XrSystemId systemId = instance.systemId; - if (!SystemSupportsEyeGazeInteraction(instance, systemId)) { - // This runtime does support eye gaze, but this headset does not which is fine. - WARN("System does not support eye gaze interaction"); - return; - } - + AutoBasicInstance instance({XR_EXT_EYE_GAZE_INTERACTION_EXTENSION_NAME}); AutoBasicSession session(AutoBasicSession::beginSession, instance); // Create action set @@ -280,6 +268,14 @@ namespace Conformance XrSpace gazeActionSpace{XR_NULL_HANDLE}; REQUIRE_RESULT(xrCreateActionSpace(session, &createActionSpaceInfo, &gazeActionSpace), XR_SUCCESS); } + } + + TEST_CASE("XR_EXT_eye_gaze_interaction", "[scenario][interactive][no_auto][XR_EXT_eye_gaze_interaction]") + { + GlobalData& globalData = GetGlobalData(); + if (!globalData.IsInstanceExtensionSupported(XR_EXT_EYE_GAZE_INTERACTION_EXTENSION_NAME)) { + SKIP(XR_EXT_EYE_GAZE_INTERACTION_EXTENSION_NAME " not supported"); + } SECTION("Localize eye gaze paths") { diff --git a/src/conformance/conformance_test/test_XR_KHR_composition_layer_cube.cpp b/src/conformance/conformance_test/test_XR_KHR_composition_layer_cube.cpp index aaf02dc3..bc8e839b 100644 --- a/src/conformance/conformance_test/test_XR_KHR_composition_layer_cube.cpp +++ b/src/conformance/conformance_test/test_XR_KHR_composition_layer_cube.cpp @@ -19,15 +19,13 @@ #include "utilities/generator.h" #include "utilities/bitmask_generator.h" #include "utilities/bitmask_to_string.h" +#include "utilities/xrduration_literals.h" #include #include #include -#include #include -#include -#include #include namespace Conformance diff --git a/src/conformance/conformance_test/test_XR_KHR_composition_layer_depth.cpp b/src/conformance/conformance_test/test_XR_KHR_composition_layer_depth.cpp index 506d5ac3..2fb6b576 100644 --- a/src/conformance/conformance_test/test_XR_KHR_composition_layer_depth.cpp +++ b/src/conformance/conformance_test/test_XR_KHR_composition_layer_depth.cpp @@ -17,6 +17,7 @@ #include "conformance_framework.h" #include "conformance_utils.h" #include "utilities/bitmask_generator.h" +#include "utilities/xrduration_literals.h" #include #include diff --git a/src/conformance/conformance_test/test_XR_KHR_composition_layer_equirect.cpp b/src/conformance/conformance_test/test_XR_KHR_composition_layer_equirect.cpp index 73abfea6..3a38b4bb 100644 --- a/src/conformance/conformance_test/test_XR_KHR_composition_layer_equirect.cpp +++ b/src/conformance/conformance_test/test_XR_KHR_composition_layer_equirect.cpp @@ -19,15 +19,13 @@ #include "utilities/bitmask_generator.h" #include "utilities/bitmask_to_string.h" #include "utilities/generator.h" +#include "utilities/xrduration_literals.h" #include #include #include -#include #include -#include -#include #include namespace Conformance diff --git a/src/conformance/conformance_test/test_XR_KHR_win32_convert_performance_counter_time.cpp b/src/conformance/conformance_test/test_XR_KHR_win32_convert_performance_counter_time.cpp index 2950868c..b8283cd0 100644 --- a/src/conformance/conformance_test/test_XR_KHR_win32_convert_performance_counter_time.cpp +++ b/src/conformance/conformance_test/test_XR_KHR_win32_convert_performance_counter_time.cpp @@ -16,6 +16,7 @@ #ifdef XR_USE_PLATFORM_WIN32 #include "utilities/utils.h" +#include "utilities/xrduration_literals.h" #include "conformance_utils.h" #include "conformance_framework.h" #include diff --git a/src/conformance/conformance_test/test_XR_MSFT_controller_model.cpp b/src/conformance/conformance_test/test_XR_MSFT_controller_model.cpp index 621fe1ff..afe38e0b 100644 --- a/src/conformance/conformance_test/test_XR_MSFT_controller_model.cpp +++ b/src/conformance/conformance_test/test_XR_MSFT_controller_model.cpp @@ -187,7 +187,7 @@ namespace Conformance syncInfo.countActiveActionSets = 1; XrActionSpaceCreateInfo actionSpaceCreateInfo{XR_TYPE_ACTION_SPACE_CREATE_INFO}; - actionSpaceCreateInfo.poseInActionSpace = XrPosef{Quat::Identity, {0, 0, 0}}; + actionSpaceCreateInfo.poseInActionSpace = XrPosefCPP(); std::vector gripSpaces; for (std::shared_ptr controller : {leftHandInputDevice, rightHandInputDevice}) { @@ -309,6 +309,13 @@ namespace Conformance TEST_CASE("XR_MSFT_controller_model_interactive", "[scenario][interactive][no_auto]") { + + GlobalData& globalData = GetGlobalData(); + + if (!globalData.IsInstanceExtensionSupported(XR_MSFT_CONTROLLER_MODEL_EXTENSION_NAME)) { + SKIP(XR_MSFT_CONTROLLER_MODEL_EXTENSION_NAME " not supported"); + } + const char* instructions = "Ensure the controller model is positioned in the same position as the physical controller. " "Press menu to complete the validation."; @@ -338,7 +345,8 @@ namespace Conformance XrPath subactionPath; XrSpace space; XrControllerModelKeyMSFT modelKey; - GLTFHandle controllerModel; + GLTFModelHandle controllerModel; + GLTFModelInstanceHandle controllerModelInstance; ControllerAnimationHandler animationHandler; }; @@ -394,6 +402,7 @@ namespace Conformance compositionHelper.BeginSession(); XrSession session = compositionHelper.GetSession(); + auto& graphicsPlugin = GetGlobalData().graphicsPlugin; // Create the instructional quad layer placed to the left. XrCompositionLayerQuad* const instructionsQuad = @@ -450,6 +459,7 @@ namespace Conformance ext.xrLoadControllerModelMSFT_(session, hand.modelKey, modelBufferSize, &modelBufferSize, modelBuffer.data())); hand.controllerModel = GetGlobalData().graphicsPlugin->LoadGLTF(modelBuffer); + hand.controllerModelInstance = GetGlobalData().graphicsPlugin->CreateGLTFModelInstance(hand.controllerModel); XrControllerModelPropertiesMSFT modelProperties{XR_TYPE_CONTROLLER_MODEL_PROPERTIES_MSFT}; REQUIRE_RESULT_UNQUALIFIED_SUCCESS(ext.xrGetControllerModelPropertiesMSFT_(session, hand.modelKey, &modelProperties)); @@ -458,8 +468,8 @@ namespace Conformance modelProperties.nodeProperties = nodePropertiesBuffer.data(); REQUIRE_RESULT_UNQUALIFIED_SUCCESS(ext.xrGetControllerModelPropertiesMSFT_(session, hand.modelKey, &modelProperties)); - hand.animationHandler = - ControllerAnimationHandler(GetGlobalData().graphicsPlugin->GetModel(hand.controllerModel), nodePropertiesBuffer); + hand.animationHandler = ControllerAnimationHandler{*GetGlobalData().graphicsPlugin->GetPbrModel(hand.controllerModel), + std::move(nodePropertiesBuffer)}; ReportF("Loaded model for key"); } @@ -481,9 +491,10 @@ namespace Conformance modelState.nodeStates = nodeStateBuffer.data(); REQUIRE_RESULT_UNQUALIFIED_SUCCESS(ext.xrGetControllerModelStateMSFT_(session, hand.modelKey, &modelState)); - hand.animationHandler.UpdateControllerParts(nodeStateBuffer); + hand.animationHandler.UpdateControllerParts(nodeStateBuffer, + graphicsPlugin->GetModelInstance(hand.controllerModelInstance)); - renderedGLTFs.push_back(GLTFDrawable{hand.controllerModel, spaceLocation.pose}); + renderedGLTFs.push_back(GLTFDrawable{hand.controllerModelInstance, spaceLocation.pose}); } } } diff --git a/src/conformance/conformance_test/test_XrCompositionLayerProjection.cpp b/src/conformance/conformance_test/test_XrCompositionLayerProjection.cpp index a5870748..c1755d96 100644 --- a/src/conformance/conformance_test/test_XrCompositionLayerProjection.cpp +++ b/src/conformance/conformance_test/test_XrCompositionLayerProjection.cpp @@ -19,6 +19,7 @@ #include "utilities/bitmask_generator.h" #include "utilities/bitmask_to_string.h" #include "utilities/generator.h" +#include "utilities/xrduration_literals.h" #include #include @@ -30,7 +31,6 @@ #include #include #include -#include #include namespace Conformance diff --git a/src/conformance/conformance_test/test_XrCompositionLayerQuad.cpp b/src/conformance/conformance_test/test_XrCompositionLayerQuad.cpp index 7fb12296..b781f28b 100644 --- a/src/conformance/conformance_test/test_XrCompositionLayerQuad.cpp +++ b/src/conformance/conformance_test/test_XrCompositionLayerQuad.cpp @@ -20,13 +20,13 @@ #include "utilities/generator.h" #include "utilities/throw_helpers.h" #include "utilities/types_and_constants.h" +#include "utilities/xrduration_literals.h" #include #include #include #include -#include #include #include #include diff --git a/src/conformance/conformance_test/test_actions.cpp b/src/conformance/conformance_test/test_actions.cpp index db378c62..f66ad37f 100644 --- a/src/conformance/conformance_test/test_actions.cpp +++ b/src/conformance/conformance_test/test_actions.cpp @@ -418,7 +418,7 @@ namespace Conformance SECTION("Unknown interaction profile") { for (const auto& invalidIP : invalidInteractionProfiles) { - bindings.interactionProfile = StringToPath(instance, invalidIP.c_str()); + bindings.interactionProfile = StringToPath(instance, invalidIP); REQUIRE_RESULT(xrSuggestInteractionProfileBindings(instance, &bindings), XR_ERROR_PATH_UNSUPPORTED); } @@ -428,7 +428,7 @@ namespace Conformance SECTION("Unknown binding path") { for (const auto& invalidBindingPath : invalidBindingPaths) { - XrActionSuggestedBinding invalidBindingPathBinding = {action, StringToPath(instance, invalidBindingPath.c_str())}; + XrActionSuggestedBinding invalidBindingPathBinding = {action, StringToPath(instance, invalidBindingPath)}; bindings.suggestedBindings = &invalidBindingPathBinding; REQUIRE_RESULT(xrSuggestInteractionProfileBindings(instance, &bindings), XR_ERROR_PATH_UNSUPPORTED); } @@ -471,7 +471,7 @@ namespace Conformance REQUIRE_RESULT(xrCreateAction(actionSet, &allIPActionCreateInfo, &hapticAction), XR_SUCCESS); CAPTURE(ipMetadata.InteractionProfilePathString); - bindings.interactionProfile = StringToPath(instance, ipMetadata.InteractionProfilePathString.c_str()); + bindings.interactionProfile = StringToPath(instance, ipMetadata.InteractionProfilePathString); bindings.countSuggestedBindings = 1; for (const auto& inputSourcePathData : ipMetadata.InputSourcePaths) { const std::string& bindingPath = inputSourcePathData.Path; @@ -498,7 +498,7 @@ namespace Conformance actionRef = &hapticAction; } - XrActionSuggestedBinding suggestedBindings{*actionRef, StringToPath(instance, bindingPath.c_str())}; + XrActionSuggestedBinding suggestedBindings{*actionRef, StringToPath(instance, bindingPath)}; bindings.suggestedBindings = &suggestedBindings; REQUIRE_RESULT(xrSuggestInteractionProfileBindings(instance, &bindings), XR_SUCCESS); } @@ -582,13 +582,13 @@ namespace Conformance std::shared_ptr inputDevice = CreateTestDevice( &actionLayerManager, &compositionHelper.GetInteractionManager(), compositionHelper.GetInstance(), compositionHelper.GetSession(), - StringToPath(compositionHelper.GetInstance(), cSimpleKHRInteractionProfileDefinition.InteractionProfilePathString.c_str()), - path, cSimpleKHRInteractionProfileDefinition.InputSourcePaths); + StringToPath(compositionHelper.GetInstance(), cSimpleKHRInteractionProfileDefinition.InteractionProfilePathString), path, + cSimpleKHRInteractionProfileDefinition.InputSourcePaths); compositionHelper.GetInteractionManager().AddActionSet(actionSet); std::string selectPathStr = std::string(pathStr) + "/input/select/click"; - XrPath selectPath = StringToPath(compositionHelper.GetInstance(), selectPathStr.c_str()); + XrPath selectPath = StringToPath(compositionHelper.GetInstance(), selectPathStr); XrActionSuggestedBinding testBinding = {selectActionA, selectPath}; XrInteractionProfileSuggestedBinding bindings{XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING}; bindings.interactionProfile = StringToPath(compositionHelper.GetInstance(), "/interaction_profiles/khr/simple_controller"); @@ -598,7 +598,7 @@ namespace Conformance // Calling attach on the interaction manager will call xrSuggestInteractionProfileBindings with the bindings provided here, overwriting the previous bindings compositionHelper.GetInteractionManager().AddActionBindings( - StringToPath(compositionHelper.GetInstance(), cSimpleKHRInteractionProfileDefinition.InteractionProfilePathString.c_str()), + StringToPath(compositionHelper.GetInstance(), cSimpleKHRInteractionProfileDefinition.InteractionProfilePathString), {{{selectActionB, selectPath}}}); compositionHelper.GetInteractionManager().AttachActionSets(); @@ -897,7 +897,7 @@ namespace Conformance std::vector interactionProfileOrder{}; auto suggestBindings = [&](const InteractionProfileMetadata& interactionProfile) { std::string interactionProfileName = interactionProfile.InteractionProfilePathString; - XrPath interactionProfilePath = StringToPath(compositionHelper.GetInstance(), interactionProfileName.c_str()); + XrPath interactionProfilePath = StringToPath(compositionHelper.GetInstance(), interactionProfileName); bool bindingSuggested = false; for (auto& bindings : interactionProfile.InputSourcePaths) { @@ -930,11 +930,11 @@ namespace Conformance } // Hardcoded path valid for simple controller - XrPath userHandLeftXrPath{StringToPath(compositionHelper.GetInstance(), topLevelPathString.c_str())}; + XrPath userHandLeftXrPath{StringToPath(compositionHelper.GetInstance(), topLevelPathString)}; std::shared_ptr inputDevice = CreateTestDevice( &actionLayerManager, &compositionHelper.GetInteractionManager(), compositionHelper.GetInstance(), compositionHelper.GetSession(), - StringToPath(compositionHelper.GetInstance(), cSimpleKHRInteractionProfileDefinition.InteractionProfilePathString.c_str()), + StringToPath(compositionHelper.GetInstance(), cSimpleKHRInteractionProfileDefinition.InteractionProfilePathString), userHandLeftXrPath, cSimpleKHRInteractionProfileDefinition.InputSourcePaths); // This function calls xrSuggestInteractionProfileBindings() before attaching the actionsets @@ -944,10 +944,10 @@ namespace Conformance inputDevice->SetDeviceActive(/*state = */ true, /*skipInteraction = */ false, boolAction, actionSet); actionLayerManager.WaitForSessionFocusWithMessage(); XrInteractionProfileState interactionProfileState{XR_TYPE_INTERACTION_PROFILE_STATE}; - REQUIRE_RESULT(xrGetCurrentInteractionProfile(compositionHelper.GetSession(), - StringToPath(compositionHelper.GetInstance(), topLevelPathString.c_str()), - &interactionProfileState), - XR_SUCCESS); + REQUIRE_RESULT( + xrGetCurrentInteractionProfile(compositionHelper.GetSession(), + StringToPath(compositionHelper.GetInstance(), topLevelPathString), &interactionProfileState), + XR_SUCCESS); // Are we expecting the topLevelPath to have a active input? if (nullPathExpected) { @@ -994,7 +994,7 @@ namespace Conformance ActionLayerManager actionLayerManager(compositionHelper); XrPath simpleControllerInteractionProfile = - StringToPath(compositionHelper.GetInstance(), cSimpleKHRInteractionProfileDefinition.InteractionProfilePathString.c_str()); + StringToPath(compositionHelper.GetInstance(), cSimpleKHRInteractionProfileDefinition.InteractionProfilePathString); XrActionSet actionSet{XR_NULL_HANDLE}; XrActionSetCreateInfo actionSetCreateInfo{XR_TYPE_ACTION_SET_CREATE_INFO}; @@ -1013,14 +1013,14 @@ namespace Conformance std::shared_ptr leftHandInputDevice = CreateTestDevice( &actionLayerManager, &compositionHelper.GetInteractionManager(), compositionHelper.GetInstance(), compositionHelper.GetSession(), - StringToPath(compositionHelper.GetInstance(), cSimpleKHRInteractionProfileDefinition.InteractionProfilePathString.c_str()), + StringToPath(compositionHelper.GetInstance(), cSimpleKHRInteractionProfileDefinition.InteractionProfilePathString), leftHandPath, cSimpleKHRInteractionProfileDefinition.InputSourcePaths); XrPath rightHandPath{StringToPath(compositionHelper.GetInstance(), "/user/hand/right")}; std::shared_ptr rightHandInputDevice = CreateTestDevice( &actionLayerManager, &compositionHelper.GetInteractionManager(), compositionHelper.GetInstance(), compositionHelper.GetSession(), - StringToPath(compositionHelper.GetInstance(), cSimpleKHRInteractionProfileDefinition.InteractionProfilePathString.c_str()), + StringToPath(compositionHelper.GetInstance(), cSimpleKHRInteractionProfileDefinition.InteractionProfilePathString), rightHandPath, cSimpleKHRInteractionProfileDefinition.InputSourcePaths); XrActionsSyncInfo syncInfo{XR_TYPE_ACTIONS_SYNC_INFO}; @@ -1036,7 +1036,7 @@ namespace Conformance compositionHelper.GetInteractionManager().AddActionSet(actionSet); compositionHelper.GetInteractionManager().AddActionBindings( - StringToPath(compositionHelper.GetInstance(), cSimpleKHRInteractionProfileDefinition.InteractionProfilePathString.c_str()), + StringToPath(compositionHelper.GetInstance(), cSimpleKHRInteractionProfileDefinition.InteractionProfilePathString), {{{selectAction, StringToPath(compositionHelper.GetInstance(), "/user/hand/left/input/select/click")}, {selectAction, StringToPath(compositionHelper.GetInstance(), "/user/hand/right/input/select/click")}}}); compositionHelper.GetInteractionManager().AttachActionSets(); @@ -1136,7 +1136,7 @@ namespace Conformance ActionLayerManager actionLayerManager(compositionHelper); XrPath simpleControllerInteractionProfile = - StringToPath(compositionHelper.GetInstance(), cSimpleKHRInteractionProfileDefinition.InteractionProfilePathString.c_str()); + StringToPath(compositionHelper.GetInstance(), cSimpleKHRInteractionProfileDefinition.InteractionProfilePathString); std::string leftHandPathString = "/user/hand/left"; XrPath leftHandPath{StringToPath(compositionHelper.GetInstance(), "/user/hand/left")}; @@ -1234,7 +1234,7 @@ namespace Conformance SECTION("Parameter validation") { std::string selectPathStr = defaultDevicePathStr + "/input/select/click"; - XrPath selectPath = StringToPath(compositionHelper.GetInstance(), selectPathStr.c_str()); + XrPath selectPath = StringToPath(compositionHelper.GetInstance(), selectPathStr); compositionHelper.GetInteractionManager().AddActionSet(actionSet); compositionHelper.GetInteractionManager().AddActionBindings(simpleControllerInteractionProfile, {{action, selectPath}}); compositionHelper.GetInteractionManager().AttachActionSets(); @@ -1851,7 +1851,7 @@ namespace Conformance actionLayerManager.WaitForSessionFocusWithMessage(); - XrPath interactionProfile = StringToPath(compositionHelper.GetInstance(), ipMetadata.InteractionProfilePathString.c_str()); + XrPath interactionProfile = StringToPath(compositionHelper.GetInstance(), ipMetadata.InteractionProfilePathString); XrPath inputDevicePath{StringToPath(compositionHelper.GetInstance(), topLevelPathString.data())}; std::shared_ptr inputDevice = CreateTestDevice(&actionLayerManager, &compositionHelper.GetInteractionManager(), compositionHelper.GetInstance(), @@ -1971,7 +1971,7 @@ namespace Conformance REQUIRE_RESULT(xrCreateAction(actionSet, &actionCreateInfo, &xAction), XR_SUCCESS); std::string xSubBindingPath = std::string(inputSourceData.Path) + "/x"; - bindingPath = StringToPath(compositionHelper.GetInstance(), xSubBindingPath.c_str()); + bindingPath = StringToPath(compositionHelper.GetInstance(), xSubBindingPath); compositionHelper.GetInteractionManager().AddActionBindings(interactionProfile, {{xAction, bindingPath}}); actionNames = GetActionNames(); @@ -1980,7 +1980,7 @@ namespace Conformance REQUIRE_RESULT(xrCreateAction(actionSet, &actionCreateInfo, &yAction), XR_SUCCESS); std::string ySubBindingPath = std::string(inputSourceData.Path) + "/y"; - bindingPath = StringToPath(compositionHelper.GetInstance(), ySubBindingPath.c_str()); + bindingPath = StringToPath(compositionHelper.GetInstance(), ySubBindingPath); compositionHelper.GetInteractionManager().AddActionBindings(interactionProfile, {{yAction, bindingPath}}); break; } @@ -2785,7 +2785,7 @@ namespace Conformance actionLayerManager.WaitForSessionFocusWithMessage(); XrPath simpleControllerInteractionProfile = - StringToPath(compositionHelper.GetInstance(), cSimpleKHRInteractionProfileDefinition.InteractionProfilePathString.c_str()); + StringToPath(compositionHelper.GetInstance(), cSimpleKHRInteractionProfileDefinition.InteractionProfilePathString); XrPath leftHandSelectClickPath = StringToPath(compositionHelper.GetInstance(), "/user/hand/left/input/select/click"); XrPath rightHandSelectClickPath = StringToPath(compositionHelper.GetInstance(), "/user/hand/right/input/select/click"); @@ -2916,6 +2916,14 @@ namespace Conformance reinterpret_cast(&hapticPacket)), XR_ERROR_PATH_UNSUPPORTED); REQUIRE_RESULT(xrStopHapticFeedback(compositionHelper.GetSession(), &hapticActionInfo), XR_ERROR_PATH_UNSUPPORTED); + + { + INFO("Action created with no subaction paths, cannot be queried with any"); + getInfo.action = confirmAction; + getInfo.subactionPath = leftHandPath; + REQUIRE_RESULT(xrGetActionStateBoolean(compositionHelper.GetSession(), &getInfo, &booleanState), + XR_ERROR_PATH_UNSUPPORTED); + } } SECTION("Type mismatch") { @@ -2994,7 +3002,7 @@ namespace Conformance ActionLayerManager actionLayerManager(compositionHelper); XrPath simpleControllerInteractionProfile = - StringToPath(compositionHelper.GetInstance(), cSimpleKHRInteractionProfileDefinition.InteractionProfilePathString.c_str()); + StringToPath(compositionHelper.GetInstance(), cSimpleKHRInteractionProfileDefinition.InteractionProfilePathString); XrActionSet actionSet{XR_NULL_HANDLE}; XrActionSetCreateInfo actionSetCreateInfo{XR_TYPE_ACTION_SET_CREATE_INFO}; @@ -3079,7 +3087,7 @@ namespace Conformance ActionLayerManager actionLayerManager(compositionHelper); XrPath simpleControllerInteractionProfile = - StringToPath(compositionHelper.GetInstance(), cSimpleKHRInteractionProfileDefinition.InteractionProfilePathString.c_str()); + StringToPath(compositionHelper.GetInstance(), cSimpleKHRInteractionProfileDefinition.InteractionProfilePathString); XrPath leftHandPath{StringToPath(compositionHelper.GetInstance(), "/user/hand/left")}; XrPath rightHandPath{StringToPath(compositionHelper.GetInstance(), "/user/hand/right")}; const XrPath bothHands[2] = {leftHandPath, rightHandPath}; @@ -3565,8 +3573,8 @@ namespace Conformance std::shared_ptr inputDevice = CreateTestDevice( &actionLayerManager, &compositionHelper.GetInteractionManager(), compositionHelper.GetInstance(), compositionHelper.GetSession(), - StringToPath(compositionHelper.GetInstance(), cSimpleKHRInteractionProfileDefinition.InteractionProfilePathString.c_str()), - path, cSimpleKHRInteractionProfileDefinition.InputSourcePaths); + StringToPath(compositionHelper.GetInstance(), cSimpleKHRInteractionProfileDefinition.InteractionProfilePathString), path, + cSimpleKHRInteractionProfileDefinition.InputSourcePaths); compositionHelper.GetInteractionManager().AddActionSet(actionSet); compositionHelper.GetInteractionManager().AddActionBindings( diff --git a/src/conformance/conformance_test/test_glTFRendering.cpp b/src/conformance/conformance_test/test_glTFRendering.cpp index d1574352..8c5b571e 100644 --- a/src/conformance/conformance_test/test_glTFRendering.cpp +++ b/src/conformance/conformance_test/test_glTFRendering.cpp @@ -167,7 +167,9 @@ namespace Conformance auto testCase = testCases[testCaseIdx]; bool testCaseInitialized = false; - GLTFHandle gltfModel; + GLTFModelHandle gltfModel; + std::vector gltfModelInstances; + gltfModelInstances.reserve(gripSpaces.size()); auto setupTest = [&]() { // Load the model file into memory @@ -176,6 +178,12 @@ namespace Conformance // Load the model gltfModel = GetGlobalData().graphicsPlugin->LoadGLTF(modelData); + gltfModelInstances.clear(); + + for (const auto& space : gripSpaces) { + (void)space; + gltfModelInstances.push_back(GetGlobalData().graphicsPlugin->CreateGLTFModelInstance(gltfModel)); + } // Configure the interactive layer manager with the corresponding description and image std::ostringstream oss; oss << "Subtest " << (testCaseIdx + 1) << "/" << ArraySize(testCases) << ": " << testCase.name << std::endl; @@ -192,9 +200,6 @@ namespace Conformance auto viewData = compositionHelper.LocateViews(localSpace, frameState.predictedDisplayTime); const auto& viewState = std::get(viewData); - // want our standard action sets on all subaction paths - compositionHelper.GetInteractionManager().SyncActions(XR_NULL_PATH); - if (!testCaseInitialized) { testCase = testCases[testCaseIdx]; setupTest(); @@ -202,7 +207,8 @@ namespace Conformance std::vector renderedGLTFs; - for (const auto& space : gripSpaces) { + for (size_t i = 0; i < gripSpaces.size(); ++i) { + const auto& space = gripSpaces[i]; XrSpaceLocation location{XR_TYPE_SPACE_LOCATION}; if (XR_SUCCEEDED(xrLocateSpace(space, localSpace, frameState.predictedDisplayTime, &location))) { if ((location.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) && @@ -210,7 +216,8 @@ namespace Conformance XrPosef adjustedPose; XrPosef_Multiply(&adjustedPose, &location.pose, &testCase.poseInGripSpace); - renderedGLTFs.push_back(GLTFDrawable{gltfModel, adjustedPose, {testCase.scale, testCase.scale, testCase.scale}}); + renderedGLTFs.push_back( + GLTFDrawable{gltfModelInstances[i], adjustedPose, {testCase.scale, testCase.scale, testCase.scale}}); } } } diff --git a/src/conformance/conformance_test/test_xrCreateSession.cpp b/src/conformance/conformance_test/test_xrCreateSession.cpp index 91ef459d..43e3ef5d 100644 --- a/src/conformance/conformance_test/test_xrCreateSession.cpp +++ b/src/conformance/conformance_test/test_xrCreateSession.cpp @@ -62,6 +62,9 @@ namespace Conformance SECTION("Missing graphics binding implies XR_ERROR_GRAPHICS_DEVICE_INVALID") { + if (graphicsPlugin) { + REQUIRE(graphicsPlugin->InitializeDevice(instance, systemId, true /* checkGraphicsRequirements */)); + } sessionCreateInfo.next = nullptr; XrResult result; CAPTURE(result = xrCreateSession(instance, &sessionCreateInfo, &session)); diff --git a/src/conformance/conformance_test/test_xrLocateSpace.cpp b/src/conformance/conformance_test/test_xrLocateSpace.cpp index 10a09308..ab6f9482 100644 --- a/src/conformance/conformance_test/test_xrLocateSpace.cpp +++ b/src/conformance/conformance_test/test_xrLocateSpace.cpp @@ -14,21 +14,18 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "composition_utils.h" #include "conformance_framework.h" #include "conformance_utils.h" #include "utilities/bitmask_to_string.h" #include "utilities/types_and_constants.h" +#include "utilities/xrduration_literals.h" #include #include #include -#include #include -#include -#include -#include -#include namespace Conformance { @@ -176,20 +173,17 @@ namespace Conformance CHECK(XR_SUCCESS == xrCreateReferenceSpace(sessionHandle, &spaceCreateInfo, &spaceB)); XrResult result = xrLocateSpace(spaceA, spaceB, time, &location); - CHECK(XR_SUCCESS == result); + { + INFO("xrLocateSpace"); + CHECK(XR_SUCCESS == result); + } // the main test: if (XR_SUCCESS == result) { // Capture the three poses and the result to generate useful error messages in case the result is not // identical to the expected values. - CAPTURE(poseSpaceA.orientation.x, poseSpaceA.orientation.y, poseSpaceA.orientation.z, poseSpaceA.orientation.w, - poseSpaceA.position.x, poseSpaceA.position.y, poseSpaceA.position.z, poseSpaceB.orientation.x, - poseSpaceB.orientation.y, poseSpaceB.orientation.z, poseSpaceB.orientation.w, poseSpaceB.position.x, - poseSpaceB.position.y, poseSpaceB.position.z, expectedResult.orientation.x, expectedResult.orientation.y, - expectedResult.orientation.z, expectedResult.orientation.w, expectedResult.position.x, - expectedResult.position.y, expectedResult.position.z, location.pose.orientation.x, location.pose.orientation.y, - location.pose.orientation.z, location.pose.orientation.w, location.pose.position.x, location.pose.position.y, - location.pose.position.z); + CAPTURE(poseSpaceA.orientation, poseSpaceA.position, poseSpaceB.orientation, poseSpaceB.position, + expectedResult.orientation, expectedResult.position, location.pose.orientation, location.pose.position); ValidateSpaceLocation(location, expectedResult); } @@ -205,7 +199,7 @@ namespace Conformance LocateAndTest(identity, identity, identity); // Exercise identical spaces which are not located at the origin of the reference space. - XrPosef space = {{0, 0, 0, 1}, {1, 2, 3}}; + XrPosef space = {Quat::Identity, {1, 2, 3}}; LocateAndTest(space, space, identity); // Exercise identical spaces which also have a rotation. @@ -213,10 +207,10 @@ namespace Conformance LocateAndTest(space, space, identity); // Exercise different spaces without a rotation. - LocateAndTest({{0, 0, 0, 1}, {1, 2, 3}}, {{0, 0, 0, 1}, {-1, -2, -3}}, {{0, 0, 0, 1}, {2, 4, 6}}); + LocateAndTest({Quat::Identity, {1, 2, 3}}, {Quat::Identity, {-1, -2, -3}}, {Quat::Identity, {2, 4, 6}}); // Another test with different spaces. - LocateAndTest({{0, 0, 0, 1}, {-1, -2, -3}}, {{0, 0, 0, 1}, {1, 2, 3}}, {{0, 0, 0, 1}, {-2, -4, -6}}); + LocateAndTest({Quat::Identity, {-1, -2, -3}}, {Quat::Identity, {1, 2, 3}}, {Quat::Identity, {-2, -4, -6}}); const float pi = std::acos(-1.0f); float deg90 = pi / 2.0f; diff --git a/src/conformance/framework/composition_utils.cpp b/src/conformance/framework/composition_utils.cpp index 2f809bdb..d4050819 100644 --- a/src/conformance/framework/composition_utils.cpp +++ b/src/conformance/framework/composition_utils.cpp @@ -54,10 +54,10 @@ namespace Conformance return image; } - XrPath StringToPath(XrInstance instance, const char* pathStr) + XrPath StringToPath(XrInstance instance, const std::string& pathStr) { XrPath path; - XRC_CHECK_THROW_XRCMD(xrStringToPath(instance, pathStr, &path)); + XRC_CHECK_THROW_XRCMD(xrStringToPath(instance, pathStr.c_str(), &path)); return path; } diff --git a/src/conformance/framework/composition_utils.h b/src/conformance/framework/composition_utils.h index c8c178a9..43582a2d 100644 --- a/src/conformance/framework/composition_utils.h +++ b/src/conformance/framework/composition_utils.h @@ -50,7 +50,7 @@ namespace Conformance RGBAImage CreateTextImage(int32_t width, int32_t height, const char* text, int32_t fontHeight, WordWrap wordWrap = WordWrap::Enabled); - XrPath StringToPath(XrInstance instance, const char* pathStr); + XrPath StringToPath(XrInstance instance, const std::string& pathStr); using UpdateLayers = std::function; using EndFrame = std::function; // Return false to stop the loop. diff --git a/src/conformance/framework/conformance_utils.cpp b/src/conformance/framework/conformance_utils.cpp index f53c7f47..3986c82f 100644 --- a/src/conformance/framework/conformance_utils.cpp +++ b/src/conformance/framework/conformance_utils.cpp @@ -21,6 +21,7 @@ #include "two_call_util.h" #include "utilities/throw_helpers.h" #include "utilities/utils.h" +#include "utilities/xrduration_literals.h" #include #include diff --git a/src/conformance/framework/conformance_utils.h b/src/conformance/framework/conformance_utils.h index 7e2c8309..b2cf7c74 100644 --- a/src/conformance/framework/conformance_utils.h +++ b/src/conformance/framework/conformance_utils.h @@ -19,7 +19,6 @@ #include "utilities/event_reader.h" #include "utilities/types_and_constants.h" -#include "utilities/xrduration_literals.h" #include @@ -30,7 +29,6 @@ #include #include #include -#include #include #include diff --git a/src/conformance/framework/controller_animation_handler.cpp b/src/conformance/framework/controller_animation_handler.cpp index b6ff2fec..3c966632 100644 --- a/src/conformance/framework/controller_animation_handler.cpp +++ b/src/conformance/framework/controller_animation_handler.cpp @@ -13,6 +13,7 @@ #include "common/xr_linear.h" #include "pbr/PbrCommon.h" #include "pbr/PbrModel.h" +#include "utilities/throw_helpers.h" #include @@ -25,28 +26,41 @@ using namespace std::literals::chrono_literals; namespace Conformance { - ControllerAnimationHandler::ControllerAnimationHandler(std::shared_ptr model, - std::vector properties) - : m_pbrModel(model), m_nodeProperties(properties) + ControllerAnimationHandler::ControllerAnimationHandler(const Pbr::Model& model, + std::vector&& properties) + { + Init(model, std::move(properties)); + } + void ControllerAnimationHandler::Init(const Pbr::Model& model, std::vector&& properties) { + m_nodeProperties = std::move(properties); // Compute the index of each node reported by runtime to be animated. // The order of m_nodeIndices exactly matches the order of the nodes properties and states. m_nodeIndices.resize(m_nodeProperties.size(), Pbr::NodeIndex_npos); for (size_t i = 0; i < m_nodeProperties.size(); ++i) { const auto& nodeProperty = m_nodeProperties[i]; - Pbr::NodeIndex_t parentNodeIndex; - if (m_pbrModel->FindFirstNode(&parentNodeIndex, nodeProperty.parentNodeName)) { - Pbr::NodeIndex_t targetNodeIndex; - if (m_pbrModel->FindFirstNode(&targetNodeIndex, nodeProperty.nodeName, &parentNodeIndex)) { - m_nodeIndices[i] = targetNodeIndex; - } - } + m_nodeIndices[i] = FindPbrNodeIndex(model, nodeProperty.parentNodeName, nodeProperty.nodeName); + } + } + + Pbr::NodeIndex_t ControllerAnimationHandler::FindPbrNodeIndex(const Pbr::Model& model, const char* parentNodeName, const char* nodeName) + { + + Pbr::NodeIndex_t parentNodeIndex; + if (!model.FindFirstNode(&parentNodeIndex, parentNodeName)) { + XRC_THROW("Could not find parent node by name"); + } + Pbr::NodeIndex_t targetNodeIndex; + if (!model.FindFirstNode(&targetNodeIndex, nodeName, &parentNodeIndex)) { + XRC_THROW("Could not find target node by name"); } + return targetNodeIndex; } // Update transforms of nodes for the animatable parts in the controller model - void ControllerAnimationHandler::UpdateControllerParts(std::vector nodeStates) + void ControllerAnimationHandler::UpdateControllerParts(const std::vector& nodeStates, + Pbr::ModelInstance& pbrModelInstance) { m_nodeStates = nodeStates; @@ -55,12 +69,11 @@ namespace Conformance for (size_t i = 0; i < end; i++) { const Pbr::NodeIndex_t nodeIndex = m_nodeIndices[i]; if (nodeIndex != Pbr::NodeIndex_npos) { - Pbr::Node& node = m_pbrModel->GetNode(nodeIndex); XrMatrix4x4f nodeTransform; XrVector3f unitScale = {1, 1, 1}; XrMatrix4x4f_CreateTranslationRotationScale(&nodeTransform, &m_nodeStates[i].nodePose.position, &m_nodeStates[i].nodePose.orientation, &unitScale); - node.SetTransform(nodeTransform); + pbrModelInstance.SetNodeTransform(nodeIndex, nodeTransform); } } } diff --git a/src/conformance/framework/controller_animation_handler.h b/src/conformance/framework/controller_animation_handler.h index 37336c9d..a1c4a4dc 100644 --- a/src/conformance/framework/controller_animation_handler.h +++ b/src/conformance/framework/controller_animation_handler.h @@ -29,11 +29,13 @@ namespace Conformance { public: ControllerAnimationHandler() = default; - ControllerAnimationHandler(std::shared_ptr model, std::vector properties); - void UpdateControllerParts(std::vector nodeStates); + ControllerAnimationHandler(const Pbr::Model& model, std::vector&& properties); + + void Init(const Pbr::Model& model, std::vector&& properties); + void UpdateControllerParts(const std::vector& nodeStates, Pbr::ModelInstance& pbrModelInstance); private: - std::shared_ptr m_pbrModel; + static Pbr::NodeIndex_t FindPbrNodeIndex(const Pbr::Model& model, const char* parentNodeName, const char* nodeName); std::vector m_nodeIndices; std::vector m_nodeProperties; std::vector m_nodeStates; diff --git a/src/conformance/framework/gltf_model.h b/src/conformance/framework/gltf_model.h index 19c2d81d..f24a40c9 100644 --- a/src/conformance/framework/gltf_model.h +++ b/src/conformance/framework/gltf_model.h @@ -15,26 +15,23 @@ namespace Conformance { /// Templated base class for API-specific model objects in the main CTS code. - template - class GltfModelBase + template + class RenderableGltfModelInstanceBase { public: - GltfModelBase(ResourcesType& pbrResources, std::shared_ptr gltf, std::shared_ptr pbrModel = nullptr, - Pbr::FillMode fillMode = Pbr::FillMode::Solid) - : m_gltf(gltf) - , m_pbrModel(pbrModel != nullptr ? std::move(pbrModel) : Gltf::FromGltfObject(pbrResources, *gltf)) - , m_fillMode(fillMode) + RenderableGltfModelInstanceBase(ModelInstanceType&& pbrModelInstance, Pbr::FillMode fillMode = Pbr::FillMode::Solid) + : m_pbrModelInstance(std::move(pbrModelInstance)), m_fillMode(fillMode) { } - void SetModel(std::shared_ptr&& model) + ModelInstanceType& GetModelInstance() noexcept { - m_pbrModel = std::move(model); + return m_pbrModelInstance; } - const std::shared_ptr& GetModel() const noexcept + const ModelInstanceType& GetModelInstance() const noexcept { - return m_pbrModel; + return m_pbrModelInstance; } void SetFillMode(const Pbr::FillMode& fillMode) @@ -49,15 +46,15 @@ namespace Conformance void SetBaseColorFactor(ResourcesType& pbrResources, Pbr::RGBAColor color) { - for (uint32_t k = 0; k < GetModel()->GetPrimitiveCount(); k++) { - auto& material = pbrResources.GetPrimitive(GetModel()->GetPrimitive(k)).GetMaterial(); + for (uint32_t k = 0; k < m_pbrModelInstance.GetPrimitiveCount(); k++) { + auto& material = pbrResources.GetPrimitive(m_pbrModelInstance.GetPrimitiveHandle(k)).GetMaterial(); material->Parameters().BaseColorFactor = color; } } private: std::shared_ptr m_gltf; - std::shared_ptr m_pbrModel; + ModelInstanceType m_pbrModelInstance; Pbr::FillMode m_fillMode; }; } // namespace Conformance diff --git a/src/conformance/framework/graphics_plugin.h b/src/conformance/framework/graphics_plugin.h index 29483423..dd4c55e8 100644 --- a/src/conformance/framework/graphics_plugin.h +++ b/src/conformance/framework/graphics_plugin.h @@ -21,7 +21,6 @@ #include "conformance_utils.h" #include "conformance_framework.h" #include "utilities/Geometry.h" -#include "utilities/throw_helpers.h" #include "RGBAImage.h" #include "pbr/PbrModel.h" @@ -30,10 +29,8 @@ #include #include #include -#include #include #include -#include // We #include all the possible graphics system headers here because openxr_platform.h assumes that // they are all visible when it is compiled. @@ -146,14 +143,25 @@ namespace Conformance } }; - /// Handle returned by a graphics plugin, used to reference plugin-internal data for a loaded GLTF model. + /// Handle returned by a graphics plugin, used to reference plugin-internal data for a loaded glTF (PBR) model. /// /// They expire at IGraphicsPlugin::Shutdown() and IGraphicsPlugin::ShutdownDevice() calls, /// so must not be persisted past those calls. /// /// They are "null" by default, so may be tested for validity by comparison against a default-constructed instance. - using GLTFHandle = nonstd::equality; + using GLTFModelHandle = nonstd::equality; + /// Handle returned by a graphics plugin, used to reference plugin-internal data for a renderable instance of a glTF (PBR) model. + /// + /// They expire at IGraphicsPlugin::Shutdown() and IGraphicsPlugin::ShutdownDevice() calls, + /// so must not be persisted past those calls. + /// + /// They are "null" by default, so may be tested for validity by comparison against a default-constructed instance. + using GLTFModelInstanceHandle = nonstd::equality; + + /// A handle referring to a node in a loaded PBR (glTF) model. + /// Only useful in the context of the GLTFModelHandle it came from: + /// it is internally just an index. using NodeHandle = nonstd::ordered; struct NodeParams @@ -162,16 +170,16 @@ namespace Conformance bool visible; }; - /// A drawable GLTF model, consisting of a reference to plugin-specific data for a GLTF model, plus pose and scale. + /// A drawable GLTF model, consisting of a reference to plugin-specific data for a glTF (PBR) model instance, pose, scale, and node/parameter pairs. struct GLTFDrawable { - GLTFHandle handle; + GLTFModelInstanceHandle handle; DrawableParams params; // or unordered_map, probably not significant std::map nodesAndParams; - GLTFDrawable(GLTFHandle handle, XrPosef pose = XrPosefCPP{}, XrVector3f scale = {1.0, 1.0, 1.0}) + GLTFDrawable(GLTFModelInstanceHandle handle, XrPosef pose = XrPosefCPP{}, XrVector3f scale = {1.0, 1.0, 1.0}) : handle(handle), params(pose, scale) { } @@ -344,7 +352,7 @@ namespace Conformance /// Create internal data for a glTF model, returning a handle to refer to it. /// This handle expires when the internal data is cleared in Shutdown() and ShutdownDevice(). - GLTFHandle LoadGLTF(span data) + GLTFModelHandle LoadGLTF(span data) { return LoadGLTF(Conformance::LoadGLTF(data)); } @@ -352,9 +360,15 @@ namespace Conformance /// Create internal data for a glTF model, returning a handle to refer to it. /// This handle expires when the internal data is cleared in Shutdown() and ShutdownDevice(). /// It retains a reference to the tinygltf model passed here. - virtual GLTFHandle LoadGLTF(std::shared_ptr tinygltfModel) = 0; + virtual GLTFModelHandle LoadGLTF(std::shared_ptr tinygltfModel) = 0; + + /// Get the underlying Pbr::Model associated with the supplied handle. + virtual std::shared_ptr GetPbrModel(GLTFModelHandle handle) const = 0; - virtual std::shared_ptr GetModel(GLTFHandle handle) const = 0; + virtual GLTFModelInstanceHandle CreateGLTFModelInstance(GLTFModelHandle handle) = 0; + + /// Get a reference to the base interface for a given ModelInstance from its handle + virtual Pbr::ModelInstance& GetModelInstance(GLTFModelInstanceHandle handle) = 0; /// Convenience helper function to make a mesh that is our standard cube (with R, G, B faces along X, Y, Z, respectively) MeshHandle MakeCubeMesh() @@ -366,7 +380,7 @@ namespace Conformance MeshHandle MakeGnomonMesh() { return MakeSimpleMesh(Geometry::AxisIndicator::GetInstance().indices, Geometry::AxisIndicator::GetInstance().vertices); - }; + } virtual void RenderView(const XrCompositionLayerProjectionView& layerView, const XrSwapchainImageBaseHeader* colorSwapchainImage, const RenderParams& params) = 0; diff --git a/src/conformance/framework/graphics_plugin_d3d11.cpp b/src/conformance/framework/graphics_plugin_d3d11.cpp index 12b1736f..93faf286 100644 --- a/src/conformance/framework/graphics_plugin_d3d11.cpp +++ b/src/conformance/framework/graphics_plugin_d3d11.cpp @@ -207,9 +207,10 @@ namespace Conformance MeshHandle MakeSimpleMesh(span idx, span vtx) override; - GLTFHandle LoadGLTF(std::shared_ptr tinygltfModel) override; - - std::shared_ptr GetModel(GLTFHandle handle) const override; + GLTFModelHandle LoadGLTF(std::shared_ptr tinygltfModel) override; + std::shared_ptr GetPbrModel(GLTFModelHandle handle) const override; + GLTFModelInstanceHandle CreateGLTFModelInstance(GLTFModelHandle handle) override; + Pbr::ModelInstance& GetModelInstance(GLTFModelInstanceHandle handle) override; void RenderView(const XrCompositionLayerProjectionView& layerView, const XrSwapchainImageBaseHeader* colorSwapchainImage, const RenderParams& params) override; @@ -234,7 +235,9 @@ namespace Conformance MeshHandle m_cubeMesh; VectorWithGenerationCountedHandles m_meshes; - VectorWithGenerationCountedHandles m_gltfs; + // This is fine to be a shared_ptr because Model doesn't directly hold any graphics state. + VectorWithGenerationCountedHandles, GLTFModelHandle> m_gltfModels; + VectorWithGenerationCountedHandles m_gltfInstances; std::unique_ptr m_pbrResources; @@ -428,7 +431,8 @@ namespace Conformance m_cubeMesh = {}; m_meshes.clear(); - m_gltfs.clear(); + m_gltfInstances.clear(); + m_gltfModels.clear(); m_pbrResources.reset(); d3d11DeviceContext.Reset(); @@ -674,7 +678,6 @@ namespace Conformance // Clear color buffer. // Create RenderTargetView with original swapchain format (swapchain is typeless). ComPtr renderTargetView = CreateRenderTargetView(*swapchainData, imageIndex, imageArrayIndex); - // TODO: Do not clear to a color when using a pass-through view configuration. FLOAT bg[] = {color.r, color.g, color.b, color.a}; d3d11DeviceContext->ClearRenderTargetView(renderTargetView.Get(), bg); @@ -689,15 +692,28 @@ namespace Conformance return handle; } - GLTFHandle D3D11GraphicsPlugin::LoadGLTF(std::shared_ptr tinygltfModel) + GLTFModelHandle D3D11GraphicsPlugin::LoadGLTF(std::shared_ptr tinygltfModel) { - auto handle = m_gltfs.emplace_back(*m_pbrResources, std::move(tinygltfModel)); + std::shared_ptr pbrModel = Gltf::FromGltfObject(*m_pbrResources, *tinygltfModel); + auto handle = m_gltfModels.emplace_back(std::move(pbrModel)); return handle; } - inline std::shared_ptr D3D11GraphicsPlugin::GetModel(GLTFHandle handle) const + std::shared_ptr D3D11GraphicsPlugin::GetPbrModel(GLTFModelHandle handle) const + { + return m_gltfModels[handle]; + } + + GLTFModelInstanceHandle D3D11GraphicsPlugin::CreateGLTFModelInstance(GLTFModelHandle handle) + { + auto pbrModelInstance = Pbr::D3D11ModelInstance(*m_pbrResources, GetPbrModel(handle)); + auto instanceHandle = m_gltfInstances.emplace_back(std::move(pbrModelInstance)); + return instanceHandle; + } + + Pbr::ModelInstance& D3D11GraphicsPlugin::GetModelInstance(GLTFModelInstanceHandle handle) { - return m_gltfs[handle].GetModel(); + return m_gltfInstances[handle].GetModelInstance(); } void D3D11GraphicsPlugin::RenderView(const XrCompositionLayerProjectionView& layerView, @@ -776,13 +792,13 @@ namespace Conformance } // Render each gltf - for (const auto& gltfHandle : params.glTFs) { - D3D11GLTF& gltf = m_gltfs[gltfHandle.handle]; + for (const auto& gltfDrawable : params.glTFs) { + D3D11GLTF& gltf = m_gltfInstances[gltfDrawable.handle]; // Compute and update the model transform. XrMatrix4x4f modelToWorld; - XrMatrix4x4f_CreateTranslationRotationScale(&modelToWorld, &gltfHandle.params.pose.position, - &gltfHandle.params.pose.orientation, &gltfHandle.params.scale); + XrMatrix4x4f_CreateTranslationRotationScale(&modelToWorld, &gltfDrawable.params.pose.position, + &gltfDrawable.params.pose.orientation, &gltfDrawable.params.scale); XrMatrix4x4f viewMatrix; XrVector3f unitScale = {1, 1, 1}; XrMatrix4x4f_CreateTranslationRotationScale(&viewMatrix, &layerView.pose.position, &layerView.pose.orientation, &unitScale); diff --git a/src/conformance/framework/graphics_plugin_d3d11_gltf.cpp b/src/conformance/framework/graphics_plugin_d3d11_gltf.cpp index 40141bbe..4c3158e1 100644 --- a/src/conformance/framework/graphics_plugin_d3d11_gltf.cpp +++ b/src/conformance/framework/graphics_plugin_d3d11_gltf.cpp @@ -21,16 +21,11 @@ using namespace DirectX; namespace Conformance { - void D3D11GLTF::Render(ComPtr deviceContext, Pbr::D3D11Resources& resources, XrMatrix4x4f& modelToWorld) const + void D3D11GLTF::Render(ComPtr deviceContext, Pbr::D3D11Resources& resources, XrMatrix4x4f& modelToWorld) { - if (!GetModel()) { - return; - } - resources.SetFillMode(GetFillMode()); - resources.SetModelToWorld(LoadXrMatrix(modelToWorld), deviceContext.Get()); resources.Bind(deviceContext.Get()); - GetModel()->Render(resources, deviceContext.Get()); + GetModelInstance().Render(resources, deviceContext.Get(), LoadXrMatrix(modelToWorld)); } } // namespace Conformance diff --git a/src/conformance/framework/graphics_plugin_d3d11_gltf.h b/src/conformance/framework/graphics_plugin_d3d11_gltf.h index e35e1752..36da9cc7 100644 --- a/src/conformance/framework/graphics_plugin_d3d11_gltf.h +++ b/src/conformance/framework/graphics_plugin_d3d11_gltf.h @@ -26,13 +26,14 @@ using Microsoft::WRL::ComPtr; namespace Conformance { + struct CmdBuffer; - class D3D11GLTF : public GltfModelBase + class D3D11GLTF : public RenderableGltfModelInstanceBase { public: - using GltfModelBase::GltfModelBase; + using RenderableGltfModelInstanceBase::RenderableGltfModelInstanceBase; - void Render(ComPtr deviceContext, Pbr::D3D11Resources& resources, XrMatrix4x4f& modelToWorld) const; + void Render(ComPtr deviceContext, Pbr::D3D11Resources& resources, XrMatrix4x4f& modelToWorld); }; } // namespace Conformance #endif diff --git a/src/conformance/framework/graphics_plugin_d3d12.cpp b/src/conformance/framework/graphics_plugin_d3d12.cpp index 05e90cd3..b4e6fd4b 100644 --- a/src/conformance/framework/graphics_plugin_d3d12.cpp +++ b/src/conformance/framework/graphics_plugin_d3d12.cpp @@ -357,9 +357,10 @@ namespace Conformance MeshHandle MakeSimpleMesh(span idx, span vtx) override; - GLTFHandle LoadGLTF(std::shared_ptr tinygltfModel) override; - - std::shared_ptr GetModel(GLTFHandle handle) const override; + GLTFModelHandle LoadGLTF(std::shared_ptr tinygltfModel) override; + std::shared_ptr GetPbrModel(GLTFModelHandle handle) const override; + GLTFModelInstanceHandle CreateGLTFModelInstance(GLTFModelHandle handle) override; + Pbr::ModelInstance& GetModelInstance(GLTFModelInstanceHandle handle) override; void RenderView(const XrCompositionLayerProjectionView& layerView, const XrSwapchainImageBaseHeader* colorSwapchainImage, const RenderParams& params) override; @@ -395,7 +396,9 @@ namespace Conformance MeshHandle m_cubeMesh; VectorWithGenerationCountedHandles m_meshes; - VectorWithGenerationCountedHandles m_gltfs; + // This is fine to be a shared_ptr because Model doesn't directly hold any graphics state. + VectorWithGenerationCountedHandles, GLTFModelHandle> m_gltfModels; + VectorWithGenerationCountedHandles m_gltfInstances; std::unique_ptr m_pbrResources; }; @@ -589,7 +592,8 @@ namespace Conformance pipelineStates.clear(); m_cubeMesh = {}; m_meshes.clear(); - m_gltfs.clear(); + m_gltfInstances.clear(); + m_gltfModels.clear(); rtvHeap.Reset(); dsvHeap.Reset(); m_swapchainImageDataMap.Reset(); @@ -937,7 +941,6 @@ namespace Conformance // Clear color buffer. D3D12_CPU_DESCRIPTOR_HANDLE renderTargetView = CreateRenderTargetView(colorTexture, imageArrayIndex, swapchainData->GetCreateInfo().format); - // TODO: Do not clear to a color when using a pass-through view configuration. FLOAT bg[] = {color.r, color.g, color.b, color.a}; cmdList->ClearRenderTargetView(renderTargetView, bg, 0, nullptr); @@ -962,15 +965,28 @@ namespace Conformance return handle; } - GLTFHandle D3D12GraphicsPlugin::LoadGLTF(std::shared_ptr tinygltfModel) + GLTFModelHandle D3D12GraphicsPlugin::LoadGLTF(std::shared_ptr tinygltfModel) { - auto handle = m_gltfs.emplace_back(*m_pbrResources, std::move(tinygltfModel)); + std::shared_ptr pbrModel = Gltf::FromGltfObject(*m_pbrResources, *tinygltfModel); + auto handle = m_gltfModels.emplace_back(std::move(pbrModel)); return handle; } - inline std::shared_ptr D3D12GraphicsPlugin::GetModel(GLTFHandle handle) const + std::shared_ptr D3D12GraphicsPlugin::GetPbrModel(GLTFModelHandle handle) const + { + return m_gltfModels[handle]; + } + + GLTFModelInstanceHandle D3D12GraphicsPlugin::CreateGLTFModelInstance(GLTFModelHandle handle) + { + auto pbrModelInstance = Pbr::D3D12ModelInstance(*m_pbrResources, GetPbrModel(handle)); + auto instanceHandle = m_gltfInstances.emplace_back(std::move(pbrModelInstance)); + return instanceHandle; + } + + Pbr::ModelInstance& D3D12GraphicsPlugin::GetModelInstance(GLTFModelInstanceHandle handle) { - return m_gltfs[handle].GetModel(); + return m_gltfInstances[handle].GetModelInstance(); } void D3D12GraphicsPlugin::RenderView(const XrCompositionLayerProjectionView& layerView, @@ -1106,13 +1122,13 @@ namespace Conformance } // Render each gltf - for (const auto& gltfHandle : params.glTFs) { - D3D12GLTF& gltf = m_gltfs[gltfHandle.handle]; + for (const auto& gltfDrawable : params.glTFs) { + D3D12GLTF& gltf = m_gltfInstances[gltfDrawable.handle]; // Compute and update the model transform. XrMatrix4x4f modelToWorld; - XrMatrix4x4f_CreateTranslationRotationScale(&modelToWorld, &gltfHandle.params.pose.position, - &gltfHandle.params.pose.orientation, &gltfHandle.params.scale); + XrMatrix4x4f_CreateTranslationRotationScale(&modelToWorld, &gltfDrawable.params.pose.position, + &gltfDrawable.params.pose.orientation, &gltfDrawable.params.scale); XrMatrix4x4f viewMatrix; XrVector3f unitScale = {1, 1, 1}; XrMatrix4x4f_CreateTranslationRotationScale(&viewMatrix, &layerView.pose.position, &layerView.pose.orientation, &unitScale); diff --git a/src/conformance/framework/graphics_plugin_d3d12_gltf.cpp b/src/conformance/framework/graphics_plugin_d3d12_gltf.cpp index a8b45f4e..d8dc88ab 100644 --- a/src/conformance/framework/graphics_plugin_d3d12_gltf.cpp +++ b/src/conformance/framework/graphics_plugin_d3d12_gltf.cpp @@ -5,15 +5,9 @@ #include "graphics_plugin_d3d12_gltf.h" -#include "conformance_framework.h" -#include "graphics_plugin_d3d12_gltf.h" -#include "report.h" - -#include "pbr/D3D12/D3D12Primitive.h" +#include "pbr/D3D12/D3D12Model.h" #include "pbr/D3D12/D3D12Resources.h" -#include "pbr/GltfLoader.h" #include "utilities/d3d_common.h" -#include "utilities/throw_helpers.h" #include #include @@ -25,17 +19,10 @@ namespace Conformance void D3D12GLTF::Render(ComPtr directCommandList, Pbr::D3D12Resources& resources, XrMatrix4x4f& modelToWorld, DXGI_FORMAT colorRenderTargetFormat, DXGI_FORMAT depthRenderTargetFormat) { - if (!GetModel()) { - return; - } - - // move these to a base class helper resources.SetFillMode(GetFillMode()); - // end move - - resources.SetModelToWorld(LoadXrMatrix(modelToWorld)); resources.Bind(directCommandList.Get()); - GetModel()->Render(resources, directCommandList.Get(), colorRenderTargetFormat, depthRenderTargetFormat); + GetModelInstance().Render(resources, directCommandList.Get(), colorRenderTargetFormat, depthRenderTargetFormat, + LoadXrMatrix(modelToWorld)); } } // namespace Conformance diff --git a/src/conformance/framework/graphics_plugin_d3d12_gltf.h b/src/conformance/framework/graphics_plugin_d3d12_gltf.h index c86da0ab..635236e0 100644 --- a/src/conformance/framework/graphics_plugin_d3d12_gltf.h +++ b/src/conformance/framework/graphics_plugin_d3d12_gltf.h @@ -27,10 +27,10 @@ using Microsoft::WRL::ComPtr; namespace Conformance { - class D3D12GLTF : public GltfModelBase + class D3D12GLTF : public RenderableGltfModelInstanceBase { public: - using GltfModelBase::GltfModelBase; + using RenderableGltfModelInstanceBase::RenderableGltfModelInstanceBase; void Render(ComPtr directCommandList, Pbr::D3D12Resources& resources, XrMatrix4x4f& modelToWorld, DXGI_FORMAT colorRenderTargetFormat, DXGI_FORMAT depthRenderTargetFormat); diff --git a/src/conformance/framework/graphics_plugin_opengl.cpp b/src/conformance/framework/graphics_plugin_opengl.cpp index 5324b875..3a501faf 100644 --- a/src/conformance/framework/graphics_plugin_opengl.cpp +++ b/src/conformance/framework/graphics_plugin_opengl.cpp @@ -51,7 +51,6 @@ #include #include #include -#include #include #include #include @@ -418,9 +417,10 @@ namespace Conformance MeshHandle MakeSimpleMesh(span idx, span vtx) override; - GLTFHandle LoadGLTF(std::shared_ptr tinygltfModel) override; - - std::shared_ptr GetModel(GLTFHandle handle) const override; + GLTFModelHandle LoadGLTF(std::shared_ptr tinygltfModel) override; + std::shared_ptr GetPbrModel(GLTFModelHandle handle) const override; + GLTFModelInstanceHandle CreateGLTFModelInstance(GLTFModelHandle handle) override; + Pbr::ModelInstance& GetModelInstance(GLTFModelInstanceHandle handle) override; void RenderView(const XrCompositionLayerProjectionView& layerView, const XrSwapchainImageBaseHeader* colorSwapchainImage, const RenderParams& params) override; @@ -455,7 +455,9 @@ namespace Conformance GLint m_vertexAttribColor{0}; MeshHandle m_cubeMesh{}; VectorWithGenerationCountedHandles m_meshes; - VectorWithGenerationCountedHandles m_gltfs; + // This is fine to be a shared_ptr because Model doesn't directly hold any graphics state. + VectorWithGenerationCountedHandles, GLTFModelHandle> m_gltfModels; + VectorWithGenerationCountedHandles m_gltfInstances; std::unique_ptr m_pbrResources; }; @@ -805,7 +807,9 @@ namespace Conformance m_swapchainImageDataMap.Reset(); m_cubeMesh = {}; m_meshes.clear(); - m_gltfs.clear(); + m_gltfInstances.clear(); + m_gltfModels.clear(); + m_pbrResources.reset(); deleteGLContext(); } @@ -1038,15 +1042,28 @@ namespace Conformance return handle; } - GLTFHandle OpenGLGraphicsPlugin::LoadGLTF(std::shared_ptr tinygltfModel) + GLTFModelHandle OpenGLGraphicsPlugin::LoadGLTF(std::shared_ptr tinygltfModel) { - auto handle = m_gltfs.emplace_back(*m_pbrResources, std::move(tinygltfModel)); + std::shared_ptr pbrModel = Gltf::FromGltfObject(*m_pbrResources, *tinygltfModel); + auto handle = m_gltfModels.emplace_back(std::move(pbrModel)); return handle; } - inline std::shared_ptr OpenGLGraphicsPlugin::GetModel(GLTFHandle handle) const + std::shared_ptr OpenGLGraphicsPlugin::GetPbrModel(GLTFModelHandle handle) const + { + return m_gltfModels[handle]; + } + + GLTFModelInstanceHandle OpenGLGraphicsPlugin::CreateGLTFModelInstance(GLTFModelHandle handle) + { + auto pbrModelInstance = Pbr::GLModelInstance(*m_pbrResources, GetPbrModel(handle)); + auto instanceHandle = m_gltfInstances.emplace_back(std::move(pbrModelInstance)); + return instanceHandle; + } + + Pbr::ModelInstance& OpenGLGraphicsPlugin::GetModelInstance(GLTFModelInstanceHandle handle) { - return m_gltfs[handle].GetModel(); + return m_gltfInstances[handle].GetModelInstance(); } void OpenGLGraphicsPlugin::RenderView(const XrCompositionLayerProjectionView& layerView, @@ -1137,13 +1154,13 @@ namespace Conformance } // Render each gltf - for (const auto& gltfHandle : params.glTFs) { - GLGLTF& gltf = m_gltfs[gltfHandle.handle]; + for (const auto& gltfDrawable : params.glTFs) { + GLGLTF& gltf = m_gltfInstances[gltfDrawable.handle]; // Compute and update the model transform. XrMatrix4x4f modelToWorld; - XrMatrix4x4f_CreateTranslationRotationScale(&modelToWorld, &gltfHandle.params.pose.position, - &gltfHandle.params.pose.orientation, &gltfHandle.params.scale); + XrMatrix4x4f_CreateTranslationRotationScale(&modelToWorld, &gltfDrawable.params.pose.position, + &gltfDrawable.params.pose.orientation, &gltfDrawable.params.scale); m_pbrResources->SetViewProjection(view, proj); diff --git a/src/conformance/framework/graphics_plugin_opengl_gltf.cpp b/src/conformance/framework/graphics_plugin_opengl_gltf.cpp index 3be230cd..ba120000 100644 --- a/src/conformance/framework/graphics_plugin_opengl_gltf.cpp +++ b/src/conformance/framework/graphics_plugin_opengl_gltf.cpp @@ -6,30 +6,16 @@ #include "graphics_plugin_opengl_gltf.h" -#include "conformance_framework.h" -#include "graphics_plugin_opengl_gltf.h" -#include "report.h" - -#include "pbr/GltfLoader.h" #include "pbr/OpenGL/GLModel.h" -#include "pbr/OpenGL/GLPrimitive.h" #include "pbr/OpenGL/GLResources.h" -#include "utilities/throw_helpers.h" - -#include namespace Conformance { - void GLGLTF::Render(Pbr::GLResources& resources, XrMatrix4x4f& modelToWorld) const + void GLGLTF::Render(Pbr::GLResources& resources, XrMatrix4x4f& modelToWorld) { - if (!GetModel()) { - return; - } - resources.SetFillMode(GetFillMode()); - resources.SetModelToWorld(modelToWorld); resources.Bind(); - GetModel()->Render(resources); + GetModelInstance().Render(resources, modelToWorld); } } // namespace Conformance diff --git a/src/conformance/framework/graphics_plugin_opengl_gltf.h b/src/conformance/framework/graphics_plugin_opengl_gltf.h index b7c1f549..1f979496 100644 --- a/src/conformance/framework/graphics_plugin_opengl_gltf.h +++ b/src/conformance/framework/graphics_plugin_opengl_gltf.h @@ -27,12 +27,12 @@ namespace Pbr namespace Conformance { - class GLGLTF : public GltfModelBase + class GLGLTF : public RenderableGltfModelInstanceBase { public: - using GltfModelBase::GltfModelBase; + using RenderableGltfModelInstanceBase::RenderableGltfModelInstanceBase; - void Render(Pbr::GLResources& resources, XrMatrix4x4f& modelToWorld) const; + void Render(Pbr::GLResources& resources, XrMatrix4x4f& modelToWorld); }; } // namespace Conformance #endif diff --git a/src/conformance/framework/graphics_plugin_opengles.cpp b/src/conformance/framework/graphics_plugin_opengles.cpp index 29e6f06f..a12860f9 100644 --- a/src/conformance/framework/graphics_plugin_opengles.cpp +++ b/src/conformance/framework/graphics_plugin_opengles.cpp @@ -299,9 +299,10 @@ namespace Conformance MeshHandle MakeSimpleMesh(span idx, span vtx) override; - GLTFHandle LoadGLTF(std::shared_ptr tinygltfModel) override; - - std::shared_ptr GetModel(GLTFHandle handle) const override; + GLTFModelHandle LoadGLTF(std::shared_ptr tinygltfModel) override; + std::shared_ptr GetPbrModel(GLTFModelHandle handle) const override; + GLTFModelInstanceHandle CreateGLTFModelInstance(GLTFModelHandle handle) override; + Pbr::ModelInstance& GetModelInstance(GLTFModelInstanceHandle handle) override; void RenderView(const XrCompositionLayerProjectionView& layerView, const XrSwapchainImageBaseHeader* colorSwapchainImage, const RenderParams& params) override; @@ -326,7 +327,9 @@ namespace Conformance GLint m_vertexAttribColor{0}; MeshHandle m_cubeMesh{}; VectorWithGenerationCountedHandles m_meshes; - VectorWithGenerationCountedHandles m_gltfs; + // This is fine to be a shared_ptr because Model doesn't directly hold any graphics state. + VectorWithGenerationCountedHandles, GLTFModelHandle> m_gltfModels; + VectorWithGenerationCountedHandles m_gltfInstances; std::unique_ptr m_pbrResources; SwapchainImageDataMap m_swapchainImageDataMap; @@ -592,7 +595,9 @@ namespace Conformance m_cubeMesh = {}; m_meshes.clear(); - m_gltfs.clear(); + m_gltfInstances.clear(); + m_gltfModels.clear(); + m_pbrResources.reset(); ksGpuWindow_Destroy(&window); } @@ -1158,15 +1163,28 @@ namespace Conformance return handle; } - GLTFHandle OpenGLESGraphicsPlugin::LoadGLTF(std::shared_ptr tinygltfModel) + GLTFModelHandle OpenGLESGraphicsPlugin::LoadGLTF(std::shared_ptr tinygltfModel) { - auto handle = m_gltfs.emplace_back(*m_pbrResources, std::move(tinygltfModel)); + std::shared_ptr pbrModel = Gltf::FromGltfObject(*m_pbrResources, *tinygltfModel); + auto handle = m_gltfModels.emplace_back(std::move(pbrModel)); return handle; } - inline std::shared_ptr OpenGLESGraphicsPlugin::GetModel(GLTFHandle handle) const + std::shared_ptr OpenGLESGraphicsPlugin::GetPbrModel(GLTFModelHandle handle) const + { + return m_gltfModels[handle]; + } + + GLTFModelInstanceHandle OpenGLESGraphicsPlugin::CreateGLTFModelInstance(GLTFModelHandle handle) + { + auto pbrModelInstance = Pbr::GLModelInstance(*m_pbrResources, GetPbrModel(handle)); + auto instanceHandle = m_gltfInstances.emplace_back(std::move(pbrModelInstance)); + return instanceHandle; + } + + Pbr::ModelInstance& OpenGLESGraphicsPlugin::GetModelInstance(GLTFModelInstanceHandle handle) { - return m_gltfs[handle].GetModel(); + return m_gltfInstances[handle].GetModelInstance(); } void OpenGLESGraphicsPlugin::RenderView(const XrCompositionLayerProjectionView& layerView, @@ -1258,13 +1276,13 @@ namespace Conformance } // Render each gltf - for (const auto& gltfHandle : params.glTFs) { - GLGLTF& gltf = m_gltfs[gltfHandle.handle]; + for (const auto& gltfDrawable : params.glTFs) { + GLGLTF& gltf = m_gltfInstances[gltfDrawable.handle]; // Compute and update the model transform. XrMatrix4x4f modelToWorld; - XrMatrix4x4f_CreateTranslationRotationScale(&modelToWorld, &gltfHandle.params.pose.position, - &gltfHandle.params.pose.orientation, &gltfHandle.params.scale); + XrMatrix4x4f_CreateTranslationRotationScale(&modelToWorld, &gltfDrawable.params.pose.position, + &gltfDrawable.params.pose.orientation, &gltfDrawable.params.scale); m_pbrResources->SetViewProjection(view, proj); diff --git a/src/conformance/framework/graphics_plugin_vulkan.cpp b/src/conformance/framework/graphics_plugin_vulkan.cpp index c830f325..13bb6345 100644 --- a/src/conformance/framework/graphics_plugin_vulkan.cpp +++ b/src/conformance/framework/graphics_plugin_vulkan.cpp @@ -32,6 +32,7 @@ #include "pbr/Vulkan/VkCommon.h" #include "pbr/Vulkan/VkResources.h" #include "pbr/Vulkan/VkTexture.h" +#include "pbr/Vulkan/VkModel.h" #include "utilities/Geometry.h" #include "utilities/swapchain_format_data.h" #include "utilities/swapchain_parameters.h" @@ -653,9 +654,10 @@ namespace Conformance MeshHandle MakeSimpleMesh(span idx, span vtx) override; - GLTFHandle LoadGLTF(std::shared_ptr tinygltfModel) override; - - std::shared_ptr GetModel(GLTFHandle handle) const override; + GLTFModelHandle LoadGLTF(std::shared_ptr tinygltfModel) override; + std::shared_ptr GetPbrModel(GLTFModelHandle handle) const override; + GLTFModelInstanceHandle CreateGLTFModelInstance(GLTFModelHandle handle) override; + Pbr::ModelInstance& GetModelInstance(GLTFModelInstanceHandle handle) override; void RenderView(const XrCompositionLayerProjectionView& layerView, const XrSwapchainImageBaseHeader* colorSwapchainImage, const RenderParams& params) override; @@ -710,7 +712,9 @@ namespace Conformance PipelineLayout m_pipelineLayout{}; MeshHandle m_cubeMesh{}; VectorWithGenerationCountedHandles m_meshes; - VectorWithGenerationCountedHandles m_gltfs; + // This is fine to be a shared_ptr because Model doesn't directly hold any graphics state. + VectorWithGenerationCountedHandles, GLTFModelHandle> m_gltfModels; + VectorWithGenerationCountedHandles m_gltfInstances; std::unique_ptr m_pbrResources; #if defined(USE_MIRROR_WINDOW) @@ -1373,7 +1377,9 @@ namespace Conformance m_swapchainImageDataMap.Reset(); m_cubeMesh = {}; m_meshes.clear(); - m_gltfs.clear(); + m_gltfInstances.clear(); + m_gltfModels.clear(); + m_pbrResources.reset(); m_queueFamilyIndex = 0; m_vkQueue = VK_NULL_HANDLE; @@ -1950,15 +1956,28 @@ namespace Conformance return handle; } - GLTFHandle VulkanGraphicsPlugin::LoadGLTF(std::shared_ptr tinygltfModel) + GLTFModelHandle VulkanGraphicsPlugin::LoadGLTF(std::shared_ptr tinygltfModel) { - auto handle = m_gltfs.emplace_back(*m_pbrResources, std::move(tinygltfModel)); + std::shared_ptr pbrModel = Gltf::FromGltfObject(*m_pbrResources, *tinygltfModel); + auto handle = m_gltfModels.emplace_back(std::move(pbrModel)); return handle; } - inline std::shared_ptr VulkanGraphicsPlugin::GetModel(GLTFHandle handle) const + std::shared_ptr VulkanGraphicsPlugin::GetPbrModel(GLTFModelHandle handle) const + { + return m_gltfModels[handle]; + } + + GLTFModelInstanceHandle VulkanGraphicsPlugin::CreateGLTFModelInstance(GLTFModelHandle handle) + { + auto pbrModelInstance = Pbr::VulkanModelInstance(*m_pbrResources, GetPbrModel(handle)); + auto instanceHandle = m_gltfInstances.emplace_back(std::move(pbrModelInstance)); + return instanceHandle; + } + + Pbr::ModelInstance& VulkanGraphicsPlugin::GetModelInstance(GLTFModelInstanceHandle handle) { - return m_gltfs[handle].GetModel(); + return m_gltfInstances[handle].GetModelInstance(); } void VulkanGraphicsPlugin::RenderView(const XrCompositionLayerProjectionView& layerView, @@ -2056,13 +2075,13 @@ namespace Conformance } // Render each gltf - for (const auto& gltfHandle : params.glTFs) { - VulkanGLTF& gltf = m_gltfs[gltfHandle.handle]; + for (const auto& gltfDrawable : params.glTFs) { + VulkanGLTF& gltf = m_gltfInstances[gltfDrawable.handle]; // Compute and update the model transform. XrMatrix4x4f modelToWorld; - XrMatrix4x4f_CreateTranslationRotationScale(&modelToWorld, &gltfHandle.params.pose.position, - &gltfHandle.params.pose.orientation, &gltfHandle.params.scale); + XrMatrix4x4f_CreateTranslationRotationScale(&modelToWorld, &gltfDrawable.params.pose.position, + &gltfDrawable.params.pose.orientation, &gltfDrawable.params.scale); // XrMatrix4x4f viewMatrix; // XrVector3f unitScale = {1, 1, 1}; // XrMatrix4x4f_CreateTranslationRotationScale(&viewMatrix, &layerView.pose.position, &layerView.pose.orientation, &unitScale); diff --git a/src/conformance/framework/graphics_plugin_vulkan_gltf.cpp b/src/conformance/framework/graphics_plugin_vulkan_gltf.cpp index b2e1d6c5..af966b09 100644 --- a/src/conformance/framework/graphics_plugin_vulkan_gltf.cpp +++ b/src/conformance/framework/graphics_plugin_vulkan_gltf.cpp @@ -6,33 +6,19 @@ #include "graphics_plugin_vulkan_gltf.h" -#include "conformance_framework.h" -#include "graphics_plugin_vulkan_gltf.h" -#include "report.h" - -#include "pbr/GltfLoader.h" #include "pbr/Vulkan/VkModel.h" #include "pbr/Vulkan/VkPrimitive.h" #include "pbr/Vulkan/VkResources.h" -#include "utilities/throw_helpers.h" - -#include namespace Conformance { struct CmdBuffer; - void VulkanGLTF::Render(CmdBuffer& directCommandBuffer, Pbr::VulkanResources& resources, XrMatrix4x4f& modelToWorld, - VkRenderPass renderPass, VkSampleCountFlagBits sampleCount) const + void VulkanGLTF::Render(CmdBuffer& directCommandBuffer, Pbr::VulkanResources& resources, const XrMatrix4x4f& modelToWorld, + VkRenderPass renderPass, VkSampleCountFlagBits sampleCount) { - if (!GetModel()) { - return; - } - resources.SetFillMode(GetFillMode()); - resources.SetModelToWorld(modelToWorld); - // resources.Bind(directCommandBuffer); - GetModel()->Render(resources, directCommandBuffer, renderPass, sampleCount); + GetModelInstance().Render(resources, directCommandBuffer, renderPass, sampleCount, modelToWorld); } } // namespace Conformance diff --git a/src/conformance/framework/graphics_plugin_vulkan_gltf.h b/src/conformance/framework/graphics_plugin_vulkan_gltf.h index 22f56e4a..1c773854 100644 --- a/src/conformance/framework/graphics_plugin_vulkan_gltf.h +++ b/src/conformance/framework/graphics_plugin_vulkan_gltf.h @@ -21,7 +21,7 @@ namespace Pbr { - class VulkanModel; + class Model; struct VulkanResources; } // namespace Pbr @@ -29,13 +29,13 @@ namespace Conformance { struct CmdBuffer; - class VulkanGLTF : public GltfModelBase + class VulkanGLTF : public RenderableGltfModelInstanceBase { public: - using GltfModelBase::GltfModelBase; + using RenderableGltfModelInstanceBase::RenderableGltfModelInstanceBase; - void Render(CmdBuffer& directCommandBuffer, Pbr::VulkanResources& resources, XrMatrix4x4f& modelToWorld, VkRenderPass renderPass, - VkSampleCountFlagBits sampleCount) const; + void Render(CmdBuffer& directCommandBuffer, Pbr::VulkanResources& resources, const XrMatrix4x4f& modelToWorld, + VkRenderPass renderPass, VkSampleCountFlagBits sampleCount); }; } // namespace Conformance #endif diff --git a/src/conformance/framework/pbr/CMakeLists.txt b/src/conformance/framework/pbr/CMakeLists.txt index adc77af2..f5c6be31 100644 --- a/src/conformance/framework/pbr/CMakeLists.txt +++ b/src/conformance/framework/pbr/CMakeLists.txt @@ -198,6 +198,16 @@ if(XR_USE_GRAPHICS_API_VULKAN) Vulkan/VkModel.cpp Vulkan/VkPrimitive.cpp ) + if(GLSLANG_VALIDATOR) + # If we can, run this check of shader structure offsets in the unit test suite. + add_test( + NAME pbr_glsl_offsets + COMMAND + "${PYTHON_EXECUTABLE}" + "${CMAKE_CURRENT_SOURCE_DIR}/check-offsets.py" + "${GLSLANG_VALIDATOR}" + ) + endif() endif() target_include_directories( diff --git a/src/conformance/framework/pbr/D3D11/D3D11Material.h b/src/conformance/framework/pbr/D3D11/D3D11Material.h index 37c35d9e..5af101a7 100644 --- a/src/conformance/framework/pbr/D3D11/D3D11Material.h +++ b/src/conformance/framework/pbr/D3D11/D3D11Material.h @@ -17,34 +17,30 @@ #include #include // For Microsoft::WRL::ComPtr -#include -#include #include -#include namespace Pbr { - // A D3D11Material contains the metallic roughness parameters and textures. - // Primitives specify which D3D11Material to use when being rendered. + /// A D3D11Material contains the metallic roughness parameters and textures. + /// Primitives specify which D3D11Material to use when being rendered. struct D3D11Material final : public Material { - // Create a uninitialized material. Textures and shader coefficients must be set. + /// Create a uninitialized material. Textures and shader coefficients must be set. D3D11Material(Pbr::D3D11Resources const& pbrResources); - // Create a clone of this material. + /// Create a clone of this material. std::shared_ptr Clone(Pbr::D3D11Resources const& pbrResources) const; - // Create a flat (no texture) material. + /// Create a flat (no texture) material. static std::shared_ptr CreateFlat(const D3D11Resources& pbrResources, RGBAColor baseColorFactor, float roughnessFactor = 1.0f, float metallicFactor = 0.0f, RGBColor emissiveFactor = RGB::Black); - // Set a Metallic-Roughness texture. + /// Set a Metallic-Roughness texture. void SetTexture(ShaderSlots::PSMaterial slot, _In_ ID3D11ShaderResourceView* textureView, _In_opt_ ID3D11SamplerState* sampler = nullptr); - // void SetTexture(ShaderSlots::PSMaterial slot, ITexture& texture) override; - // Bind this material to current context. + /// Bind this material to current context. void Bind(_In_ ID3D11DeviceContext* context, const D3D11Resources& pbrResources) const; std::string Name; diff --git a/src/conformance/framework/pbr/D3D11/D3D11Model.cpp b/src/conformance/framework/pbr/D3D11/D3D11Model.cpp index 321921cf..1e7b5896 100644 --- a/src/conformance/framework/pbr/D3D11/D3D11Model.cpp +++ b/src/conformance/framework/pbr/D3D11/D3D11Model.cpp @@ -10,6 +10,7 @@ #include "D3D11Model.h" +#include "D3D11Material.h" #include "D3D11Primitive.h" #include "D3D11Resources.h" @@ -22,15 +23,19 @@ namespace Pbr { - - void D3D11Model::Render(Pbr::D3D11Resources const& pbrResources, _In_ ID3D11DeviceContext* context) + void D3D11ModelInstance::Render(Pbr::D3D11Resources const& pbrResources, _In_ ID3D11DeviceContext* context, + DirectX::FXMMATRIX modelToWorld) { + XMStoreFloat4x4(&m_modelBuffer.ModelToWorld, XMMatrixTranspose(modelToWorld)); + context->UpdateSubresource(m_modelConstantBuffer.Get(), 0, nullptr, &m_modelBuffer, 0, 0); + pbrResources.BindConstantBuffers(context, m_modelConstantBuffer.Get()); + UpdateTransforms(pbrResources, context); ID3D11ShaderResourceView* vsShaderResources[] = {m_modelTransformsResourceView.Get()}; context->VSSetShaderResources(Pbr::ShaderSlots::Transforms, _countof(vsShaderResources), vsShaderResources); - for (PrimitiveHandle primitiveHandle : GetPrimitives()) { + for (PrimitiveHandle primitiveHandle : GetModel().GetPrimitiveHandles()) { const Pbr::D3D11Primitive& primitive = pbrResources.GetPrimitive(primitiveHandle); if (primitive.GetMaterial()->Hidden) continue; @@ -38,66 +43,51 @@ namespace Pbr primitive.GetMaterial()->Bind(context, pbrResources); primitive.Render(context); } - - // Expect the caller to reset other state, but the geometry shader is cleared specially. - //context->GSSetShader(nullptr, nullptr, 0); } - void D3D11Model::UpdateTransforms(Pbr::D3D11Resources const& pbrResources, _In_ ID3D11DeviceContext* context) + D3D11ModelInstance::D3D11ModelInstance(Pbr::D3D11Resources& pbrResources, std::shared_ptr model) + : ModelInstance(std::move(model)) { - const auto& nodes = GetNodes(); - const uint32_t newTotalModifyCount = std::accumulate(nodes.begin(), nodes.end(), 0, [](uint32_t sumChangeCount, const Node& node) { - return sumChangeCount + node.GetModifyCount(); - }); + // Set up the model constant buffer. + const CD3D11_BUFFER_DESC modelConstantBufferDesc(sizeof(ModelConstantBuffer), D3D11_BIND_CONSTANT_BUFFER); + XRC_CHECK_THROW_HRCMD( + pbrResources.GetDevice()->CreateBuffer(&modelConstantBufferDesc, nullptr, m_modelConstantBuffer.ReleaseAndGetAddressOf())); + + // Set up the transforms buffer. + XrMatrix4x4f identityMatrix; + XrMatrix4x4f_CreateIdentity(&identityMatrix); // or better yet poison it + size_t nodeCount = GetModel().GetNodes().size(); + + // Create/recreate the structured buffer and SRV which holds the node transforms. + // Use Usage=D3D11_USAGE_DYNAMIC and CPUAccessFlags=D3D11_CPU_ACCESS_WRITE with Map/Unmap instead? + D3D11_BUFFER_DESC desc{}; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED; + desc.StructureByteStride = sizeof(XrMatrix4x4f); + desc.ByteWidth = (UINT)(nodeCount * desc.StructureByteStride); + XRC_CHECK_THROW_HRCMD( + pbrResources.GetDevice()->CreateBuffer(&desc, nullptr, m_modelTransformsStructuredBuffer.ReleaseAndGetAddressOf())); + + D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc{}; + srvDesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER; + // TODO this looks weird + srvDesc.Buffer.NumElements = (UINT)nodeCount; + srvDesc.Buffer.ElementWidth = (UINT)nodeCount; + m_modelTransformsResourceView = nullptr; + XRC_CHECK_THROW_HRCMD(pbrResources.GetDevice()->CreateShaderResourceView(m_modelTransformsStructuredBuffer.Get(), &srvDesc, + m_modelTransformsResourceView.ReleaseAndGetAddressOf())); + } + void D3D11ModelInstance::UpdateTransforms(Pbr::D3D11Resources const& /*pbrResources*/, _In_ ID3D11DeviceContext* context) + { // If none of the node transforms have changed, no need to recompute/update the model transform structured buffer. - if (newTotalModifyCount != TotalModifyCount || m_modelTransformsStructuredBufferInvalid) { - if (m_modelTransformsStructuredBufferInvalid) // The structured buffer is reset when a Node is added. - { - XrMatrix4x4f identityMatrix; - XrMatrix4x4f_CreateIdentity(&identityMatrix); // or better yet poison it - m_modelTransforms.resize(nodes.size(), identityMatrix); - - // Create/recreate the structured buffer and SRV which holds the node transforms. - // Use Usage=D3D11_USAGE_DYNAMIC and CPUAccessFlags=D3D11_CPU_ACCESS_WRITE with Map/Unmap instead? - D3D11_BUFFER_DESC desc{}; - desc.Usage = D3D11_USAGE_DEFAULT; - desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; - desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED; - desc.StructureByteStride = sizeof(decltype(m_modelTransforms)::value_type); - desc.ByteWidth = (UINT)(m_modelTransforms.size() * desc.StructureByteStride); - XRC_CHECK_THROW_HRCMD( - pbrResources.GetDevice()->CreateBuffer(&desc, nullptr, m_modelTransformsStructuredBuffer.ReleaseAndGetAddressOf())); - - D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc{}; - srvDesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER; - // TODO this looks weird - srvDesc.Buffer.NumElements = (UINT)m_modelTransforms.size(); - srvDesc.Buffer.ElementWidth = (UINT)m_modelTransforms.size(); - m_modelTransformsResourceView = nullptr; - XRC_CHECK_THROW_HRCMD(pbrResources.GetDevice()->CreateShaderResourceView( - m_modelTransformsStructuredBuffer.Get(), &srvDesc, m_modelTransformsResourceView.ReleaseAndGetAddressOf())); - - m_modelTransformsStructuredBufferInvalid = false; - } - - // Nodes are guaranteed to come after their parents, so each node transform can be multiplied by its parent transform in a single pass. - assert(nodes.size() == m_modelTransforms.size()); - XrMatrix4x4f identityMatrix; - XrMatrix4x4f_CreateIdentity(&identityMatrix); - for (const auto& node : nodes) { - assert(node.ParentNodeIndex == RootParentNodeIndex || node.ParentNodeIndex < node.Index); - const XrMatrix4x4f& parentTransform = - (node.ParentNodeIndex == RootParentNodeIndex) ? identityMatrix : m_modelTransforms[node.ParentNodeIndex]; - XrMatrix4x4f nodeTransform = node.GetTransform(); - XrMatrix4x4f nodeTransformTranspose; - XrMatrix4x4f_Transpose(&nodeTransformTranspose, &nodeTransform); - XrMatrix4x4f_Multiply(&m_modelTransforms[node.Index], &nodeTransformTranspose, &parentTransform); - } + if (WereNodeLocalTransformsUpdated()) { + ResolveTransforms(true); // Update node transform structured buffer. - context->UpdateSubresource(m_modelTransformsStructuredBuffer.Get(), 0, nullptr, this->m_modelTransforms.data(), 0, 0); - TotalModifyCount = newTotalModifyCount; + context->UpdateSubresource(m_modelTransformsStructuredBuffer.Get(), 0, nullptr, GetResolvedTransforms().data(), 0, 0); + ClearTransformsUpdatedFlag(); } } } // namespace Pbr diff --git a/src/conformance/framework/pbr/D3D11/D3D11Model.h b/src/conformance/framework/pbr/D3D11/D3D11Model.h index 57007504..bde687e0 100644 --- a/src/conformance/framework/pbr/D3D11/D3D11Model.h +++ b/src/conformance/framework/pbr/D3D11/D3D11Model.h @@ -14,24 +14,33 @@ namespace Pbr { - struct D3D11Primitive; + struct D3D11Resources; + + struct ModelConstantBuffer + { + DirectX::XMFLOAT4X4 ModelToWorld; + }; + + static_assert((sizeof(ModelConstantBuffer) % 16) == 0, "Constant Buffer must be divisible by 16 bytes"); - class D3D11Model final : public Model + class D3D11ModelInstance final : public ModelInstance { public: - // Render the model. - void Render(Pbr::D3D11Resources const& pbrResources, _In_ ID3D11DeviceContext* context); + D3D11ModelInstance(Pbr::D3D11Resources& pbrResources, std::shared_ptr model); + + /// Render the model. + void Render(Pbr::D3D11Resources const& pbrResources, _In_ ID3D11DeviceContext* context, DirectX::FXMMATRIX modelToWorld); private: - // Updated the transforms used to render the model. This needs to be called any time a node transform is changed. + void AllocateDescriptorSets(Pbr::D3D11Resources& pbrResources, uint32_t numSets); + /// Update the transforms used to render the model. This needs to be called any time a node transform is changed. void UpdateTransforms(Pbr::D3D11Resources const& pbrResources, _In_ ID3D11DeviceContext* context); - // Temporary buffer holds the world transforms, computed from the node's local transforms. - mutable std::vector m_modelTransforms; - mutable Microsoft::WRL::ComPtr m_modelTransformsStructuredBuffer; - mutable Microsoft::WRL::ComPtr m_modelTransformsResourceView; + ModelConstantBuffer m_modelBuffer; + Microsoft::WRL::ComPtr m_modelConstantBuffer; - mutable uint32_t TotalModifyCount{0}; + Microsoft::WRL::ComPtr m_modelTransformsStructuredBuffer; + Microsoft::WRL::ComPtr m_modelTransformsResourceView; }; } // namespace Pbr diff --git a/src/conformance/framework/pbr/D3D11/D3D11Primitive.h b/src/conformance/framework/pbr/D3D11/D3D11Primitive.h index 9a540138..92f709bb 100644 --- a/src/conformance/framework/pbr/D3D11/D3D11Primitive.h +++ b/src/conformance/framework/pbr/D3D11/D3D11Primitive.h @@ -14,11 +14,12 @@ #include #include // For Microsoft::WRL::ComPtr +#include #include namespace Pbr { - // A primitive holds a vertex buffer, index buffer, and a pointer to a PBR material. + /// A primitive holds a vertex buffer, index buffer, and a pointer to a PBR material. struct D3D11Primitive final { using Collection = std::vector; @@ -31,25 +32,20 @@ namespace Pbr void UpdateBuffers(_In_ ID3D11Device* device, _In_ ID3D11DeviceContext* context, const Pbr::PrimitiveBuilder& primitiveBuilder); - // Get the material for the primitive. - std::shared_ptr& GetMaterial() - { - return m_material; - } + /// Get the material for the primitive. const std::shared_ptr& GetMaterial() const { return m_material; } - // Replace the material for the primitive + /// Replace the material for the primitive void SetMaterial(std::shared_ptr material) { m_material = std::move(material); } protected: - // friend class Model; - friend class D3D11Model; + friend class D3D11ModelInstance; void Render(_In_ ID3D11DeviceContext* context) const; D3D11Primitive Clone(Pbr::D3D11Resources const& pbrResources) const; diff --git a/src/conformance/framework/pbr/D3D11/D3D11Resources.cpp b/src/conformance/framework/pbr/D3D11/D3D11Resources.cpp index 4857d09c..e18e7902 100644 --- a/src/conformance/framework/pbr/D3D11/D3D11Resources.cpp +++ b/src/conformance/framework/pbr/D3D11/D3D11Resources.cpp @@ -52,13 +52,6 @@ namespace static_assert(offsetof(SceneConstantBuffer, LightDiffuseColor) == 96, "Offsets must match shader"); static_assert(offsetof(SceneConstantBuffer, NumSpecularMipLevels) == 112, "Offsets must match shader"); - struct ModelConstantBuffer - { - DirectX::XMFLOAT4X4 ModelToWorld; - }; - - static_assert((sizeof(ModelConstantBuffer) % 16) == 0, "Constant Buffer must be divisible by 16 bytes"); - const D3D11_INPUT_ELEMENT_DESC s_vertexDesc[6] = { {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0}, {"NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0}, @@ -87,15 +80,11 @@ namespace Pbr XRC_CHECK_THROW_HRCMD(device->CreateVertexShader(g_PbrVertexShader, sizeof(g_PbrVertexShader), nullptr, Resources.PbrVertexShader.ReleaseAndGetAddressOf())); - // Set up the constant buffers. + // Set up the scene constant buffer. const CD3D11_BUFFER_DESC pbrConstantBufferDesc(sizeof(SceneConstantBuffer), D3D11_BIND_CONSTANT_BUFFER); XRC_CHECK_THROW_HRCMD( device->CreateBuffer(&pbrConstantBufferDesc, nullptr, Resources.SceneConstantBuffer.ReleaseAndGetAddressOf())); - const CD3D11_BUFFER_DESC modelConstantBufferDesc(sizeof(ModelConstantBuffer), D3D11_BIND_CONSTANT_BUFFER); - XRC_CHECK_THROW_HRCMD( - device->CreateBuffer(&modelConstantBufferDesc, nullptr, Resources.ModelConstantBuffer.ReleaseAndGetAddressOf())); - // Samplers for environment map and BRDF. Resources.EnvironmentMapSampler = D3D11Texture::CreateSampler(device); Resources.BrdfSampler = D3D11Texture::CreateSampler(device); @@ -152,7 +141,6 @@ namespace Pbr Microsoft::WRL::ComPtr PbrVertexShader; Microsoft::WRL::ComPtr PbrPixelShader; Microsoft::WRL::ComPtr SceneConstantBuffer; - Microsoft::WRL::ComPtr ModelConstantBuffer; Microsoft::WRL::ComPtr BrdfLut; Microsoft::WRL::ComPtr SpecularEnvironmentMap; Microsoft::WRL::ComPtr DiffuseEnvironmentMap; @@ -167,7 +155,6 @@ namespace Pbr DeviceResources Resources; SceneConstantBuffer SceneBuffer; - ModelConstantBuffer ModelBuffer; struct LoaderResources { @@ -348,12 +335,6 @@ namespace Pbr m_impl->SceneBuffer.LightDiffuseColor = {diffuseColor.x, diffuseColor.y, diffuseColor.z}; } - void XM_CALLCONV D3D11Resources::SetModelToWorld(DirectX::FXMMATRIX modelToWorld, _In_ ID3D11DeviceContext* context) const - { - XMStoreFloat4x4(&m_impl->ModelBuffer.ModelToWorld, XMMatrixTranspose(modelToWorld)); - context->UpdateSubresource(m_impl->Resources.ModelConstantBuffer.Get(), 0, nullptr, &m_impl->ModelBuffer, 0, 0); - } - void XM_CALLCONV D3D11Resources::SetViewProjection(DirectX::FXMMATRIX view, DirectX::CXMMATRIX projection) { XMStoreFloat4x4(&m_impl->SceneBuffer.ViewProjection, XMMatrixTranspose(XMMatrixMultiply(view, projection))); @@ -391,10 +372,9 @@ namespace Pbr context->VSSetShader(m_impl->Resources.PbrVertexShader.Get(), nullptr, 0); context->PSSetShader(m_impl->Resources.PbrPixelShader.Get(), nullptr, 0); - ID3D11Buffer* vsBuffers[] = {m_impl->Resources.SceneConstantBuffer.Get(), m_impl->Resources.ModelConstantBuffer.Get()}; - context->VSSetConstantBuffers(Pbr::ShaderSlots::ConstantBuffers::Scene, _countof(vsBuffers), vsBuffers); ID3D11Buffer* psBuffers[] = {m_impl->Resources.SceneConstantBuffer.Get()}; context->PSSetConstantBuffers(Pbr::ShaderSlots::ConstantBuffers::Scene, _countof(psBuffers), psBuffers); + context->IASetInputLayout(m_impl->Resources.InputLayout.Get()); static_assert(ShaderSlots::DiffuseTexture == ShaderSlots::SpecularTexture + 1, "Diffuse must follow Specular slot"); @@ -406,6 +386,13 @@ namespace Pbr context->PSSetSamplers(ShaderSlots::Brdf, _countof(samplers), samplers); } + void D3D11Resources::BindConstantBuffers(_In_ ID3D11DeviceContext* context, ID3D11Buffer* modelConstantBuffer) const + { + ID3D11Buffer* vsBuffers[] = {m_impl->Resources.SceneConstantBuffer.Get(), modelConstantBuffer}; + context->VSSetConstantBuffers(Pbr::ShaderSlots::ConstantBuffers::Scene, _countof(vsBuffers), vsBuffers); + // PSSetConstantBuffers is done in Bind because it is not model-dependent + } + PrimitiveHandle D3D11Resources::MakePrimitive(const Pbr::PrimitiveBuilder& primitiveBuilder, const std::shared_ptr& material) { diff --git a/src/conformance/framework/pbr/D3D11/D3D11Resources.h b/src/conformance/framework/pbr/D3D11/D3D11Resources.h index 73c1609b..af314227 100644 --- a/src/conformance/framework/pbr/D3D11/D3D11Resources.h +++ b/src/conformance/framework/pbr/D3D11/D3D11Resources.h @@ -58,39 +58,39 @@ namespace Pbr const std::shared_ptr& material) override; void DropLoaderCaches() override; - // Sets the Bidirectional Reflectance Distribution Function Lookup Table texture, required by the shader to compute surface - // reflectance from the IBL. + /// Sets the Bidirectional Reflectance Distribution Function Lookup Table texture, required by the shader to compute surface + /// reflectance from the IBL. void SetBrdfLut(_In_ ID3D11ShaderResourceView* brdfLut); - // Create device-dependent resources. + /// Create device-dependent resources. void CreateDeviceDependentResources(_In_ ID3D11Device* device); - // Release device-dependent resources. + /// Release device-dependent resources. void ReleaseDeviceDependentResources(); - // Get the D3D11Device that the PBR resources are associated with. + /// Get the D3D11Device that the PBR resources are associated with. Microsoft::WRL::ComPtr GetDevice() const; - // Set the directional light. + /// Set the directional light. void SetLight(DirectX::XMFLOAT3 direction, RGBColor diffuseColor); - // Set the specular and diffuse image-based lighting (IBL) maps. ShaderResourceViews must be TextureCubes. + /// Set the specular and diffuse image-based lighting (IBL) maps. ShaderResourceViews must be TextureCubes. void SetEnvironmentMap(_In_ ID3D11ShaderResourceView* specularEnvironmentMap, _In_ ID3D11ShaderResourceView* diffuseEnvironmentMap); - // Set the current view and projection matrices. + /// Set the current view and projection matrices. void XM_CALLCONV SetViewProjection(DirectX::FXMMATRIX view, DirectX::CXMMATRIX projection); - // Many 1x1 pixel colored textures are used in the PBR system. This is used to create textures backed by a cache to reduce the - // number of textures created. + /// Many 1x1 pixel colored textures are used in the PBR system. This is used to create textures backed by a cache to reduce the + /// number of textures created. Microsoft::WRL::ComPtr CreateTypedSolidColorTexture(RGBAColor color) const; - // Bind the the PBR resources to the current context. + /// Bind the the PBR resources to the current context. void Bind(_In_ ID3D11DeviceContext* context) const; - // Set and update the model to world constant buffer value. - void XM_CALLCONV SetModelToWorld(DirectX::FXMMATRIX modelToWorld, _In_ ID3D11DeviceContext* context) const; - + /// Get the D3D11Primitive from a primitive handle. D3D11Primitive& GetPrimitive(PrimitiveHandle p); + + /// Get the D3D11Primitive from a primitive handle, const overload. const D3D11Primitive& GetPrimitive(PrimitiveHandle p) const; // Set or get the shading and fill modes. @@ -105,6 +105,10 @@ namespace Pbr void SetRasterizerState(_In_ ID3D11DeviceContext* context, bool doubleSided) const; void SetDepthStencilState(_In_ ID3D11DeviceContext* context, bool disableDepthWrite) const; + // Bind the scene constant buffer as well as a provided model constant buffer. + void BindConstantBuffers(_In_ ID3D11DeviceContext* context, ID3D11Buffer* modelConstantBuffer) const; + + friend class D3D11ModelInstance; friend struct D3D11Material; struct Impl; diff --git a/src/conformance/framework/pbr/D3D11/D3D11Texture.h b/src/conformance/framework/pbr/D3D11/D3D11Texture.h index b4190ef9..a302d638 100644 --- a/src/conformance/framework/pbr/D3D11/D3D11Texture.h +++ b/src/conformance/framework/pbr/D3D11/D3D11Texture.h @@ -21,7 +21,6 @@ #include // For Microsoft::WRL::ComPtr #include -#include namespace Pbr { diff --git a/src/conformance/framework/pbr/D3D11/D3D11TextureCache.cpp b/src/conformance/framework/pbr/D3D11/D3D11TextureCache.cpp index 0149bedc..d49cafcd 100644 --- a/src/conformance/framework/pbr/D3D11/D3D11TextureCache.cpp +++ b/src/conformance/framework/pbr/D3D11/D3D11TextureCache.cpp @@ -18,6 +18,7 @@ #include #include #include +#include namespace Pbr { diff --git a/src/conformance/framework/pbr/D3D11/D3D11TextureCache.h b/src/conformance/framework/pbr/D3D11/D3D11TextureCache.h index 40724964..aa723561 100644 --- a/src/conformance/framework/pbr/D3D11/D3D11TextureCache.h +++ b/src/conformance/framework/pbr/D3D11/D3D11TextureCache.h @@ -21,7 +21,6 @@ namespace Pbr { template using ComPtr = Microsoft::WRL::ComPtr; - // using Microsoft::WRL::ComPtr; /// Cache of single-color textures. /// diff --git a/src/conformance/framework/pbr/D3D12/D3D12Material.h b/src/conformance/framework/pbr/D3D12/D3D12Material.h index feb00e4b..7d16707f 100644 --- a/src/conformance/framework/pbr/D3D12/D3D12Material.h +++ b/src/conformance/framework/pbr/D3D12/D3D12Material.h @@ -5,6 +5,7 @@ // Licensed under the MIT License. See License.txt in the project root for license information. // // SPDX-License-Identifier: MIT AND Apache-2.0 + #pragma once #include "D3D12Resources.h" @@ -15,38 +16,34 @@ #include #include // For Microsoft::WRL::ComPtr -#include -#include #include -#include namespace Pbr { - // A D3D12Material contains the metallic roughness parameters and textures. - // Primitives specify which D3D12Material to use when being rendered. + /// A D3D12Material contains the metallic roughness parameters and textures. + /// Primitives specify which D3D12Material to use when being rendered. struct D3D12Material final : public Material { - // Create a uninitialized material. Textures and shader coefficients must be set. + /// Create a uninitialized material. Textures and shader coefficients must be set. D3D12Material(Pbr::D3D12Resources const& pbrResources); - // Create a clone of this material. Shares the texture and sampler heap with this material. + /// Create a clone of this material. Shares the texture and sampler heap with this material. std::shared_ptr Clone(Pbr::D3D12Resources const& pbrResources) const; - // Create a flat (no texture) material. + /// Create a flat (no texture) material. static std::shared_ptr CreateFlat(D3D12Resources& pbrResources, RGBAColor baseColorFactor, float roughnessFactor = 1.0f, float metallicFactor = 0.0f, RGBColor emissiveFactor = RGB::Black); - // Set a Metallic-Roughness texture. + /// Set a Metallic-Roughness texture. void SetTexture(_In_ ID3D12Device* device, ShaderSlots::PSMaterial slot, Conformance::D3D12ResourceWithSRVDesc& texture, _In_opt_ D3D12_SAMPLER_DESC* sampler); - // void SetTexture(ShaderSlots::PSMaterial slot, ITexture& texture) override; - // Write the descriptors of this material to a texture and sampler heap + /// Write the descriptors of this material to a texture and sampler heap void GetDescriptors(_In_ ID3D12Device* device, D3D12_CPU_DESCRIPTOR_HANDLE destTextureDescriptors, D3D12_CPU_DESCRIPTOR_HANDLE destSamplerDescriptors); - // Bind this material to current context. + /// Bind this material to current context. void Bind(_In_ ID3D12GraphicsCommandList* directCommandList, D3D12Resources& pbrResources); std::string Name; diff --git a/src/conformance/framework/pbr/D3D12/D3D12Model.cpp b/src/conformance/framework/pbr/D3D12/D3D12Model.cpp index da4a09db..e0f96c1a 100644 --- a/src/conformance/framework/pbr/D3D12/D3D12Model.cpp +++ b/src/conformance/framework/pbr/D3D12/D3D12Model.cpp @@ -26,91 +26,83 @@ namespace Pbr { - void D3D12Model::Render(Pbr::D3D12Resources& pbrResources, _In_ ID3D12GraphicsCommandList* directCommandList, - DXGI_FORMAT colorRenderTargetFormat, DXGI_FORMAT depthRenderTargetFormat) + void D3D12ModelInstance::Render(Pbr::D3D12Resources& pbrResources, _In_ ID3D12GraphicsCommandList* directCommandList, + DXGI_FORMAT colorRenderTargetFormat, DXGI_FORMAT depthRenderTargetFormat, + DirectX::FXMMATRIX modelToWorld) { + XMStoreFloat4x4(&m_modelBuffer.ModelToWorld, XMMatrixTranspose(modelToWorld)); + pbrResources.WithCopyCommandList( + [&](ID3D12GraphicsCommandList* cmdList) { m_modelConstantBuffer.AsyncUpload(cmdList, &m_modelBuffer); }); + // xxx: why do we copy the transform descriptor to a separate heap, again? is that relevant here? + pbrResources.BindConstantBufferViews(directCommandList, m_modelConstantBuffer.GetResource()->GetGPUVirtualAddress()); + UpdateTransforms(pbrResources); pbrResources.SetTransforms(m_modelTransformsResourceViewHeap->GetCPUDescriptorHandleForHeapStart()); - for (PrimitiveHandle primitiveHandle : GetPrimitives()) { + for (PrimitiveHandle primitiveHandle : GetModel().GetPrimitiveHandles()) { const Pbr::D3D12Primitive& primitive = pbrResources.GetPrimitive(primitiveHandle); if (primitive.GetMaterial()->Hidden) continue; primitive.Render(directCommandList, pbrResources, colorRenderTargetFormat, depthRenderTargetFormat); } - - // Expect the caller to reset other state, but the geometry shader is cleared specially. - //context->GSSetShader(nullptr, nullptr, 0); } - void D3D12Model::UpdateTransforms(Pbr::D3D12Resources& pbrResources) + D3D12ModelInstance::D3D12ModelInstance(Pbr::D3D12Resources& pbrResources, std::shared_ptr model) + : ModelInstance(std::move(model)) { - const auto& nodes = GetNodes(); - const uint32_t newTotalModifyCount = std::accumulate(nodes.begin(), nodes.end(), 0, [](uint32_t sumChangeCount, const Node& node) { - return sumChangeCount + node.GetModifyCount(); - }); + // Set up the model constant buffer. + static_assert((sizeof(ModelConstantBuffer) % 16) == 0, "Constant Buffer must be divisible by 16 bytes"); + m_modelConstantBuffer.Allocate(pbrResources.GetDevice().Get()); + + // Set up the transforms buffer. + XrMatrix4x4f identityMatrix; + XrMatrix4x4f_CreateIdentity(&identityMatrix); // or better yet poison it + size_t nodeCount = GetModel().GetNodes().size(); + + // Create/recreate the structured buffer and SRV which holds the node transforms. + UINT elemSize = sizeof(XrMatrix4x4f); + UINT count = (UINT)(nodeCount); + UINT size = (UINT)(count * elemSize); + + m_modelTransformsStructuredBuffer = Conformance::D3D12BufferWithUpload(pbrResources.GetDevice().Get(), size); + + D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc{}; + srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + srvDesc.Format = DXGI_FORMAT_UNKNOWN; + srvDesc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER; + srvDesc.Buffer.NumElements = count; + srvDesc.Buffer.StructureByteStride = elemSize; + + if (m_modelTransformsResourceViewHeap == nullptr) { + D3D12_DESCRIPTOR_HEAP_DESC transformHeapDesc; + transformHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; + transformHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; + transformHeapDesc.NumDescriptors = ShaderSlots::NumTextures; + transformHeapDesc.NodeMask = 1; + + XRC_CHECK_THROW_HRCMD( + pbrResources.GetDevice()->CreateDescriptorHeap(&transformHeapDesc, IID_PPV_ARGS(&m_modelTransformsResourceViewHeap))); + } + pbrResources.GetDevice()->CreateShaderResourceView( + m_modelTransformsStructuredBuffer.GetResource(), &srvDesc, + CD3DX12_CPU_DESCRIPTOR_HANDLE(m_modelTransformsResourceViewHeap->GetCPUDescriptorHandleForHeapStart())); + } + + void D3D12ModelInstance::UpdateTransforms(Pbr::D3D12Resources& pbrResources) + { // If none of the node transforms have changed, no need to recompute/update the model transform structured buffer. - if (newTotalModifyCount != TotalModifyCount || m_modelTransformsStructuredBufferInvalid) { - if (m_modelTransformsStructuredBufferInvalid) // The structured buffer is reset when a Node is added. - { - XrMatrix4x4f identityMatrix; - XrMatrix4x4f_CreateIdentity(&identityMatrix); // or better yet poison it - m_modelTransforms.resize(nodes.size(), identityMatrix); - - // Create/recreate the structured buffer and SRV which holds the node transforms. - UINT elemSize = sizeof(decltype(m_modelTransforms)::value_type); - UINT count = (UINT)(m_modelTransforms.size()); - UINT size = (UINT)(count * elemSize); - - m_modelTransformsStructuredBuffer = Conformance::D3D12BufferWithUpload(pbrResources.GetDevice().Get(), size); - - D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc{}; - srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; - srvDesc.Format = DXGI_FORMAT_UNKNOWN; - srvDesc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER; - srvDesc.Buffer.NumElements = count; - srvDesc.Buffer.StructureByteStride = elemSize; - - if (m_modelTransformsResourceViewHeap == nullptr) { - D3D12_DESCRIPTOR_HEAP_DESC transformHeapDesc; - transformHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; - transformHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; - transformHeapDesc.NumDescriptors = ShaderSlots::NumTextures; - transformHeapDesc.NodeMask = 1; - - XRC_CHECK_THROW_HRCMD(pbrResources.GetDevice()->CreateDescriptorHeap(&transformHeapDesc, - IID_PPV_ARGS(&m_modelTransformsResourceViewHeap))); - } - - pbrResources.GetDevice()->CreateShaderResourceView( - m_modelTransformsStructuredBuffer.GetResource(), &srvDesc, - CD3DX12_CPU_DESCRIPTOR_HANDLE(m_modelTransformsResourceViewHeap->GetCPUDescriptorHandleForHeapStart())); - - m_modelTransformsStructuredBufferInvalid = false; - } - - // Nodes are guaranteed to come after their parents, so each node transform can be multiplied by its parent transform in a single pass. - assert(nodes.size() == m_modelTransforms.size()); - XrMatrix4x4f identityMatrix; - XrMatrix4x4f_CreateIdentity(&identityMatrix); - for (const auto& node : nodes) { - assert(node.ParentNodeIndex == RootParentNodeIndex || node.ParentNodeIndex < node.Index); - const XrMatrix4x4f& parentTransform = - (node.ParentNodeIndex == RootParentNodeIndex) ? identityMatrix : m_modelTransforms[node.ParentNodeIndex]; - XrMatrix4x4f nodeTransform = node.GetTransform(); - XrMatrix4x4f nodeTransformTranspose; - XrMatrix4x4f_Transpose(&nodeTransformTranspose, &nodeTransform); - XrMatrix4x4f_Multiply(&m_modelTransforms[node.Index], &nodeTransformTranspose, &parentTransform); - } + if (WereNodeLocalTransformsUpdated()) { + ResolveTransforms(true); // Update node transform structured buffer. + auto& resolvedTransforms = GetResolvedTransforms(); pbrResources.WithCopyCommandList([&](ID3D12GraphicsCommandList* cmdList) { - m_modelTransformsStructuredBuffer.AsyncUpload(cmdList, this->m_modelTransforms.data(), this->m_modelTransforms.size()); + m_modelTransformsStructuredBuffer.AsyncUpload(cmdList, resolvedTransforms.data(), resolvedTransforms.size()); }); - TotalModifyCount = newTotalModifyCount; + ClearTransformsUpdatedFlag(); } } } // namespace Pbr diff --git a/src/conformance/framework/pbr/D3D12/D3D12Model.h b/src/conformance/framework/pbr/D3D12/D3D12Model.h index b58d867e..caaa6bce 100644 --- a/src/conformance/framework/pbr/D3D12/D3D12Model.h +++ b/src/conformance/framework/pbr/D3D12/D3D12Model.h @@ -5,6 +5,7 @@ // Licensed under the MIT License. See License.txt in the project root for license information. // // SPDX-License-Identifier: MIT AND Apache-2.0 + #pragma once #include "D3D12Resources.h" @@ -16,22 +17,30 @@ namespace Pbr struct D3D12Primitive; - class D3D12Model final : public Model + struct ModelConstantBuffer + { + DirectX::XMFLOAT4X4 ModelToWorld; + }; + + static_assert((sizeof(ModelConstantBuffer) % 16) == 0, "Constant Buffer must be divisible by 16 bytes"); + + class D3D12ModelInstance final : public ModelInstance { public: - // Render the model. + D3D12ModelInstance(Pbr::D3D12Resources& pbrResources, std::shared_ptr model); + + /// Render the model. void Render(Pbr::D3D12Resources& pbrResources, _In_ ID3D12GraphicsCommandList* directCommandList, - DXGI_FORMAT colorRenderTargetFormat, DXGI_FORMAT depthRenderTargetFormat); + DXGI_FORMAT colorRenderTargetFormat, DXGI_FORMAT depthRenderTargetFormat, DirectX::FXMMATRIX modelToWorld); private: - // Updated the transforms used to render the model. This needs to be called any time a node transform is changed. + /// Update the transforms used to render the model. This needs to be called any time a node transform is changed. void UpdateTransforms(Pbr::D3D12Resources& pbrResources); - // Temporary buffer holds the world transforms, computed from the node's local transforms. - mutable std::vector m_modelTransforms; - mutable Conformance::D3D12BufferWithUpload m_modelTransformsStructuredBuffer; - mutable Microsoft::WRL::ComPtr m_modelTransformsResourceViewHeap; + ModelConstantBuffer m_modelBuffer; + Conformance::D3D12BufferWithUpload m_modelConstantBuffer; - mutable uint32_t TotalModifyCount{0}; + Conformance::D3D12BufferWithUpload m_modelTransformsStructuredBuffer; + Microsoft::WRL::ComPtr m_modelTransformsResourceViewHeap; }; } // namespace Pbr diff --git a/src/conformance/framework/pbr/D3D12/D3D12PipelineStates.h b/src/conformance/framework/pbr/D3D12/D3D12PipelineStates.h index dbe121de..1138cfba 100644 --- a/src/conformance/framework/pbr/D3D12/D3D12PipelineStates.h +++ b/src/conformance/framework/pbr/D3D12/D3D12PipelineStates.h @@ -8,6 +8,7 @@ // SPDX-License-Identifier: MIT AND Apache-2.0 #pragma once + #include "../PbrSharedState.h" #include @@ -49,17 +50,6 @@ namespace Pbr BlendState blendState, DoubleSided doubleSided, DepthDirection depthDirection); - // void DropStates() - // { - // m_pipelineStates.clear(); - // } - // void Reset(Microsoft::WRL::ComPtr rootSignature = nullptr) - // { - // DropStates(); - // m_rootSignature = std::move(rootSignature); - // m_basePipelineStateDesc.pRootSignature = m_rootSignature.Get(); - // } - private: using PipelineStateKey = std::tuple; diff --git a/src/conformance/framework/pbr/D3D12/D3D12Primitive.h b/src/conformance/framework/pbr/D3D12/D3D12Primitive.h index 5d83307a..26b6f383 100644 --- a/src/conformance/framework/pbr/D3D12/D3D12Primitive.h +++ b/src/conformance/framework/pbr/D3D12/D3D12Primitive.h @@ -5,6 +5,7 @@ // Licensed under the MIT License. See License.txt in the project root for license information. // // SPDX-License-Identifier: MIT AND Apache-2.0 + #pragma once #include "D3D12Material.h" @@ -12,11 +13,12 @@ #include #include // For Microsoft::WRL::ComPtr +#include #include namespace Pbr { - // A primitive holds a vertex buffer, index buffer, and a pointer to a PBR material. + /// A primitive holds a vertex buffer, index buffer, and a pointer to a PBR material. struct D3D12Primitive final { using Collection = std::vector; @@ -29,25 +31,20 @@ namespace Pbr void UpdateBuffers(Pbr::D3D12Resources& pbrResources, const Pbr::PrimitiveBuilder& primitiveBuilder); - // Get the material for the primitive. - std::shared_ptr& GetMaterial() - { - return m_material; - } + /// Get the material for the primitive. const std::shared_ptr& GetMaterial() const { return m_material; } - // Replace the material for the primitive + /// Replace the material for the primitive void SetMaterial(std::shared_ptr material) { m_material = std::move(material); } protected: - // friend class Model; - friend class D3D12Model; + friend class D3D12ModelInstance; void Render(_In_ ID3D12GraphicsCommandList* directCommandList, D3D12Resources& pbrResources, DXGI_FORMAT colorRenderTargetFormat, DXGI_FORMAT depthRenderTargetFormat) const; diff --git a/src/conformance/framework/pbr/D3D12/D3D12Resources.cpp b/src/conformance/framework/pbr/D3D12/D3D12Resources.cpp index 3680fac8..5dc50e8f 100644 --- a/src/conformance/framework/pbr/D3D12/D3D12Resources.cpp +++ b/src/conformance/framework/pbr/D3D12/D3D12Resources.cpp @@ -58,13 +58,6 @@ namespace static_assert(offsetof(SceneConstantBuffer, LightDiffuseColor) == 96, "Offsets must match shader"); static_assert(offsetof(SceneConstantBuffer, NumSpecularMipLevels) == 112, "Offsets must match shader"); - struct ModelConstantBuffer - { - DirectX::XMFLOAT4X4 ModelToWorld; - }; - - static_assert((sizeof(ModelConstantBuffer) % 16) == 0, "Constant Buffer must be divisible by 16 bytes"); - const D3D12_INPUT_ELEMENT_DESC s_vertexDesc[6] = { {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}, {"NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}, @@ -158,13 +151,10 @@ namespace Pbr Resources.PipelineStates = std::make_unique(Resources.RootSignature, basePipelineStateDesc, s_vertexDesc, g_PbrVertexShader, g_PbrPixelShader); - // Set up the constant buffers. + // Set up the scene constant buffer. static_assert((sizeof(SceneConstantBuffer) % 16) == 0, "Constant Buffer must be divisible by 16 bytes"); Resources.SceneConstantBuffer.Allocate(device); - static_assert((sizeof(ModelConstantBuffer) % 16) == 0, "Constant Buffer must be divisible by 16 bytes"); - Resources.ModelConstantBuffer.Allocate(device); - D3D12_DESCRIPTOR_HEAP_DESC transformHeapDesc; transformHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; transformHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; @@ -176,32 +166,32 @@ namespace Pbr D3D12_DESCRIPTOR_HEAP_DESC textureHeapDesc; textureHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; textureHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; - textureHeapDesc.NumDescriptors = ShaderSlots::NumTextures - ShaderSlots::NumMaterialSlots; + textureHeapDesc.NumDescriptors = (int)ShaderSlots::NumTextures - (int)ShaderSlots::NumMaterialSlots; textureHeapDesc.NodeMask = 1; XRC_CHECK_THROW_HRCMD(device->CreateDescriptorHeap(&textureHeapDesc, IID_PPV_ARGS(&Resources.TextureHeap))); UINT textureDescriptorSize = device->GetDescriptorHandleIncrementSize(textureHeapDesc.Type); CD3DX12_CPU_DESCRIPTOR_HANDLE textureBaseHandle(Resources.TextureHeap->GetCPUDescriptorHandleForHeapStart()); Resources.BrdfLutTextureDescriptor = CD3DX12_CPU_DESCRIPTOR_HANDLE( // - textureBaseHandle, ShaderSlots::Brdf - ShaderSlots::NumMaterialSlots, textureDescriptorSize); + textureBaseHandle, (int)ShaderSlots::Brdf - (int)ShaderSlots::NumMaterialSlots, textureDescriptorSize); Resources.SpecularEnvMapTextureDescriptor = CD3DX12_CPU_DESCRIPTOR_HANDLE( // - textureBaseHandle, ShaderSlots::SpecularTexture - ShaderSlots::NumMaterialSlots, textureDescriptorSize); + textureBaseHandle, (int)ShaderSlots::SpecularTexture - (int)ShaderSlots::NumMaterialSlots, textureDescriptorSize); Resources.DiffuseEnvMapTextureDescriptor = CD3DX12_CPU_DESCRIPTOR_HANDLE( // - textureBaseHandle, ShaderSlots::DiffuseTexture - ShaderSlots::NumMaterialSlots, textureDescriptorSize); + textureBaseHandle, (int)ShaderSlots::DiffuseTexture - (int)ShaderSlots::NumMaterialSlots, textureDescriptorSize); D3D12_DESCRIPTOR_HEAP_DESC samplerHeapDesc; samplerHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER; samplerHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; - samplerHeapDesc.NumDescriptors = ShaderSlots::NumSamplers - ShaderSlots::NumMaterialSlots; + samplerHeapDesc.NumDescriptors = (int)ShaderSlots::NumSamplers - (int)ShaderSlots::NumMaterialSlots; samplerHeapDesc.NodeMask = 1; XRC_CHECK_THROW_HRCMD(device->CreateDescriptorHeap(&samplerHeapDesc, IID_PPV_ARGS(&Resources.SamplerHeap))); UINT samplerDescriptorSize = device->GetDescriptorHandleIncrementSize(samplerHeapDesc.Type); CD3DX12_CPU_DESCRIPTOR_HANDLE samplerBaseHandle(Resources.SamplerHeap->GetCPUDescriptorHandleForHeapStart()); Resources.BrdfSamplerDescriptor = CD3DX12_CPU_DESCRIPTOR_HANDLE( // - samplerBaseHandle, ShaderSlots::Brdf - ShaderSlots::NumMaterialSlots, samplerDescriptorSize); + samplerBaseHandle, (int)ShaderSlots::Brdf - (int)ShaderSlots::NumMaterialSlots, samplerDescriptorSize); Resources.EnvironmentMapSamplerDescriptor = CD3DX12_CPU_DESCRIPTOR_HANDLE( // - samplerBaseHandle, ShaderSlots::EnvironmentMapSampler - ShaderSlots::NumMaterialSlots, samplerDescriptorSize); + samplerBaseHandle, (int)ShaderSlots::EnvironmentMapSampler - (int)ShaderSlots::NumMaterialSlots, samplerDescriptorSize); D3D12Texture::CreateSampler(device, Resources.BrdfSamplerDescriptor); D3D12Texture::CreateSampler(device, Resources.EnvironmentMapSamplerDescriptor); @@ -241,7 +231,6 @@ namespace Pbr D3D12_CPU_DESCRIPTOR_HANDLE EnvironmentMapSamplerDescriptor; Microsoft::WRL::ComPtr RootSignature; Conformance::D3D12BufferWithUpload SceneConstantBuffer; - Conformance::D3D12BufferWithUpload ModelConstantBuffer; std::unique_ptr PipelineStates{}; mutable D3D12TextureCache SolidColorTextureCache; }; @@ -250,7 +239,6 @@ namespace Pbr D3D12_GRAPHICS_PIPELINE_STATE_DESC BasePipelineStateDesc; DeviceResources Resources; SceneConstantBuffer SceneBuffer; - ModelConstantBuffer ModelBuffer; struct LoaderResources { @@ -338,7 +326,7 @@ namespace Pbr return filter; } - // Create a DirectX sampler state from a tinygltf Sampler. + /// Create a DirectX sampler state from a tinygltf Sampler. static D3D12_SAMPLER_DESC CreateGLTFSampler(_In_ ID3D12Device* /*device*/, const tinygltf::Sampler& sampler) { D3D12_SAMPLER_DESC samplerDesc{}; @@ -392,6 +380,7 @@ namespace Pbr pbrMaterial->SetTexture(GetDevice().Get(), slot, *textureView, samplerState.get()); } + void D3D12Resources::DropLoaderCaches() { m_impl->loaderResources = {}; @@ -455,10 +444,10 @@ namespace Pbr void D3D12Resources::GetGlobalTexturesAndSamplers(D3D12_CPU_DESCRIPTOR_HANDLE destTextureDescriptors, D3D12_CPU_DESCRIPTOR_HANDLE destSamplerDescriptors) { - GetDevice()->CopyDescriptorsSimple(ShaderSlots::NumTextures - ShaderSlots::NumMaterialSlots, destTextureDescriptors, + GetDevice()->CopyDescriptorsSimple((int)ShaderSlots::NumTextures - (int)ShaderSlots::NumMaterialSlots, destTextureDescriptors, m_impl->Resources.TextureHeap->GetCPUDescriptorHandleForHeapStart(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); - GetDevice()->CopyDescriptorsSimple(ShaderSlots::NumSamplers - ShaderSlots::NumMaterialSlots, destSamplerDescriptors, + GetDevice()->CopyDescriptorsSimple((int)ShaderSlots::NumSamplers - (int)ShaderSlots::NumMaterialSlots, destSamplerDescriptors, m_impl->Resources.SamplerHeap->GetCPUDescriptorHandleForHeapStart(), D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); } @@ -478,13 +467,6 @@ namespace Pbr m_impl->SceneBuffer.LightDiffuseColor = {diffuseColor.x, diffuseColor.y, diffuseColor.z}; } - void XM_CALLCONV D3D12Resources::SetModelToWorld(DirectX::FXMMATRIX modelToWorld) const - { - XMStoreFloat4x4(&m_impl->ModelBuffer.ModelToWorld, XMMatrixTranspose(modelToWorld)); - WithCopyCommandList( - [&](ID3D12GraphicsCommandList* cmdList) { m_impl->Resources.ModelConstantBuffer.AsyncUpload(cmdList, &m_impl->ModelBuffer); }); - } - void XM_CALLCONV D3D12Resources::SetViewProjection(DirectX::FXMMATRIX view, DirectX::CXMMATRIX projection) const { XMStoreFloat4x4(&m_impl->SceneBuffer.ViewProjection, XMMatrixTranspose(XMMatrixMultiply(view, projection))); @@ -523,11 +505,14 @@ namespace Pbr WithCopyCommandList( [&](ID3D12GraphicsCommandList* cmdList) { m_impl->Resources.SceneConstantBuffer.AsyncUpload(cmdList, &m_impl->SceneBuffer); }); + } + void D3D12Resources::BindConstantBufferViews(_In_ ID3D12GraphicsCommandList* directCommandList, + D3D12_GPU_VIRTUAL_ADDRESS modelConstantBufferAddress) const + { directCommandList->SetGraphicsRootConstantBufferView(ShaderSlots::ConstantBuffers::Scene, m_impl->Resources.SceneConstantBuffer.GetResource()->GetGPUVirtualAddress()); - directCommandList->SetGraphicsRootConstantBufferView(ShaderSlots::ConstantBuffers::Model, - m_impl->Resources.ModelConstantBuffer.GetResource()->GetGPUVirtualAddress()); + directCommandList->SetGraphicsRootConstantBufferView(ShaderSlots::ConstantBuffers::Model, modelConstantBufferAddress); } void D3D12Resources::BindDescriptorHeaps(_In_ ID3D12GraphicsCommandList* directCommandList, ID3D12DescriptorHeap* srvDescriptorHeap, diff --git a/src/conformance/framework/pbr/D3D12/D3D12Resources.h b/src/conformance/framework/pbr/D3D12/D3D12Resources.h index 79a3c21c..7ea37271 100644 --- a/src/conformance/framework/pbr/D3D12/D3D12Resources.h +++ b/src/conformance/framework/pbr/D3D12/D3D12Resources.h @@ -42,7 +42,7 @@ namespace Pbr bool samplerSet; }; - // Global PBR resources required for rendering a scene. + /// Global PBR resources required for rendering a scene. struct D3D12Resources final : public IResources { D3D12Resources(_In_ ID3D12Device* device, const D3D12_GRAPHICS_PIPELINE_STATE_DESC& basePipelineStateDesc); @@ -61,23 +61,23 @@ namespace Pbr const std::shared_ptr& material) override; void DropLoaderCaches() override; - // Sets the Bidirectional Reflectance Distribution Function Lookup Table texture, required by the shader to compute surface - // reflectance from the IBL. + /// Sets the Bidirectional Reflectance Distribution Function Lookup Table texture, required by the shader to compute surface + /// reflectance from the IBL. void SetBrdfLut(_In_ Conformance::D3D12ResourceWithSRVDesc brdfLut); - // Create device-dependent resources. + /// Create device-dependent resources. void CreateDeviceDependentResources(_In_ ID3D12Device* device); - // Release device-dependent resources. + /// Release device-dependent resources. void ReleaseDeviceDependentResources(); - // Get the D3D12Device that the PBR resources are associated with. + /// Get the D3D12Device that the PBR resources are associated with. Microsoft::WRL::ComPtr GetDevice() const; - // Create a new copy command list, which can later be executed with ExecuteCopyCommandList + /// Create a new copy command list, which can later be executed with ExecuteCopyCommandList Microsoft::WRL::ComPtr CreateCopyCommandList() const; - // Execute a copy command list on the internal copy queue, which can be waited on using GetFenceAndValue + /// Execute a copy command list on the internal copy queue, which can be waited on using GetFenceAndValue void ExecuteCopyCommandList(ID3D12GraphicsCommandList* cmdList, std::vector> destroyAfterCopy = {}) const; @@ -94,35 +94,35 @@ namespace Pbr ExecuteCopyCommandList(cmdList.Get()); } - // Get a pipeline state matching some parameters as well as the current settings inside D3D12Resources. + /// Get a pipeline state matching some parameters as well as the current settings inside D3D12Resources. Microsoft::WRL::ComPtr GetOrCreatePipelineState(DXGI_FORMAT colorRenderTargetFormat, DXGI_FORMAT depthRenderTargetFormat, BlendState blendState, DoubleSided doubleSided); - // Set the directional light. + /// Set the directional light. void SetLight(DirectX::XMFLOAT3 direction, RGBColor diffuseColor); - // Set the specular and diffuse image-based lighting (IBL) maps. ShaderResourceViews must be TextureCubes. + /// Set the specular and diffuse image-based lighting (IBL) maps. ShaderResourceViews must be TextureCubes. void SetEnvironmentMap(_In_ Conformance::D3D12ResourceWithSRVDesc specularEnvironmentMap, _In_ Conformance::D3D12ResourceWithSRVDesc diffuseEnvironmentMap); - // Set the current view and projection matrices. + /// Set the current view and projection matrices. void XM_CALLCONV SetViewProjection(DirectX::FXMMATRIX view, DirectX::CXMMATRIX projection) const; - // Many 1x1 pixel colored textures are used in the PBR system. This is used to create textures backed by a cache to reduce the - // number of textures created. + /// Many 1x1 pixel colored textures are used in the PBR system. This is used to create textures backed by a cache to reduce the + /// number of textures created. Conformance::D3D12ResourceWithSRVDesc CreateTypedSolidColorTexture(RGBAColor color); - // Bind the the PBR resources to the current context. + /// Bind the the PBR resources to the current context. void Bind(_In_ ID3D12GraphicsCommandList* directCommandList) const; - // Get the fence to wait on before executing any command list built on this Resources. + /// Get the fence to wait on before executing any command list built on this Resources. std::pair GetFenceAndValue() const; - // Set and update the model to world constant buffer value. - void XM_CALLCONV SetModelToWorld(DirectX::FXMMATRIX modelToWorld) const; - + /// Get the D3D12Primitive from a primitive handle. D3D12Primitive& GetPrimitive(PrimitiveHandle p); + + /// Get the D3D12Primitive from a primitive handle, const overload. const D3D12Primitive& GetPrimitive(PrimitiveHandle p) const; // Set or get the shading and fill modes. @@ -137,12 +137,15 @@ namespace Pbr void GetTransforms(D3D12_CPU_DESCRIPTOR_HANDLE destTransformDescriptor); void GetGlobalTexturesAndSamplers(D3D12_CPU_DESCRIPTOR_HANDLE destTextureDescriptors, D3D12_CPU_DESCRIPTOR_HANDLE destSamplerDescriptors); - // Bind a material's descriptors according to the root signature. + // Bind the scene constant buffer as well as a provided model constant buffer. + void BindConstantBufferViews(_In_ ID3D12GraphicsCommandList* directCommandList, + D3D12_GPU_VIRTUAL_ADDRESS modelConstantBufferAddress) const; + /// Bind a material's descriptors according to the root signature. void BindDescriptorHeaps(_In_ ID3D12GraphicsCommandList* directCommandList, ID3D12DescriptorHeap* srvDescriptorHeap, ID3D12DescriptorHeap* samplerDescriptorHeap) const; friend struct D3D12Material; - friend class D3D12Model; + friend class D3D12ModelInstance; friend struct D3D12Primitive; struct Impl; diff --git a/src/conformance/framework/pbr/D3D12/D3D12Texture.h b/src/conformance/framework/pbr/D3D12/D3D12Texture.h index 01a06996..3a96198d 100644 --- a/src/conformance/framework/pbr/D3D12/D3D12Texture.h +++ b/src/conformance/framework/pbr/D3D12/D3D12Texture.h @@ -1,13 +1,11 @@ // Copyright 2022-2024, The Khronos Group Inc. // // Based in part on code that is: +// // Copyright (C) Microsoft Corporation. All Rights Reserved // Licensed under the MIT License. See License.txt in the project root for license information. // // SPDX-License-Identifier: MIT AND Apache-2.0 -// -// Shared data types and functions used throughout the Pbr rendering library. -// #pragma once @@ -22,7 +20,6 @@ #include // For Microsoft::WRL::ComPtr #include -#include namespace Pbr { diff --git a/src/conformance/framework/pbr/D3D12/D3D12TextureCache.h b/src/conformance/framework/pbr/D3D12/D3D12TextureCache.h index 69949558..37783cc5 100644 --- a/src/conformance/framework/pbr/D3D12/D3D12TextureCache.h +++ b/src/conformance/framework/pbr/D3D12/D3D12TextureCache.h @@ -23,7 +23,6 @@ namespace Pbr { template using ComPtr = Microsoft::WRL::ComPtr; - // using Microsoft::WRL::ComPtr; /// Cache of single-color textures. /// @@ -34,9 +33,6 @@ namespace Pbr /// Default constructor makes an invalid cache. D3D12TextureCache() = default; - // D3D12TextureCache(const D3D12TextureCache&) = default; - // D3D12TextureCache& operator=(const D3D12TextureCache&) = default; - D3D12TextureCache(D3D12TextureCache&&) = default; D3D12TextureCache& operator=(D3D12TextureCache&&) = default; diff --git a/src/conformance/framework/pbr/GltfLoader.cpp b/src/conformance/framework/pbr/GltfLoader.cpp index 2df91d7e..70049669 100644 --- a/src/conformance/framework/pbr/GltfLoader.cpp +++ b/src/conformance/framework/pbr/GltfLoader.cpp @@ -101,9 +101,6 @@ namespace Gltf { void PopulateFromGltfObject(Pbr::Model& model, Pbr::IResources& pbrResources, const tinygltf::Model& gltfModel) { - // Empty the model to ensure we're starting from scratch - model.Clear(); - // Read and transform mesh/node data. Primitives with the same material are merged to reduce draw calls. PrimitiveBuilderMap primitiveBuilderMap; GltfHelper::PrimitiveCache primitiveCache{gltfModel}; diff --git a/src/conformance/framework/pbr/GltfLoader.h b/src/conformance/framework/pbr/GltfLoader.h index 5c7c6d08..24fb638f 100644 --- a/src/conformance/framework/pbr/GltfLoader.h +++ b/src/conformance/framework/pbr/GltfLoader.h @@ -37,31 +37,25 @@ namespace Gltf void PopulateFromGltfBinary(Pbr::Model& model, Pbr::IResources& pbrResources, const uint8_t* buffer, uint32_t bufferBytes); // Creates a Pbr Model from tinygltf model. - template - std::shared_ptr FromGltfObject(Pbr::IResources& pbrResources, const tinygltf::Model& gltfModel) + inline std::shared_ptr FromGltfObject(Pbr::IResources& pbrResources, const tinygltf::Model& gltfModel) { - static_assert(std::is_base_of::value, "DerivedModel not derived from Pbr::Model"); - // Start off with an empty Model. - auto model = std::make_shared(); + auto model = std::make_shared(); PopulateFromGltfObject(*model, pbrResources, gltfModel); return model; } // Creates a Pbr Model from glTF 2.0 GLB file content. - template - std::shared_ptr FromGltfBinary(Pbr::IResources& pbrResources, const uint8_t* buffer, uint32_t bufferBytes) + inline std::shared_ptr FromGltfBinary(Pbr::IResources& pbrResources, const uint8_t* buffer, uint32_t bufferBytes) { - static_assert(std::is_base_of::value, "DerivedModel not derived from Pbr::Model"); - // Start off with an empty Model. - auto model = std::make_shared(); + auto model = std::make_shared(); PopulateFromGltfBinary(*model, pbrResources, buffer, bufferBytes); return model; } - template - std::shared_ptr FromGltfBinary(Pbr::IResources& pbrResources, const Container& buffer) + template + std::shared_ptr FromGltfBinary(Pbr::IResources& pbrResources, const Container& buffer) { return FromGltfBinary(pbrResources, buffer.data(), static_cast(buffer.size())); } diff --git a/src/conformance/framework/pbr/OpenGL/GLMaterial.h b/src/conformance/framework/pbr/OpenGL/GLMaterial.h index 05d5fe32..193ed6b3 100644 --- a/src/conformance/framework/pbr/OpenGL/GLMaterial.h +++ b/src/conformance/framework/pbr/OpenGL/GLMaterial.h @@ -17,37 +17,34 @@ #include "common/gfxwrapper_opengl.h" #include -#include #include #include #include -#include namespace Pbr { struct GLResources; - // A GLMaterial contains the metallic roughness parameters and textures. - // Primitives specify which GLMaterial to use when being rendered. + /// A GLMaterial contains the metallic roughness parameters and textures. + /// Primitives specify which GLMaterial to use when being rendered. struct GLMaterial final : public Material { - // Create a uninitialized material. Textures and shader coefficients must be set. + /// Create a uninitialized material. Textures and shader coefficients must be set. GLMaterial(Pbr::GLResources const& pbrResources); - // Create a clone of this material. + /// Create a clone of this material. std::shared_ptr Clone(Pbr::GLResources const& pbrResources) const; - // Create a flat (no texture) material. + /// Create a flat (no texture) material. static std::shared_ptr CreateFlat(const GLResources& pbrResources, RGBAColor baseColorFactor, float roughnessFactor = 1.0f, float metallicFactor = 0.0f, RGBColor emissiveFactor = RGB::Black); - // Set a Metallic-Roughness texture. + /// Set a Metallic-Roughness texture. void SetTexture(ShaderSlots::PSMaterial slot, std::shared_ptr textureView, std::shared_ptr sampler = nullptr); - // void SetTexture(ShaderSlots::PSMaterial slot, ITexture& texture) override; - // Bind this material to current context. + /// Bind this material to current context. void Bind(const GLResources& pbrResources) const; std::string Name; diff --git a/src/conformance/framework/pbr/OpenGL/GLModel.cpp b/src/conformance/framework/pbr/OpenGL/GLModel.cpp index 23d4cdc3..9e80598f 100644 --- a/src/conformance/framework/pbr/OpenGL/GLModel.cpp +++ b/src/conformance/framework/pbr/OpenGL/GLModel.cpp @@ -15,6 +15,7 @@ #include "GLPrimitive.h" #include "GLResources.h" +#include "../GlslBuffers.h" #include "../PbrHandles.h" #include "../PbrModel.h" #include "../PbrSharedState.h" @@ -22,22 +23,27 @@ #include "common/gfxwrapper_opengl.h" #include "utilities/opengl_utils.h" -#include -#include -#include #include namespace Pbr { - void GLModel::Render(Pbr::GLResources const& pbrResources) + void GLModelInstance::Render(Pbr::GLResources const& pbrResources, XrMatrix4x4f modelToWorld) { + // Update model buffer + m_modelBuffer.ModelToWorld = modelToWorld; + XRC_CHECK_THROW_GLCMD(glBindBuffer(GL_UNIFORM_BUFFER, m_modelConstantBuffer.get())); + XRC_CHECK_THROW_GLCMD(glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(Glsl::ModelConstantBuffer), &m_modelBuffer)); + // Bind model buffer + XRC_CHECK_THROW_GLCMD(glBindBufferBase(GL_UNIFORM_BUFFER, ShaderSlots::ConstantBuffers::Model, m_modelConstantBuffer.get())); + UpdateTransforms(pbrResources); - XRC_CHECK_THROW_GLCMD(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, ShaderSlots::GLSL::VSResourceViewsOffset + ShaderSlots::Transforms, + XRC_CHECK_THROW_GLCMD(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, + (int)ShaderSlots::GLSL::VSResourceViewsOffset + (int)ShaderSlots::Transforms, m_modelTransformsStructuredBuffer.get())); - for (PrimitiveHandle primitiveHandle : GetPrimitives()) { + for (PrimitiveHandle primitiveHandle : GetModel().GetPrimitiveHandles()) { const Pbr::GLPrimitive& primitive = pbrResources.GetPrimitive(primitiveHandle); if (primitive.GetMaterial()->Hidden) continue; @@ -45,55 +51,44 @@ namespace Pbr primitive.GetMaterial()->Bind(pbrResources); primitive.Render(pbrResources.GetFillMode()); } - - // Expect the caller to reset other state, but the geometry shader is cleared specially. - //context->GSSetShader(nullptr, nullptr, 0); } - void GLModel::UpdateTransforms(Pbr::GLResources const& /* pbrResources */) + GLModelInstance::GLModelInstance(Pbr::GLResources& /* pbrResources */, std::shared_ptr model) + : ModelInstance(std::move(model)) { - const auto& nodes = GetNodes(); - const uint32_t newTotalModifyCount = std::accumulate(nodes.begin(), nodes.end(), 0, [](uint32_t sumChangeCount, const Node& node) { - return sumChangeCount + node.GetModifyCount(); - }); + // Set up the model constant buffer. + XRC_CHECK_THROW_GLCMD(glGenBuffers(1, m_modelConstantBuffer.resetAndPut())); + XRC_CHECK_THROW_GLCMD(glBindBuffer(GL_UNIFORM_BUFFER, m_modelConstantBuffer.get())); + XRC_CHECK_THROW_GLCMD(glBufferData(GL_UNIFORM_BUFFER, sizeof(Glsl::ModelConstantBuffer), nullptr, GL_DYNAMIC_DRAW)); + + // Set up the transforms buffer. + XrMatrix4x4f identityMatrix; + XrMatrix4x4f_CreateIdentity(&identityMatrix); // or better yet poison it + size_t nodeCount = GetModel().GetNodes().size(); + + size_t elemSize = sizeof(XrMatrix4x4f); + size_t count = nodeCount; + size_t size = count * elemSize; + + XRC_CHECK_THROW_GLCMD(glGenBuffers(1, m_modelTransformsStructuredBuffer.resetAndPut())); + XRC_CHECK_THROW_GLCMD(glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_modelTransformsStructuredBuffer.get())); + XRC_CHECK_THROW_GLCMD(glBufferData(GL_SHADER_STORAGE_BUFFER, size, nullptr, GL_DYNAMIC_DRAW)); + } + void GLModelInstance::UpdateTransforms(Pbr::GLResources const& /* pbrResources */) + { // If none of the node transforms have changed, no need to recompute/update the model transform structured buffer. - if (newTotalModifyCount != TotalModifyCount || m_modelTransformsStructuredBufferInvalid) { - if (m_modelTransformsStructuredBufferInvalid) // The structured buffer is reset when a Node is added. - { - XrMatrix4x4f identityMatrix; - XrMatrix4x4f_CreateIdentity(&identityMatrix); // or better yet poison it - m_modelTransforms.resize(nodes.size(), identityMatrix); - - size_t elemSize = sizeof(decltype(m_modelTransforms)::value_type); - XRC_CHECK_THROW_GLCMD(glGenBuffers(1, m_modelTransformsStructuredBuffer.resetAndPut())); - XRC_CHECK_THROW_GLCMD(glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_modelTransformsStructuredBuffer.get())); - XRC_CHECK_THROW_GLCMD( - glBufferData(GL_SHADER_STORAGE_BUFFER, elemSize * m_modelTransforms.size(), m_modelTransforms.data(), GL_DYNAMIC_DRAW)); - - m_modelTransformsStructuredBufferInvalid = false; - } - - // Nodes are guaranteed to come after their parents, so each node transform can be multiplied by its parent transform in a single pass. - assert(nodes.size() == m_modelTransforms.size()); - XrMatrix4x4f identityMatrix; - XrMatrix4x4f_CreateIdentity(&identityMatrix); - for (const auto& node : nodes) { - assert(node.ParentNodeIndex == RootParentNodeIndex || node.ParentNodeIndex < node.Index); - const XrMatrix4x4f& parentTransform = - (node.ParentNodeIndex == RootParentNodeIndex) ? identityMatrix : m_modelTransforms[node.ParentNodeIndex]; - XrMatrix4x4f nodeTransform = node.GetTransform(); - XrMatrix4x4f_Multiply(&m_modelTransforms[node.Index], &parentTransform, &nodeTransform); - } + if (WereNodeLocalTransformsUpdated()) { + ResolveTransforms(false); // Update node transform structured buffer. + auto& resolvedTransforms = GetResolvedTransforms(); XRC_CHECK_THROW_GLCMD(glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_modelTransformsStructuredBuffer.get())); - XRC_CHECK_THROW_GLCMD(glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, - sizeof(decltype(m_modelTransforms)::value_type) * m_modelTransforms.size(), - this->m_modelTransforms.data())); - TotalModifyCount = newTotalModifyCount; + XRC_CHECK_THROW_GLCMD( + glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, sizeof(XrMatrix4x4f) * resolvedTransforms.size(), resolvedTransforms.data())); + ClearTransformsUpdatedFlag(); } } } // namespace Pbr -#endif +#endif // defined(XR_USE_GRAPHICS_API_OPENGL) || defined(XR_USE_GRAPHICS_API_OPENGL_ES) diff --git a/src/conformance/framework/pbr/OpenGL/GLModel.h b/src/conformance/framework/pbr/OpenGL/GLModel.h index 6d44bbb0..d377b2b0 100644 --- a/src/conformance/framework/pbr/OpenGL/GLModel.h +++ b/src/conformance/framework/pbr/OpenGL/GLModel.h @@ -5,38 +5,36 @@ // Licensed under the MIT License. See License.txt in the project root for license information. // // SPDX-License-Identifier: MIT AND Apache-2.0 + #pragma once #include "GLCommon.h" #include "GLResources.h" +#include "../GlslBuffers.h" #include "../PbrHandles.h" #include "../PbrModel.h" -#include "common/xr_linear.h" - -#include -#include - namespace Pbr { struct GLPrimitive; struct GLResources; - class GLModel final : public Model + class GLModelInstance final : public ModelInstance { public: - // Render the model. - void Render(Pbr::GLResources const& pbrResources); + GLModelInstance(Pbr::GLResources& pbrResources, std::shared_ptr model); + + /// Render the model. + void Render(Pbr::GLResources const& pbrResources, XrMatrix4x4f modelToWorld); private: - // Updated the transforms used to render the model. This needs to be called any time a node transform is changed. + /// Update the transforms used to render the model. This needs to be called any time a node transform is changed. void UpdateTransforms(Pbr::GLResources const& pbrResources); - // Temporary buffer holds the world transforms, computed from the node's local transforms. - mutable std::vector m_modelTransforms; - mutable ScopedGLBuffer m_modelTransformsStructuredBuffer; + Glsl::ModelConstantBuffer m_modelBuffer; + ScopedGLBuffer m_modelConstantBuffer; - mutable uint32_t TotalModifyCount{0}; + ScopedGLBuffer m_modelTransformsStructuredBuffer; }; } // namespace Pbr diff --git a/src/conformance/framework/pbr/OpenGL/GLPrimitive.cpp b/src/conformance/framework/pbr/OpenGL/GLPrimitive.cpp index 8d02b3fd..813e9782 100644 --- a/src/conformance/framework/pbr/OpenGL/GLPrimitive.cpp +++ b/src/conformance/framework/pbr/OpenGL/GLPrimitive.cpp @@ -176,4 +176,4 @@ namespace Pbr } } // namespace Pbr -#endif \ No newline at end of file +#endif // defined(XR_USE_GRAPHICS_API_OPENGL) || defined(XR_USE_GRAPHICS_API_OPENGL_ES) diff --git a/src/conformance/framework/pbr/OpenGL/GLPrimitive.h b/src/conformance/framework/pbr/OpenGL/GLPrimitive.h index 1ace214f..5d948470 100644 --- a/src/conformance/framework/pbr/OpenGL/GLPrimitive.h +++ b/src/conformance/framework/pbr/OpenGL/GLPrimitive.h @@ -23,7 +23,7 @@ namespace Pbr struct GLMaterial; struct PrimitiveBuilder; - // A primitive holds a vertex buffer, index buffer, and a pointer to a PBR material. + /// A primitive holds a vertex buffer, index buffer, and a pointer to a PBR material. struct GLPrimitive final { using Collection = std::vector; @@ -35,25 +35,20 @@ namespace Pbr void UpdateBuffers(const Pbr::PrimitiveBuilder& primitiveBuilder); - // Get the material for the primitive. - std::shared_ptr& GetMaterial() - { - return m_material; - } + /// Get the material for the primitive. const std::shared_ptr& GetMaterial() const { return m_material; } - // Replace the material for the primitive + /// Replace the material for the primitive void SetMaterial(std::shared_ptr material) { m_material = std::move(material); } protected: - // friend class Model; - friend class GLModel; + friend class GLModelInstance; void Render(FillMode fillMode) const; private: diff --git a/src/conformance/framework/pbr/OpenGL/GLResources.cpp b/src/conformance/framework/pbr/OpenGL/GLResources.cpp index 425432c0..acfbfafd 100644 --- a/src/conformance/framework/pbr/OpenGL/GLResources.cpp +++ b/src/conformance/framework/pbr/OpenGL/GLResources.cpp @@ -15,7 +15,7 @@ #include "GLPrimitive.h" #include "GLTexture.h" #include "GLTextureCache.h" -#include "GlslBuffers.h" +#include "../GlslBuffers.h" #include "../../gltf/GltfHelper.h" #include "../PbrCommon.h" @@ -24,17 +24,14 @@ #include "common/gfxwrapper_opengl.h" #include "utilities/opengl_utils.h" -#include "utilities/throw_helpers.h" #include #include -#include #include #include #include #include -#include #include #include @@ -111,10 +108,6 @@ namespace Pbr XRC_CHECK_THROW_GLCMD(glBindBuffer(GL_UNIFORM_BUFFER, Resources.SceneConstantBuffer.get())); XRC_CHECK_THROW_GLCMD(glBufferData(GL_UNIFORM_BUFFER, sizeof(Glsl::SceneConstantBuffer), nullptr, GL_DYNAMIC_DRAW)); - XRC_CHECK_THROW_GLCMD(glGenBuffers(1, Resources.ModelConstantBuffer.resetAndPut())); - XRC_CHECK_THROW_GLCMD(glBindBuffer(GL_UNIFORM_BUFFER, Resources.ModelConstantBuffer.get())); - XRC_CHECK_THROW_GLCMD(glBufferData(GL_UNIFORM_BUFFER, sizeof(Glsl::ModelConstantBuffer), nullptr, GL_DYNAMIC_DRAW)); - // Samplers for environment map and BRDF. Resources.BrdfSampler = GLTexture::CreateSampler(); Resources.EnvironmentMapSampler = GLTexture::CreateSampler(); @@ -128,7 +121,6 @@ namespace Pbr ScopedGLSampler BrdfSampler; ScopedGLSampler EnvironmentMapSampler; ScopedGLBuffer SceneConstantBuffer; - ScopedGLBuffer ModelConstantBuffer; std::shared_ptr BrdfLut; std::shared_ptr SpecularEnvironmentMap; std::shared_ptr DiffuseEnvironmentMap; @@ -138,7 +130,6 @@ namespace Pbr DeviceResources Resources; Glsl::SceneConstantBuffer SceneBuffer; - Glsl::ModelConstantBuffer ModelBuffer; struct LoaderResources { @@ -283,13 +274,6 @@ namespace Pbr m_impl->SceneBuffer.LightDiffuseColor = diffuseColor; } - void GLResources::SetModelToWorld(XrMatrix4x4f modelToWorld) const - { - m_impl->ModelBuffer.ModelToWorld = modelToWorld; - XRC_CHECK_THROW_GLCMD(glBindBuffer(GL_UNIFORM_BUFFER, m_impl->Resources.ModelConstantBuffer.get())); - XRC_CHECK_THROW_GLCMD(glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(Glsl::ModelConstantBuffer), &m_impl->ModelBuffer)); - } - void GLResources::SetViewProjection(XrMatrix4x4f view, XrMatrix4x4f projection) const { XrMatrix4x4f_Multiply(&m_impl->SceneBuffer.ViewProjection, &projection, &view); @@ -316,7 +300,6 @@ namespace Pbr void GLResources::Bind() const { - // SetModelToWorld must always be called before this, populating the ModelConstantBuffer. XRC_CHECK_THROW_GLCMD(glBindBuffer(GL_UNIFORM_BUFFER, m_impl->Resources.SceneConstantBuffer.get())); XRC_CHECK_THROW_GLCMD(glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(Glsl::SceneConstantBuffer), &m_impl->SceneBuffer)); @@ -324,8 +307,7 @@ namespace Pbr XRC_CHECK_THROW_GLCMD( glBindBufferBase(GL_UNIFORM_BUFFER, ShaderSlots::ConstantBuffers::Scene, m_impl->Resources.SceneConstantBuffer.get())); - XRC_CHECK_THROW_GLCMD( - glBindBufferBase(GL_UNIFORM_BUFFER, ShaderSlots::ConstantBuffers::Model, m_impl->Resources.ModelConstantBuffer.get())); + // ModelConstantBuffer is bound in GLModelInstance::Render XRC_CHECK_THROW_GLCMD( // glActiveTexture(GL_TEXTURE0 + ShaderSlots::GLSL::MaterialTexturesOffset + ShaderSlots::Brdf)); @@ -425,4 +407,4 @@ namespace Pbr } } // namespace Pbr -#endif +#endif // defined(XR_USE_GRAPHICS_API_OPENGL) || defined(XR_USE_GRAPHICS_API_OPENGL_ES) diff --git a/src/conformance/framework/pbr/OpenGL/GLResources.h b/src/conformance/framework/pbr/OpenGL/GLResources.h index 84960435..3535bec1 100644 --- a/src/conformance/framework/pbr/OpenGL/GLResources.h +++ b/src/conformance/framework/pbr/OpenGL/GLResources.h @@ -19,18 +19,15 @@ #include +#include +#include + namespace tinygltf { struct Image; struct Sampler; } // namespace tinygltf -#include -#include -#include -#include -#include - namespace Pbr { struct Primitive; @@ -50,7 +47,7 @@ namespace Pbr std::shared_ptr sampler; }; - // Global PBR resources required for rendering a scene. + /// Global PBR resources required for rendering a scene. struct GLResources final : public IResources { explicit GLResources(); @@ -62,37 +59,38 @@ namespace Pbr RGBColor emissiveFactor = RGB::Black) override; std::shared_ptr CreateMaterial() override; std::shared_ptr CreateSolidColorTexture(RGBAColor color); + void LoadTexture(const std::shared_ptr& pbrMaterial, Pbr::ShaderSlots::PSMaterial slot, const tinygltf::Image* image, const tinygltf::Sampler* sampler, bool sRGB, Pbr::RGBAColor defaultRGBA) override; PrimitiveHandle MakePrimitive(const Pbr::PrimitiveBuilder& primitiveBuilder, const std::shared_ptr& material) override; void DropLoaderCaches() override; - // Sets the Bidirectional Reflectance Distribution Function Lookup Table texture, required by the shader to compute surface - // reflectance from the IBL. + /// Sets the Bidirectional Reflectance Distribution Function Lookup Table texture, required by the shader to compute surface + /// reflectance from the IBL. void SetBrdfLut(std::shared_ptr brdfLut); - // Set the directional light. + /// Set the directional light. void SetLight(XrVector3f direction, RGBColor diffuseColor); - // Set the specular and diffuse image-based lighting (IBL) maps. ShaderResourceViews must be TextureCubes. + /// Set the specular and diffuse image-based lighting (IBL) maps. ShaderResourceViews must be TextureCubes. void SetEnvironmentMap(std::shared_ptr specularEnvironmentMap, std::shared_ptr diffuseEnvironmentMap); - // Set the current view and projection matrices. + /// Set the current view and projection matrices. void SetViewProjection(XrMatrix4x4f view, XrMatrix4x4f projection) const; - // Many 1x1 pixel colored textures are used in the PBR system. This is used to create textures backed by a cache to reduce the - // number of textures created. + /// Many 1x1 pixel colored textures are used in the PBR system. This is used to create textures backed by a cache to reduce the + /// number of textures created. std::shared_ptr CreateTypedSolidColorTexture(RGBAColor color) const; - // Bind the the PBR resources to the current context. + /// Bind the the PBR resources to the current context. void Bind() const; - // Set and update the model to world constant buffer value. - void SetModelToWorld(XrMatrix4x4f modelToWorld) const; - + /// Get the GLPrimitive from a primitive handle. GLPrimitive& GetPrimitive(PrimitiveHandle p); + + /// Get the GLPrimitive from a primitive handle, const overload const GLPrimitive& GetPrimitive(PrimitiveHandle p) const; // Set or get the shading and fill modes. @@ -110,7 +108,6 @@ namespace Pbr friend struct GLMaterial; struct Impl; - std::unique_ptr m_impl; SharedState m_sharedState; diff --git a/src/conformance/framework/pbr/OpenGL/GLTextureCache.cpp b/src/conformance/framework/pbr/OpenGL/GLTextureCache.cpp index e1e6a5ce..1005f237 100644 --- a/src/conformance/framework/pbr/OpenGL/GLTextureCache.cpp +++ b/src/conformance/framework/pbr/OpenGL/GLTextureCache.cpp @@ -23,7 +23,6 @@ #include #include #include -#include namespace Pbr { diff --git a/src/conformance/framework/pbr/PbrModel.cpp b/src/conformance/framework/pbr/PbrModel.cpp index 53840e76..10eff3d3 100644 --- a/src/conformance/framework/pbr/PbrModel.cpp +++ b/src/conformance/framework/pbr/PbrModel.cpp @@ -11,19 +11,15 @@ #include "common/xr_linear.h" -#include -#include #include namespace Pbr { - Model::Model(bool createRootNode /*= true*/) + Model::Model() { - if (createRootNode) { - XrMatrix4x4f identityMatrix; - XrMatrix4x4f_CreateIdentity(&identityMatrix); - AddNode(identityMatrix, RootParentNodeIndex, "root"); - } + XrMatrix4x4f identityMatrix; + XrMatrix4x4f_CreateIdentity(&identityMatrix); + AddNode(identityMatrix, RootParentNodeIndex, "root"); } NodeIndex_t Model::AddNode(const XrMatrix4x4f& transform, Pbr::NodeIndex_t parentIndex, std::string name) @@ -34,14 +30,7 @@ namespace Pbr } m_nodes.emplace_back(transform, std::move(name), newNodeIndex, parentIndex); - // m_modelTransformsStructuredBuffer = nullptr; // Structured buffer will need to be recreated. - InvalidateBuffer(); // Structured buffer will need to be recreated. - return m_nodes.back().Index; - } - - void Model::Clear() - { - m_primitives.clear(); + return m_nodes.back().GetNodeIndex(); } bool Model::FindFirstNode(NodeIndex_t* outNodeIndex, const char* name, const NodeIndex_t* parentNodeIndex) const @@ -50,42 +39,22 @@ namespace Pbr const NodeIndex_t startIndex = parentNodeIndex ? *parentNodeIndex + 1 : Pbr::RootNodeIndex; for (NodeIndex_t i = startIndex; i < m_nodes.size(); ++i) { const Pbr::Node& node = m_nodes[i]; - if ((!parentNodeIndex || node.ParentNodeIndex == *parentNodeIndex) && (node.Name.compare(name) == 0)) { - *outNodeIndex = node.Index; + if ((!parentNodeIndex || node.GetParentNodeIndex() == *parentNodeIndex) && (node.CompareName(name) == 0)) { + *outNodeIndex = node.GetNodeIndex(); return true; } } return false; } - XrMatrix4x4f Model::GetNodeToModelRootTransform(NodeIndex_t nodeIndex) const - { - const Pbr::Node& node = GetNode(nodeIndex); - - // Compute the transform recursively. - XrMatrix4x4f identityMatrix; - XrMatrix4x4f_CreateIdentity(&identityMatrix); - const XrMatrix4x4f parentTransform = - node.ParentNodeIndex == Pbr::RootNodeIndex ? identityMatrix : GetNodeToModelRootTransform(node.ParentNodeIndex); - XrMatrix4x4f nodeTransform = node.GetTransform(); - XrMatrix4x4f result; - XrMatrix4x4f_Multiply(&result, &nodeTransform, &parentTransform); - return result; - } - void Model::AddPrimitive(PrimitiveHandle primitive) { - m_primitives.push_back(primitive); + m_primitiveHandles.push_back(primitive); } Node::Node(Node&& other) noexcept { - using std::swap; - swap(Name, other.Name); - swap(Index, other.Index); - swap(ParentNodeIndex, other.ParentNodeIndex); - m_modifyCount.store(other.m_modifyCount); - swap(m_localTransform, other.m_localTransform); + *this = std::move(other); } Node& Node::operator=(Node&& other) noexcept @@ -97,7 +66,6 @@ namespace Pbr swap(Name, other.Name); swap(Index, other.Index); swap(ParentNodeIndex, other.ParentNodeIndex); - m_modifyCount.store(other.m_modifyCount); swap(m_localTransform, other.m_localTransform); return *this; } diff --git a/src/conformance/framework/pbr/PbrModel.h b/src/conformance/framework/pbr/PbrModel.h index 03ff1cfd..3e0f8d5b 100644 --- a/src/conformance/framework/pbr/PbrModel.h +++ b/src/conformance/framework/pbr/PbrModel.h @@ -12,9 +12,6 @@ #include "common/xr_linear.h" -#include -#include -#include #include #include #include @@ -29,39 +26,42 @@ namespace Pbr using Collection = std::vector; Node(const XrMatrix4x4f& localTransform, std::string name, NodeIndex_t index, NodeIndex_t parentNodeIndex) - : Name(std::move(name)), Index(index), ParentNodeIndex(parentNodeIndex) + : Name(std::move(name)), Index(index), ParentNodeIndex(parentNodeIndex), m_localTransform(localTransform) { - SetTransform(localTransform); } Node(Node&& other) noexcept; Node& operator=(Node&& other) noexcept; - // Set the local transform for this node. - void SetTransform(const XrMatrix4x4f& transform) + // Compare this node's name to a given name. + int CompareName(const char* value) const { - m_localTransform = transform; - ++m_modifyCount; + return Name.compare(value); } // Get the local transform for this node. - XrMatrix4x4f GetTransform() const + const XrMatrix4x4f& GetLocalTransform() const { return m_localTransform; } - uint32_t GetModifyCount() const + + // Get the index of this node. + NodeIndex_t GetNodeIndex() const + { + return Index; + } + + // Get the index of the parent node of this node. + NodeIndex_t GetParentNodeIndex() const { - return m_modifyCount; + return ParentNodeIndex; } + private: + // All immutable, but we need copy-assign for vector std::string Name; NodeIndex_t Index; NodeIndex_t ParentNodeIndex; - - private: - friend class Model; - // TODO std::atomic_uint32_t - std::atomic_uint32_t m_modifyCount{0}; XrMatrix4x4f m_localTransform; }; @@ -69,9 +69,7 @@ namespace Pbr class Model { public: - std::string Name; - - Model(bool createRootNode = true); + Model(); /// Add a node to the model. NodeIndex_t AddNode(const XrMatrix4x4f& transform, NodeIndex_t parentIndex, std::string name = ""); @@ -79,9 +77,6 @@ namespace Pbr /// Add a primitive to the model. void AddPrimitive(PrimitiveHandle primitive); - // Remove all primitives. - void Clear(); - NodeIndex_t GetNodeCount() const { return (NodeIndex_t)m_nodes.size(); @@ -95,29 +90,24 @@ namespace Pbr return m_nodes[nodeIndex]; } + /// Get the number of primitives used in this model uint32_t GetPrimitiveCount() const { - return (uint32_t)m_primitives.size(); + return (uint32_t)m_primitiveHandles.size(); } - PrimitiveHandle GetPrimitive(uint32_t index) const + + /// Get a primitive handle by index of primitives used in this model. + PrimitiveHandle GetPrimitiveHandle(uint32_t index) const { - return m_primitives[index]; + return m_primitiveHandles[index]; } /// Find the first node (after an optional parent node) which matches a given name. bool FindFirstNode(NodeIndex_t* outNodeIndex, const char* name, const NodeIndex_t* parentNodeIndex = nullptr) const; - protected: - // Invalidate buffers associated with model transforms - bool m_modelTransformsStructuredBufferInvalid{true}; - void InvalidateBuffer() - { - m_modelTransformsStructuredBufferInvalid = true; - } - - const std::vector& GetPrimitives() const + const std::vector& GetPrimitiveHandles() const { - return m_primitives; + return m_primitiveHandles; } const Node::Collection& GetNodes() const @@ -126,20 +116,106 @@ namespace Pbr } static constexpr Pbr::NodeIndex_t RootParentNodeIndex = (Pbr::NodeIndex_t)-1; - private: - // Compute the transform relative to the root of the model for a given node. - XrMatrix4x4f GetNodeToModelRootTransform(NodeIndex_t nodeIndex) const; - - // Updated the transforms used to render the model. This needs to be called any time a node transform is changed. - // void UpdateTransforms(Pbr::D3D11Resources const& pbrResources, std::runtime_error ID3D11DeviceContext* context) const; - private: // A model is made up of one or more Primitives. Each Primitive has a unique material. // Ideally primitives with the same material should be merged to reduce draw calls. - std::vector m_primitives; + std::vector m_primitiveHandles; // A model contains one or more nodes. Each vertex of a primitive references a node to have the // node's transform applied. Node::Collection m_nodes; }; + + /// A model instance is a collection of node transforms for an instance of a model. + /// A model instance can only have its transforms updated once per command queue. + /// A model instance holds a strong shared reference to its corresponding model. + class ModelInstance + { + protected: + ModelInstance(std::shared_ptr model) : m_model(std::move(model)) + { + const auto nodeCount = m_model->GetNodeCount(); + + m_nodeLocalTransforms.reserve(m_model->GetNodeCount()); + for (const Node& node : m_model->GetNodes()) { + m_nodeLocalTransforms.push_back(node.GetLocalTransform()); + } + XrMatrix4x4f identityMatrix; + XrMatrix4x4f_CreateIdentity(&identityMatrix); // or better yet poison it + m_resolvedTransforms.resize(nodeCount, identityMatrix); + } + + public: + /// Overrides the local transform of a node + void SetNodeTransform(NodeIndex_t nodeIndex, const XrMatrix4x4f& transform) + { + m_nodeLocalTransforms[nodeIndex] = transform; + m_nodeLocalTransformsUpdated = true; + } + + /// Combine a transform with the original transform from the asset + void SetAdditionalNodeTransform(NodeIndex_t nodeIndex, const XrMatrix4x4f& transform) + { + XrMatrix4x4f compositeTransform; + // Node transform is the immutable original transform + const XrMatrix4x4f& originalNodeTransform = m_model->GetNode(nodeIndex).GetLocalTransform(); + XrMatrix4x4f_Multiply(&compositeTransform, &originalNodeTransform, &transform); + SetNodeTransform(nodeIndex, compositeTransform); + } + + protected: + bool WereNodeLocalTransformsUpdated() const noexcept + { + return m_nodeLocalTransformsUpdated; + } + void ClearTransformsUpdatedFlag() noexcept + { + m_nodeLocalTransformsUpdated = false; + } + void ResolveTransforms(bool transpose) + { + const auto& nodes = m_model->GetNodes(); + + // Nodes are guaranteed to come after their parents, so each node transform can be multiplied by its parent transform in a single pass. + assert(nodes.size() == m_nodeLocalTransforms.size()); + assert(nodes.size() == m_resolvedTransforms.size()); + XrMatrix4x4f identityMatrix; + XrMatrix4x4f_CreateIdentity(&identityMatrix); + for (const auto& node : nodes) { + assert(node.GetParentNodeIndex() == Model::RootParentNodeIndex || node.GetParentNodeIndex() < node.GetNodeIndex()); + const XrMatrix4x4f& parentTransform = (node.GetParentNodeIndex() == Model::RootParentNodeIndex) + ? identityMatrix + : m_resolvedTransforms[node.GetParentNodeIndex()]; + const XrMatrix4x4f& nodeTransform = m_nodeLocalTransforms[node.GetNodeIndex()]; + if (transpose) { + XrMatrix4x4f nodeTransformTranspose; + XrMatrix4x4f_Transpose(&nodeTransformTranspose, &nodeTransform); + XrMatrix4x4f_Multiply(&m_resolvedTransforms[node.GetNodeIndex()], &nodeTransformTranspose, &parentTransform); + } + else { + XrMatrix4x4f_Multiply(&m_resolvedTransforms[node.GetNodeIndex()], &parentTransform, &nodeTransform); + } + } + } + + const Model& GetModel() const + { + return *m_model; + } + + const std::vector& GetResolvedTransforms() const noexcept + { + return m_resolvedTransforms; + } + + private: + bool m_nodeLocalTransformsUpdated{true}; + + // Derived classes may depend on this being immutable. + std::shared_ptr m_model; + // This is initialized to the local transform of every node, + // but can be updated for this instance. + std::vector m_nodeLocalTransforms; + std::vector m_resolvedTransforms; + }; } // namespace Pbr diff --git a/src/conformance/framework/pbr/PbrSharedState.h b/src/conformance/framework/pbr/PbrSharedState.h index ff935748..31941636 100644 --- a/src/conformance/framework/pbr/PbrSharedState.h +++ b/src/conformance/framework/pbr/PbrSharedState.h @@ -72,8 +72,8 @@ namespace Pbr enum BindingOffsets { VSResourceViewsOffset = NumConstantBuffers, - MaterialTexturesOffset = VSResourceViewsOffset + NumVSResourceViews, - GlobalTexturesOffset = MaterialTexturesOffset + NumMaterialSlots, + MaterialTexturesOffset = (int)VSResourceViewsOffset + (int)NumVSResourceViews, + GlobalTexturesOffset = (int)MaterialTexturesOffset + (int)NumMaterialSlots, }; } } // namespace ShaderSlots diff --git a/src/conformance/framework/pbr/Vulkan/VkMaterial.cpp b/src/conformance/framework/pbr/Vulkan/VkMaterial.cpp index 7b09b694..1bec4b0d 100644 --- a/src/conformance/framework/pbr/Vulkan/VkMaterial.cpp +++ b/src/conformance/framework/pbr/Vulkan/VkMaterial.cpp @@ -6,6 +6,9 @@ // Licensed under the MIT License. See License.txt in the project root for license information. // // SPDX-License-Identifier: MIT AND Apache-2.0 + +#if defined(XR_USE_GRAPHICS_API_VULKAN) + #include "VkMaterial.h" #include "VkCommon.h" @@ -17,6 +20,8 @@ #include "utilities/vulkan_scoped_handle.h" #include "utilities/vulkan_utils.h" +#include + namespace Pbr { VulkanMaterial::VulkanMaterial(Pbr::VulkanResources const& pbrResources) @@ -72,20 +77,18 @@ namespace Pbr void VulkanMaterial::SetTexture(ShaderSlots::PSMaterial slot, std::shared_ptr textureView, std::shared_ptr sampler) { - m_textures[slot] = textureView; + m_textures[slot] = std::move(textureView); if (sampler) { - m_samplers[slot] = sampler; + m_samplers[slot] = std::move(sampler); } } - // Get the material constant buffer for binding VkDescriptorBufferInfo VulkanMaterial::GetMaterialConstantBuffer() { return m_constantBuffer.MakeDescriptor(); } - // Get the combined image sampler descriptors for binding std::vector VulkanMaterial::GetTextureDescriptors() { std::vector ret(TextureCount); @@ -108,3 +111,5 @@ namespace Pbr } } } // namespace Pbr + +#endif // defined(XR_USE_GRAPHICS_API_VULKAN) diff --git a/src/conformance/framework/pbr/Vulkan/VkMaterial.h b/src/conformance/framework/pbr/Vulkan/VkMaterial.h index 4f762047..e6a7e9e9 100644 --- a/src/conformance/framework/pbr/Vulkan/VkMaterial.h +++ b/src/conformance/framework/pbr/Vulkan/VkMaterial.h @@ -6,6 +6,7 @@ // Licensed under the MIT License. See License.txt in the project root for license information. // // SPDX-License-Identifier: MIT AND Apache-2.0 + #pragma once #include "VkResources.h" @@ -21,7 +22,6 @@ #include #include -#include #include #include #include @@ -32,32 +32,33 @@ namespace Pbr struct VulkanResources; struct VulkanTextureBundle; - // A VulkanMaterial contains the metallic roughness parameters and textures. - // Primitives specify which VulkanMaterial to use when being rendered. + /// A VulkanMaterial contains the metallic roughness parameters and textures. + /// Primitives specify which VulkanMaterial to use when being rendered. struct VulkanMaterial final : public Material { - // Create a uninitialized material. Textures and shader coefficients must be set. + /// Create a uninitialized material. Textures and shader coefficients must be set. VulkanMaterial(Pbr::VulkanResources const& pbrResources); ~VulkanMaterial() override = default; - // Create a clone of this material. Shares the texture and sampler heap with this material. + /// Create a clone of this material. Shares the texture and sampler heap with this material. std::shared_ptr Clone(Pbr::VulkanResources const& pbrResources) const; - // Create a flat (no texture) material. + /// Create a flat (no texture) material. static std::shared_ptr CreateFlat(VulkanResources& pbrResources, RGBAColor baseColorFactor, float roughnessFactor = 1.0f, float metallicFactor = 0.0f, RGBColor emissiveFactor = RGB::Black); - // Set a Metallic-Roughness texture. + /// Set a Metallic-Roughness texture. void SetTexture(ShaderSlots::PSMaterial slot, std::shared_ptr textureView, std::shared_ptr sampler); - // Get the material constant buffer for binding + /// Get the material constant buffer for binding VkDescriptorBufferInfo GetMaterialConstantBuffer(); - // Get the combined image sampler descriptors for binding + + /// Get the combined image sampler descriptors for binding std::vector GetTextureDescriptors(); - // Update the material constant buffer + /// Update the material constant buffer void UpdateBuffer(); std::string Name; diff --git a/src/conformance/framework/pbr/Vulkan/VkModel.cpp b/src/conformance/framework/pbr/Vulkan/VkModel.cpp index e9410657..e1e2a307 100644 --- a/src/conformance/framework/pbr/Vulkan/VkModel.cpp +++ b/src/conformance/framework/pbr/Vulkan/VkModel.cpp @@ -6,6 +6,8 @@ // // SPDX-License-Identifier: MIT AND Apache-2.0 +#if defined(XR_USE_GRAPHICS_API_VULKAN) + #include "VkModel.h" #include "VkMaterial.h" @@ -18,42 +20,38 @@ #include "utilities/vulkan_scoped_handle.h" #include "utilities/vulkan_utils.h" -#include -#include #include -#include #include namespace Pbr { - void VulkanModel::Render(Pbr::VulkanResources& pbrResources, Conformance::CmdBuffer& directCommandBuffer, VkRenderPass renderPass, - VkSampleCountFlagBits sampleCount) + void VulkanModelInstance::Render(Pbr::VulkanResources& pbrResources, Conformance::CmdBuffer& directCommandBuffer, + VkRenderPass renderPass, VkSampleCountFlagBits sampleCount, XrMatrix4x4f modelToWorld) { pbrResources.UpdateBuffer(); + m_modelBuffer.ModelToWorld = modelToWorld; + m_modelConstantBuffer.Update({&m_modelBuffer, 1}); UpdateTransforms(pbrResources); - auto primitives = GetPrimitives(); - if (m_descriptorSets.size() < primitives.size()) { - AllocateDescriptorSets(pbrResources, (uint32_t)primitives.size()); + auto& primitiveHandles = GetModel().GetPrimitiveHandles(); + if (m_descriptorSets.size() < primitiveHandles.size()) { + AllocateDescriptorSets(pbrResources, (uint32_t)primitiveHandles.size()); } - for (size_t i = 0; i < primitives.size(); i++) { - PrimitiveHandle primitiveHandle = primitives[i]; + for (size_t i = 0; i < primitiveHandles.size(); i++) { + PrimitiveHandle primitiveHandle = primitiveHandles[i]; VkDescriptorSet descriptorSet = m_descriptorSets[i]; const Pbr::VulkanPrimitive& primitive = pbrResources.GetPrimitive(primitiveHandle); if (primitive.GetMaterial()->Hidden) continue; primitive.Render(directCommandBuffer, pbrResources, descriptorSet, renderPass, sampleCount, - m_modelTransformsStructuredBuffer.MakeDescriptor()); + m_modelConstantBuffer.MakeDescriptor(), m_modelTransformsStructuredBuffer.MakeDescriptor()); } - - // Expect the caller to reset other state, but the geometry shader is cleared specially. - //context->GSSetShader(nullptr, nullptr, 0); } - void VulkanModel::AllocateDescriptorSets(Pbr::VulkanResources& pbrResources, uint32_t numSets) + void VulkanModelInstance::AllocateDescriptorSets(Pbr::VulkanResources& pbrResources, uint32_t numSets) { m_descriptorPool.adopt(pbrResources.MakeDescriptorPool(numSets), pbrResources.GetDevice()); @@ -69,48 +67,37 @@ namespace Pbr m_descriptorSets.resize(numSets); XRC_CHECK_THROW_VKCMD(vkAllocateDescriptorSets(pbrResources.GetDevice(), &allocInfo, m_descriptorSets.data())); } - - void VulkanModel::UpdateTransforms(Pbr::VulkanResources& pbrResources) + VulkanModelInstance::VulkanModelInstance(Pbr::VulkanResources& pbrResources, std::shared_ptr model) + : ModelInstance(std::move(model)) + { + // Set up the model constant buffer. + m_modelConstantBuffer.Init(pbrResources.GetDevice(), pbrResources.GetMemoryAllocator()); + m_modelConstantBuffer.Create(1, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT); + + // Set up the transforms buffer. + XrMatrix4x4f identityMatrix; + XrMatrix4x4f_CreateIdentity(&identityMatrix); // or better yet poison it + size_t nodeCount = GetModel().GetNodes().size(); + + // Create/recreate the structured buffer and SRV which holds the node transforms. + size_t elemSize = sizeof(XrMatrix4x4f); + uint32_t count = (uint32_t)(nodeCount); + uint32_t size = (uint32_t)(count * elemSize); + + m_modelTransformsStructuredBuffer.Init(pbrResources.GetDevice(), pbrResources.GetMemoryAllocator()); + m_modelTransformsStructuredBuffer.Create(size, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT); + } + void VulkanModelInstance::UpdateTransforms(Pbr::VulkanResources& /* pbrResources */) { - const auto& nodes = GetNodes(); - const uint32_t newTotalModifyCount = std::accumulate(nodes.begin(), nodes.end(), 0, [](uint32_t sumChangeCount, const Node& node) { - return sumChangeCount + node.GetModifyCount(); - }); - // If none of the node transforms have changed, no need to recompute/update the model transform structured buffer. - if (newTotalModifyCount != TotalModifyCount || m_modelTransformsStructuredBufferInvalid) { - if (m_modelTransformsStructuredBufferInvalid) // The structured buffer is reset when a Node is added. - { - XrMatrix4x4f identityMatrix; - XrMatrix4x4f_CreateIdentity(&identityMatrix); // or better yet poison it - m_modelTransforms.resize(nodes.size(), identityMatrix); - - // Create/recreate the structured buffer and SRV which holds the node transforms. - size_t elemSize = sizeof(decltype(m_modelTransforms)::value_type); - uint32_t count = (uint32_t)(m_modelTransforms.size()); - uint32_t size = (uint32_t)(count * elemSize); - - m_modelTransformsStructuredBuffer.Init(pbrResources.GetDevice(), pbrResources.GetMemoryAllocator()); - m_modelTransformsStructuredBuffer.Create(size, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT); - - m_modelTransformsStructuredBufferInvalid = false; - } - - // Nodes are guaranteed to come after their parents, so each node transform can be multiplied by its parent transform in a single pass. - assert(nodes.size() == m_modelTransforms.size()); - XrMatrix4x4f identityMatrix; - XrMatrix4x4f_CreateIdentity(&identityMatrix); - for (const auto& node : nodes) { - assert(node.ParentNodeIndex == RootParentNodeIndex || node.ParentNodeIndex < node.Index); - const XrMatrix4x4f& parentTransform = - (node.ParentNodeIndex == RootParentNodeIndex) ? identityMatrix : m_modelTransforms[node.ParentNodeIndex]; - XrMatrix4x4f nodeTransform = node.GetTransform(); - XrMatrix4x4f_Multiply(&m_modelTransforms[node.Index], &parentTransform, &nodeTransform); - } + if (WereNodeLocalTransformsUpdated()) { + ResolveTransforms(false); // Update node transform structured buffer. - m_modelTransformsStructuredBuffer.Update(this->m_modelTransforms); - TotalModifyCount = newTotalModifyCount; + m_modelTransformsStructuredBuffer.Update(GetResolvedTransforms()); + ClearTransformsUpdatedFlag(); } } } // namespace Pbr + +#endif // defined(XR_USE_GRAPHICS_API_VULKAN) diff --git a/src/conformance/framework/pbr/Vulkan/VkModel.h b/src/conformance/framework/pbr/Vulkan/VkModel.h index 54b0e91e..9ed6925d 100644 --- a/src/conformance/framework/pbr/Vulkan/VkModel.h +++ b/src/conformance/framework/pbr/Vulkan/VkModel.h @@ -9,6 +9,7 @@ #pragma once #include "VkResources.h" +#include "../GlslBuffers.h" #include "../PbrHandles.h" #include "../PbrModel.h" @@ -23,29 +24,28 @@ namespace Pbr { - struct VulkanPrimitive; struct VulkanResources; - class VulkanModel final : public Model + class VulkanModelInstance final : public ModelInstance { public: - // Render the model. + VulkanModelInstance(Pbr::VulkanResources& pbrResources, std::shared_ptr model); + + /// Render the model. void Render(Pbr::VulkanResources& pbrResources, Conformance::CmdBuffer& directCommandBuffer, VkRenderPass renderPass, - VkSampleCountFlagBits sampleCount); + VkSampleCountFlagBits sampleCount, XrMatrix4x4f modelToWorld); private: void AllocateDescriptorSets(Pbr::VulkanResources& pbrResources, uint32_t numSets); - // Updated the transforms used to render the model. This needs to be called any time a node transform is changed. + /// Update the transforms used to render the model. This needs to be called any time a node transform is changed. void UpdateTransforms(Pbr::VulkanResources& pbrResources); - // Temporary buffer holds the world transforms, computed from the node's local transforms. - mutable std::vector m_modelTransforms; - mutable Conformance::StructuredBuffer m_modelTransformsStructuredBuffer; - // mutable Microsoft::WRL::ComPtr m_modelTransformsResourceViewHeap; + Glsl::ModelConstantBuffer m_modelBuffer; + Conformance::StructuredBuffer m_modelConstantBuffer; + + Conformance::StructuredBuffer m_modelTransformsStructuredBuffer; Conformance::ScopedVkDescriptorPool m_descriptorPool; std::vector m_descriptorSets; - - mutable uint32_t TotalModifyCount{0}; }; } // namespace Pbr diff --git a/src/conformance/framework/pbr/Vulkan/VkPipelineStates.cpp b/src/conformance/framework/pbr/Vulkan/VkPipelineStates.cpp index 7195a7f4..b7870dc6 100644 --- a/src/conformance/framework/pbr/Vulkan/VkPipelineStates.cpp +++ b/src/conformance/framework/pbr/Vulkan/VkPipelineStates.cpp @@ -7,6 +7,8 @@ // // SPDX-License-Identifier: MIT AND Apache-2.0 +#if defined(XR_USE_GRAPHICS_API_VULKAN) + #include "VkPipelineStates.h" #include "../PbrSharedState.h" @@ -150,3 +152,5 @@ namespace Pbr return pipeline; } } // namespace Pbr + +#endif // defined(XR_USE_GRAPHICS_API_VULKAN) diff --git a/src/conformance/framework/pbr/Vulkan/VkPrimitive.cpp b/src/conformance/framework/pbr/Vulkan/VkPrimitive.cpp index f0f4fc66..03d0f569 100644 --- a/src/conformance/framework/pbr/Vulkan/VkPrimitive.cpp +++ b/src/conformance/framework/pbr/Vulkan/VkPrimitive.cpp @@ -7,6 +7,8 @@ // // SPDX-License-Identifier: MIT AND Apache-2.0 +#if defined(XR_USE_GRAPHICS_API_VULKAN) + #include "VkPrimitive.h" #include "VkMaterial.h" @@ -53,14 +55,15 @@ namespace Pbr } void VulkanPrimitive::Render(Conformance::CmdBuffer& directCommandBuffer, VulkanResources& pbrResources, VkDescriptorSet descriptorSet, - VkRenderPass renderPass, VkSampleCountFlagBits sampleCount, VkDescriptorBufferInfo transformBuffer) const + VkRenderPass renderPass, VkSampleCountFlagBits sampleCount, VkDescriptorBufferInfo modelConstantBuffer, + VkDescriptorBufferInfo transformBuffer) const { GetMaterial()->UpdateBuffer(); auto materialConstantBuffer = GetMaterial()->GetMaterialConstantBuffer(); auto materialTextures = GetMaterial()->GetTextureDescriptors(); - std::unique_ptr wds = - pbrResources.BuildWriteDescriptorSets(materialConstantBuffer, transformBuffer, materialTextures, descriptorSet); + std::unique_ptr wds = pbrResources.BuildWriteDescriptorSets( + modelConstantBuffer, materialConstantBuffer, transformBuffer, materialTextures, descriptorSet); vkUpdateDescriptorSets(pbrResources.GetDevice(), static_cast(wds->writeDescriptorSets.size()), wds->writeDescriptorSets.data(), 0, NULL); @@ -91,3 +94,5 @@ namespace Pbr CHECKPOINT(); } } // namespace Pbr + +#endif // defined(XR_USE_GRAPHICS_API_VULKAN) diff --git a/src/conformance/framework/pbr/Vulkan/VkPrimitive.h b/src/conformance/framework/pbr/Vulkan/VkPrimitive.h index f8e2edcc..520f60c7 100644 --- a/src/conformance/framework/pbr/Vulkan/VkPrimitive.h +++ b/src/conformance/framework/pbr/Vulkan/VkPrimitive.h @@ -29,7 +29,7 @@ namespace Pbr struct VulkanMaterial; struct VulkanResources; - // A primitive holds a vertex buffer, index buffer, and a pointer to a PBR material. + /// A primitive holds a vertex buffer, index buffer, and a pointer to a PBR material. struct VulkanPrimitive final { using Collection = std::vector; @@ -39,13 +39,7 @@ namespace Pbr VulkanPrimitive(Pbr::VulkanResources const& pbrResources, const Pbr::PrimitiveBuilder& primitiveBuilder, const std::shared_ptr& material); - // void UpdateBuffers(Pbr::VulkanResources& pbrResources, const Pbr::PrimitiveBuilder& primitiveBuilder); - /// Get the material for the primitive. - std::shared_ptr& GetMaterial() - { - return m_material; - } const std::shared_ptr& GetMaterial() const { return m_material; @@ -58,10 +52,10 @@ namespace Pbr } protected: - // friend class Model; - friend class VulkanModel; + friend class VulkanModelInstance; void Render(Conformance::CmdBuffer& directCommandBuffer, VulkanResources& pbrResources, VkDescriptorSet descriptorSet, - VkRenderPass renderPass, VkSampleCountFlagBits sampleCount, VkDescriptorBufferInfo transformBuffer) const; + VkRenderPass renderPass, VkSampleCountFlagBits sampleCount, VkDescriptorBufferInfo modelConstantBuffer, + VkDescriptorBufferInfo transformBuffer) const; /// The clone shares the vertex and index buffers - they are not cloned VulkanPrimitive Clone(Pbr::VulkanResources const& pbrResources) const; diff --git a/src/conformance/framework/pbr/Vulkan/VkResources.cpp b/src/conformance/framework/pbr/Vulkan/VkResources.cpp index f168176a..17a6e267 100644 --- a/src/conformance/framework/pbr/Vulkan/VkResources.cpp +++ b/src/conformance/framework/pbr/Vulkan/VkResources.cpp @@ -7,9 +7,10 @@ // // SPDX-License-Identifier: MIT AND Apache-2.0 +#if defined(XR_USE_GRAPHICS_API_VULKAN) + #include "VkResources.h" -#include "GlslBuffers.h" #include "VkCommon.h" #include "VkMaterial.h" #include "VkPipelineStates.h" @@ -18,6 +19,7 @@ #include "VkTextureCache.h" #include "../../gltf/GltfHelper.h" +#include "../GlslBuffers.h" #include "../PbrCommon.h" #include "../PbrHandles.h" #include "../PbrSharedState.h" @@ -30,7 +32,6 @@ #include #include -#include #include #include #include @@ -255,7 +256,7 @@ namespace Pbr VK_SHADER_STAGE_FRAGMENT_BIT); // transform buffer - layoutBuilder.SetBindings(TransformsBuffer, ShaderSlots::GLSL::VSResourceViewsOffset + ShaderSlots::Transforms, + layoutBuilder.SetBindings(TransformsBuffer, (int)ShaderSlots::GLSL::VSResourceViewsOffset + (int)ShaderSlots::Transforms, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, // ShaderSlots::NumVSResourceViews); @@ -265,7 +266,7 @@ namespace Pbr ShaderSlots::NumMaterialSlots); layoutBuilder.SetBindings(GlobalTextures, ShaderSlots::GLSL::GlobalTexturesOffset, // VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, // - ShaderSlots::NumTextures - ShaderSlots::NumMaterialSlots); + (int)ShaderSlots::NumTextures - (int)ShaderSlots::NumMaterialSlots); } // very basic for now, can grow if needed @@ -304,14 +305,10 @@ namespace Pbr Resources.Pipelines = std::make_unique(device, Resources.PipelineLayout, c_attrDesc, c_bindingDesc, g_PbrVertexShader, g_PbrPixelShader); - // Set up the constant buffers. - + // Set up the scene constant buffer. Resources.SceneBuffer.Init(device, allocator); Resources.SceneBuffer.Create(1, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT); - Resources.ModelBuffer.Init(device, allocator); - Resources.ModelBuffer.Create(1, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT); - Resources.BrdfSampler.adopt(VulkanTexture::CreateSampler(device), device); Resources.EnvironmentMapSampler.adopt(VulkanTexture::CreateSampler(device), device); @@ -338,7 +335,6 @@ namespace Pbr mutable VulkanTextureCache SolidColorTextureCache; Conformance::StructuredBuffer SceneBuffer; - Conformance::StructuredBuffer ModelBuffer; Conformance::ScopedVkSampler BrdfSampler; Conformance::ScopedVkSampler EnvironmentMapSampler; std::shared_ptr DescriptorSetLayout; @@ -357,7 +353,6 @@ namespace Pbr DeviceResources Resources; Glsl::SceneConstantBuffer SceneBuffer; - Glsl::ModelConstantBuffer ModelBuffer; PipelineLayout::VulkanDescriptorSetLayout VulkanLayout{}; struct LoaderResources @@ -442,7 +437,7 @@ namespace Pbr : glMagFilter == TINYGLTF_TEXTURE_FILTER_LINEAR ? VK_FILTER_LINEAR : VK_FILTER_NEAREST; } - // Create a Vulkan sampler from a tinygltf Sampler. + /// Create a Vulkan sampler from a tinygltf Sampler. static VkSampler CreateGLTFSampler(VkDevice device, const tinygltf::Sampler& sampler) { VkSamplerCreateInfo info{VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO}; @@ -512,9 +507,9 @@ namespace Pbr m_impl->Resources.BrdfLut = std::move(brdfLut); } - std::unique_ptr - VulkanResources::BuildWriteDescriptorSets(VkDescriptorBufferInfo materialConstantBuffer, VkDescriptorBufferInfo transformBuffer, - nonstd::span materialCombinedImageSamplers, VkDescriptorSet dstSet) + std::unique_ptr VulkanResources::BuildWriteDescriptorSets( + VkDescriptorBufferInfo modelConstantBuffer, VkDescriptorBufferInfo materialConstantBuffer, VkDescriptorBufferInfo transformBuffer, + nonstd::span materialCombinedImageSamplers, VkDescriptorSet dstSet) { PipelineLayout::VulkanWriteDescriptorSetsBuilder builder(m_impl->VulkanLayout, dstSet); @@ -525,10 +520,7 @@ namespace Pbr builder.BindBuffers(PipelineLayout::SceneConstantBuffer, sceneConstantBuffer); // ModelConstantBuffer - VkDescriptorBufferInfo modelConstantBuffer[] = { - m_impl->Resources.ModelBuffer.MakeDescriptor(), - }; - builder.BindBuffers(PipelineLayout::ModelConstantBuffer, modelConstantBuffer); + builder.BindBuffers(PipelineLayout::ModelConstantBuffer, {&modelConstantBuffer, 1}); // MaterialConstantBuffer builder.BindBuffers(PipelineLayout::MaterialConstantBuffer, {&materialConstantBuffer, 1}); @@ -567,12 +559,6 @@ namespace Pbr m_impl->SceneBuffer.LightDiffuseColor = diffuseColor; } - void VulkanResources::SetModelToWorld(XrMatrix4x4f modelToWorld) const - { - m_impl->ModelBuffer.ModelToWorld = modelToWorld; - m_impl->Resources.ModelBuffer.Update({&m_impl->ModelBuffer, 1}); - } - void VulkanResources::SetViewProjection(XrMatrix4x4f view, XrMatrix4x4f projection) const { XrMatrix4x4f_Multiply(&m_impl->SceneBuffer.ViewProjection, &projection, &view); @@ -704,3 +690,5 @@ namespace Pbr } } // namespace Pbr + +#endif // defined(XR_USE_GRAPHICS_API_VULKAN) diff --git a/src/conformance/framework/pbr/Vulkan/VkResources.h b/src/conformance/framework/pbr/Vulkan/VkResources.h index b24b1d30..d0b6ba43 100644 --- a/src/conformance/framework/pbr/Vulkan/VkResources.h +++ b/src/conformance/framework/pbr/Vulkan/VkResources.h @@ -22,15 +22,12 @@ #include #include #include -#include #include #include -#include #include #include #include -#include class VulkanDebugObjectNamer; @@ -47,10 +44,6 @@ namespace tinygltf struct Sampler; } // namespace tinygltf -// namespace Conformance -// { -// struct MemoryAllocator; -// } namespace Pbr { struct Primitive; @@ -64,15 +57,12 @@ namespace Pbr struct VulkanTextureAndSampler : public ITexture { ~VulkanTextureAndSampler() override = default; - /// Required - // Microsoft::WRL::ComPtr texture; - /// Optional - // Vulkan_SAMPLER_DESC sampler; bool samplerSet; }; static constexpr size_t BindingCount = ShaderSlots::NumConstantBuffers + ShaderSlots::NumVSResourceViews + ShaderSlots::NumTextures; + class VulkanWriteDescriptorSets { public: @@ -103,7 +93,7 @@ namespace Pbr VulkanWriteDescriptorSets& operator=(VulkanWriteDescriptorSets&&) = delete; }; - // Global PBR resources required for rendering a scene. + /// Global PBR resources required for rendering a scene. struct VulkanResources final : public IResources { VulkanResources(const VulkanDebugObjectNamer& namer, VkPhysicalDevice physicalDevice, VkDevice device, uint32_t queueFamilyIndex); @@ -122,38 +112,35 @@ namespace Pbr const std::shared_ptr& material) override; void DropLoaderCaches() override; - // Sets the Bidirectional Reflectance Distribution Function Lookup Table texture, required by the shader to compute surface - // reflectance from the IBL. + /// Sets the Bidirectional Reflectance Distribution Function Lookup Table texture, required by the shader to compute surface + /// reflectance from the IBL. void SetBrdfLut(std::shared_ptr brdfLut); - // Get a pipeline state matching some parameters as well as the current settings inside VkResources + /// Get a pipeline state matching some parameters as well as the current settings inside VkResources Conformance::Pipeline& GetOrCreatePipeline(VkRenderPass renderPass, VkSampleCountFlagBits sampleCount, BlendState blendState, DoubleSided doubleSided); - // Set the directional light. + /// Set the directional light. void SetLight(XrVector3f direction, RGBColor diffuseColor); - // Set the specular and diffuse image-based lighting (IBL) maps. ShaderResourceViews must be TextureCubes. + /// Set the specular and diffuse image-based lighting (IBL) maps. ShaderResourceViews must be TextureCubes. void SetEnvironmentMap(std::shared_ptr specularEnvironmentMap, std::shared_ptr diffuseEnvironmentMap); - // Set the current view and projection matrices. + /// Set the current view and projection matrices. void SetViewProjection(XrMatrix4x4f view, XrMatrix4x4f projection) const; - // Many 1x1 pixel colored textures are used in the PBR system. This is used to create textures backed by a cache to reduce the - // number of textures created. + /// Many 1x1 pixel colored textures are used in the PBR system. This is used to create textures backed by a cache to reduce the + /// number of textures created. std::shared_ptr CreateTypedSolidColorTexture(RGBAColor color); - // Update the scene buffer in GPU memory. + /// Update the scene buffer in GPU memory. void UpdateBuffer() const; - // Get the fence to wait on before executing any command list built on this Resources. - // std::pair GetFenceAndValue() const; - - // Set and update the model to world constant buffer value. - void SetModelToWorld(XrMatrix4x4f modelToWorld) const; - + /// Get the VulkanPrimitive from a primitive handle. VulkanPrimitive& GetPrimitive(PrimitiveHandle p); + + /// Get the VulkanPrimitive from a primitive handle, const overload. const VulkanPrimitive& GetPrimitive(PrimitiveHandle p) const; // Set or get the shading and fill modes. @@ -176,13 +163,13 @@ namespace Pbr private: std::unique_ptr - BuildWriteDescriptorSets(VkDescriptorBufferInfo materialConstantBuffer, VkDescriptorBufferInfo transformBuffer, - nonstd::span materialCombinedImageSamplers, VkDescriptorSet dstSet); + BuildWriteDescriptorSets(VkDescriptorBufferInfo modelConstantBuffer, VkDescriptorBufferInfo materialConstantBuffer, + VkDescriptorBufferInfo transformBuffer, nonstd::span materialCombinedImageSamplers, + VkDescriptorSet dstSet); friend struct VulkanMaterial; friend struct VulkanPrimitive; struct Impl; - std::unique_ptr m_impl; SharedState m_sharedState; diff --git a/src/conformance/framework/pbr/Vulkan/VkTexture.cpp b/src/conformance/framework/pbr/Vulkan/VkTexture.cpp index 8e79a94b..531e7b14 100644 --- a/src/conformance/framework/pbr/Vulkan/VkTexture.cpp +++ b/src/conformance/framework/pbr/Vulkan/VkTexture.cpp @@ -7,6 +7,8 @@ // // SPDX-License-Identifier: MIT AND Apache-2.0 +#if defined(XR_USE_GRAPHICS_API_VULKAN) + #include "VkTexture.h" #include "VkCommon.h" @@ -21,6 +23,7 @@ #include "utilities/vulkan_utils.h" #include +#include #include #include #include @@ -58,53 +61,6 @@ namespace Pbr const uint8_t* rgba, uint32_t elemSize, uint32_t width, uint32_t height, bool cubemap, VkFormat format) { - // Microsoft::WRL::ComPtr device = pbrResources.GetDevice(); - - // Microsoft::WRL::ComPtr cmdList = pbrResources.CreateCopyCommandList(); - - // std::vector> imageUploadBuffers; - // Microsoft::WRL::ComPtr image = - // Conformance::VulkanCreateImage(device.get(), width, height, arraySize, format, Vulkan_HEAP_TYPE_DEFAULT); - - // Vulkan_RESOURCE_DESC imageDesc = image->GetDesc(); - // assert(imageDesc.DepthOrArraySize == arraySize); - // imageUploadBuffers.reserve(arraySize); - // // TODO: maybe call GetCopyableFootprints only once, as all out fields accept arrays - // // TODO: put the upload buffer in a staging resources vector and make async - // for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { - // UINT subresourceIndex = VulkanCalcSubresource(0, arrayIndex, 0, imageDesc.MipLevels, arraySize); - - // Vulkan_PLACED_SUBRESOURCE_FOOTPRINT footprint; - // UINT rowCount; - // UINT64 rowSize; - // UINT64 uploadBufferSize; - // device->GetCopyableFootprints(&imageDesc, subresourceIndex, 1, 0, &footprint, &rowCount, &rowSize, &uploadBufferSize); - - // assert( - // rowCount == - // height); // doesn't hold for compressed textures, see: https://www.gamedev.net/forums/topic/677932-getcopyablefootprints-question/ - // assert(rowSize == width * elemSize); // assert this for now, probably doesn't hold for e.g. compressed textures - - // Microsoft::WRL::ComPtr imageUpload = - // Conformance::VulkanCreateBuffer(device.get(), (uint32_t)uploadBufferSize, Vulkan_HEAP_TYPE_UPLOAD); - // imageUploadBuffers.push_back(imageUpload); - - // Vulkan_SUBRESOURCE_DATA initData{}; - // initData.pData = rgba; - // initData.RowPitch = elemSize * width; - // initData.SlicePitch = elemSize * width * height; - - // // this does a row-by-row memcpy internally or we would have used our own CopyWithStride - // Internal::ThrowIf(!UpdateSubresources(cmdList.get(), image.get(), imageUpload.get(), 0, 1, uploadBufferSize, &footprint, - // &rowCount, &rowSize, &initData), - // "Call to UpdateSubresources helper failed"); - // } - - // XRC_CHECK_THROW_HRCMD(cmdList->Close()); - // pbrResources.ExecuteCopyCommandList(cmdList.get(), std::move(imageUploadBuffers)); - - // return image; - VkDevice device = pbrResources.GetDevice(); const Conformance::MemoryAllocator& memAllocator = pbrResources.GetMemoryAllocator(); const Conformance::CmdBuffer& copyCmdBuffer = pbrResources.GetCopyCommandBuffer(); @@ -121,11 +77,11 @@ namespace Pbr // Create a staging buffer VkBufferCreateInfo bufferCreateInfo{VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO}; bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; - bufferCreateInfo.size = width * height * elemSize; + bufferCreateInfo.size = static_cast(width) * height * elemSize; Conformance::BufferAndMemory stagingBuffer; stagingBuffer.Create(device, memAllocator, bufferCreateInfo); - stagingBuffer.Update(device, {rgba, width * height * elemSize}, 0); + stagingBuffer.Update(device, {rgba, static_cast(bufferCreateInfo.size)}, 0); // create image VkImage image{VK_NULL_HANDLE}; @@ -157,24 +113,6 @@ namespace Pbr bundle.deviceMemory = Conformance::ScopedVkDeviceMemory(imageMemory, device); - // // Switch the source buffer to TRANSFER_DST_OPTIMAL - // VkBufferMemoryBarrier bufferBarrier{VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER}; - // // VkAccessFlags srcAccessMask; - // // VkAccessFlags dstAccessMask; - // // uint32_t srcQueueFamilyIndex; - // // uint32_t dstQueueFamilyIndex; - // // VkBuffer buffer; - // // VkDeviceSize offset; - // // VkDeviceSize size; - // bufferBarrier.srcAccessMask = 0; - // bufferBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - // bufferBarrier.oldLayout = VK_BUFFER_; - // bufferBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - // bufferBarrier.= stagingBuffer.buf; - // bufferBarrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, layerCount}; - // vkCmdPipelineBarrier(copyCmdBuffer.buf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, - // nullptr, 1, &bufferBarrier); - // Switch the destination image to TRANSFER_DST_OPTIMAL VkImageMemoryBarrier imgBarrier{VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER}; imgBarrier.srcAccessMask = 0; @@ -313,3 +251,5 @@ namespace Pbr } } // namespace VulkanTexture } // namespace Pbr + +#endif // defined(XR_USE_GRAPHICS_API_VULKAN) diff --git a/src/conformance/framework/pbr/Vulkan/VkTextureCache.cpp b/src/conformance/framework/pbr/Vulkan/VkTextureCache.cpp index 1f9a1b90..7f8c3f0d 100644 --- a/src/conformance/framework/pbr/Vulkan/VkTextureCache.cpp +++ b/src/conformance/framework/pbr/Vulkan/VkTextureCache.cpp @@ -7,6 +7,8 @@ // // SPDX-License-Identifier: MIT AND Apache-2.0 +#if defined(XR_USE_GRAPHICS_API_VULKAN) + #include "VkTextureCache.h" #include "VkCommon.h" @@ -56,3 +58,5 @@ namespace Pbr return m_solidColorTextureCache.emplace(colorKey, texture).first->second; } } // namespace Pbr + +#endif // defined(XR_USE_GRAPHICS_API_VULKAN) diff --git a/src/conformance/framework/pbr/Vulkan/VkTextureCache.h b/src/conformance/framework/pbr/Vulkan/VkTextureCache.h index 965798e6..a1a80f47 100644 --- a/src/conformance/framework/pbr/Vulkan/VkTextureCache.h +++ b/src/conformance/framework/pbr/Vulkan/VkTextureCache.h @@ -34,9 +34,6 @@ namespace Pbr /// Default constructor makes an invalid cache. VulkanTextureCache() = default; - // VulkanTextureCache(const VulkanTextureCache&) = default; - // VulkanTextureCache& operator=(const VulkanTextureCache&) = default; - VulkanTextureCache(VulkanTextureCache&&) = default; VulkanTextureCache& operator=(VulkanTextureCache&&) = default; diff --git a/src/conformance/framework/pbr/check-offsets.py b/src/conformance/framework/pbr/check-offsets.py new file mode 100755 index 00000000..7c6aea95 --- /dev/null +++ b/src/conformance/framework/pbr/check-offsets.py @@ -0,0 +1,260 @@ +#!/usr/bin/env python3 +# Copyright 2023-2024, The Khronos Group, Inc. +# +# SPDX-License-Identifier: Apache-2.0 + +import os +import subprocess +from pathlib import Path +from dataclasses import dataclass, field +import re +import sys +from typing import Dict, List, Optional, Tuple + +_PBR_DIR = Path(__file__).parent.resolve() + +_SHADER = "PbrPixelShader_glsl.frag" + +_GL_TYPES = { + 0x8B51: "GL_FLOAT_VEC3", + 0x1405: "GL_UNSIGNED_INT", # 32 bit (4 bytes) + 0x1406: "GL_FLOAT", # 32 bit (4 bytes) +} + + +@dataclass +class GlslObjectReflectionData: + name: str + offset: int + data_type: int # typically shown in hex + size: int + index: int + binding: int + stages: int + counter: Optional[int] = None + num_members: Optional[int] = None + array_stride: Optional[int] = None + top_level_array_stride: Optional[int] = None + + +def parse_reflection_line(line: str) -> GlslObjectReflectionData: + """ + Process a single object reflection dump line from glslangValidator. + + Parses the output of TObjectReflection::dump(), effectively. + + See: + https://github.com/KhronosGroup/glslang/blob/db4d6f85afb8cf6aa404a141855f556d172c1ed2/glslang/MachineIndependent/reflection.cpp#L1092 + """ + name, _, all_fields = line.strip().partition(": ") + if all_fields is None: + raise RuntimeError( + f"Could not parse line as a reflection data line: {all_fields}" + ) + # field_list = [] + # fields = {name: val for name, val in field_list} + fields = dict(field.split(" ") for field in all_fields.split(", ")) + + # These fields are always printed - see TObjectReflection::dump + # So, let's parse them. + + offset = int(fields["offset"]) + + # parse hex + data_type = int(fields["type"], 16) + + size = int(fields["size"]) + + index = int(fields["index"]) + + binding = int(fields["binding"]) + + stages = int(fields["stages"]) + data = GlslObjectReflectionData( + name=name, + offset=offset, + data_type=data_type, + size=size, + index=index, + binding=binding, + stages=stages, + ) + + # These are not always printed + counter = fields.get("counter") + if counter is not None: + data.counter = int(counter) + + num_members = fields.get("numMembers") + if num_members is not None: + data.num_members = int(num_members) + + array_stride = fields.get("arrayStride") + if array_stride is not None: + data.array_stride = int(array_stride) + + top_level_array_stride = fields.get("topLevelArrayStride") + if top_level_array_stride is not None: + data.top_level_array_stride = int(top_level_array_stride) + + return data + + +def process_reflection_data_section( + reflection_lines: List[str], heading: str +) -> Dict[str, GlslObjectReflectionData]: + """ + Process a section of reflection data from glslangValidator. + + Effectively handles a single section (loop) from TReflection::dump() + + See: + https://github.com/KhronosGroup/glslang/blob/db4d6f85afb8cf6aa404a141855f556d172c1ed2/glslang/MachineIndependent/reflection.cpp#L1222 + """ + start_idx = reflection_lines.index(heading) + end_idx = None + try: + end_idx = reflection_lines.index("", start_idx + 1) + except ValueError: + # don't care, last section + pass + if end_idx is None: + section_lines = reflection_lines[start_idx + 1 :] + else: + section_lines = reflection_lines[start_idx + 1 : end_idx - 1] + ret = {} + for line in section_lines: + data = parse_reflection_line(line) + ret[data.name] = data + return ret + + +@dataclass +class ReflectionData: + uniforms: Dict[str, GlslObjectReflectionData] = field(default_factory=dict) + + +def compile_and_get_reflection_data( + shader_source: Path, glslangvalidator_cmd: str = "glslangValidator" +) -> ReflectionData: + output = subprocess.check_output( + [glslangvalidator_cmd, "-V", str(shader_source), "-q"] + ) + + ret = ReflectionData() + + lines = [line.strip().decode(encoding="utf-8") for line in output.splitlines()] + start_uniform = lines.index("Uniform reflection:") + end_uniform = lines.index("", start_uniform + 1) + uniform_lines = lines[start_uniform + 1 : end_uniform - 1] + for line in uniform_lines: + data = parse_reflection_line(line) + ret.uniforms[data.name] = data + + return ret + + +@dataclass +class COffsetData: + structure_type: str + member_name: str + offset: int + + +_RE_OFFSET_ASSERTS = re.compile( + r"offsetof\((?P[A-Za-z]+), (?P[A-Za-z]+)\) == (?P[0-9]+)," +) + + +def parse_c_offsets(source: Path) -> Dict[Tuple[str, str], COffsetData]: + ret = {} + with open(source, "r", encoding="utf-8") as fp: + for line in fp: + m = _RE_OFFSET_ASSERTS.search(line) + if m: + structure_type = m.group("structure_type") + member_name = m.group("member_name") + offset = int(m.group("offset")) + ret[(structure_type, member_name)] = COffsetData( + structure_type=structure_type, + member_name=member_name, + offset=offset, + ) + return ret + + +def compare_glsl_and_c( + shader_data: ReflectionData, + source_data: Dict[Tuple[str, str], COffsetData], + glsl_qualified_member: str, + c_structure: str, + c_member: str, +): + uniform_data = shader_data.uniforms.get(glsl_qualified_member) + if uniform_data is None: + raise RuntimeError( + f"Could not find qualified GLSL name {glsl_qualified_member} in reflection data!" + ) + c_data = source_data.get((c_structure, c_member)) + if c_data is None: + raise RuntimeError( + f"Could not find parsed source code offset assertion for type {c_structure}, member name {c_member}" + ) + + glsl_offset = shader_data.uniforms[glsl_qualified_member].offset + c_offset = c_data.offset + gl_type_enum = shader_data.uniforms[glsl_qualified_member].data_type + # print(f"GLSL: datatype: {gl_type_enum:04x}") + gl_type_name = _GL_TYPES[gl_type_enum] + + print( + f"GLSL: offset {glsl_offset:03d} {glsl_qualified_member} (datatype: {gl_type_name})" + ) + print(f"C: offset {c_offset:03d} {c_structure}.{c_member}") + if glsl_offset != c_offset: + raise RuntimeError("Mismatch!") + + +if __name__ == "__main__": + shader = _PBR_DIR / "Shaders" / _SHADER + + if len(sys.argv) > 1: + glslangValidator = sys.argv[1] + else: + + # glslangValidator = os.getenv("GLSLANGVALIDATOR") + # if not glslangValidator: + glslangValidator = "glslangValidator" + + shader_data = compile_and_get_reflection_data(shader, glslangValidator) + + source_data = parse_c_offsets(_PBR_DIR / "GlslBuffers.h") + + compare_glsl_and_c( + shader_data, + source_data, + "type_SceneBuffer.EyePosition", + "SceneConstantBuffer", + "EyePosition", + ) + compare_glsl_and_c( + shader_data, + source_data, + "type_SceneBuffer.LightDirection", + "SceneConstantBuffer", + "LightDirection", + ) + compare_glsl_and_c( + shader_data, + source_data, + "type_SceneBuffer.LightColor", + "SceneConstantBuffer", + "LightDiffuseColor", + ) + compare_glsl_and_c( + shader_data, + source_data, + "type_SceneBuffer.NumSpecularMipLevels", + "SceneConstantBuffer", + "NumSpecularMipLevels", + ) diff --git a/src/loader/manifest_file.cpp b/src/loader/manifest_file.cpp index f9699ece..ae0842f3 100644 --- a/src/loader/manifest_file.cpp +++ b/src/loader/manifest_file.cpp @@ -600,14 +600,8 @@ void RuntimeManifestFile::CreateIfValid(const Json::Value &root_node, const std: // If the library_path variable has no directory symbol, it's just a file name and should be accessible on the // global library path. if (lib_path.find('\\') != std::string::npos || lib_path.find('/') != std::string::npos) { - // If the library_path is an absolute path, just use that if it exists - if (FileSysUtilsIsAbsolutePath(lib_path)) { - if (!FileSysUtilsPathExists(lib_path)) { - error_ss << filename << " library " << lib_path << " does not appear to exist"; - LoaderLogger::LogErrorMessage("", error_ss.str()); - return; - } - } else { + // If the library_path is an absolute path, just use that as-is. + if (!FileSysUtilsIsAbsolutePath(lib_path)) { // Otherwise, treat the library path as a relative path based on the JSON file. std::string canonical_path; std::string combined_path; @@ -618,8 +612,8 @@ void RuntimeManifestFile::CreateIfValid(const Json::Value &root_node, const std: canonical_path = filename; } if (!FileSysUtilsGetParentPath(canonical_path, file_parent) || - !FileSysUtilsCombinePaths(file_parent, lib_path, combined_path) || !FileSysUtilsPathExists(combined_path)) { - error_ss << filename << " library " << combined_path << " does not appear to exist"; + !FileSysUtilsCombinePaths(file_parent, lib_path, combined_path)) { + error_ss << filename << " filesystem operations failed for path " << canonical_path; LoaderLogger::LogErrorMessage("", error_ss.str()); return; } diff --git a/src/loader/openxr-loader.map b/src/loader/openxr-loader.map index 982f63d1..7ac55701 100644 --- a/src/loader/openxr-loader.map +++ b/src/loader/openxr-loader.map @@ -1,3 +1,9 @@ +/* +Copyright (c) 2019-2024, The Khronos Group Inc. + +SPDX-License-Identifier: Apache-2.0 OR MIT +*/ + { global: xrCreateInstance; diff --git a/src/loader/openxr-loader.map.license b/src/loader/openxr-loader.map.license deleted file mode 100644 index f386f870..00000000 --- a/src/loader/openxr-loader.map.license +++ /dev/null @@ -1,3 +0,0 @@ -Copyright (c) 2019-2024, The Khronos Group Inc. - -SPDX-License-Identifier: Apache-2.0 OR MIT diff --git a/src/scripts/template_function_info.cpp b/src/scripts/template_function_info.cpp index 9883fd80..bb02e710 100644 --- a/src/scripts/template_function_info.cpp +++ b/src/scripts/template_function_info.cpp @@ -11,6 +11,7 @@ namespace Conformance { // This is a generated list of information about functions. 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), //# if cur_cmd.name in null_instance_ok @@ -24,6 +25,7 @@ static const FunctionInfoMap functionInfoMapInternal{ nullptr, //# endif {/*{ gen.allReturnCodesForCommand(cur_cmd) | join(', ') }*/})}, +//# endif //# endfor };