Add render graph runtime UAV support

This commit is contained in:
2026-04-14 14:46:13 +08:00
parent 91c62c6b14
commit af6de86647
3 changed files with 186 additions and 4 deletions

View File

@@ -28,6 +28,9 @@ struct MockTransientAllocationState {
int createShaderViewCalls = 0;
int shutdownShaderViewCalls = 0;
int destroyShaderViewCalls = 0;
int createUnorderedAccessViewCalls = 0;
int shutdownUnorderedAccessViewCalls = 0;
int destroyUnorderedAccessViewCalls = 0;
};
class MockTransientTexture final : public RHITexture {
@@ -88,6 +91,8 @@ public:
++m_state->destroyRenderTargetViewCalls;
} else if (m_viewType == ResourceViewType::DepthStencil) {
++m_state->destroyDepthViewCalls;
} else if (m_viewType == ResourceViewType::UnorderedAccess) {
++m_state->destroyUnorderedAccessViewCalls;
} else {
++m_state->destroyShaderViewCalls;
}
@@ -98,6 +103,8 @@ public:
++m_state->shutdownRenderTargetViewCalls;
} else if (m_viewType == ResourceViewType::DepthStencil) {
++m_state->shutdownDepthViewCalls;
} else if (m_viewType == ResourceViewType::UnorderedAccess) {
++m_state->shutdownUnorderedAccessViewCalls;
} else {
++m_state->shutdownShaderViewCalls;
}
@@ -223,8 +230,14 @@ public:
}
RHIResourceView* CreateUnorderedAccessView(RHIBuffer*, const ResourceViewDesc&) override { return nullptr; }
RHIResourceView* CreateUnorderedAccessView(RHITexture*, const ResourceViewDesc&) override {
return nullptr;
RHIResourceView* CreateUnorderedAccessView(
RHITexture*,
const ResourceViewDesc& desc) override {
++m_state->createUnorderedAccessViewCalls;
return new MockTransientView(
m_state,
ResourceViewType::UnorderedAccess,
static_cast<Format>(desc.format));
}
const RHICapabilities& GetCapabilities() const override { return m_capabilities; }
@@ -721,6 +734,130 @@ TEST(RenderGraph_Test, ExecutesGraphOwnedImportedDepthTransitionsAtGraphBoundari
EXPECT_EQ(commandList.transitionCalls[1].after, ResourceStates::DepthWrite);
}
TEST(RenderGraph_Test, ExecutesTransientComputeTransitionsWithUnorderedAccessView) {
RenderGraph graph;
RenderGraphBuilder builder(graph);
const RenderGraphTextureDesc desc = BuildTestTextureDesc();
const RenderGraphTextureHandle sceneColor = builder.CreateTransientTexture("SceneColor", desc);
RHIResourceView* computeWriteView = nullptr;
RHIResourceView* computeReadView = nullptr;
builder.AddComputePass(
"Generate",
[&](RenderGraphPassBuilder& pass) {
pass.WriteTexture(sceneColor);
pass.SetExecuteCallback(
[&](const RenderGraphExecutionContext& executionContext) {
computeWriteView =
executionContext.ResolveTextureView(
sceneColor,
RenderGraphTextureViewType::UnorderedAccess);
EXPECT_NE(computeWriteView, nullptr);
});
});
builder.AddComputePass(
"Consume",
[&](RenderGraphPassBuilder& pass) {
pass.ReadTexture(sceneColor);
pass.SetExecuteCallback(
[&](const RenderGraphExecutionContext& executionContext) {
computeReadView =
executionContext.ResolveTextureView(
sceneColor,
RenderGraphTextureViewType::ShaderResource);
EXPECT_NE(computeReadView, nullptr);
EXPECT_NE(computeReadView, computeWriteView);
});
});
CompiledRenderGraph compiledGraph;
XCEngine::Containers::String errorMessage;
ASSERT_TRUE(RenderGraphCompiler::Compile(graph, compiledGraph, &errorMessage))
<< errorMessage.CStr();
auto allocationState = std::make_shared<MockTransientAllocationState>();
MockTransientDevice device(allocationState);
MockTransientCommandList commandList;
RenderContext renderContext = {};
renderContext.device = &device;
renderContext.commandList = &commandList;
renderContext.commandQueue = reinterpret_cast<RHICommandQueue*>(1);
ASSERT_TRUE(RenderGraphExecutor::Execute(compiledGraph, renderContext, &errorMessage))
<< errorMessage.CStr();
ASSERT_EQ(commandList.transitionCalls.size(), 2u);
EXPECT_EQ(commandList.transitionCalls[0].resource, computeWriteView);
EXPECT_EQ(commandList.transitionCalls[0].before, ResourceStates::Common);
EXPECT_EQ(commandList.transitionCalls[0].after, ResourceStates::UnorderedAccess);
EXPECT_EQ(commandList.transitionCalls[1].resource, computeReadView);
EXPECT_EQ(commandList.transitionCalls[1].before, ResourceStates::UnorderedAccess);
EXPECT_EQ(commandList.transitionCalls[1].after, ResourceStates::GenericRead);
EXPECT_EQ(allocationState->createTextureCalls, 1);
EXPECT_EQ(allocationState->createRenderTargetViewCalls, 1);
EXPECT_EQ(allocationState->createDepthViewCalls, 0);
EXPECT_EQ(allocationState->createShaderViewCalls, 1);
EXPECT_EQ(allocationState->createUnorderedAccessViewCalls, 1);
EXPECT_EQ(allocationState->shutdownTextureCalls, 1);
EXPECT_EQ(allocationState->shutdownRenderTargetViewCalls, 1);
EXPECT_EQ(allocationState->shutdownDepthViewCalls, 0);
EXPECT_EQ(allocationState->shutdownShaderViewCalls, 1);
EXPECT_EQ(allocationState->shutdownUnorderedAccessViewCalls, 1);
EXPECT_EQ(allocationState->destroyTextureCalls, 1);
EXPECT_EQ(allocationState->destroyRenderTargetViewCalls, 1);
EXPECT_EQ(allocationState->destroyDepthViewCalls, 0);
EXPECT_EQ(allocationState->destroyShaderViewCalls, 1);
EXPECT_EQ(allocationState->destroyUnorderedAccessViewCalls, 1);
}
TEST(RenderGraph_Test, ExecutesGraphOwnedImportedUnorderedAccessTransitionsAtGraphBoundaries) {
RenderGraph graph;
RenderGraphBuilder builder(graph);
const RenderGraphTextureDesc desc = BuildTestTextureDesc();
RenderGraphImportedTextureOptions importedOptions = {};
importedOptions.initialState = ResourceStates::GenericRead;
importedOptions.finalState = ResourceStates::GenericRead;
importedOptions.graphOwnsTransitions = true;
MockImportedView importedView(
ResourceViewType::UnorderedAccess,
Format::R8G8B8A8_UNorm);
const RenderGraphTextureHandle importedTexture = builder.ImportTexture(
"ImportedComputeTarget",
desc,
&importedView,
importedOptions);
builder.AddComputePass(
"WriteImported",
[&](RenderGraphPassBuilder& pass) {
pass.WriteTexture(importedTexture);
});
CompiledRenderGraph compiledGraph;
XCEngine::Containers::String errorMessage;
ASSERT_TRUE(RenderGraphCompiler::Compile(graph, compiledGraph, &errorMessage))
<< errorMessage.CStr();
MockTransientCommandList commandList;
RenderContext renderContext = {};
renderContext.device = reinterpret_cast<RHIDevice*>(1);
renderContext.commandList = &commandList;
renderContext.commandQueue = reinterpret_cast<RHICommandQueue*>(1);
ASSERT_TRUE(RenderGraphExecutor::Execute(compiledGraph, renderContext, &errorMessage))
<< errorMessage.CStr();
ASSERT_EQ(commandList.transitionCalls.size(), 2u);
EXPECT_EQ(commandList.transitionCalls[0].resource, &importedView);
EXPECT_EQ(commandList.transitionCalls[0].before, ResourceStates::GenericRead);
EXPECT_EQ(commandList.transitionCalls[0].after, ResourceStates::UnorderedAccess);
EXPECT_EQ(commandList.transitionCalls[1].resource, &importedView);
EXPECT_EQ(commandList.transitionCalls[1].before, ResourceStates::UnorderedAccess);
EXPECT_EQ(commandList.transitionCalls[1].after, ResourceStates::GenericRead);
}
TEST(RenderGraph_Test, ExecutesCompiledPassCallbacksInCompiledOrder) {
RenderGraph graph;
RenderGraphBuilder builder(graph);