diff --git a/include/ppx/application.h b/include/ppx/application.h index 74c9384cb..ed6bbb8f1 100644 --- a/include/ppx/application.h +++ b/include/ppx/application.h @@ -89,6 +89,7 @@ struct StandardOptions #if defined(PPX_BUILD_XR) std::shared_ptr>> pXrUiResolution; std::shared_ptr>> pXrRequiredExtensions; + std::shared_ptr> pXrEnableMultiview; #endif std::shared_ptr>> pAssetsPaths; @@ -119,11 +120,6 @@ struct ApplicationSettings { bool enable = false; - // Multiview will create one swapchain with layers per view - // One Application::Render then should use multiview shaders - // to render to both layers, as opposed to non multiview - // where there is one swapchain per view, each with a ::Render - bool enableMultiView = false; // Whether to create depth swapchains in addition to color swapchains, // and submit the depth info to the runtime as an additional layer. bool enableDepthSwapchain = false; @@ -211,6 +207,7 @@ struct ApplicationSettings #if defined(PPX_BUILD_XR) std::pair xrUiResolution = std::make_pair(0, 0); std::vector xrRequiredExtensions = {}; + bool xrEnableMultiview = false; #endif } standardKnobsDefaultValue; }; diff --git a/projects/cube_xr/CMakeLists.txt b/projects/cube_xr/CMakeLists.txt index 272af3bc7..558ff4a0d 100644 --- a/projects/cube_xr/CMakeLists.txt +++ b/projects/cube_xr/CMakeLists.txt @@ -19,4 +19,4 @@ add_samples_for_all_apis( "CubeXr.h" "CubeXr.cpp" "main.cpp" - SHADER_DEPENDENCIES "shader_vertex_colors") + SHADER_DEPENDENCIES "shader_vertex_colors_multi") diff --git a/projects/cube_xr/CubeXr.cpp b/projects/cube_xr/CubeXr.cpp index c614c8960..f51d3c651 100644 --- a/projects/cube_xr/CubeXr.cpp +++ b/projects/cube_xr/CubeXr.cpp @@ -38,7 +38,7 @@ void CubeXrApp::Setup() // Uniform buffer { grfx::BufferCreateInfo bufferCreateInfo = {}; - bufferCreateInfo.size = PPX_MINIMUM_UNIFORM_BUFFER_SIZE; + bufferCreateInfo.size = std::max(sizeof(UniformBufferData), (uint64_t)PPX_MINIMUM_UNIFORM_BUFFER_SIZE); bufferCreateInfo.usageFlags.bits.uniformBuffer = true; bufferCreateInfo.memoryUsage = grfx::MEMORY_USAGE_CPU_TO_GPU; @@ -68,12 +68,12 @@ void CubeXrApp::Setup() // Pipeline { - std::vector bytecode = LoadShader("basic/shaders", "VertexColors.vs"); + std::vector bytecode = LoadShader("basic/shaders", "VertexColorsMulti.vs"); PPX_ASSERT_MSG(!bytecode.empty(), "VS shader bytecode load failed"); grfx::ShaderModuleCreateInfo shaderCreateInfo = {static_cast(bytecode.size()), bytecode.data()}; PPX_CHECKED_CALL(GetDevice()->CreateShaderModule(&shaderCreateInfo, &mVS)); - bytecode = LoadShader("basic/shaders", "VertexColors.ps"); + bytecode = LoadShader("basic/shaders", "VertexColorsMulti.ps"); PPX_ASSERT_MSG(!bytecode.empty(), "PS shader bytecode load failed"); shaderCreateInfo = {static_cast(bytecode.size()), bytecode.data()}; PPX_CHECKED_CALL(GetDevice()->CreateShaderModule(&shaderCreateInfo, &mPS)); @@ -103,6 +103,8 @@ void CubeXrApp::Setup() gpCreateInfo.outputState.renderTargetFormats[0] = GetSwapchain()->GetColorFormat(); gpCreateInfo.outputState.depthStencilFormat = GetSwapchain()->GetDepthFormat(); gpCreateInfo.pPipelineInterface = mPipelineInterface; + gpCreateInfo.multiViewState.viewMask = GetXrComponent().GetDefaultViewMask(); + gpCreateInfo.multiViewState.correlationMask = GetXrComponent().GetDefaultViewMask(); PPX_CHECKED_CALL(GetDevice()->CreateGraphicsPipeline(&gpCreateInfo, &mPipeline)); } @@ -201,11 +203,13 @@ void CubeXrApp::Setup() void CubeXrApp::Render() { - PerFrame& frame = mPerFrame[0]; - uint32_t imageIndex = UINT32_MAX; - uint32_t currentViewIndex = 0; + PerFrame& frame = mPerFrame[0]; + uint32_t imageIndex = UINT32_MAX; + uint32_t currentViewIndex = 0; + XrComponent& xrComponent = GetXrComponent(); + if (IsXrEnabled()) { - currentViewIndex = GetXrComponent().GetCurrentViewIndex(); + currentViewIndex = xrComponent.GetCurrentViewIndex(); } // Render UI into a different composition layer. @@ -269,20 +273,32 @@ void CubeXrApp::Render() // Update uniform buffer. { float t = GetElapsedSeconds(); - float4x4 P = glm::perspective(glm::radians(60.0f), GetWindowAspect(), 0.001f, 10000.0f); - float4x4 V = glm::lookAt(float3(0, 0, 0), float3(0, 0, 1), float3(0, 1, 0)); + float4x4 M = glm::translate(float3(0, 0, -3)) * glm::rotate(t, float3(0, 0, 1)) * glm::rotate(t, float3(0, 1, 0)) * glm::rotate(t, float3(1, 0, 0)); if (IsXrEnabled()) { - const Camera& camera = GetXrComponent().GetCamera(); - P = camera.GetProjectionMatrix(); - V = camera.GetViewMatrix(); + xrComponent.SetCurrentViewIndex(0); + frame.uniform_buffer_data.M[0] = xrComponent.GetCamera().GetViewProjectionMatrix() * M; + + xrComponent.SetCurrentViewIndex(1); + frame.uniform_buffer_data.M[1] = xrComponent.GetCamera().GetViewProjectionMatrix() * M; + } + else { + const Camera& camera = xrComponent.GetCamera(); + float4x4 P = camera.GetProjectionMatrix(); + float4x4 V = camera.GetViewMatrix(); + frame.uniform_buffer_data.M[0] = frame.uniform_buffer_data.M[1] = P * V * M; + } + + // If multiview is active, we have one render pass with Left/Right poses loaded. + // If not multiview, this entire render call will happen again, and we switch to current view index. + + if (!xrComponent.IsMultiView()) { + frame.uniform_buffer_data.M[0] = frame.uniform_buffer_data.M[IsXrEnabled() ? xrComponent.GetCurrentViewIndex() : 0]; } - float4x4 M = glm::translate(float3(0, 0, -3)) * glm::rotate(t, float3(0, 0, 1)) * glm::rotate(t, float3(0, 1, 0)) * glm::rotate(t, float3(1, 0, 0)); - float4x4 mat = P * V * M; void* pData = nullptr; PPX_CHECKED_CALL(mUniformBuffer->MapMemory(0, &pData)); - memcpy(pData, &mat, sizeof(mat)); + memcpy(pData, &frame.uniform_buffer_data, sizeof(frame.uniform_buffer_data)); mUniformBuffer->UnmapMemory(); } diff --git a/projects/cube_xr/CubeXr.h b/projects/cube_xr/CubeXr.h index fa4a159fc..83a21a8e3 100644 --- a/projects/cube_xr/CubeXr.h +++ b/projects/cube_xr/CubeXr.h @@ -28,6 +28,13 @@ class CubeXrApp virtual void Render() override; private: + struct UniformBufferData + { + // Pose of each Cube + // One for each eye, but second only used in multi-view + ppx::float4x4 M[2]; + }; + struct PerFrame { grfx::CommandBufferPtr cmd; @@ -36,6 +43,8 @@ class CubeXrApp grfx::SemaphorePtr renderCompleteSemaphore; grfx::FencePtr renderCompleteFence; + UniformBufferData uniform_buffer_data = {}; + // XR UI per frame elements. grfx::CommandBufferPtr uiCmd; grfx::FencePtr uiRenderCompleteFence; diff --git a/src/ppx/application.cpp b/src/ppx/application.cpp index d91b7f88e..891bb95f8 100644 --- a/src/ppx/application.cpp +++ b/src/ppx/application.cpp @@ -162,7 +162,7 @@ Result Application::InitializeGrfxDevice() ci.pVulkanDeviceFeatures = nullptr; ci.supportShadingRateMode = mSettings.grfx.device.supportShadingRateMode; #if defined(PPX_BUILD_XR) - ci.multiView = IsXrEnabled() && mSettings.xr.enableMultiView; + ci.multiView = IsXrEnabled() && mStandardOpts.pXrEnableMultiview->GetValue(); ci.pXrComponent = IsXrEnabled() ? &mXrComponent : nullptr; #endif @@ -682,6 +682,10 @@ void Application::InitStandardKnobs() "to the base extensions. Any required extensions that are not supported by the " "target system will cause the application to immediately exit."); mStandardOpts.pXrRequiredExtensions->SetFlagParameters(""); + + GetKnobManager().InitKnob(&mStandardOpts.pXrEnableMultiview, "xr-enable-multiview", mSettings.standardKnobsDefaultValue.xrEnableMultiview); + mStandardOpts.pXrEnableMultiview->SetFlagDescription( + "Specify whether or not multiview should be enabled for the application."); #endif GetKnobManager().InitKnob(&mStandardOpts.pShadingRateMode, "shading-rate-mode", ""); @@ -973,7 +977,7 @@ void Application::InitializeXRComponentBeforeGrfxDeviceInit() createInfo.enableDebug = mSettings.grfx.enableDebug; createInfo.enableQuadLayer = mSettings.enableImGui; createInfo.enableDepthSwapchain = mSettings.xr.enableDepthSwapchain; - createInfo.enableMultiView = mSettings.xr.enableMultiView; + createInfo.enableMultiView = mStandardOpts.pXrEnableMultiview->GetValue(); const auto resolution = mStandardOpts.pResolution->GetValue(); const bool hasResolutionFlag = (resolution.first > 0 && resolution.second > 0); if (hasResolutionFlag) {