From 140be444fb50b2c716e64e9b07506d88193f65bd Mon Sep 17 00:00:00 2001 From: Patrick Hodoul Date: Fri, 21 Nov 2025 11:32:10 -0500 Subject: [PATCH 01/20] OGSMOD-8260 - Some tries Signed-off-by: Patrick Hodoul --- .../hvt/resources/shaders/copyDepth.glslfx | 34 ++++ source/engine/renderBufferManager.cpp | 156 ++++++++++++++++-- test/tests/composeTaskHelpers.cpp | 21 ++- test/tests/composeTaskHelpers.h | 3 +- test/tests/testComposeTask.cpp | 6 +- 5 files changed, 190 insertions(+), 30 deletions(-) create mode 100644 include/hvt/resources/shaders/copyDepth.glslfx diff --git a/include/hvt/resources/shaders/copyDepth.glslfx b/include/hvt/resources/shaders/copyDepth.glslfx new file mode 100644 index 00000000..ff566748 --- /dev/null +++ b/include/hvt/resources/shaders/copyDepth.glslfx @@ -0,0 +1,34 @@ +-- glslfx version 0.1 + +// Copyright 2025 Autodesk, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +-- configuration +{ + "techniques": { + "default": { + "CopyDepthFragment": { + "source": [ "CopyDepth.Fragment" ] + } + } + } +} + +-- glsl CopyDepth.Fragment + +void main(void) +{ + gl_FragDepth = HgiTexelFetch_depthIn(ivec2(uvOut * screenSize)).r; +} + diff --git a/source/engine/renderBufferManager.cpp b/source/engine/renderBufferManager.cpp index 80e882a4..a8949529 100644 --- a/source/engine/renderBufferManager.cpp +++ b/source/engine/renderBufferManager.cpp @@ -172,11 +172,16 @@ class RenderBufferManager::Impl : public RenderBufferSettingsProvider private: - /// Copy the contents of the input buffer into the output buffer + /// Copy the color & depth AOVs of the input buffers into the output buffers. void PrepareBuffersFromInputs(RenderBufferBinding const& colorInput, RenderBufferBinding const& depthInput, HdRenderBufferDescriptor const& desc, SdfPath const& controllerId); + /// Copy the depth AOV of the input buffer into the output buffer. + void PrepareBufferFromInput(RenderBufferBinding const& input, + HdRenderBufferDescriptor const& desc, + SdfPath const& controllerId); + /// Sets the viewport render output (color or buffer visualization). void SetViewportRenderOutput(const TfToken& name, const SdfPath& controllerId); @@ -226,8 +231,9 @@ class RenderBufferManager::Impl : public RenderBufferSettingsProvider SyncDelegatePtr _syncDelegate; /// The shaders used to copy the contents of the input into the output render buffer. - std::unique_ptr _copyShader; - std::unique_ptr _copyShaderNoDepth; + std::unique_ptr _copyColorShader; + std::unique_ptr _copyColorShaderNoDepth; + std::unique_ptr _copyDepthShader; }; @@ -399,21 +405,21 @@ void RenderBufferManager::Impl::PrepareBuffersFromInputs(RenderBufferBinding con } // Initialize the shader that will copy the contents from the input to the output. - if (!_copyShader) + if (!_copyColorShader) { - _copyShader = + _copyColorShader = std::make_unique(hgi, "Copy Color Buffer"); } // Initialize the shader that will copy the contents from the input to the output. - if (!_copyShaderNoDepth) + if (!_copyColorShaderNoDepth) { - _copyShaderNoDepth = + _copyColorShaderNoDepth = std::make_unique(hgi, "Copy Color Buffer No Depth"); } HdxFullscreenShader* shader = - (!depthInput ? _copyShaderNoDepth.get() : _copyShader.get()); + (!depthInput ? _copyColorShaderNoDepth.get() : _copyColorShader.get()); // Set the screen size constant on the shader. GfVec2f screenSize { static_cast(desc.dimensions[0]), @@ -439,6 +445,109 @@ void RenderBufferManager::Impl::PrepareBuffersFromInputs(RenderBufferBinding con colorInput->SubmitLayoutChange(HgiTextureUsageBitsColorTarget); } +void RenderBufferManager::Impl::PrepareBufferFromInput(RenderBufferBinding const& inputAov, + HdRenderBufferDescriptor const& desc, SdfPath const& controllerId) +{ + HD_TRACE_FUNCTION(); + HF_MALLOC_TAG_FUNCTION(); + + HgiTextureHandle input = inputAov.texture; + + if (!input) + { + return; + } + + const SdfPath aovPath = GetAovPath(controllerId, inputAov.aovName); + // Get the buffer that the renderer will draw into from the render index. + HdRenderBuffer* buffer = static_cast( + _pRenderIndex->GetBprim(HdPrimTypeTokens->renderBuffer, aovPath)); + + // If there is no buffer in this render index it was determined that the buffer + // to write into should come from the input buffer from the previous pass. + if (!buffer) + { + // Use the input buffer + buffer = inputAov.buffer; + } + else + { + if (!buffer->IsMapped()) + { + // This might be a newly created BPrim. Allocate the GPU texture if needed. + buffer->Allocate(desc.dimensions, desc.format, desc.multiSampled); + } + } + + HgiTextureHandle output; + VtValue outputValue = buffer->GetResource(desc.multiSampled); + if (outputValue.IsHolding()) + { + output = outputValue.Get(); + if (!output) + { + TF_CODING_ERROR("The output render buffer does not have a valid texture %s.", + inputAov.aovName.GetText()); + return; + } + } + else + { + // The output render buffer is not holding a writeable buffer. + // You will need to composite to blend passes results. + return; + } + + // If the input and output are the same texture, no need to copy. + if (output == input) + { + return; + } + + Hgi* hgi = GetHgi(_pRenderIndex); + if (!hgi) + { + TF_CODING_ERROR("There is no valid Hgi driver."); + return; + } + + // Initialize the shader that will copy the contents from the input to the output. + if (!_copyDepthShader) + { + _copyDepthShader = + std::make_unique(hgi, "Copy Depth Buffer"); + + // Configure the shader to handle depth texture. + HgiShaderFunctionDesc shaderDesc; + shaderDesc.debugName = "Copy Depth Shader"; + shaderDesc.shaderStage = HgiShaderStageFragment; + HgiShaderFunctionAddStageInput(&shaderDesc, "uvOut", "vec2"); + HgiShaderFunctionAddTexture(&shaderDesc, "depthIn", 0); + HgiShaderFunctionAddStageOutput(&shaderDesc, "gl_FragDepth", "float", "depth(any) "); + HgiShaderFunctionAddConstantParam(&shaderDesc, "screenSize", "vec2"); + + static const TfToken copyDepthShaderPath { GetShaderPath("copyDepth.glslfx").generic_u8string() }; + static const TfToken copyDepthToken("CopyDepthFragment"); + + _copyDepthShader->SetProgram(copyDepthShaderPath, copyDepthToken, shaderDesc); + } + + HdxFullscreenShader* shader = _copyDepthShader.get(); + + // Set the screen size constant on the shader. + GfVec2f screenSize { static_cast(desc.dimensions[0]), + static_cast(desc.dimensions[1]) }; + shader->SetShaderConstants(sizeof(screenSize), &screenSize); + + // Submit the layout change to read from the texture. + input->SubmitLayoutChange(HgiTextureUsageBitsShaderRead); + + shader->BindTextures({ input }); + shader->Draw(HgiTextureHandle(), output); + + input->SubmitLayoutChange(HgiTextureUsageBitsDepthTarget); +} + bool RenderBufferManager::Impl::SetRenderOutputs(TfTokenVector const& outputs, RenderBufferBindings const& inputs, GfVec4d const& viewport, SdfPath const& controllerId) { @@ -520,10 +629,10 @@ bool RenderBufferManager::Impl::SetRenderOutputs(TfTokenVector const& outputs, // Add the new RenderBuffers. // NOTE: GetAovPath returns ids of the form {controller_id}/aov_{name}. - std::string rendererName = _pRenderIndex->GetRenderDelegate()->GetRendererDisplayName(); - RenderBufferBinding colorInput {}; - RenderBufferBinding depthInput {}; - HdRenderBufferDescriptor colorDesc; + const std::string rendererName + = _pRenderIndex->GetRenderDelegate()->GetRendererDisplayName(); + RenderBufferBinding colorInput, depthInput; + HdRenderBufferDescriptor colorDesc, depthDesc; for (size_t i = 0; i < localOutputs.size(); ++i) { HdRenderBufferDescriptor desc; @@ -539,18 +648,20 @@ bool RenderBufferManager::Impl::SetRenderOutputs(TfTokenVector const& outputs, inputFound = (rendererName == input.rendererName); if (localOutputs[i] == pxr::HdAovTokens->depth) { + depthDesc = desc; depthInput = input; + if (inputFound) { // If the renderer remains the same, we don't want to copy the depth buffer. // The existing depth buffer will continue to be used. // We do this in order to not loose sub-pixel depth information. - // However, this means that if any Tasks write to the depth after a sub-pixel - // resolve then the depth buffer will be inconsistent with the color buffer and - // that depth information will be lost. I don't think this currently happens in + // However, this means that if any Tasks write to the depth after a sub-pixel + // resolve then the depth buffer will be inconsistent with the color buffer and + // that depth information will be lost. I don't think this currently happens in // practice, so we are opting in favor of keeping the sub-pixel resolution. - // - // FUTURE: We may want to revisit this decision in the future. + // + // FUTURE: We may want to revisit this decision in the future. // The long-term solution may be to do post processing at the sub-pixel accuracy. depthInput.texture = HgiTextureHandle(); } @@ -580,10 +691,19 @@ bool RenderBufferManager::Impl::SetRenderOutputs(TfTokenVector const& outputs, _aovBufferIds.push_back(aovId); } } - if (colorInput.texture) + + // Copy the AOV to visualize i.e, be careful that's not always the color one! + + // Color AOV always means color & depth AOVs. + if (outputs[0] == pxr::HdAovTokens->color && colorInput.texture) { PrepareBuffersFromInputs(colorInput, depthInput, colorDesc, controllerId); } + // But depth AOV only means depth AOV :-) + if (outputs[0] == pxr::HdAovTokens->depth && depthInput.texture) + { + PrepareBufferFromInput(depthInput, depthDesc, controllerId); + } // Create the list of AOV bindings. // This section only fills the 3 vectors below: aovBindingsClear, aovBindingsNoClear, diff --git a/test/tests/composeTaskHelpers.cpp b/test/tests/composeTaskHelpers.cpp index 6138d019..1bbfad77 100644 --- a/test/tests/composeTaskHelpers.cpp +++ b/test/tests/composeTaskHelpers.cpp @@ -82,9 +82,14 @@ void RenderFirstFramePass(TestHelpers::FramePassInstance& framePass1, int width, params.viewInfo.material = stage.defaultMaterial(); params.viewInfo.ambient = stage.defaultAmbient(); - params.colorspace = HdxColorCorrectionTokens->disabled; - params.backgroundColor = TestHelpers::ColorDarkGrey; - params.selectionColor = TestHelpers::ColorYellow; + params.colorspace = HdxColorCorrectionTokens->disabled; + params.selectionColor = TestHelpers::ColorYellow; + + params.clearBackgroundColor = true; + params.backgroundColor = TestHelpers::ColorDarkGrey; + + params.clearBackgroundDepth = true; + params.backgroundDepth = 1.0f; // Delays the display to the next frame pass. params.enablePresentation = false; @@ -97,7 +102,6 @@ void RenderFirstFramePass(TestHelpers::FramePassInstance& framePass1, int width, void RenderSecondFramePass(TestHelpers::FramePassInstance& framePass2, int width, int height, bool enablePresentTask, TestHelpers::TestStage const& stage, hvt::RenderBufferBindings const& inputAOVs, - TestHelpers::RenderingBackend /*renderingBackend*/, bool clearBackground /*= true*/) { auto& params = framePass2.sceneFramePass->params(); @@ -113,11 +117,14 @@ void RenderSecondFramePass(TestHelpers::FramePassInstance& framePass2, int width params.viewInfo.ambient = stage.defaultAmbient(); params.colorspace = HdxColorCorrectionTokens->disabled; - params.clearBackgroundColor = clearBackground; - // NoAlpha is mandatory for the alpha blending. - params.backgroundColor = TestHelpers::ColorBlackNoAlpha; params.selectionColor = TestHelpers::ColorYellow; + params.clearBackgroundColor = clearBackground; + params.backgroundColor = TestHelpers::ColorDarkGrey; + + params.clearBackgroundDepth = clearBackground; + params.backgroundDepth = 1.0f; + params.enablePresentation = enablePresentTask; // Gets the list of tasks to render but use the render buffers from the first frame diff --git a/test/tests/composeTaskHelpers.h b/test/tests/composeTaskHelpers.h index aad09b0d..44a6b9b9 100644 --- a/test/tests/composeTaskHelpers.h +++ b/test/tests/composeTaskHelpers.h @@ -35,7 +35,6 @@ void RenderFirstFramePass(TestHelpers::FramePassInstance& framePass1, int width, // Renders the second frame pass which also display the result. void RenderSecondFramePass(TestHelpers::FramePassInstance& framePass2, int width, int height, bool enablePresentTask, TestHelpers::TestStage const& stage, - hvt::RenderBufferBindings const& inputAOVs, TestHelpers::RenderingBackend renderingBackend, - bool clearBackground = true); + hvt::RenderBufferBindings const& inputAOVs, bool clearBackground = true); } // namespace TestHelpers diff --git a/test/tests/testComposeTask.cpp b/test/tests/testComposeTask.cpp index 7f8259e2..71d3e2bd 100644 --- a/test/tests/testComposeTask.cpp +++ b/test/tests/testComposeTask.cpp @@ -321,7 +321,7 @@ HVT_TEST(TestViewportToolbox, compose_ComposeTask2) pass->GetRenderBufferBindingsForNextPass({ pxr::HdAovTokens->depth }); TestHelpers::RenderSecondFramePass( framePass2, context->width(), context->height(), context->presentationEnabled(), - stage, inputAOVs, GetParam()); + stage, inputAOVs); return --frameCount > 0; }; @@ -413,7 +413,7 @@ HVT_TEST(TestViewportToolbox, compose_ComposeTask3) TestHelpers::RenderSecondFramePass( framePass2, context->width(), context->height(), context->presentationEnabled(), - stage, inputAOVs, GetParam()); + stage, inputAOVs); return --frameCount > 0; }; @@ -500,7 +500,7 @@ HVT_TEST(TestViewportToolbox, compose_ShareTextures4) // result of the previous frame pass. TestHelpers::RenderSecondFramePass( framePass2, context->width(), context->height(), context->presentationEnabled(), - stage, inputAOVs, GetParam(), false); + stage, inputAOVs, false); return --frameCount > 0; }; From fe43e7d821dcde8c9b8851472c5d3bfcb317a999 Mon Sep 17 00:00:00 2001 From: Patrick Hodoul Date: Tue, 25 Nov 2025 00:32:00 -0500 Subject: [PATCH 02/20] Fix depth-depth case Signed-off-by: Patrick Hodoul --- source/engine/CMakeLists.txt | 2 + source/engine/copyDepthShader.cpp | 405 ++++++++++++++++++++++++++ source/engine/copyDepthShader.h | 71 +++++ source/engine/renderBufferManager.cpp | 51 +--- 4 files changed, 493 insertions(+), 36 deletions(-) create mode 100644 source/engine/copyDepthShader.cpp create mode 100644 source/engine/copyDepthShader.h diff --git a/source/engine/CMakeLists.txt b/source/engine/CMakeLists.txt index 2181416c..471a22ea 100644 --- a/source/engine/CMakeLists.txt +++ b/source/engine/CMakeLists.txt @@ -20,6 +20,8 @@ set(_ENGINE_INCLUDE_DIR "${_HVT_INCLUDE_DIR}/hvt/engine") # Collect the source files. set(_SOURCE_FILES + "copyDepthShader.cpp" + "copyDepthShader.h" "delegateStreamUtils.h" "framePass.cpp" "framePassUtils.cpp" diff --git a/source/engine/copyDepthShader.cpp b/source/engine/copyDepthShader.cpp new file mode 100644 index 00000000..24a4a239 --- /dev/null +++ b/source/engine/copyDepthShader.cpp @@ -0,0 +1,405 @@ +// Copyright 2025 Autodesk, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "copyDepthShader.h" + +#include +#include +#include +#include +#include +#include + +PXR_NAMESPACE_USING_DIRECTIVE + +namespace HVT_NS +{ + +namespace +{ +static const std::string vsCode = R"( +void main(void) { + gl_Position = position; + uvOut = uvIn; +})"; + +static const std::string fsCode = R"( +void main(void) { + float depth = HgiTexelFetch_depthIn(ivec2(uvOut * screenSize)).r; + gl_FragDepth = depth; +})"; + +// Prepare uniform buffer for GPU computation. +struct Uniforms +{ + GfVec2f screenSize; +}; + +} // namespace + +CopyDepthShader::CopyDepthShader(Hgi* hgi) : _hgi(hgi) {} +CopyDepthShader::~CopyDepthShader() +{ + _Cleanup(); +} + +bool CopyDepthShader::_CreateShaderProgram() +{ + if (_shaderProgram) + { + return true; + } + + // Vertex Shader. + + HgiShaderFunctionDesc vertDesc; + vertDesc.debugName = "CopyDepthShader Vertex"; + vertDesc.shaderStage = HgiShaderStageVertex; + HgiShaderFunctionAddStageInput(&vertDesc, "position", "vec4"); + HgiShaderFunctionAddStageInput(&vertDesc, "uvIn", "vec2"); + HgiShaderFunctionAddStageOutput(&vertDesc, "gl_Position", "vec4", "position"); + HgiShaderFunctionAddStageOutput(&vertDesc, "uvOut", "vec2"); + + vertDesc.shaderCode = vsCode.c_str(); + HgiShaderFunctionHandle vertFn = _hgi->CreateShaderFunction(vertDesc); + + // Check for error. + if (!vertFn->IsValid()) + { + TF_CODING_ERROR("%s", vertFn->GetCompileErrors().c_str()); + _Cleanup(); + return false; + } + + // Fragment Shader. + + HgiShaderFunctionDesc fragDesc; + fragDesc.debugName = "CopyDepthShader Fragment"; + fragDesc.shaderStage = HgiShaderStageFragment; + HgiShaderFunctionAddStageInput(&fragDesc, "uvOut", "vec2"); + HgiShaderFunctionAddTexture(&fragDesc, "depthIn", 0); + HgiShaderFunctionAddStageOutput(&fragDesc, "gl_FragDepth", "float", "depth(any)"); + HgiShaderFunctionAddConstantParam(&fragDesc, "screenSize", "vec2"); + + fragDesc.shaderCode = fsCode.c_str(); + HgiShaderFunctionHandle fragFn = _hgi->CreateShaderFunction(fragDesc); + + // Check for error. + if (!fragFn->IsValid()) + { + TF_CODING_ERROR("%s", fragFn->GetCompileErrors().c_str()); + _Cleanup(); + return false; + } + + // Shader program. + + HgiShaderProgramDesc programDesc; + programDesc.debugName = "CopyDepthShader Program"; + programDesc.shaderFunctions.push_back(std::move(vertFn)); + programDesc.shaderFunctions.push_back(std::move(fragFn)); + _shaderProgram = _hgi->CreateShaderProgram(programDesc); + + // Check for error. + if (!_shaderProgram->IsValid()) + { + TF_CODING_ERROR("%s", _shaderProgram->GetCompileErrors().c_str()); + _Cleanup(); + return false; + } + + return true; +} + +bool CopyDepthShader::_CreateBufferResources() +{ + if (_vertexBuffer) + { + return true; + } + + // A larger-than screen triangle made to fit the screen. + constexpr float vertData[][6] = { { -1, 3, 0, 1, 0, 2 }, { -1, -1, 0, 1, 0, 0 }, + { 3, -1, 0, 1, 2, 0 } }; + + HgiBufferDesc vboDesc; + vboDesc.debugName = "CopyDepthShader VertexBuffer"; + vboDesc.usage = HgiBufferUsageVertex; + vboDesc.initialData = vertData; + vboDesc.byteSize = sizeof(vertData); + vboDesc.vertexStride = sizeof(vertData[0]); + _vertexBuffer = _hgi->CreateBuffer(vboDesc); + if (!_vertexBuffer) + { + return false; + } + + constexpr int32_t indices[3] = { 0, 1, 2 }; + + HgiBufferDesc iboDesc; + iboDesc.debugName = "CopyDepthShader IndexBuffer"; + iboDesc.usage = HgiBufferUsageIndex32; + iboDesc.initialData = indices; + iboDesc.byteSize = sizeof(indices); + _indexBuffer = _hgi->CreateBuffer(iboDesc); + if (!_indexBuffer) + { + return false; + } + + return true; +} + +bool CopyDepthShader::_CreateResourceBindings(HgiTextureHandle const& inputTexture) +{ + HgiResourceBindingsDesc resourceDesc; + resourceDesc.debugName = "CopyDepthShader Resources"; + + HgiTextureBindDesc texBind; + texBind.bindingIndex = 0; + texBind.stageUsage = HgiShaderStageFragment; + texBind.textures.push_back(inputTexture); + texBind.samplers.push_back(_sampler); + + resourceDesc.textures.push_back(std::move(texBind)); + + // If nothing has changed in the descriptor we avoid re-creating thebresource bindings object. + if (_resourceBindings) + { + HgiResourceBindingsDesc const& desc = _resourceBindings->GetDescriptor(); + if (desc == resourceDesc) + { + return true; + } + else + { + _hgi->DestroyResourceBindings(&_resourceBindings); + } + } + + _resourceBindings = _hgi->CreateResourceBindings(resourceDesc); + + return true; +} + +bool CopyDepthShader::_CreatePipeline(HgiTextureHandle const& outputTexture) +{ + if (_pipeline) + { + if (_depthAttachment.format == outputTexture->GetDescriptor().format) + { + return true; + } + + _hgi->DestroyGraphicsPipeline(&_pipeline); + } + + HgiGraphicsPipelineDesc pipelineDesc; + pipelineDesc.debugName = "CopyDepthShader Pipeline"; + pipelineDesc.shaderProgram = _shaderProgram; + + // Describe the vertex buffer + HgiVertexAttributeDesc posAttr; + posAttr.format = HgiFormatFloat32Vec3; + posAttr.offset = 0; + posAttr.shaderBindLocation = 0; + + HgiVertexAttributeDesc uvAttr; + uvAttr.format = HgiFormatFloat32Vec2; + uvAttr.offset = sizeof(float) * 4; // after posAttr + uvAttr.shaderBindLocation = 1; + + uint32_t bindSlots = 0; + + HgiVertexBufferDesc vboDesc; + + vboDesc.bindingIndex = bindSlots++; + vboDesc.vertexStride = sizeof(float) * 6; // pos, uv + vboDesc.vertexAttributes.clear(); + vboDesc.vertexAttributes.push_back(posAttr); + vboDesc.vertexAttributes.push_back(uvAttr); + + pipelineDesc.vertexBuffers.push_back(std::move(vboDesc)); + + // Set up depth attachment. + _depthAttachment.format = outputTexture->GetDescriptor().format; + _depthAttachment.usage = outputTexture->GetDescriptor().usage; + _depthAttachment.loadOp = HgiAttachmentLoadOpDontCare; + _depthAttachment.storeOp = HgiAttachmentStoreOpStore; + + pipelineDesc.depthAttachmentDesc = _depthAttachment; + + // Alpha to coverage would prevent any pixels that have an alpha of 0.0 from + // being written. We want to color correct all pixels. Even background + // pixels that were set with a clearColor alpha of 0.0. + pipelineDesc.multiSampleState.alphaToCoverageEnable = false; + + // The MSAA on renderPipelineState has to match the render target. + pipelineDesc.multiSampleState.sampleCount = outputTexture->GetDescriptor().sampleCount; + pipelineDesc.multiSampleState.multiSampleEnable = pipelineDesc.multiSampleState.sampleCount > 1; + + pipelineDesc.depthState.depthTestEnabled = true; + pipelineDesc.depthState.depthWriteEnabled = true; + pipelineDesc.depthState.depthCompareFn = HgiCompareFunctionAlways; + + // Uniform. + pipelineDesc.shaderConstantsDesc.stageUsage = HgiShaderStageFragment; + pipelineDesc.shaderConstantsDesc.byteSize = sizeof(Uniforms); + + _pipeline = _hgi->CreateGraphicsPipeline(pipelineDesc); + + return (bool)_pipeline; +} + +bool CopyDepthShader::_CreateSampler() +{ + if (_sampler) + { + return true; + } + + HgiSamplerDesc sampDesc; + + sampDesc.magFilter = HgiSamplerFilterLinear; + sampDesc.minFilter = HgiSamplerFilterLinear; + + sampDesc.addressModeU = HgiSamplerAddressModeClampToEdge; + sampDesc.addressModeV = HgiSamplerAddressModeClampToEdge; + + _sampler = _hgi->CreateSampler(sampDesc); + + return true; +} + +void CopyDepthShader::_Execute( + HgiTextureHandle const& inputTexture, HgiTextureHandle const& outputTexture) +{ + GfVec3i const& dimensions = inputTexture->GetDescriptor().dimensions; + + // Prepare graphics cmds. + + HgiGraphicsCmdsDesc gfxDesc; + gfxDesc.depthAttachmentDesc = _depthAttachment; + gfxDesc.depthTexture = outputTexture; + + const GfVec4i viewport(0, 0, dimensions[0], dimensions[1]); + + Uniforms uniform; + uniform.screenSize[0] = static_cast(dimensions[0]); + uniform.screenSize[1] = static_cast(dimensions[1]); + + // Begin rendering. + + HgiGraphicsCmdsUniquePtr gfxCmds = _hgi->CreateGraphicsCmds(gfxDesc); + gfxCmds->PushDebugGroup("CopyDepthShader"); + gfxCmds->BindResources(_resourceBindings); + gfxCmds->BindPipeline(_pipeline); + gfxCmds->BindVertexBuffers({ { _vertexBuffer, 0, 0 } }); + gfxCmds->SetConstantValues(_pipeline, HgiShaderStageFragment, 0, sizeof(uniform), &uniform); + gfxCmds->SetViewport(viewport); + gfxCmds->DrawIndexed(_indexBuffer, 3, 0, 0, 1, 0); + gfxCmds->PopDebugGroup(); + + // Done recording commands, submit work. + _hgi->SubmitCmds(gfxCmds.get()); +} + +void CopyDepthShader::Execute( + HgiTextureHandle const& inputTexture, HgiTextureHandle const& outputTexture) +{ + HD_TRACE_FUNCTION(); + HF_MALLOC_TAG_FUNCTION(); + + if (inputTexture == outputTexture) + { + return; + } + + class Guard + { + public: + Guard(HgiTextureHandle const& inputTexture) : _inputTexture(inputTexture) + { + _inputTexture->SubmitLayoutChange(HgiTextureUsageBitsShaderRead); + } + ~Guard() { _inputTexture->SubmitLayoutChange(HgiTextureUsageBitsDepthTarget); } + + private: + HgiTextureHandle const& _inputTexture; + } guard(inputTexture); + + if (!TF_VERIFY(_CreateBufferResources(), "Resource creation failed.")) + { + return; + } + if (!TF_VERIFY(_CreateSampler(), "Sampler creation failed.")) + { + return; + } + if (!TF_VERIFY(_CreateShaderProgram(), "Shader creation failed.")) + { + return; + } + if (!TF_VERIFY(_CreateResourceBindings(inputTexture), "Resource binding failed.")) + { + return; + } + if (!TF_VERIFY(_CreatePipeline(outputTexture), "Pipeline creation failed.")) + { + return; + } + + _Execute(inputTexture, outputTexture); +} + +void CopyDepthShader::_Cleanup() +{ + if (_sampler) + { + _hgi->DestroySampler(&_sampler); + } + + if (_vertexBuffer) + { + _hgi->DestroyBuffer(&_vertexBuffer); + } + + if (_indexBuffer) + { + _hgi->DestroyBuffer(&_indexBuffer); + } + + if (_shaderProgram) + { + auto shaderFunctions = _shaderProgram->GetShaderFunctions(); + + for (auto& fn : shaderFunctions) + { + _hgi->DestroyShaderFunction(&fn); + } + _hgi->DestroyShaderProgram(&_shaderProgram); + } + + if (_resourceBindings) + { + _hgi->DestroyResourceBindings(&_resourceBindings); + } + + if (_pipeline) + { + _hgi->DestroyGraphicsPipeline(&_pipeline); + } +} + +} // namespace HVT_NS diff --git a/source/engine/copyDepthShader.h b/source/engine/copyDepthShader.h new file mode 100644 index 00000000..32f8c204 --- /dev/null +++ b/source/engine/copyDepthShader.h @@ -0,0 +1,71 @@ +// Copyright 2025 Autodesk, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace HVT_NS +{ + +/// Copy the depth AOV from the input to the output texture. +/// \note That's the strip version of HdxFullscreenShader which always needs the color AOV. +class CopyDepthShader +{ +public: + explicit CopyDepthShader(PXR_NS::Hgi* hgi); + + CopyDepthShader() = delete; + CopyDepthShader(const CopyDepthShader&) = delete; + CopyDepthShader& operator=(const CopyDepthShader&) = delete; + + ~CopyDepthShader(); + + void Execute(PXR_NS::HgiTextureHandle const& inputTexture, + PXR_NS::HgiTextureHandle const& outputTexture); + +protected: + bool _CreateShaderProgram(); + bool _CreateBufferResources(); + bool _CreateResourceBindings(PXR_NS::HgiTextureHandle const& inputTexture); + bool _CreatePipeline(PXR_NS::HgiTextureHandle const& outputTexture); + bool _CreateSampler(); + void _Execute(PXR_NS::HgiTextureHandle const& inputTexture, + PXR_NS::HgiTextureHandle const& outputTexture); + void _Cleanup(); + +private: + PXR_NS::Hgi* _hgi { nullptr }; + + PXR_NS::HgiAttachmentDesc _depthAttachment; + PXR_NS::HgiSamplerHandle _sampler; + PXR_NS::HgiBufferHandle _vertexBuffer; + PXR_NS::HgiBufferHandle _indexBuffer; + PXR_NS::HgiShaderProgramHandle _shaderProgram; + PXR_NS::HgiResourceBindingsHandle _resourceBindings; + PXR_NS::HgiGraphicsPipelineHandle _pipeline; +}; + +} // namespace HVT_NS diff --git a/source/engine/renderBufferManager.cpp b/source/engine/renderBufferManager.cpp index a8949529..de4b2699 100644 --- a/source/engine/renderBufferManager.cpp +++ b/source/engine/renderBufferManager.cpp @@ -18,6 +18,8 @@ #include #include +#include "copyDepthShader.h" + // clang-format off #if defined(__clang__) #pragma clang diagnostic push @@ -53,11 +55,13 @@ #include #include +// clang-format off #if defined(__clang__) - #pragma clang diagnostic pop +#pragma clang diagnostic pop #elif defined(_MSC_VER) - #pragma warning(pop) +#pragma warning(pop) #endif +// clang-format on PXR_NAMESPACE_USING_DIRECTIVE @@ -230,10 +234,10 @@ class RenderBufferManager::Impl : public RenderBufferSettingsProvider /// The SyncDelegate used to create RenderBufferDescriptor data for use by the render index. SyncDelegatePtr _syncDelegate; - /// The shaders used to copy the contents of the input into the output render buffer. + /// The shaders used to copy the contents of the input into the output render buffer. std::unique_ptr _copyColorShader; std::unique_ptr _copyColorShaderNoDepth; - std::unique_ptr _copyDepthShader; + std::unique_ptr _copyDepthShader; }; @@ -510,42 +514,17 @@ void RenderBufferManager::Impl::PrepareBufferFromInput(RenderBufferBinding const TF_CODING_ERROR("There is no valid Hgi driver."); return; } - - // Initialize the shader that will copy the contents from the input to the output. - if (!_copyDepthShader) - { - _copyDepthShader = - std::make_unique(hgi, "Copy Depth Buffer"); - - // Configure the shader to handle depth texture. - HgiShaderFunctionDesc shaderDesc; - shaderDesc.debugName = "Copy Depth Shader"; - shaderDesc.shaderStage = HgiShaderStageFragment; - HgiShaderFunctionAddStageInput(&shaderDesc, "uvOut", "vec2"); - HgiShaderFunctionAddTexture(&shaderDesc, "depthIn", 0); - HgiShaderFunctionAddStageOutput(&shaderDesc, "gl_FragDepth", "float", "depth(any) "); - HgiShaderFunctionAddConstantParam(&shaderDesc, "screenSize", "vec2"); - static const TfToken copyDepthShaderPath { GetShaderPath("copyDepth.glslfx").generic_u8string() }; - static const TfToken copyDepthToken("CopyDepthFragment"); + // Note: HdxFullscreenShader must include the color AOV so it cannot be used here + // because the code only needs to copy the depth AOV. - _copyDepthShader->SetProgram(copyDepthShaderPath, copyDepthToken, shaderDesc); + if (!_copyDepthShader) + { + _copyDepthShader = std::make_unique(hgi); } - HdxFullscreenShader* shader = _copyDepthShader.get(); - - // Set the screen size constant on the shader. - GfVec2f screenSize { static_cast(desc.dimensions[0]), - static_cast(desc.dimensions[1]) }; - shader->SetShaderConstants(sizeof(screenSize), &screenSize); - - // Submit the layout change to read from the texture. - input->SubmitLayoutChange(HgiTextureUsageBitsShaderRead); - - shader->BindTextures({ input }); - shader->Draw(HgiTextureHandle(), output); - - input->SubmitLayoutChange(HgiTextureUsageBitsDepthTarget); + // Copy the input to the output texture. + _copyDepthShader->Execute(input, output); } bool RenderBufferManager::Impl::SetRenderOutputs(TfTokenVector const& outputs, From 20e7b70f70538de026ba9ce971c0aff926a4b519 Mon Sep 17 00:00:00 2001 From: Patrick Hodoul Date: Wed, 26 Nov 2025 17:38:17 -0500 Subject: [PATCH 03/20] Improve depth-depth case Signed-off-by: Patrick Hodoul --- source/engine/copyDepthShader.cpp | 5 ++++- source/engine/renderBufferManager.cpp | 22 ++++++++++++---------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/source/engine/copyDepthShader.cpp b/source/engine/copyDepthShader.cpp index 24a4a239..c358eedd 100644 --- a/source/engine/copyDepthShader.cpp +++ b/source/engine/copyDepthShader.cpp @@ -333,7 +333,10 @@ void CopyDepthShader::Execute( { _inputTexture->SubmitLayoutChange(HgiTextureUsageBitsShaderRead); } - ~Guard() { _inputTexture->SubmitLayoutChange(HgiTextureUsageBitsDepthTarget); } + ~Guard() + { + _inputTexture->SubmitLayoutChange(HgiTextureUsageBitsDepthTarget); + } private: HgiTextureHandle const& _inputTexture; diff --git a/source/engine/renderBufferManager.cpp b/source/engine/renderBufferManager.cpp index de4b2699..7d148883 100644 --- a/source/engine/renderBufferManager.cpp +++ b/source/engine/renderBufferManager.cpp @@ -182,7 +182,7 @@ class RenderBufferManager::Impl : public RenderBufferSettingsProvider SdfPath const& controllerId); /// Copy the depth AOV of the input buffer into the output buffer. - void PrepareBufferFromInput(RenderBufferBinding const& input, + void PrepareDepthOnlyFromInput(RenderBufferBinding const& inputDepthAov, HdRenderBufferDescriptor const& desc, SdfPath const& controllerId); @@ -449,20 +449,22 @@ void RenderBufferManager::Impl::PrepareBuffersFromInputs(RenderBufferBinding con colorInput->SubmitLayoutChange(HgiTextureUsageBitsColorTarget); } -void RenderBufferManager::Impl::PrepareBufferFromInput(RenderBufferBinding const& inputAov, +// The code does not use the HdxFullscreenShader helper here because it only needs to copy the depth AOVs +// and HdxFullscreenShader always needs the color AOVs. +void RenderBufferManager::Impl::PrepareDepthOnlyFromInput(RenderBufferBinding const& inputDepthAov, HdRenderBufferDescriptor const& desc, SdfPath const& controllerId) { HD_TRACE_FUNCTION(); HF_MALLOC_TAG_FUNCTION(); - HgiTextureHandle input = inputAov.texture; + HgiTextureHandle input = inputDepthAov.texture; if (!input) { return; } - const SdfPath aovPath = GetAovPath(controllerId, inputAov.aovName); + const SdfPath aovPath = GetAovPath(controllerId, inputDepthAov.aovName); // Get the buffer that the renderer will draw into from the render index. HdRenderBuffer* buffer = static_cast( _pRenderIndex->GetBprim(HdPrimTypeTokens->renderBuffer, aovPath)); @@ -472,7 +474,7 @@ void RenderBufferManager::Impl::PrepareBufferFromInput(RenderBufferBinding const if (!buffer) { // Use the input buffer - buffer = inputAov.buffer; + buffer = inputDepthAov.buffer; } else { @@ -491,7 +493,7 @@ void RenderBufferManager::Impl::PrepareBufferFromInput(RenderBufferBinding const if (!output) { TF_CODING_ERROR("The output render buffer does not have a valid texture %s.", - inputAov.aovName.GetText()); + inputDepthAov.aovName.GetText()); return; } } @@ -673,15 +675,15 @@ bool RenderBufferManager::Impl::SetRenderOutputs(TfTokenVector const& outputs, // Copy the AOV to visualize i.e, be careful that's not always the color one! - // Color AOV always means color & depth AOVs. + // Color AOV always means color & depth AOVs (where depth is optional). if (outputs[0] == pxr::HdAovTokens->color && colorInput.texture) { PrepareBuffersFromInputs(colorInput, depthInput, colorDesc, controllerId); } - // But depth AOV only means depth AOV :-) - if (outputs[0] == pxr::HdAovTokens->depth && depthInput.texture) + // But depth AOV only means depth AOV only. + else if (outputs[0] == pxr::HdAovTokens->depth && depthInput.texture) { - PrepareBufferFromInput(depthInput, depthDesc, controllerId); + PrepareDepthOnlyFromInput(depthInput, depthDesc, controllerId); } // Create the list of AOV bindings. From 1e7f986743686ff0185d0953c3e93e75ceffdf53 Mon Sep 17 00:00:00 2001 From: Patrick Hodoul Date: Wed, 26 Nov 2025 19:38:55 -0500 Subject: [PATCH 04/20] Update baseline images Signed-off-by: Patrick Hodoul --- .../baselines/compose_ComposeTask2_osx.png | Bin 7088 -> 6380 bytes .../baselines/compose_ComposeTask3_osx.png | Bin 6980 -> 3428 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/test/data/baselines/compose_ComposeTask2_osx.png b/test/data/baselines/compose_ComposeTask2_osx.png index fc27f9b6e56fb2ca5e3de3eabfe0449fa2f1bd0d..f7d595d439d34bdc0f4a67ebb616bb662da67e9f 100644 GIT binary patch delta 4516 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 S@{JgX>01-FF|>)vm;4_&_MGSd delta 5248 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 diff --git a/test/data/baselines/compose_ComposeTask3_osx.png b/test/data/baselines/compose_ComposeTask3_osx.png index 9e306d743c04e3e1618647d1bc50c5cc380ee010..35102f317016ac88dc8387083cdb6b6aa201b13b 100644 GIT binary patch delta 1190 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 lIrL4`iOm?yiIu2oJi_A~7 Date: Thu, 27 Nov 2025 11:43:22 -0500 Subject: [PATCH 05/20] Few changes Signed-off-by: Patrick Hodoul --- source/engine/renderBufferManager.cpp | 3 ++- .../baselines/compose_ComposeTask2_osx.png | Bin 6380 -> 7088 bytes .../baselines/compose_ComposeTask3_osx.png | Bin 3428 -> 6980 bytes test/tests/composeTaskHelpers.cpp | 11 ++++++----- test/tests/composeTaskHelpers.h | 2 +- test/tests/testComposeTask.cpp | 16 ++++++++++------ 6 files changed, 19 insertions(+), 13 deletions(-) diff --git a/source/engine/renderBufferManager.cpp b/source/engine/renderBufferManager.cpp index 7d148883..78a8e45d 100644 --- a/source/engine/renderBufferManager.cpp +++ b/source/engine/renderBufferManager.cpp @@ -673,7 +673,8 @@ bool RenderBufferManager::Impl::SetRenderOutputs(TfTokenVector const& outputs, } } - // Copy the AOV to visualize i.e, be careful that's not always the color one! + // In case, we want to share the AOV buffers between frame passes but they are from different render delegates, + // we then need to copy the AOV to visualize. But be careful that's not always the color one we visualize. // Color AOV always means color & depth AOVs (where depth is optional). if (outputs[0] == pxr::HdAovTokens->color && colorInput.texture) diff --git a/test/data/baselines/compose_ComposeTask2_osx.png b/test/data/baselines/compose_ComposeTask2_osx.png index f7d595d439d34bdc0f4a67ebb616bb662da67e9f..fc27f9b6e56fb2ca5e3de3eabfe0449fa2f1bd0d 100644 GIT binary patch delta 5248 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 delta 4516 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 S@{JgX>01-FF|>)vm;4_&_MGSd diff --git a/test/data/baselines/compose_ComposeTask3_osx.png b/test/data/baselines/compose_ComposeTask3_osx.png index 35102f317016ac88dc8387083cdb6b6aa201b13b..9e306d743c04e3e1618647d1bc50c5cc380ee010 100644 GIT binary patch delta 5119 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 @@ -119,10 +120,10 @@ void RenderSecondFramePass(TestHelpers::FramePassInstance& framePass2, int width params.colorspace = HdxColorCorrectionTokens->disabled; params.selectionColor = TestHelpers::ColorYellow; - params.clearBackgroundColor = clearBackground; + params.clearBackgroundColor = clearColorBackground; params.backgroundColor = TestHelpers::ColorDarkGrey; - params.clearBackgroundDepth = clearBackground; + params.clearBackgroundDepth = clearDepthBackground; params.backgroundDepth = 1.0f; params.enablePresentation = enablePresentTask; @@ -134,4 +135,4 @@ void RenderSecondFramePass(TestHelpers::FramePassInstance& framePass2, int width framePass2.sceneFramePass->Render(renderTasks); } -} // namespace TestHelpers \ No newline at end of file +} // namespace TestHelpers diff --git a/test/tests/composeTaskHelpers.h b/test/tests/composeTaskHelpers.h index 44a6b9b9..934eca74 100644 --- a/test/tests/composeTaskHelpers.h +++ b/test/tests/composeTaskHelpers.h @@ -35,6 +35,6 @@ void RenderFirstFramePass(TestHelpers::FramePassInstance& framePass1, int width, // Renders the second frame pass which also display the result. void RenderSecondFramePass(TestHelpers::FramePassInstance& framePass2, int width, int height, bool enablePresentTask, TestHelpers::TestStage const& stage, - hvt::RenderBufferBindings const& inputAOVs, bool clearBackground = true); + hvt::RenderBufferBindings const& inputAOVs, bool clearColorBackground = true, bool clearDepthBackground = false); } // namespace TestHelpers diff --git a/test/tests/testComposeTask.cpp b/test/tests/testComposeTask.cpp index 71d3e2bd..20512e52 100644 --- a/test/tests/testComposeTask.cpp +++ b/test/tests/testComposeTask.cpp @@ -255,7 +255,7 @@ HVT_TEST(TestViewportToolbox, compose_ComposeTask2) { // This unit test uses the 'Storm' render delegate for the two frame passes, to demonstrate that // the compose task works. But the first frame pass displays the bounding box of the model and - // the second one displays the model + // the second one displays the model. auto context = TestHelpers::CreateTestContext(); @@ -295,9 +295,9 @@ HVT_TEST(TestViewportToolbox, compose_ComposeTask2) // Defines the second frame pass using the Storm render delegate. framePass2 = TestHelpers::FramePassInstance::CreateInstance( - "HdStormRendererPlugin", stage.stage(), context->_backend); + "HdStormRendererPlugin", stage.stage(), context->_backend, "/sceneFramePass2"); - // Adds the 'Compose' task to the second frame pass. + // Adds the 'Compose' task to the second frame pass i.e., compose the color AOV. TestHelpers::AddComposeTask(framePass1, framePass2); @@ -316,11 +316,13 @@ HVT_TEST(TestViewportToolbox, compose_ComposeTask2) // depth buffer (because depth has always the same bit depth i.e., 32-bit float for all // the render delegates). + // No need to share the color AOV as the ComposeTask will take care of it. + auto& pass = framePass1.sceneFramePass; hvt::RenderBufferBindings inputAOVs = pass->GetRenderBufferBindingsForNextPass({ pxr::HdAovTokens->depth }); TestHelpers::RenderSecondFramePass( - framePass2, context->width(), context->height(), context->presentationEnabled(), + framePass2, context->width(), context->height(), context->presentationEnabled(), stage, inputAOVs); return --frameCount > 0; @@ -387,7 +389,7 @@ HVT_TEST(TestViewportToolbox, compose_ComposeTask3) passDesc.uid = SdfPath("/sceneFramePass2"); framePass2.sceneFramePass = hvt::ViewportEngine::CreateFramePass(passDesc); - // Adds the 'Compose' task to the second frame pass. + // Adds the 'Compose' task to the second frame pass i.e., compose the color AOV. TestHelpers::AddComposeTask(framePass1, framePass2); } @@ -407,12 +409,14 @@ HVT_TEST(TestViewportToolbox, compose_ComposeTask3) // depth buffer (because depth has always the same bit depth i.e., 32-bit float for all // the render delegates). + // No need to share the color AOV as the ComposeTask will take care of it. + auto& pass = framePass1.sceneFramePass; hvt::RenderBufferBindings inputAOVs = pass->GetRenderBufferBindingsForNextPass( { pxr::HdAovTokens->depth }); TestHelpers::RenderSecondFramePass( - framePass2, context->width(), context->height(), context->presentationEnabled(), + framePass2, context->width(), context->height(), context->presentationEnabled(), stage, inputAOVs); return --frameCount > 0; From bbc6c7b6069b19b29e43e4d30ccb678dd81beaf8 Mon Sep 17 00:00:00 2001 From: Patrick Hodoul Date: Thu, 27 Nov 2025 14:00:29 -0500 Subject: [PATCH 06/20] Improve utests Signed-off-by: Patrick Hodoul --- test/tests/composeTaskHelpers.cpp | 15 ++++++++------- test/tests/composeTaskHelpers.h | 4 ++-- test/tests/testComposeTask.cpp | 24 +++++++++++++++++------- 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/test/tests/composeTaskHelpers.cpp b/test/tests/composeTaskHelpers.cpp index 0c82279c..8a4710ba 100644 --- a/test/tests/composeTaskHelpers.cpp +++ b/test/tests/composeTaskHelpers.cpp @@ -67,10 +67,10 @@ void AddComposeTask( } // Renders the first frame pass i.e., do not display it and let the next frame pass doing it. -void RenderFirstFramePass(TestHelpers::FramePassInstance& framePass1, int width, int height, +void RenderFirstFramePass(TestHelpers::FramePassInstance& framePass, int width, int height, TestHelpers::TestStage const& stage) { - hvt::FramePassParams& params = framePass1.sceneFramePass->params(); + hvt::FramePassParams& params = framePass.sceneFramePass->params(); params.renderBufferSize = GfVec2i(width, height); params.viewInfo.framing = @@ -95,17 +95,18 @@ void RenderFirstFramePass(TestHelpers::FramePassInstance& framePass1, int width, params.enablePresentation = false; // Renders the frame pass. - framePass1.sceneFramePass->Render(); + framePass.sceneFramePass->Render(); } // Renders the second frame pass which also display the result. -void RenderSecondFramePass(TestHelpers::FramePassInstance& framePass2, int width, +void RenderSecondFramePass(TestHelpers::FramePassInstance& framePass, int width, int height, bool enablePresentTask, TestHelpers::TestStage const& stage, hvt::RenderBufferBindings const& inputAOVs, bool clearColorBackground /*= true*/, bool clearDepthBackground /*= false*/) { - auto& params = framePass2.sceneFramePass->params(); + auto& pass = framePass.sceneFramePass; + auto& params = pass->params(); params.renderBufferSize = GfVec2i(width, height); params.viewInfo.framing = @@ -130,9 +131,9 @@ void RenderSecondFramePass(TestHelpers::FramePassInstance& framePass2, int width // Gets the list of tasks to render but use the render buffers from the first frame // pass. - const HdTaskSharedPtrVector renderTasks = framePass2.sceneFramePass->GetRenderTasks(inputAOVs); + const HdTaskSharedPtrVector renderTasks = pass->GetRenderTasks(inputAOVs); - framePass2.sceneFramePass->Render(renderTasks); + pass->Render(renderTasks); } } // namespace TestHelpers diff --git a/test/tests/composeTaskHelpers.h b/test/tests/composeTaskHelpers.h index 934eca74..b99c8072 100644 --- a/test/tests/composeTaskHelpers.h +++ b/test/tests/composeTaskHelpers.h @@ -29,11 +29,11 @@ void AddComposeTask( TestHelpers::FramePassInstance const& framePass1, TestHelpers::FramePassInstance& framePass2); // Renders the first frame pass i.e., do not display it and let the next frame pass doing it. -void RenderFirstFramePass(TestHelpers::FramePassInstance& framePass1, int width, int height, +void RenderFirstFramePass(TestHelpers::FramePassInstance& framePass, int width, int height, TestHelpers::TestStage const& stage); // Renders the second frame pass which also display the result. -void RenderSecondFramePass(TestHelpers::FramePassInstance& framePass2, int width, int height, +void RenderSecondFramePass(TestHelpers::FramePassInstance& framePass, int width, int height, bool enablePresentTask, TestHelpers::TestStage const& stage, hvt::RenderBufferBindings const& inputAOVs, bool clearColorBackground = true, bool clearDepthBackground = false); diff --git a/test/tests/testComposeTask.cpp b/test/tests/testComposeTask.cpp index 20512e52..977792e2 100644 --- a/test/tests/testComposeTask.cpp +++ b/test/tests/testComposeTask.cpp @@ -245,9 +245,10 @@ HVT_TEST(TestViewportToolbox, compose_ShareTextures) // Note: The 'Bounding Box' is used (instead of the 'WireFrame' one) because it works on all // desktop platforms. -// Disabled for iOS as the result is not stable. Refer to OGSMOD-7344 -// Disabled for Android due to baseline inconsistancy between runners. Refer to OGSMOD-8067 -#if TARGET_OS_IPHONE == 1 || defined(__ANDROID__) +// OGSMOD-7344: Disabled for iOS as the result is not stable. +// OGSMOD-8067: Disabled for Android due to baseline inconsistency between runs. +// OGSMOD-8303: Disabled for origin/dev as the ComposeTask now fails following the last USD commit. +#if TARGET_OS_IPHONE == 1 || defined(__ANDROID__) || !defined(ADSK_OPENUSD_PENDING) HVT_TEST(TestViewportToolbox, DISABLED_compose_ComposeTask2) #else HVT_TEST(TestViewportToolbox, compose_ComposeTask2) @@ -318,7 +319,11 @@ HVT_TEST(TestViewportToolbox, compose_ComposeTask2) // No need to share the color AOV as the ComposeTask will take care of it. - auto& pass = framePass1.sceneFramePass; + auto& pass = framePass1.sceneFramePass; + + // NoAlpha is mandatory for the alpha blending. + pass->params().backgroundColor = TestHelpers::ColorBlackNoAlpha; + hvt::RenderBufferBindings inputAOVs = pass->GetRenderBufferBindingsForNextPass({ pxr::HdAovTokens->depth }); TestHelpers::RenderSecondFramePass( @@ -338,9 +343,10 @@ HVT_TEST(TestViewportToolbox, compose_ComposeTask2) ASSERT_TRUE(context->validateImages(computedImagePath, TestHelpers::gTestNames.fixtureName)); } -// Disabled for iOS as the result is not stable. Refer to OGSMOD-7344 -// Disabled for Android due to baseline inconsistancy between runners. Refer to OGSMOD-8067 -#if TARGET_OS_IPHONE == 1 || defined(__ANDROID__) +// OGSMOD-7344: Disabled for iOS as the result is not stable. +// OGSMOD-8067: Disabled for Android due to baseline inconsistency between runs. +// OGSMOD-8303: Disabled for origin/dev as the ComposeTask now fails following the last USD commit. +#if TARGET_OS_IPHONE == 1 || defined(__ANDROID__) || !defined(ADSK_OPENUSD_PENDING) HVT_TEST(TestViewportToolbox, DISABLED_compose_ComposeTask3) #else HVT_TEST(TestViewportToolbox, compose_ComposeTask3) @@ -412,6 +418,10 @@ HVT_TEST(TestViewportToolbox, compose_ComposeTask3) // No need to share the color AOV as the ComposeTask will take care of it. auto& pass = framePass1.sceneFramePass; + + // NoAlpha is mandatory for the alpha blending. + pass->params().backgroundColor = TestHelpers::ColorBlackNoAlpha; + hvt::RenderBufferBindings inputAOVs = pass->GetRenderBufferBindingsForNextPass( { pxr::HdAovTokens->depth }); From 9c011b1db81b7c8a9fc5f70cb76cb598d0a6e91e Mon Sep 17 00:00:00 2001 From: Patrick Hodoul Date: Thu, 27 Nov 2025 15:26:34 -0500 Subject: [PATCH 07/20] Fix compose issue Signed-off-by: Patrick Hodoul --- test/tests/composeTaskHelpers.cpp | 28 +++++++--------- test/tests/composeTaskHelpers.h | 3 +- test/tests/testComposeTask.cpp | 56 ++++++++++++++++--------------- 3 files changed, 44 insertions(+), 43 deletions(-) diff --git a/test/tests/composeTaskHelpers.cpp b/test/tests/composeTaskHelpers.cpp index 8a4710ba..3bedde4f 100644 --- a/test/tests/composeTaskHelpers.cpp +++ b/test/tests/composeTaskHelpers.cpp @@ -43,7 +43,8 @@ void AddComposeTask( // Create the fnCommit for the compose task. auto fnCommit = [&](hvt::TaskManager::GetTaskValueFn const& fnGetValue, - hvt::TaskManager::SetTaskValueFn const& fnSetValue) { + hvt::TaskManager::SetTaskValueFn const& fnSetValue) + { const VtValue value = fnGetValue(HdTokens->params); hvt::ComposeTaskParams params = value.Get(); @@ -73,8 +74,7 @@ void RenderFirstFramePass(TestHelpers::FramePassInstance& framePass, int width, hvt::FramePassParams& params = framePass.sceneFramePass->params(); params.renderBufferSize = GfVec2i(width, height); - params.viewInfo.framing = - hvt::ViewParams::GetDefaultFraming(width, height); + params.viewInfo.framing = hvt::ViewParams::GetDefaultFraming(width, height); params.viewInfo.viewMatrix = stage.viewMatrix(); params.viewInfo.projectionMatrix = stage.projectionMatrix(); @@ -82,8 +82,8 @@ void RenderFirstFramePass(TestHelpers::FramePassInstance& framePass, int width, params.viewInfo.material = stage.defaultMaterial(); params.viewInfo.ambient = stage.defaultAmbient(); - params.colorspace = HdxColorCorrectionTokens->disabled; - params.selectionColor = TestHelpers::ColorYellow; + params.colorspace = HdxColorCorrectionTokens->disabled; + params.selectionColor = TestHelpers::ColorYellow; params.clearBackgroundColor = true; params.backgroundColor = TestHelpers::ColorDarkGrey; @@ -99,18 +99,16 @@ void RenderFirstFramePass(TestHelpers::FramePassInstance& framePass, int width, } // Renders the second frame pass which also display the result. -void RenderSecondFramePass(TestHelpers::FramePassInstance& framePass, int width, - int height, bool enablePresentTask, TestHelpers::TestStage const& stage, - hvt::RenderBufferBindings const& inputAOVs, - bool clearColorBackground /*= true*/, - bool clearDepthBackground /*= false*/) +void RenderSecondFramePass(TestHelpers::FramePassInstance& framePass, int width, int height, + bool enablePresentTask, TestHelpers::TestStage const& stage, + hvt::RenderBufferBindings const& inputAOVs, bool clearColorBackground, + pxr::GfVec4f const& colorBackground, bool clearDepthBackground) { auto& pass = framePass.sceneFramePass; auto& params = pass->params(); params.renderBufferSize = GfVec2i(width, height); - params.viewInfo.framing = - hvt::ViewParams::GetDefaultFraming(width, height); + params.viewInfo.framing = hvt::ViewParams::GetDefaultFraming(width, height); params.viewInfo.viewMatrix = stage.viewMatrix(); params.viewInfo.projectionMatrix = stage.projectionMatrix(); @@ -118,11 +116,11 @@ void RenderSecondFramePass(TestHelpers::FramePassInstance& framePass, int width, params.viewInfo.material = stage.defaultMaterial(); params.viewInfo.ambient = stage.defaultAmbient(); - params.colorspace = HdxColorCorrectionTokens->disabled; - params.selectionColor = TestHelpers::ColorYellow; + params.colorspace = HdxColorCorrectionTokens->disabled; + params.selectionColor = TestHelpers::ColorYellow; params.clearBackgroundColor = clearColorBackground; - params.backgroundColor = TestHelpers::ColorDarkGrey; + params.backgroundColor = colorBackground; params.clearBackgroundDepth = clearDepthBackground; params.backgroundDepth = 1.0f; diff --git a/test/tests/composeTaskHelpers.h b/test/tests/composeTaskHelpers.h index b99c8072..cbebff9b 100644 --- a/test/tests/composeTaskHelpers.h +++ b/test/tests/composeTaskHelpers.h @@ -35,6 +35,7 @@ void RenderFirstFramePass(TestHelpers::FramePassInstance& framePass, int width, // Renders the second frame pass which also display the result. void RenderSecondFramePass(TestHelpers::FramePassInstance& framePass, int width, int height, bool enablePresentTask, TestHelpers::TestStage const& stage, - hvt::RenderBufferBindings const& inputAOVs, bool clearColorBackground = true, bool clearDepthBackground = false); + hvt::RenderBufferBindings const& inputAOVs, bool clearColorBackground, + pxr::GfVec4f const& colorBackground, bool clearDepthBackground); } // namespace TestHelpers diff --git a/test/tests/testComposeTask.cpp b/test/tests/testComposeTask.cpp index 977792e2..1c7225cb 100644 --- a/test/tests/testComposeTask.cpp +++ b/test/tests/testComposeTask.cpp @@ -79,7 +79,8 @@ HVT_TEST(TestViewportToolbox, compose_ComposeTask) // Render 10 times (i.e., arbitrary number to guaranty best result). int frameCount = 10; - auto render = [&]() { + auto render = [&]() + { TestHelpers::RenderFirstFramePass(framePass1, context->width(), context->height(), stage); // Force GPU sync. Wait for all GPU commands to complete before proceeding. @@ -90,18 +91,18 @@ HVT_TEST(TestViewportToolbox, compose_ComposeTask) // Gets the depth from the first frame pass and use it so the overlays draw into the same // depth buffer (because depth has always the same bit depth i.e., 32-bit float for all // the render delegates). - auto& pass = framePass1.sceneFramePass; - hvt::RenderBufferBindings inputAOVs = pass->GetRenderBufferBindingsForNextPass( - { pxr::HdAovTokens->depth }); + auto& pass = framePass1.sceneFramePass; + hvt::RenderBufferBindings inputAOVs = + pass->GetRenderBufferBindingsForNextPass({ pxr::HdAovTokens->depth }); { hvt::FramePassParams& params = framePass2.sceneFramePass->params(); - params.renderBufferSize = GfVec2i(context->width(), context->height()); + params.renderBufferSize = GfVec2i(context->width(), context->height()); params.viewInfo.framing = hvt::ViewParams::GetDefaultFraming(context->width(), context->height()); - params.viewInfo.viewMatrix = stage.viewMatrix(); + params.viewInfo.viewMatrix = stage.viewMatrix(); params.viewInfo.projectionMatrix = stage.projectionMatrix(); params.viewInfo.lights = stage.defaultLights(); params.viewInfo.material = stage.defaultMaterial(); @@ -174,7 +175,8 @@ HVT_TEST(TestViewportToolbox, compose_ShareTextures) // Render 10 times (i.e., arbitrary number to guaranty best result). int frameCount = 10; - auto render = [&]() { + auto render = [&]() + { TestHelpers::RenderFirstFramePass(framePass1, context->width(), context->height(), stage); // Force GPU sync. Wait for all GPU commands to complete before proceeding. @@ -192,11 +194,11 @@ HVT_TEST(TestViewportToolbox, compose_ShareTextures) { hvt::FramePassParams& params = framePass2.sceneFramePass->params(); - params.renderBufferSize = GfVec2i(context->width(), context->height()); + params.renderBufferSize = GfVec2i(context->width(), context->height()); params.viewInfo.framing = hvt::ViewParams::GetDefaultFraming(context->width(), context->height()); - params.viewInfo.viewMatrix = stage.viewMatrix(); + params.viewInfo.viewMatrix = stage.viewMatrix(); params.viewInfo.projectionMatrix = stage.projectionMatrix(); params.viewInfo.lights = stage.defaultLights(); params.viewInfo.material = stage.defaultMaterial(); @@ -247,8 +249,7 @@ HVT_TEST(TestViewportToolbox, compose_ShareTextures) // OGSMOD-7344: Disabled for iOS as the result is not stable. // OGSMOD-8067: Disabled for Android due to baseline inconsistency between runs. -// OGSMOD-8303: Disabled for origin/dev as the ComposeTask now fails following the last USD commit. -#if TARGET_OS_IPHONE == 1 || defined(__ANDROID__) || !defined(ADSK_OPENUSD_PENDING) +#if TARGET_OS_IPHONE == 1 || defined(__ANDROID__) HVT_TEST(TestViewportToolbox, DISABLED_compose_ComposeTask2) #else HVT_TEST(TestViewportToolbox, compose_ComposeTask2) @@ -305,7 +306,8 @@ HVT_TEST(TestViewportToolbox, compose_ComposeTask2) // Render 10 times (i.e., arbitrary number to guarantee best result). int frameCount = 10; - auto render = [&]() { + auto render = [&]() + { TestHelpers::RenderFirstFramePass(framePass1, context->width(), context->height(), stage); // Force GPU sync. Wait for all GPU commands to complete before proceeding. @@ -326,9 +328,9 @@ HVT_TEST(TestViewportToolbox, compose_ComposeTask2) hvt::RenderBufferBindings inputAOVs = pass->GetRenderBufferBindingsForNextPass({ pxr::HdAovTokens->depth }); - TestHelpers::RenderSecondFramePass( - framePass2, context->width(), context->height(), context->presentationEnabled(), - stage, inputAOVs); + TestHelpers::RenderSecondFramePass(framePass2, context->width(), context->height(), + context->presentationEnabled(), stage, inputAOVs, true, TestHelpers::ColorBlackNoAlpha, + false); return --frameCount > 0; }; @@ -345,8 +347,7 @@ HVT_TEST(TestViewportToolbox, compose_ComposeTask2) // OGSMOD-7344: Disabled for iOS as the result is not stable. // OGSMOD-8067: Disabled for Android due to baseline inconsistency between runs. -// OGSMOD-8303: Disabled for origin/dev as the ComposeTask now fails following the last USD commit. -#if TARGET_OS_IPHONE == 1 || defined(__ANDROID__) || !defined(ADSK_OPENUSD_PENDING) +#if TARGET_OS_IPHONE == 1 || defined(__ANDROID__) HVT_TEST(TestViewportToolbox, DISABLED_compose_ComposeTask3) #else HVT_TEST(TestViewportToolbox, compose_ComposeTask3) @@ -403,7 +404,8 @@ HVT_TEST(TestViewportToolbox, compose_ComposeTask3) // Render 10 times (i.e., arbitrary number to guarantee best result). int frameCount = 10; - auto render = [&]() { + auto render = [&]() + { TestHelpers::RenderFirstFramePass(framePass1, context->width(), context->height(), stage); // Force GPU sync. Wait for all GPU commands to complete before proceeding. @@ -422,12 +424,12 @@ HVT_TEST(TestViewportToolbox, compose_ComposeTask3) // NoAlpha is mandatory for the alpha blending. pass->params().backgroundColor = TestHelpers::ColorBlackNoAlpha; - hvt::RenderBufferBindings inputAOVs = pass->GetRenderBufferBindingsForNextPass( - { pxr::HdAovTokens->depth }); + hvt::RenderBufferBindings inputAOVs = + pass->GetRenderBufferBindingsForNextPass({ pxr::HdAovTokens->depth }); - TestHelpers::RenderSecondFramePass( - framePass2, context->width(), context->height(), context->presentationEnabled(), - stage, inputAOVs); + TestHelpers::RenderSecondFramePass(framePass2, context->width(), context->height(), + context->presentationEnabled(), stage, inputAOVs, true, TestHelpers::ColorBlackNoAlpha, + false); return --frameCount > 0; }; @@ -495,7 +497,8 @@ HVT_TEST(TestViewportToolbox, compose_ShareTextures4) // Render 10 times (i.e., arbitrary number to guarantee best result). int frameCount = 10; - auto render = [&]() { + auto render = [&]() + { TestHelpers::RenderFirstFramePass(framePass1, context->width(), context->height(), stage); // Force GPU sync. Wait for all GPU commands to complete before proceeding. @@ -512,9 +515,8 @@ HVT_TEST(TestViewportToolbox, compose_ShareTextures4) // When sharing the render buffers, do not clear the background as it contains the rendering // result of the previous frame pass. - TestHelpers::RenderSecondFramePass( - framePass2, context->width(), context->height(), context->presentationEnabled(), - stage, inputAOVs, false); + TestHelpers::RenderSecondFramePass(framePass2, context->width(), context->height(), + context->presentationEnabled(), stage, inputAOVs, false); return --frameCount > 0; }; From dfce6dd7da021c56787b56af616f96a5f24cd1d7 Mon Sep 17 00:00:00 2001 From: Patrick Hodoul Date: Thu, 27 Nov 2025 15:39:24 -0500 Subject: [PATCH 08/20] Fix ComposeTask Signed-off-by: Patrick Hodoul --- test/tests/testComposeTask.cpp | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/test/tests/testComposeTask.cpp b/test/tests/testComposeTask.cpp index 1c7225cb..8efdd8e2 100644 --- a/test/tests/testComposeTask.cpp +++ b/test/tests/testComposeTask.cpp @@ -320,14 +320,10 @@ HVT_TEST(TestViewportToolbox, compose_ComposeTask2) // the render delegates). // No need to share the color AOV as the ComposeTask will take care of it. - - auto& pass = framePass1.sceneFramePass; - - // NoAlpha is mandatory for the alpha blending. - pass->params().backgroundColor = TestHelpers::ColorBlackNoAlpha; - hvt::RenderBufferBindings inputAOVs = - pass->GetRenderBufferBindingsForNextPass({ pxr::HdAovTokens->depth }); + framePass1.sceneFramePass->GetRenderBufferBindingsForNextPass({ pxr::HdAovTokens->depth }); + + // NoAlpha mandatory for the blending used by ComposeTask. TestHelpers::RenderSecondFramePass(framePass2, context->width(), context->height(), context->presentationEnabled(), stage, inputAOVs, true, TestHelpers::ColorBlackNoAlpha, false); @@ -418,15 +414,10 @@ HVT_TEST(TestViewportToolbox, compose_ComposeTask3) // the render delegates). // No need to share the color AOV as the ComposeTask will take care of it. - - auto& pass = framePass1.sceneFramePass; - - // NoAlpha is mandatory for the alpha blending. - pass->params().backgroundColor = TestHelpers::ColorBlackNoAlpha; - hvt::RenderBufferBindings inputAOVs = - pass->GetRenderBufferBindingsForNextPass({ pxr::HdAovTokens->depth }); + framePass1.sceneFramePass->GetRenderBufferBindingsForNextPass({ pxr::HdAovTokens->depth }); + // NoAlpha mandatory for the blending used by ComposeTask. TestHelpers::RenderSecondFramePass(framePass2, context->width(), context->height(), context->presentationEnabled(), stage, inputAOVs, true, TestHelpers::ColorBlackNoAlpha, false); @@ -516,7 +507,7 @@ HVT_TEST(TestViewportToolbox, compose_ShareTextures4) // When sharing the render buffers, do not clear the background as it contains the rendering // result of the previous frame pass. TestHelpers::RenderSecondFramePass(framePass2, context->width(), context->height(), - context->presentationEnabled(), stage, inputAOVs, false); + context->presentationEnabled(), stage, inputAOVs, false, TestHelpers::ColorDarkGrey, false); return --frameCount > 0; }; From 144737552098a1ec16836843b7e011d5aa5d08b9 Mon Sep 17 00:00:00 2001 From: Patrick Hodoul Date: Fri, 28 Nov 2025 16:39:31 -0500 Subject: [PATCH 09/20] Simplify the CopyDepthShader code Signed-off-by: Patrick Hodoul --- source/engine/copyDepthShader.cpp | 112 ++++++------------------------ source/engine/copyDepthShader.h | 4 +- 2 files changed, 22 insertions(+), 94 deletions(-) diff --git a/source/engine/copyDepthShader.cpp b/source/engine/copyDepthShader.cpp index c358eedd..f18ae5dd 100644 --- a/source/engine/copyDepthShader.cpp +++ b/source/engine/copyDepthShader.cpp @@ -20,6 +20,7 @@ #include #include #include +#include PXR_NAMESPACE_USING_DIRECTIVE @@ -30,13 +31,14 @@ namespace { static const std::string vsCode = R"( void main(void) { - gl_Position = position; - uvOut = uvIn; + uvOut = vec2((hd_VertexID << 1) & 2, hd_VertexID & 2); + gl_Position = vec4(uvOut * 2.0f + -1.0f, 0.0f, 1.0f); })"; static const std::string fsCode = R"( void main(void) { - float depth = HgiTexelFetch_depthIn(ivec2(uvOut * screenSize)).r; + vec2 fragCoord = uvOut * screenSize; + float depth = HgiTexelFetch_depthIn(ivec2(fragCoord)).x; gl_FragDepth = depth; })"; @@ -54,7 +56,7 @@ CopyDepthShader::~CopyDepthShader() _Cleanup(); } -bool CopyDepthShader::_CreateShaderProgram() +bool CopyDepthShader::_CreateShaderProgram(HgiTextureDesc const& inputTextureDesc) { if (_shaderProgram) { @@ -66,9 +68,11 @@ bool CopyDepthShader::_CreateShaderProgram() HgiShaderFunctionDesc vertDesc; vertDesc.debugName = "CopyDepthShader Vertex"; vertDesc.shaderStage = HgiShaderStageVertex; - HgiShaderFunctionAddStageInput(&vertDesc, "position", "vec4"); - HgiShaderFunctionAddStageInput(&vertDesc, "uvIn", "vec2"); + + HgiShaderFunctionAddStageInput( + &vertDesc, "hd_VertexID", "uint", HgiShaderKeywordTokens->hdVertexID); HgiShaderFunctionAddStageOutput(&vertDesc, "gl_Position", "vec4", "position"); + HgiShaderFunctionAddStageOutput(&vertDesc, "uvOut", "vec2"); vertDesc.shaderCode = vsCode.c_str(); @@ -87,8 +91,13 @@ bool CopyDepthShader::_CreateShaderProgram() HgiShaderFunctionDesc fragDesc; fragDesc.debugName = "CopyDepthShader Fragment"; fragDesc.shaderStage = HgiShaderStageFragment; + HgiShaderFunctionAddStageInput(&fragDesc, "uvOut", "vec2"); - HgiShaderFunctionAddTexture(&fragDesc, "depthIn", 0); + + HgiShaderFunctionAddTexture(&fragDesc, "depthIn", + /*bindIndex = */ 0, + /*dimensions = */ 2, inputTextureDesc.format); + HgiShaderFunctionAddStageOutput(&fragDesc, "gl_FragDepth", "float", "depth(any)"); HgiShaderFunctionAddConstantParam(&fragDesc, "screenSize", "vec2"); @@ -122,45 +131,6 @@ bool CopyDepthShader::_CreateShaderProgram() return true; } -bool CopyDepthShader::_CreateBufferResources() -{ - if (_vertexBuffer) - { - return true; - } - - // A larger-than screen triangle made to fit the screen. - constexpr float vertData[][6] = { { -1, 3, 0, 1, 0, 2 }, { -1, -1, 0, 1, 0, 0 }, - { 3, -1, 0, 1, 2, 0 } }; - - HgiBufferDesc vboDesc; - vboDesc.debugName = "CopyDepthShader VertexBuffer"; - vboDesc.usage = HgiBufferUsageVertex; - vboDesc.initialData = vertData; - vboDesc.byteSize = sizeof(vertData); - vboDesc.vertexStride = sizeof(vertData[0]); - _vertexBuffer = _hgi->CreateBuffer(vboDesc); - if (!_vertexBuffer) - { - return false; - } - - constexpr int32_t indices[3] = { 0, 1, 2 }; - - HgiBufferDesc iboDesc; - iboDesc.debugName = "CopyDepthShader IndexBuffer"; - iboDesc.usage = HgiBufferUsageIndex32; - iboDesc.initialData = indices; - iboDesc.byteSize = sizeof(indices); - _indexBuffer = _hgi->CreateBuffer(iboDesc); - if (!_indexBuffer) - { - return false; - } - - return true; -} - bool CopyDepthShader::_CreateResourceBindings(HgiTextureHandle const& inputTexture) { HgiResourceBindingsDesc resourceDesc; @@ -209,29 +179,6 @@ bool CopyDepthShader::_CreatePipeline(HgiTextureHandle const& outputTexture) pipelineDesc.debugName = "CopyDepthShader Pipeline"; pipelineDesc.shaderProgram = _shaderProgram; - // Describe the vertex buffer - HgiVertexAttributeDesc posAttr; - posAttr.format = HgiFormatFloat32Vec3; - posAttr.offset = 0; - posAttr.shaderBindLocation = 0; - - HgiVertexAttributeDesc uvAttr; - uvAttr.format = HgiFormatFloat32Vec2; - uvAttr.offset = sizeof(float) * 4; // after posAttr - uvAttr.shaderBindLocation = 1; - - uint32_t bindSlots = 0; - - HgiVertexBufferDesc vboDesc; - - vboDesc.bindingIndex = bindSlots++; - vboDesc.vertexStride = sizeof(float) * 6; // pos, uv - vboDesc.vertexAttributes.clear(); - vboDesc.vertexAttributes.push_back(posAttr); - vboDesc.vertexAttributes.push_back(uvAttr); - - pipelineDesc.vertexBuffers.push_back(std::move(vboDesc)); - // Set up depth attachment. _depthAttachment.format = outputTexture->GetDescriptor().format; _depthAttachment.usage = outputTexture->GetDescriptor().usage; @@ -305,10 +252,9 @@ void CopyDepthShader::_Execute( gfxCmds->PushDebugGroup("CopyDepthShader"); gfxCmds->BindResources(_resourceBindings); gfxCmds->BindPipeline(_pipeline); - gfxCmds->BindVertexBuffers({ { _vertexBuffer, 0, 0 } }); gfxCmds->SetConstantValues(_pipeline, HgiShaderStageFragment, 0, sizeof(uniform), &uniform); gfxCmds->SetViewport(viewport); - gfxCmds->DrawIndexed(_indexBuffer, 3, 0, 0, 1, 0); + gfxCmds->Draw(3, 0, 1, 0); gfxCmds->PopDebugGroup(); // Done recording commands, submit work. @@ -331,26 +277,20 @@ void CopyDepthShader::Execute( public: Guard(HgiTextureHandle const& inputTexture) : _inputTexture(inputTexture) { - _inputTexture->SubmitLayoutChange(HgiTextureUsageBitsShaderRead); - } - ~Guard() - { - _inputTexture->SubmitLayoutChange(HgiTextureUsageBitsDepthTarget); + _oldUsage = _inputTexture->SubmitLayoutChange(HgiTextureUsageBitsShaderRead); } + ~Guard() { _inputTexture->SubmitLayoutChange(_oldUsage); } private: HgiTextureHandle const& _inputTexture; + HgiTextureUsage _oldUsage { HgiTextureUsageBitsDepthTarget }; } guard(inputTexture); - if (!TF_VERIFY(_CreateBufferResources(), "Resource creation failed.")) - { - return; - } if (!TF_VERIFY(_CreateSampler(), "Sampler creation failed.")) { return; } - if (!TF_VERIFY(_CreateShaderProgram(), "Shader creation failed.")) + if (!TF_VERIFY(_CreateShaderProgram(inputTexture->GetDescriptor()), "Shader creation failed.")) { return; } @@ -373,16 +313,6 @@ void CopyDepthShader::_Cleanup() _hgi->DestroySampler(&_sampler); } - if (_vertexBuffer) - { - _hgi->DestroyBuffer(&_vertexBuffer); - } - - if (_indexBuffer) - { - _hgi->DestroyBuffer(&_indexBuffer); - } - if (_shaderProgram) { auto shaderFunctions = _shaderProgram->GetShaderFunctions(); diff --git a/source/engine/copyDepthShader.h b/source/engine/copyDepthShader.h index 32f8c204..bb831a7c 100644 --- a/source/engine/copyDepthShader.h +++ b/source/engine/copyDepthShader.h @@ -47,7 +47,7 @@ class CopyDepthShader PXR_NS::HgiTextureHandle const& outputTexture); protected: - bool _CreateShaderProgram(); + bool _CreateShaderProgram(PXR_NS::HgiTextureDesc const& inputTextureDesc); bool _CreateBufferResources(); bool _CreateResourceBindings(PXR_NS::HgiTextureHandle const& inputTexture); bool _CreatePipeline(PXR_NS::HgiTextureHandle const& outputTexture); @@ -61,8 +61,6 @@ class CopyDepthShader PXR_NS::HgiAttachmentDesc _depthAttachment; PXR_NS::HgiSamplerHandle _sampler; - PXR_NS::HgiBufferHandle _vertexBuffer; - PXR_NS::HgiBufferHandle _indexBuffer; PXR_NS::HgiShaderProgramHandle _shaderProgram; PXR_NS::HgiResourceBindingsHandle _resourceBindings; PXR_NS::HgiGraphicsPipelineHandle _pipeline; From 2dee5df2cde1a89966549d49a750aa7892a9fce7 Mon Sep 17 00:00:00 2001 From: Patrick Hodoul Date: Fri, 28 Nov 2025 17:14:35 -0500 Subject: [PATCH 10/20] Cleanup Signed-off-by: Patrick Hodoul --- include/hvt/resources/shaders/copy.glslfx | 44 ------------------- .../hvt/resources/shaders/copyDepth.glslfx | 34 -------------- 2 files changed, 78 deletions(-) delete mode 100644 include/hvt/resources/shaders/copy.glslfx delete mode 100644 include/hvt/resources/shaders/copyDepth.glslfx diff --git a/include/hvt/resources/shaders/copy.glslfx b/include/hvt/resources/shaders/copy.glslfx deleted file mode 100644 index 9e32dfb9..00000000 --- a/include/hvt/resources/shaders/copy.glslfx +++ /dev/null @@ -1,44 +0,0 @@ --- glslfx version 0.1 - -// Copyright 2025 Autodesk, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - --- configuration -{ - "techniques": { - "default": { - "CopyVertex": { - "source": [ "Copy.Vertex" ] - }, - "CopyFragment": { - "source": [ "Copy.Fragment" ] - } - } - } -} - --- glsl Copy.Vertex - -void main(void) -{ - gl_Position = position; - uvOut = uvIn; -} - --- glsl Copy.Fragment - -void main(void) -{ - hd_FragColor = HgiTexelFetch_colorIn(ivec2(uvOut * screenSize)); -} diff --git a/include/hvt/resources/shaders/copyDepth.glslfx b/include/hvt/resources/shaders/copyDepth.glslfx deleted file mode 100644 index ff566748..00000000 --- a/include/hvt/resources/shaders/copyDepth.glslfx +++ /dev/null @@ -1,34 +0,0 @@ --- glslfx version 0.1 - -// Copyright 2025 Autodesk, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - --- configuration -{ - "techniques": { - "default": { - "CopyDepthFragment": { - "source": [ "CopyDepth.Fragment" ] - } - } - } -} - --- glsl CopyDepth.Fragment - -void main(void) -{ - gl_FragDepth = HgiTexelFetch_depthIn(ivec2(uvOut * screenSize)).r; -} - From 98a7909c34e6e41e64352f6982087888c1a81e71 Mon Sep 17 00:00:00 2001 From: Patrick Hodoul Date: Mon, 1 Dec 2025 11:47:39 -0500 Subject: [PATCH 11/20] Fix build break Signed-off-by: Patrick Hodoul --- source/engine/copyDepthShader.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/source/engine/copyDepthShader.cpp b/source/engine/copyDepthShader.cpp index f18ae5dd..0c19b72e 100644 --- a/source/engine/copyDepthShader.cpp +++ b/source/engine/copyDepthShader.cpp @@ -277,13 +277,12 @@ void CopyDepthShader::Execute( public: Guard(HgiTextureHandle const& inputTexture) : _inputTexture(inputTexture) { - _oldUsage = _inputTexture->SubmitLayoutChange(HgiTextureUsageBitsShaderRead); + _inputTexture->SubmitLayoutChange(HgiTextureUsageBitsShaderRead); } - ~Guard() { _inputTexture->SubmitLayoutChange(_oldUsage); } + ~Guard() { _inputTexture->SubmitLayoutChange(HgiTextureUsageBitsDepthTarget); } private: HgiTextureHandle const& _inputTexture; - HgiTextureUsage _oldUsage { HgiTextureUsageBitsDepthTarget }; } guard(inputTexture); if (!TF_VERIFY(_CreateSampler(), "Sampler creation failed.")) From 389eb4d74065d45387160c704c0e055805a55b61 Mon Sep 17 00:00:00 2001 From: Patrick Hodoul Date: Tue, 2 Dec 2025 10:48:31 -0500 Subject: [PATCH 12/20] Update source/engine/copyDepthShader.cpp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sébastien Bérubé --- source/engine/copyDepthShader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/engine/copyDepthShader.cpp b/source/engine/copyDepthShader.cpp index 0c19b72e..9d924d97 100644 --- a/source/engine/copyDepthShader.cpp +++ b/source/engine/copyDepthShader.cpp @@ -144,7 +144,7 @@ bool CopyDepthShader::_CreateResourceBindings(HgiTextureHandle const& inputTextu resourceDesc.textures.push_back(std::move(texBind)); - // If nothing has changed in the descriptor we avoid re-creating thebresource bindings object. + // If nothing has changed in the descriptor we avoid re-creating the resource bindings object. if (_resourceBindings) { HgiResourceBindingsDesc const& desc = _resourceBindings->GetDescriptor(); From 4fa2f55623eb37d52067c04249b131db7a31544d Mon Sep 17 00:00:00 2001 From: Patrick Hodoul Date: Tue, 2 Dec 2025 13:41:58 -0500 Subject: [PATCH 13/20] More obvious which AOV to visualize Signed-off-by: Patrick Hodoul --- include/hvt/engine/renderBufferManager.h | 15 +++++++++---- source/engine/framePass.cpp | 2 +- source/engine/renderBufferManager.cpp | 28 ++++++++++++------------ 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/include/hvt/engine/renderBufferManager.h b/include/hvt/engine/renderBufferManager.h index ee7e1c64..dee81b0a 100644 --- a/include/hvt/engine/renderBufferManager.h +++ b/include/hvt/engine/renderBufferManager.h @@ -73,9 +73,16 @@ class HVT_API RenderBufferManager : public RenderBufferSettingsProvider PXR_NS::GfVec2i const& newRenderBufferSize, size_t msaaSampleCount, bool msaaEnabled); /// Set the render outputs. - /// It does NOT update any RenderTaskParams, but updates the AovParamCache and the viewport AOV. - bool SetRenderOutputs(PXR_NS::TfTokenVector const& names, RenderBufferBindings const& inputs, - PXR_NS::GfVec4d const& viewport); + /// \note It does NOT update any RenderTaskParams, but updates the AovParamCache and the viewport AOV. + /// \param visualizeAOV The AOV to to visualize in the viewport. + /// \param outputs The names of the AOVs to be used for the render outputs. + /// \param inputs The bindings of the AOVs to be used for the render inputs. + /// \param viewport The viewport dimensions to be used for the render outputs. + /// \return True if the render outputs were set successfully, false otherwise. + /// \note An empty list of inputs means to create its own render buffers for the inputs. + bool SetRenderOutputs( + PXR_NS::TfToken const& visualizeAOV, PXR_NS::TfTokenVector const& outputs, + RenderBufferBindings const& inputs, PXR_NS::GfVec4d const& viewport); /// Set the render output clear color in the AovParamCache. void SetRenderOutputClearColor(PXR_NS::TfToken const& name, PXR_NS::VtValue const& clearValue); @@ -134,4 +141,4 @@ class HVT_API RenderBufferManager : public RenderBufferSettingsProvider std::unique_ptr _impl; }; -} // namespace HVT_NS \ No newline at end of file +} // namespace HVT_NS diff --git a/source/engine/framePass.cpp b/source/engine/framePass.cpp index a7fb5001..7627cebb 100644 --- a/source/engine/framePass.cpp +++ b/source/engine/framePass.cpp @@ -313,7 +313,7 @@ HdTaskSharedPtrVector FramePass::GetRenderTasks(RenderBufferBindings const& inpu } } - _bufferManager->SetRenderOutputs(renderOutputs, inputAOVs, {}); + _bufferManager->SetRenderOutputs(_passParams.visualizeAOV, renderOutputs, inputAOVs, {}); // Some selection tasks needs to update their buffer paths. _selectionHelper->SetVisualizeAOV(_passParams.visualizeAOV); diff --git a/source/engine/renderBufferManager.cpp b/source/engine/renderBufferManager.cpp index 78a8e45d..231d85ac 100644 --- a/source/engine/renderBufferManager.cpp +++ b/source/engine/renderBufferManager.cpp @@ -116,7 +116,7 @@ class RenderBufferManager::Impl : public RenderBufferSettingsProvider /// Updates render output parameters and creates new render buffers if needed. /// Note: AOV binding values are stored here and consulted later by RenderTasks. - bool SetRenderOutputs(TfTokenVector const& names, RenderBufferBindings const& inputs, + bool SetRenderOutputs(TfToken const& visualizeAOV, TfTokenVector const& names, RenderBufferBindings const& inputs, GfVec4d const& viewport, SdfPath const& controllerId); /// Updates the render output clear color. @@ -277,7 +277,7 @@ Hgi* GetHgi(HdRenderIndex const* renderIndex) Hgi* hgi = hvt::HgiInstance::instance().hgi(); if (hgi) return hgi; - + // If it wasn't created by the HgiInstance look for it on the render index. HdDriverVector const& drivers = renderIndex->GetDrivers(); for (HdDriver* hdDriver : drivers) @@ -394,7 +394,7 @@ void RenderBufferManager::Impl::PrepareBuffersFromInputs(RenderBufferBinding con } else { - //The output render buffer is not holding a writeable buffer. + //The output render buffer is not holding a writeable buffer. //You will need to composite to blend passes results. return; } @@ -407,7 +407,7 @@ void RenderBufferManager::Impl::PrepareBuffersFromInputs(RenderBufferBinding con TF_CODING_ERROR("There is no valid Hgi driver."); return; } - + // Initialize the shader that will copy the contents from the input to the output. if (!_copyColorShader) { @@ -429,7 +429,7 @@ void RenderBufferManager::Impl::PrepareBuffersFromInputs(RenderBufferBinding con GfVec2f screenSize { static_cast(desc.dimensions[0]), static_cast(desc.dimensions[1]) }; shader->SetShaderConstants(sizeof(screenSize), &screenSize); - + // Submit the layout change to read from the textures. colorInput->SubmitLayoutChange(HgiTextureUsageBitsShaderRead); @@ -529,7 +529,7 @@ void RenderBufferManager::Impl::PrepareDepthOnlyFromInput(RenderBufferBinding co _copyDepthShader->Execute(input, output); } -bool RenderBufferManager::Impl::SetRenderOutputs(TfTokenVector const& outputs, +bool RenderBufferManager::Impl::SetRenderOutputs(TfToken const& visualizeAOV, TfTokenVector const& outputs, RenderBufferBindings const& inputs, GfVec4d const& viewport, SdfPath const& controllerId) { if (!IsAovSupported()) @@ -574,7 +574,7 @@ bool RenderBufferManager::Impl::SetRenderOutputs(TfTokenVector const& outputs, // If progressive rendering is enabled, render buffer clear is only required when // `_aovOutputs != outputs`. bool needClear = !_isProgressiveRenderingEnabled || _aovOutputs != outputs; - + // This will delete Bprims from the RenderIndex and clear the _viewportAov and _aovBufferIds // SdfPathVector. if (needClear) @@ -656,8 +656,8 @@ bool RenderBufferManager::Impl::SetRenderOutputs(TfTokenVector const& outputs, } } - // If something has changed and the input was not found or the previous renderer is different - // than the current one, then we need to create a new render buffer. + // If something has changed and the input was not found or the previous renderer is different + // than the current one, then we need to create a new render buffer. // This will be the buffer used and the previous contents potentially copied into. if (somethingChanged && !inputFound) { @@ -677,12 +677,12 @@ bool RenderBufferManager::Impl::SetRenderOutputs(TfTokenVector const& outputs, // we then need to copy the AOV to visualize. But be careful that's not always the color one we visualize. // Color AOV always means color & depth AOVs (where depth is optional). - if (outputs[0] == pxr::HdAovTokens->color && colorInput.texture) + if (visualizeAOV == pxr::HdAovTokens->color && colorInput.texture) { PrepareBuffersFromInputs(colorInput, depthInput, colorDesc, controllerId); } // But depth AOV only means depth AOV only. - else if (outputs[0] == pxr::HdAovTokens->depth && depthInput.texture) + else if (visualizeAOV == pxr::HdAovTokens->depth && depthInput.texture) { PrepareDepthOnlyFromInput(depthInput, depthDesc, controllerId); } @@ -716,7 +716,7 @@ bool RenderBufferManager::Impl::SetRenderOutputs(TfTokenVector const& outputs, aovBindingsClear[i].aovSettings = outputDescs[i].aovSettings; // Note, it would be better to just assign the output buffer here, but this breaks some - // unit tests that expect this to be null and do a pointer-as-string comparison if it is not + // unit tests that expect this to be null and do a pointer-as-string comparison if it is not // which is not easily fixable. HdRenderBuffer* outputBuffer = static_cast(_pRenderIndex->GetBprim( HdPrimTypeTokens->renderBuffer, aovBindingsClear[i].renderBufferId)); @@ -967,9 +967,9 @@ void RenderBufferManager::SetRenderOutputClearColor(const TfToken& name, const V } bool RenderBufferManager::SetRenderOutputs( - TfTokenVector const& names, RenderBufferBindings const& inputs, GfVec4d const& viewport) + TfToken const& visualizeAOV, TfTokenVector const& outputs, RenderBufferBindings const& inputs, GfVec4d const& viewport) { - return _impl->SetRenderOutputs(names, inputs, viewport, _taskManagerUid); + return _impl->SetRenderOutputs(visualizeAOV, outputs, inputs, viewport, _taskManagerUid); } void RenderBufferManager::SetPresentationOutput(TfToken const& api, VtValue const& framebuffer) From 057a93f705c2676ad2d1eb27ef77117b0618a493 Mon Sep 17 00:00:00 2001 From: Patrick Hodoul Date: Tue, 2 Dec 2025 14:10:04 -0500 Subject: [PATCH 14/20] Improve parameter names Signed-off-by: Patrick Hodoul --- include/hvt/engine/renderBufferManager.h | 4 +- source/engine/renderBufferManager.cpp | 52 ++++++++++++------------ 2 files changed, 29 insertions(+), 27 deletions(-) diff --git a/include/hvt/engine/renderBufferManager.h b/include/hvt/engine/renderBufferManager.h index dee81b0a..32c8e000 100644 --- a/include/hvt/engine/renderBufferManager.h +++ b/include/hvt/engine/renderBufferManager.h @@ -74,14 +74,14 @@ class HVT_API RenderBufferManager : public RenderBufferSettingsProvider /// Set the render outputs. /// \note It does NOT update any RenderTaskParams, but updates the AovParamCache and the viewport AOV. - /// \param visualizeAOV The AOV to to visualize in the viewport. + /// \param outputToVisualize The AOV to visualize in the viewport. /// \param outputs The names of the AOVs to be used for the render outputs. /// \param inputs The bindings of the AOVs to be used for the render inputs. /// \param viewport The viewport dimensions to be used for the render outputs. /// \return True if the render outputs were set successfully, false otherwise. /// \note An empty list of inputs means to create its own render buffers for the inputs. bool SetRenderOutputs( - PXR_NS::TfToken const& visualizeAOV, PXR_NS::TfTokenVector const& outputs, + PXR_NS::TfToken const& outputToVisualize, PXR_NS::TfTokenVector const& outputs, RenderBufferBindings const& inputs, PXR_NS::GfVec4d const& viewport); /// Set the render output clear color in the AovParamCache. diff --git a/source/engine/renderBufferManager.cpp b/source/engine/renderBufferManager.cpp index 231d85ac..5b1f5a5e 100644 --- a/source/engine/renderBufferManager.cpp +++ b/source/engine/renderBufferManager.cpp @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include #include +#include #include #include #include @@ -116,8 +116,8 @@ class RenderBufferManager::Impl : public RenderBufferSettingsProvider /// Updates render output parameters and creates new render buffers if needed. /// Note: AOV binding values are stored here and consulted later by RenderTasks. - bool SetRenderOutputs(TfToken const& visualizeAOV, TfTokenVector const& names, RenderBufferBindings const& inputs, - GfVec4d const& viewport, SdfPath const& controllerId); + bool SetRenderOutputs(TfToken const& outputToVisualize, TfTokenVector const& outputs, + RenderBufferBindings const& inputs, GfVec4d const& viewport, SdfPath const& controllerId); /// Updates the render output clear color. /// Note: Clear color values are stored here and consulted later by RenderTasks. @@ -411,8 +411,7 @@ void RenderBufferManager::Impl::PrepareBuffersFromInputs(RenderBufferBinding con // Initialize the shader that will copy the contents from the input to the output. if (!_copyColorShader) { - _copyColorShader = - std::make_unique(hgi, "Copy Color Buffer"); + _copyColorShader = std::make_unique(hgi, "Copy Color Buffer"); } // Initialize the shader that will copy the contents from the input to the output. @@ -449,8 +448,8 @@ void RenderBufferManager::Impl::PrepareBuffersFromInputs(RenderBufferBinding con colorInput->SubmitLayoutChange(HgiTextureUsageBitsColorTarget); } -// The code does not use the HdxFullscreenShader helper here because it only needs to copy the depth AOVs -// and HdxFullscreenShader always needs the color AOVs. +// The code does not use the HdxFullscreenShader helper here because it only needs to copy the depth +// AOVs and HdxFullscreenShader always needs the color AOVs. void RenderBufferManager::Impl::PrepareDepthOnlyFromInput(RenderBufferBinding const& inputDepthAov, HdRenderBufferDescriptor const& desc, SdfPath const& controllerId) { @@ -529,8 +528,9 @@ void RenderBufferManager::Impl::PrepareDepthOnlyFromInput(RenderBufferBinding co _copyDepthShader->Execute(input, output); } -bool RenderBufferManager::Impl::SetRenderOutputs(TfToken const& visualizeAOV, TfTokenVector const& outputs, - RenderBufferBindings const& inputs, GfVec4d const& viewport, SdfPath const& controllerId) +bool RenderBufferManager::Impl::SetRenderOutputs(TfToken const& outputToVisualize, + TfTokenVector const& outputs, RenderBufferBindings const& inputs, GfVec4d const& viewport, + SdfPath const& controllerId) { if (!IsAovSupported()) { @@ -610,8 +610,7 @@ bool RenderBufferManager::Impl::SetRenderOutputs(TfToken const& visualizeAOV, Tf // Add the new RenderBuffers. // NOTE: GetAovPath returns ids of the form {controller_id}/aov_{name}. - const std::string rendererName - = _pRenderIndex->GetRenderDelegate()->GetRendererDisplayName(); + const std::string rendererName = _pRenderIndex->GetRenderDelegate()->GetRendererDisplayName(); RenderBufferBinding colorInput, depthInput; HdRenderBufferDescriptor colorDesc, depthDesc; for (size_t i = 0; i < localOutputs.size(); ++i) @@ -637,13 +636,15 @@ bool RenderBufferManager::Impl::SetRenderOutputs(TfToken const& visualizeAOV, Tf // If the renderer remains the same, we don't want to copy the depth buffer. // The existing depth buffer will continue to be used. // We do this in order to not loose sub-pixel depth information. - // However, this means that if any Tasks write to the depth after a sub-pixel - // resolve then the depth buffer will be inconsistent with the color buffer and - // that depth information will be lost. I don't think this currently happens in - // practice, so we are opting in favor of keeping the sub-pixel resolution. + // However, this means that if any Tasks write to the depth after a + // sub-pixel resolve then the depth buffer will be inconsistent with the + // color buffer and that depth information will be lost. I don't think this + // currently happens in practice, so we are opting in favor of keeping the + // sub-pixel resolution. // // FUTURE: We may want to revisit this decision in the future. - // The long-term solution may be to do post processing at the sub-pixel accuracy. + // The long-term solution may be to do post processing at the sub-pixel + // accuracy. depthInput.texture = HgiTextureHandle(); } } @@ -656,9 +657,9 @@ bool RenderBufferManager::Impl::SetRenderOutputs(TfToken const& visualizeAOV, Tf } } - // If something has changed and the input was not found or the previous renderer is different - // than the current one, then we need to create a new render buffer. - // This will be the buffer used and the previous contents potentially copied into. + // If something has changed and the input was not found or the previous renderer is + // different than the current one, then we need to create a new render buffer. This will be + // the buffer used and the previous contents potentially copied into. if (somethingChanged && !inputFound) { const SdfPath aovId = GetAovPath(controllerId, localOutputs[i]); @@ -673,16 +674,17 @@ bool RenderBufferManager::Impl::SetRenderOutputs(TfToken const& visualizeAOV, Tf } } - // In case, we want to share the AOV buffers between frame passes but they are from different render delegates, - // we then need to copy the AOV to visualize. But be careful that's not always the color one we visualize. + // In case, we want to share the AOV buffers between frame passes but they are from different + // render delegates, we then need to copy the AOV to visualize. But be careful that's not always + // the color one we visualize. // Color AOV always means color & depth AOVs (where depth is optional). - if (visualizeAOV == pxr::HdAovTokens->color && colorInput.texture) + if (outputToVisualize == pxr::HdAovTokens->color && colorInput.texture) { PrepareBuffersFromInputs(colorInput, depthInput, colorDesc, controllerId); } // But depth AOV only means depth AOV only. - else if (visualizeAOV == pxr::HdAovTokens->depth && depthInput.texture) + else if (outputToVisualize == pxr::HdAovTokens->depth && depthInput.texture) { PrepareDepthOnlyFromInput(depthInput, depthDesc, controllerId); } @@ -966,8 +968,8 @@ void RenderBufferManager::SetRenderOutputClearColor(const TfToken& name, const V _impl->SetRenderOutputClearColor(name, _taskManagerUid, clearValue); } -bool RenderBufferManager::SetRenderOutputs( - TfToken const& visualizeAOV, TfTokenVector const& outputs, RenderBufferBindings const& inputs, GfVec4d const& viewport) +bool RenderBufferManager::SetRenderOutputs(TfToken const& visualizeAOV, + TfTokenVector const& outputs, RenderBufferBindings const& inputs, GfVec4d const& viewport) { return _impl->SetRenderOutputs(visualizeAOV, outputs, inputs, viewport, _taskManagerUid); } From a7365b2ea424b1de814af48b178bb1f401726036 Mon Sep 17 00:00:00 2001 From: Patrick Hodoul Date: Wed, 3 Dec 2025 16:36:39 -0500 Subject: [PATCH 15/20] Better define the outputs Signed-off-by: Patrick Hodoul --- include/hvt/engine/basicLayerParams.h | 3 --- include/hvt/engine/framePass.h | 19 ++++++++++++---- include/hvt/engine/renderBufferManager.h | 8 +++++++ source/engine/framePass.cpp | 24 ++++++++++++++++---- source/engine/renderBufferManager.cpp | 28 +++++++++++++++++++++++- 5 files changed, 70 insertions(+), 12 deletions(-) diff --git a/include/hvt/engine/basicLayerParams.h b/include/hvt/engine/basicLayerParams.h index f4b91e92..400e5af4 100644 --- a/include/hvt/engine/basicLayerParams.h +++ b/include/hvt/engine/basicLayerParams.h @@ -97,9 +97,6 @@ struct HVT_API BasicLayerParams /// Defines the render buffer size. PXR_NS::GfVec2i renderBufferSize; - /// The AOV buffer ID to visualize (color or depth). - PXR_NS::TfToken visualizeAOV; - /// Enable selection is on by default. bool enableSelection { true }; diff --git a/include/hvt/engine/framePass.h b/include/hvt/engine/framePass.h index 28d5cd98..a6e2bef4 100644 --- a/include/hvt/engine/framePass.h +++ b/include/hvt/engine/framePass.h @@ -143,6 +143,21 @@ struct HVT_API ViewParams /// parameters specific to the FramePass. struct HVT_API FramePassParams : public BasicLayerParams { + /// Defines the inputs, outputs and the aov to visualize. + /// @{ + + /// The AOV buffer to visualize e.g., color, depth, etc. + PXR_NS::TfToken visualizeAOV; + + /// The list of AOV buffers to render e.g., color, depth, etc. + /// \note This is used to override the default render outputs. + PXR_NS::TfTokenVector renderOutputs; + + /// Enable eye relative normal render output. + /// \note this adds an extra cost for all geometry render passes. + bool enableNeyeRenderOutput { false }; + /// @} + /// View, model and world settings. /// @{ ViewParams viewInfo; @@ -167,10 +182,6 @@ struct HVT_API FramePassParams : public BasicLayerParams bool enableMultisampling { true }; size_t msaaSampleCount { 4 }; /// @} - - /// Enable eye relative normal render output. - /// \note this adds an extra cost for all geometry render passes. - bool enableNeyeRenderOutput { false }; }; /// A FramePass is used to render or select from a collection of Prims using a set of HdTasks and diff --git a/include/hvt/engine/renderBufferManager.h b/include/hvt/engine/renderBufferManager.h index 32c8e000..e8593162 100644 --- a/include/hvt/engine/renderBufferManager.h +++ b/include/hvt/engine/renderBufferManager.h @@ -55,6 +55,14 @@ class HVT_API RenderBufferManager : public RenderBufferSettingsProvider /// Destructor. ~RenderBufferManager(); + /// Get the all the possible renderer AOV tokens. + /// \return Returns the renderer AOV tokens. + static PXR_NS::TfTokenVector GetAllRendererAovs(); + + /// Get the all the renderer AOV tokens supported by the selected render delegate e.g. HdStorm. + /// \return Returns the supported renderer AOV tokens. + PXR_NS::TfTokenVector GetSupportedRendererAovs() const; + /// Gets the dimensions of the render buffers. /// \return Returns the render buffer dimensions. inline PXR_NS::GfVec2i const& GetRenderBufferDimensions() const { return _size; } diff --git a/source/engine/framePass.cpp b/source/engine/framePass.cpp index 7627cebb..8ee6e7a1 100644 --- a/source/engine/framePass.cpp +++ b/source/engine/framePass.cpp @@ -301,12 +301,28 @@ HdTaskSharedPtrVector FramePass::GetRenderTasks(RenderBufferBindings const& inpu } else { - if (!IsStormRenderDelegate(GetRenderIndex()) || params().enableOutline) - renderOutputs = { HdAovTokens->color, HdAovTokens->depth, HdAovTokens->primId, - HdAovTokens->elementId, HdAovTokens->instanceId}; + if ( _passParams.enableOutline) + { + renderOutputs = _bufferManager->GetSupportedRendererAovs(); + } + else if (_passParams.renderOutputs.empty()) + { + // Add the default AOVs. + if (!IsStormRenderDelegate(GetRenderIndex())) + { + renderOutputs = _bufferManager->GetSupportedRendererAovs(); + } + else + { + renderOutputs = { HdAovTokens->color, HdAovTokens->depth }; + } + } else - renderOutputs = { HdAovTokens->color, HdAovTokens->depth}; + { + renderOutputs = _passParams.renderOutputs; + } + // Add the Neye AOV if needed. if (_passParams.enableNeyeRenderOutput) { renderOutputs.push_back(HdAovTokens->Neye); diff --git a/source/engine/renderBufferManager.cpp b/source/engine/renderBufferManager.cpp index 5b1f5a5e..1d331684 100644 --- a/source/engine/renderBufferManager.cpp +++ b/source/engine/renderBufferManager.cpp @@ -898,11 +898,37 @@ RenderBufferManager::RenderBufferManager( SdfPath const& taskManagerUid, HdRenderIndex* pRenderIndex, SyncDelegatePtr& syncDelegate) : _taskManagerUid(taskManagerUid), _pRenderIndex(pRenderIndex) { - _impl = std::make_unique(pRenderIndex, syncDelegate); + _impl = std::make_unique(_pRenderIndex, syncDelegate); } RenderBufferManager::~RenderBufferManager() {} +TfTokenVector RenderBufferManager::GetAllRendererAovs() +{ + return { HdAovTokens->color, HdAovTokens->depth, + HdAovTokens->primId, HdAovTokens->elementId, HdAovTokens->instanceId }; +} + +TfTokenVector RenderBufferManager::GetSupportedRendererAovs() const +{ + if (_pRenderIndex->IsBprimTypeSupported(HdPrimTypeTokens->renderBuffer)) + { + auto const& candidates = GetAllRendererAovs(); + + TfTokenVector aovs; + for (auto const& aov : candidates) + { + if (_pRenderIndex->GetRenderDelegate()->GetDefaultAovDescriptor(aov).format != + HdFormatInvalid) + { + aovs.push_back(aov); + } + } + return aovs; + } + return {}; +} + HgiTextureHandle RenderBufferManager::GetAovTexture(TfToken const& token, HdEngine* engine) const { VtValue aov; From 96dbdf1662f020d39dd4ee2cd3f9345e014da19d Mon Sep 17 00:00:00 2001 From: Patrick Hodoul Date: Wed, 3 Dec 2025 17:33:43 -0500 Subject: [PATCH 16/20] Fix build break Signed-off-by: Patrick Hodoul --- test/RenderingFramework/OpenGLTestContext.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/RenderingFramework/OpenGLTestContext.cpp b/test/RenderingFramework/OpenGLTestContext.cpp index 8b8b6ac9..62acd6df 100644 --- a/test/RenderingFramework/OpenGLTestContext.cpp +++ b/test/RenderingFramework/OpenGLTestContext.cpp @@ -115,7 +115,12 @@ OpenGLWindow::OpenGLWindow(int w, int h) glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_TRUE); glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); + + // Not defined in the commit id from the Autodesk fork of GLFW 3.3. + // Refer to sourceConfig.json for the details. +#ifdef GLFW_SCALE_FRAMEBUFFER glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_FALSE); +#endif if (isCoreProfile()) { From a824300c78fd6b94c7b5f9c43dc3feb30dc7607e Mon Sep 17 00:00:00 2001 From: Patrick Hodoul Date: Wed, 3 Dec 2025 17:50:05 -0500 Subject: [PATCH 17/20] Update OpenGLTestContext.cpp Remove misleading comments. --- test/RenderingFramework/OpenGLTestContext.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/RenderingFramework/OpenGLTestContext.cpp b/test/RenderingFramework/OpenGLTestContext.cpp index 62acd6df..f9506621 100644 --- a/test/RenderingFramework/OpenGLTestContext.cpp +++ b/test/RenderingFramework/OpenGLTestContext.cpp @@ -116,8 +116,6 @@ OpenGLWindow::OpenGLWindow(int w, int h) glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_TRUE); glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); - // Not defined in the commit id from the Autodesk fork of GLFW 3.3. - // Refer to sourceConfig.json for the details. #ifdef GLFW_SCALE_FRAMEBUFFER glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_FALSE); #endif From 759cb703aa7222134e4c4ce04ae1b365d6f0e42c Mon Sep 17 00:00:00 2001 From: Patrick Hodoul Date: Wed, 3 Dec 2025 18:53:22 -0500 Subject: [PATCH 18/20] Add a utest Signed-off-by: Patrick Hodoul --- include/hvt/engine/framePass.h | 10 ---- include/hvt/engine/renderBufferManager.h | 3 + source/engine/renderBufferManager.cpp | 10 +++- test/tests/testFramePass.cpp | 73 +++++++++++++++++++++++- 4 files changed, 84 insertions(+), 12 deletions(-) diff --git a/include/hvt/engine/framePass.h b/include/hvt/engine/framePass.h index a6e2bef4..ccd78586 100644 --- a/include/hvt/engine/framePass.h +++ b/include/hvt/engine/framePass.h @@ -261,16 +261,6 @@ class HVT_API FramePass /// \return An handle to the associated render texture or null if not found. PXR_NS::HgiTextureHandle GetRenderTexture(PXR_NS::TfToken const& aovToken) const; - /// It holds the token (e.g., color, depth) and its corresponding texture handle. - struct RenderOutput - { - /// The AOV tag i.e., color or depth. - PXR_NS::TfToken aovToken; - /// The corresponding render texture handle. - PXR_NS::HgiTextureHandle aovTextureHandle; - }; - using RenderOutputs = std::vector; - /// Return the render index used by this frame pass. PXR_NS::HdRenderIndex* GetRenderIndex() const; diff --git a/include/hvt/engine/renderBufferManager.h b/include/hvt/engine/renderBufferManager.h index e8593162..3021a30e 100644 --- a/include/hvt/engine/renderBufferManager.h +++ b/include/hvt/engine/renderBufferManager.h @@ -92,6 +92,9 @@ class HVT_API RenderBufferManager : public RenderBufferSettingsProvider PXR_NS::TfToken const& outputToVisualize, PXR_NS::TfTokenVector const& outputs, RenderBufferBindings const& inputs, PXR_NS::GfVec4d const& viewport); + /// Get the renderer outputs. + PXR_NS::TfTokenVector const& GetRenderOutputs() const; + /// Set the render output clear color in the AovParamCache. void SetRenderOutputClearColor(PXR_NS::TfToken const& name, PXR_NS::VtValue const& clearValue); diff --git a/source/engine/renderBufferManager.cpp b/source/engine/renderBufferManager.cpp index 1d331684..595b7e58 100644 --- a/source/engine/renderBufferManager.cpp +++ b/source/engine/renderBufferManager.cpp @@ -119,6 +119,9 @@ class RenderBufferManager::Impl : public RenderBufferSettingsProvider bool SetRenderOutputs(TfToken const& outputToVisualize, TfTokenVector const& outputs, RenderBufferBindings const& inputs, GfVec4d const& viewport, SdfPath const& controllerId); + /// Get the render outputs. + TfTokenVector const& GetRenderOutputs() const { return _aovOutputs; } + /// Updates the render output clear color. /// Note: Clear color values are stored here and consulted later by RenderTasks. void SetRenderOutputClearColor( @@ -746,7 +749,7 @@ bool RenderBufferManager::Impl::SetRenderOutputs(TfToken const& outputToVisualiz if (localOutputs.size() > 0) { - SetViewportRenderOutput(localOutputs[0], controllerId); + SetViewportRenderOutput(outputToVisualize, controllerId); } // NOTE: The viewport data plumbed to tasks unfortunately depends on whether aovs are being @@ -1000,6 +1003,11 @@ bool RenderBufferManager::SetRenderOutputs(TfToken const& visualizeAOV, return _impl->SetRenderOutputs(visualizeAOV, outputs, inputs, viewport, _taskManagerUid); } +TfTokenVector const& RenderBufferManager::GetRenderOutputs() const +{ + return _impl->GetRenderOutputs(); +} + void RenderBufferManager::SetPresentationOutput(TfToken const& api, VtValue const& framebuffer) { _impl->SetPresentationOutput(api, framebuffer); diff --git a/test/tests/testFramePass.cpp b/test/tests/testFramePass.cpp index 0c3cf639..2e0d8857 100644 --- a/test/tests/testFramePass.cpp +++ b/test/tests/testFramePass.cpp @@ -464,4 +464,75 @@ HVT_TEST(TestViewportToolbox, TestFramePassSelectionSettingsProvider) EXPECT_TRUE(clearedLocatePaths.empty()); // Clean up. - FramePass destructor will handle cleanup -} \ No newline at end of file +} + +// The test is independent from the backend. +TEST(TestViewportToolbox, TestFramePassAOVs) +{ + // The goal of this unit test is to validate that the FramePass correctly provides + // access to AOVs and that the AOVs are properly set. + + auto testContext = TestHelpers::CreateTestContext(); + + TestHelpers::TestStage stage(testContext->_backend); + ASSERT_TRUE(stage.open(testContext->_sceneFilepath)); + + hvt::RenderIndexProxyPtr renderIndexProxy; + hvt::FramePassPtr framePass; + + { + // Create the render index. + hvt::RendererDescriptor rendererDesc; + rendererDesc.hgiDriver = &testContext->_backend->hgiDriver(); + rendererDesc.rendererName = "HdStormRendererPlugin"; + hvt::ViewportEngine::CreateRenderer(renderIndexProxy, rendererDesc); + + // Create a FramePass which internally creates a SelectionHelper. + // (SelectionSettingsProvider) + static const SdfPath framePassId("/sceneFramePass"); + hvt::FramePassDescriptor desc { renderIndexProxy->RenderIndex(), framePassId, {} }; + framePass = hvt::ViewportEngine::CreateFramePass(desc); + } + + auto& params = framePass->params(); + + // Partial initialization of the FramePass parameters. + const GfVec2i renderSize(testContext->width(), testContext->height()); + params.renderBufferSize = renderSize; + params.viewInfo.framing = hvt::ViewParams::GetDefaultFraming(renderSize[0], renderSize[1]); + + // The caller knows what AOVs to render. + params = framePass->params(); + params.renderOutputs = { HdAovTokens->color, HdAovTokens->depth }; + framePass->Render(); + + // Verify the AOVs are properly set. + auto aovs = framePass->GetRenderBufferManager()->GetRenderOutputs(); + ASSERT_EQ(aovs.size(), 2); + ASSERT_EQ(aovs[0], HdAovTokens->color); + ASSERT_EQ(aovs[1], HdAovTokens->depth); + + // The caller doesn't know what AOVs to render, so it's expected that the default AOVs are + // rendered. + params = framePass->params(); + params.renderOutputs = {}; + framePass->Render(); + + // Verify the AOVs are properly set. + aovs = framePass->GetRenderBufferManager()->GetRenderOutputs(); + ASSERT_EQ(aovs.size(), 2); + ASSERT_EQ(aovs[0], HdAovTokens->color); + ASSERT_EQ(aovs[1], HdAovTokens->depth); + + // The caller doesn't know what AOVs to render so it's expected that the default AOVs are + // rendered. However, the visualizeAOV is set to depth, so only the depth AOV is rendered. + params = framePass->params(); + params.renderOutputs = {}; + params.visualizeAOV = HdAovTokens->depth; + framePass->Render(); + + // Verify the AOVs are properly set. + aovs = framePass->GetRenderBufferManager()->GetRenderOutputs(); + ASSERT_EQ(aovs.size(), 1); + ASSERT_EQ(aovs[1], HdAovTokens->depth); +} From d43434b3ca6d6ccec3965e0cfa912840f15d99d9 Mon Sep 17 00:00:00 2001 From: Patrick Hodoul Date: Wed, 3 Dec 2025 19:00:57 -0500 Subject: [PATCH 19/20] Fix bug Signed-off-by: Patrick Hodoul --- test/tests/testFramePass.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tests/testFramePass.cpp b/test/tests/testFramePass.cpp index 2e0d8857..d8f39932 100644 --- a/test/tests/testFramePass.cpp +++ b/test/tests/testFramePass.cpp @@ -534,5 +534,5 @@ TEST(TestViewportToolbox, TestFramePassAOVs) // Verify the AOVs are properly set. aovs = framePass->GetRenderBufferManager()->GetRenderOutputs(); ASSERT_EQ(aovs.size(), 1); - ASSERT_EQ(aovs[1], HdAovTokens->depth); + ASSERT_EQ(aovs[0], HdAovTokens->depth); } From cb176cce119b3700a40d9e7540ac71c3fc6183b9 Mon Sep 17 00:00:00 2001 From: Patrick Hodoul Date: Fri, 5 Dec 2025 18:10:05 -0500 Subject: [PATCH 20/20] Update OpenGLTestContext.cpp --- test/RenderingFramework/OpenGLTestContext.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/RenderingFramework/OpenGLTestContext.cpp b/test/RenderingFramework/OpenGLTestContext.cpp index f9506621..8b8b6ac9 100644 --- a/test/RenderingFramework/OpenGLTestContext.cpp +++ b/test/RenderingFramework/OpenGLTestContext.cpp @@ -115,10 +115,7 @@ OpenGLWindow::OpenGLWindow(int w, int h) glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_TRUE); glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); - -#ifdef GLFW_SCALE_FRAMEBUFFER glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_FALSE); -#endif if (isCoreProfile()) {