Align RenderGraph imported texture ownership
This commit is contained in:
@@ -143,6 +143,7 @@ native RenderGraph 当前支持:
|
||||
- 执行阶段通过 `RenderGraphExecutionContext` 解析 view 和 texture desc。
|
||||
- Graph-managed surface 必须关闭 `RenderSurface::autoTransition`,让 graph 统一负责 transition。
|
||||
- imported surface 的 transition ownership 只能通过 `RenderGraphImportedTextureOptions` 表达,不要在 pass 内外重复 barrier。
|
||||
- imported texture 的资源身份是 `RHITexture*`,不是某个 primary view。需要 graph 管理 transition 时,必须通过 `RenderGraphImportedTextureDesc.texture` 显式给出资源;`primaryView` 只能作为默认访问 view。surface import registry 应优先按 texture 合并,只有拿不到 texture 的非 graph-owned legacy/view-only import 才能退回按 view 区分。
|
||||
- fullscreen chain 中间输出优先使用 transient texture,不要把后处理链硬绑到 backbuffer。
|
||||
|
||||
## 6. Builtin Forward
|
||||
|
||||
@@ -83,6 +83,10 @@ public:
|
||||
|
||||
void Reset();
|
||||
|
||||
RenderGraphTextureHandle ImportTexture(
|
||||
const Containers::String& name,
|
||||
const RenderGraphImportedTextureDesc& importedDesc);
|
||||
|
||||
RenderGraphTextureHandle ImportTexture(
|
||||
const Containers::String& name,
|
||||
const RenderGraphTextureDesc& desc,
|
||||
|
||||
@@ -92,6 +92,13 @@ struct RenderGraphImportedTextureOptions {
|
||||
bool graphOwnsTransitions = false;
|
||||
};
|
||||
|
||||
struct RenderGraphImportedTextureDesc {
|
||||
RenderGraphTextureDesc textureDesc = {};
|
||||
RHI::RHITexture* texture = nullptr;
|
||||
RHI::RHIResourceView* primaryView = nullptr;
|
||||
RenderGraphImportedTextureOptions options = {};
|
||||
};
|
||||
|
||||
struct RenderGraphTextureTransitionPlan {
|
||||
bool graphOwnsTransitions = false;
|
||||
bool hasFirstAccessState = false;
|
||||
|
||||
@@ -77,15 +77,25 @@ RenderGraphTextureHandle ImportRenderGraphTexture(
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto existing = registry.find(view);
|
||||
RHI::RHITexture* const texture = view->GetTextureResource();
|
||||
RenderGraphImportedTextureRegistryKey registryKey = {};
|
||||
registryKey.texture = texture;
|
||||
registryKey.view = texture == nullptr ? view : nullptr;
|
||||
|
||||
const auto existing = registry.find(registryKey);
|
||||
if (existing != registry.end()) {
|
||||
builder.MergeImportedTextureOptions(existing->second, importedOptions);
|
||||
return existing->second;
|
||||
}
|
||||
|
||||
RenderGraphImportedTextureDesc importedDesc = {};
|
||||
importedDesc.textureDesc = desc;
|
||||
importedDesc.texture = texture;
|
||||
importedDesc.primaryView = view;
|
||||
importedDesc.options = importedOptions;
|
||||
const RenderGraphTextureHandle handle =
|
||||
builder.ImportTexture(name, desc, view, importedOptions);
|
||||
registry.emplace(view, handle);
|
||||
builder.ImportTexture(name, importedDesc);
|
||||
registry.emplace(registryKey, handle);
|
||||
return handle;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,11 +5,13 @@
|
||||
#include <XCEngine/Rendering/Graph/RenderGraphTypes.h>
|
||||
#include <XCEngine/Rendering/RenderSurface.h>
|
||||
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace RHI {
|
||||
class RHITexture;
|
||||
class RHIResourceView;
|
||||
} // namespace RHI
|
||||
|
||||
@@ -30,8 +32,27 @@ enum class RenderGraphSurfaceAccessMode {
|
||||
ColorDepth = 1
|
||||
};
|
||||
|
||||
struct RenderGraphImportedTextureRegistryKey {
|
||||
RHI::RHITexture* texture = nullptr;
|
||||
RHI::RHIResourceView* view = nullptr;
|
||||
|
||||
bool operator==(const RenderGraphImportedTextureRegistryKey& other) const {
|
||||
return texture == other.texture && view == other.view;
|
||||
}
|
||||
};
|
||||
|
||||
struct RenderGraphImportedTextureRegistryKeyHash {
|
||||
size_t operator()(const RenderGraphImportedTextureRegistryKey& key) const {
|
||||
return std::hash<RHI::RHITexture*>{}(key.texture) ^
|
||||
(std::hash<RHI::RHIResourceView*>{}(key.view) << 1u);
|
||||
}
|
||||
};
|
||||
|
||||
using RenderGraphImportedTextureRegistry =
|
||||
std::unordered_map<RHI::RHIResourceView*, RenderGraphTextureHandle>;
|
||||
std::unordered_map<
|
||||
RenderGraphImportedTextureRegistryKey,
|
||||
RenderGraphTextureHandle,
|
||||
RenderGraphImportedTextureRegistryKeyHash>;
|
||||
|
||||
enum class RenderGraphSurfaceImportUsage {
|
||||
Source = 0,
|
||||
|
||||
@@ -79,17 +79,14 @@ void RenderGraphBuilder::Reset() {
|
||||
|
||||
RenderGraphTextureHandle RenderGraphBuilder::ImportTexture(
|
||||
const Containers::String& name,
|
||||
const RenderGraphTextureDesc& desc,
|
||||
RHI::RHIResourceView* importedView,
|
||||
const RenderGraphImportedTextureOptions& importedOptions) {
|
||||
const RenderGraphImportedTextureDesc& importedDesc) {
|
||||
RenderGraph::TextureResource resource = {};
|
||||
resource.name = name;
|
||||
resource.desc = desc;
|
||||
resource.desc = importedDesc.textureDesc;
|
||||
resource.kind = RenderGraphTextureKind::Imported;
|
||||
resource.importedView = importedView;
|
||||
resource.importedTexture =
|
||||
importedView != nullptr ? importedView->GetTextureResource() : nullptr;
|
||||
resource.importedOptions = importedOptions;
|
||||
resource.importedView = importedDesc.primaryView;
|
||||
resource.importedTexture = importedDesc.texture;
|
||||
resource.importedOptions = importedDesc.options;
|
||||
m_graph.m_textures.push_back(resource);
|
||||
|
||||
RenderGraphTextureHandle handle = {};
|
||||
@@ -97,6 +94,20 @@ RenderGraphTextureHandle RenderGraphBuilder::ImportTexture(
|
||||
return handle;
|
||||
}
|
||||
|
||||
RenderGraphTextureHandle RenderGraphBuilder::ImportTexture(
|
||||
const Containers::String& name,
|
||||
const RenderGraphTextureDesc& desc,
|
||||
RHI::RHIResourceView* importedView,
|
||||
const RenderGraphImportedTextureOptions& importedOptions) {
|
||||
RenderGraphImportedTextureDesc importedDesc = {};
|
||||
importedDesc.textureDesc = desc;
|
||||
importedDesc.texture =
|
||||
importedView != nullptr ? importedView->GetTextureResource() : nullptr;
|
||||
importedDesc.primaryView = importedView;
|
||||
importedDesc.options = importedOptions;
|
||||
return ImportTexture(name, importedDesc);
|
||||
}
|
||||
|
||||
RenderGraphTextureHandle RenderGraphBuilder::CreateTransientTexture(
|
||||
const Containers::String& name,
|
||||
const RenderGraphTextureDesc& desc) {
|
||||
|
||||
@@ -117,7 +117,7 @@ bool RenderGraphCompiler::Compile(
|
||||
texture.importedOptions.graphOwnsTransitions &&
|
||||
texture.importedTexture == nullptr) {
|
||||
WriteError(
|
||||
Containers::String("RenderGraph imported texture requires a texture-backed view when graph owns transitions: ") +
|
||||
Containers::String("RenderGraph graph-owned imported texture requires an explicit RHI texture resource: ") +
|
||||
texture.name,
|
||||
outErrorMessage);
|
||||
return false;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "Rendering/Internal/ShaderVariantUtils.h"
|
||||
#include "Rendering/Pipelines/Internal/BuiltinForwardSceneSetup.h"
|
||||
|
||||
#include <XCEngine/Debug/Logger.h>
|
||||
#include <XCEngine/Rendering/Builtin/BuiltinPassLayoutUtils.h>
|
||||
@@ -12,6 +13,7 @@
|
||||
#include <XCEngine/Rendering/Passes/BuiltinShadowCasterPass.h>
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#define private public
|
||||
#include <XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h>
|
||||
@@ -102,6 +104,35 @@ void AppendDefaultBuiltinPassResources(ShaderPass& pass) {
|
||||
}
|
||||
}
|
||||
|
||||
class TestResourceViewBackingTexture final : public XCEngine::RHI::RHITexture {
|
||||
public:
|
||||
explicit TestResourceViewBackingTexture(
|
||||
XCEngine::RHI::Format format,
|
||||
XCEngine::RHI::TextureType textureType = XCEngine::RHI::TextureType::Texture2D)
|
||||
: m_format(format)
|
||||
, m_textureType(textureType) {
|
||||
}
|
||||
|
||||
uint32_t GetWidth() const override { return 1u; }
|
||||
uint32_t GetHeight() const override { return 1u; }
|
||||
uint32_t GetDepth() const override { return 1u; }
|
||||
uint32_t GetMipLevels() const override { return 1u; }
|
||||
XCEngine::RHI::Format GetFormat() const override { return m_format; }
|
||||
XCEngine::RHI::TextureType GetTextureType() const override { return m_textureType; }
|
||||
XCEngine::RHI::ResourceStates GetState() const override { return m_state; }
|
||||
void SetState(XCEngine::RHI::ResourceStates state) override { m_state = state; }
|
||||
void* GetNativeHandle() override { return nullptr; }
|
||||
const std::string& GetName() const override { return m_name; }
|
||||
void SetName(const std::string& name) override { m_name = name; }
|
||||
void Shutdown() override {}
|
||||
|
||||
private:
|
||||
XCEngine::RHI::Format m_format = XCEngine::RHI::Format::Unknown;
|
||||
XCEngine::RHI::TextureType m_textureType = XCEngine::RHI::TextureType::Texture2D;
|
||||
XCEngine::RHI::ResourceStates m_state = XCEngine::RHI::ResourceStates::Common;
|
||||
std::string m_name;
|
||||
};
|
||||
|
||||
class TestResourceView final : public XCEngine::RHI::RHIResourceView {
|
||||
public:
|
||||
TestResourceView(
|
||||
@@ -136,10 +167,16 @@ public:
|
||||
return m_format;
|
||||
}
|
||||
|
||||
XCEngine::RHI::RHITexture* GetTextureResource() const override {
|
||||
return m_texture.get();
|
||||
}
|
||||
|
||||
private:
|
||||
XCEngine::RHI::ResourceViewType m_viewType;
|
||||
XCEngine::RHI::ResourceViewDimension m_dimension;
|
||||
XCEngine::RHI::Format m_format;
|
||||
std::unique_ptr<TestResourceViewBackingTexture> m_texture =
|
||||
std::make_unique<TestResourceViewBackingTexture>(m_format);
|
||||
};
|
||||
|
||||
class MockForwardPipelineLayout final : public XCEngine::RHI::RHIPipelineLayout {
|
||||
@@ -1188,6 +1225,8 @@ TEST(SceneRenderFeaturePass_Test, ReadsSourceColorTextureAndCopiesSourceSurfaceT
|
||||
|
||||
TEST(BuiltinForwardPipeline_Test, RegistersBuiltinDefaultForwardSceneFeatures) {
|
||||
BuiltinForwardPipeline pipeline;
|
||||
XCEngine::Rendering::Pipelines::Internal::ConfigureBuiltinForwardSceneDrawBackend(
|
||||
pipeline);
|
||||
|
||||
ASSERT_EQ(pipeline.GetForwardSceneFeaturePassCount(), 2u);
|
||||
ASSERT_NE(pipeline.GetForwardSceneFeaturePass(0u), nullptr);
|
||||
@@ -1414,6 +1453,8 @@ TEST(BuiltinForwardPipeline_Test, RegistersAdditionalForwardSceneFeaturePasses)
|
||||
|
||||
TEST(BuiltinForwardPipeline_Test, RecordsActiveFeatureInjectionPassesIntoMainSceneGraph) {
|
||||
BuiltinForwardPipeline pipeline;
|
||||
XCEngine::Rendering::Pipelines::Internal::ConfigureBuiltinForwardSceneDrawBackend(
|
||||
pipeline);
|
||||
|
||||
RenderGraph graph = {};
|
||||
RenderGraphBuilder graphBuilder(graph);
|
||||
|
||||
@@ -188,11 +188,11 @@ RenderGraphTextureDesc BuildTestRenderGraphTextureDesc() {
|
||||
RenderGraphTextureHandle ImportTestTexture(
|
||||
RenderGraphBuilder& graphBuilder,
|
||||
size_t viewId) {
|
||||
return graphBuilder.ImportTexture(
|
||||
"Imported",
|
||||
BuildTestRenderGraphTextureDesc(),
|
||||
reinterpret_cast<XCEngine::RHI::RHIResourceView*>(viewId),
|
||||
{});
|
||||
RenderGraphImportedTextureDesc importedDesc = {};
|
||||
importedDesc.textureDesc = BuildTestRenderGraphTextureDesc();
|
||||
importedDesc.primaryView =
|
||||
reinterpret_cast<XCEngine::RHI::RHIResourceView*>(viewId);
|
||||
return graphBuilder.ImportTexture("Imported", importedDesc);
|
||||
}
|
||||
|
||||
RenderSurface CreateColorSurface(
|
||||
@@ -483,6 +483,7 @@ TEST(CameraFrameRenderGraphStageContract_Test, ResolvesScenePassRequestsFromRunt
|
||||
testContext.plan.request.depthOnly.surface = RenderSurface(320, 180);
|
||||
testContext.plan.request.depthOnly.surface.SetDepthAttachment(
|
||||
reinterpret_cast<XCEngine::RHI::RHIResourceView*>(142));
|
||||
testContext.plan.RequestDepthOnlyStage(true);
|
||||
|
||||
const CameraFrameRenderGraphStageContext context = testContext.BuildStageContext();
|
||||
|
||||
@@ -1534,6 +1535,7 @@ TEST(CameraFrameRenderGraphStageContract_Test, ExecutesDepthOnlyFallbackPassUsin
|
||||
testContext.plan.request.depthOnly.surface.SetDepthAttachment(
|
||||
reinterpret_cast<XCEngine::RHI::RHIResourceView*>(501));
|
||||
testContext.plan.request.depthOnly.clearFlags = RenderClearFlags::Color;
|
||||
testContext.plan.RequestDepthOnlyStage(true);
|
||||
testContext.sceneData.cameraData.viewportWidth = 111u;
|
||||
|
||||
CameraFrameStageGraphBuildState stageState = {};
|
||||
|
||||
@@ -122,13 +122,50 @@ private:
|
||||
Format m_format = Format::Unknown;
|
||||
};
|
||||
|
||||
class MockImportedTexture final : public RHITexture {
|
||||
public:
|
||||
explicit MockImportedTexture(
|
||||
Format format = Format::R8G8B8A8_UNorm,
|
||||
uint32_t width = 1280u,
|
||||
uint32_t height = 720u)
|
||||
: m_format(format)
|
||||
, m_width(width)
|
||||
, m_height(height) {
|
||||
}
|
||||
|
||||
uint32_t GetWidth() const override { return m_width; }
|
||||
uint32_t GetHeight() const override { return m_height; }
|
||||
uint32_t GetDepth() const override { return 1u; }
|
||||
uint32_t GetMipLevels() const override { return 1u; }
|
||||
Format GetFormat() const override { return m_format; }
|
||||
TextureType GetTextureType() const override { return TextureType::Texture2D; }
|
||||
ResourceStates GetState() const override { return m_stateValue; }
|
||||
void SetState(ResourceStates state) override { m_stateValue = state; }
|
||||
void* GetNativeHandle() override { return nullptr; }
|
||||
const std::string& GetName() const override { return m_name; }
|
||||
void SetName(const std::string& name) override { m_name = name; }
|
||||
void Shutdown() override {}
|
||||
|
||||
private:
|
||||
Format m_format = Format::Unknown;
|
||||
uint32_t m_width = 0u;
|
||||
uint32_t m_height = 0u;
|
||||
ResourceStates m_stateValue = ResourceStates::Common;
|
||||
std::string m_name;
|
||||
};
|
||||
|
||||
class MockImportedView final : public RHIResourceView {
|
||||
public:
|
||||
explicit MockImportedView(
|
||||
ResourceViewType viewType = ResourceViewType::RenderTarget,
|
||||
Format format = Format::R8G8B8A8_UNorm)
|
||||
Format format = Format::R8G8B8A8_UNorm,
|
||||
RHITexture* texture = nullptr)
|
||||
: m_viewType(viewType)
|
||||
, m_format(format) {
|
||||
, m_format(format)
|
||||
, m_ownedTexture(texture == nullptr
|
||||
? std::make_unique<MockImportedTexture>(format)
|
||||
: nullptr)
|
||||
, m_texture(texture != nullptr ? texture : m_ownedTexture.get()) {
|
||||
}
|
||||
|
||||
void Shutdown() override {}
|
||||
@@ -137,10 +174,13 @@ public:
|
||||
ResourceViewType GetViewType() const override { return m_viewType; }
|
||||
ResourceViewDimension GetDimension() const override { return ResourceViewDimension::Texture2D; }
|
||||
Format GetFormat() const override { return m_format; }
|
||||
RHITexture* GetTextureResource() const override { return m_texture; }
|
||||
|
||||
private:
|
||||
ResourceViewType m_viewType = ResourceViewType::RenderTarget;
|
||||
Format m_format = Format::R8G8B8A8_UNorm;
|
||||
std::unique_ptr<MockImportedTexture> m_ownedTexture;
|
||||
RHITexture* m_texture = nullptr;
|
||||
};
|
||||
|
||||
class MockTransientDevice final : public RHIDevice {
|
||||
@@ -396,14 +436,16 @@ TEST(RenderGraph_Test, OrdersImportedTextureHazardsAcrossFullscreenStyleChain) {
|
||||
RenderGraphBuilder builder(graph);
|
||||
|
||||
const RenderGraphTextureDesc desc = BuildTestTextureDesc();
|
||||
MockImportedView sceneColorView;
|
||||
MockImportedView postColorView;
|
||||
const RenderGraphTextureHandle sceneColor = builder.ImportTexture(
|
||||
"SceneColor",
|
||||
desc,
|
||||
reinterpret_cast<RHIResourceView*>(1));
|
||||
&sceneColorView);
|
||||
const RenderGraphTextureHandle postColor = builder.ImportTexture(
|
||||
"PostColor",
|
||||
desc,
|
||||
reinterpret_cast<RHIResourceView*>(2));
|
||||
&postColorView);
|
||||
|
||||
builder.AddRasterPass(
|
||||
"MainScene",
|
||||
@@ -457,10 +499,13 @@ TEST(RenderGraph_Test, PreservesImportedTextureStateContractAcrossCompile) {
|
||||
importedOptions.finalState = ResourceStates::PixelShaderResource;
|
||||
importedOptions.graphOwnsTransitions = true;
|
||||
|
||||
MockImportedView importedView(
|
||||
ResourceViewType::ShaderResource,
|
||||
Format::R8G8B8A8_UNorm);
|
||||
const RenderGraphTextureHandle importedTexture = builder.ImportTexture(
|
||||
"ImportedColor",
|
||||
desc,
|
||||
reinterpret_cast<RHIResourceView*>(7),
|
||||
&importedView,
|
||||
importedOptions);
|
||||
|
||||
builder.AddRasterPass(
|
||||
@@ -676,15 +721,15 @@ TEST(RenderGraph_Test, ExecutesTransientDepthTransitionsWithDepthStencilView) {
|
||||
EXPECT_EQ(allocationState->createTextureCalls, 1);
|
||||
EXPECT_EQ(allocationState->createRenderTargetViewCalls, 0);
|
||||
EXPECT_EQ(allocationState->createDepthViewCalls, 1);
|
||||
EXPECT_EQ(allocationState->createShaderViewCalls, 0);
|
||||
EXPECT_EQ(allocationState->createShaderViewCalls, 1);
|
||||
EXPECT_EQ(allocationState->shutdownTextureCalls, 1);
|
||||
EXPECT_EQ(allocationState->shutdownRenderTargetViewCalls, 0);
|
||||
EXPECT_EQ(allocationState->shutdownDepthViewCalls, 1);
|
||||
EXPECT_EQ(allocationState->shutdownShaderViewCalls, 0);
|
||||
EXPECT_EQ(allocationState->shutdownShaderViewCalls, 1);
|
||||
EXPECT_EQ(allocationState->destroyTextureCalls, 1);
|
||||
EXPECT_EQ(allocationState->destroyRenderTargetViewCalls, 0);
|
||||
EXPECT_EQ(allocationState->destroyDepthViewCalls, 1);
|
||||
EXPECT_EQ(allocationState->destroyShaderViewCalls, 0);
|
||||
EXPECT_EQ(allocationState->destroyShaderViewCalls, 1);
|
||||
}
|
||||
|
||||
TEST(RenderGraph_Test, ExecutesGraphOwnedImportedDepthTransitionsAtGraphBoundaries) {
|
||||
@@ -864,10 +909,11 @@ TEST(RenderGraph_Test, ExecutesCompiledPassCallbacksInCompiledOrder) {
|
||||
|
||||
const RenderGraphTextureDesc desc = BuildTestTextureDesc();
|
||||
const RenderGraphTextureHandle sceneColor = builder.CreateTransientTexture("SceneColor", desc);
|
||||
MockImportedView backBufferView;
|
||||
const RenderGraphTextureHandle backBuffer = builder.ImportTexture(
|
||||
"BackBuffer",
|
||||
desc,
|
||||
reinterpret_cast<RHIResourceView*>(1));
|
||||
&backBufferView);
|
||||
|
||||
std::vector<std::string> eventLog;
|
||||
auto allocationState = std::make_shared<MockTransientAllocationState>();
|
||||
|
||||
Reference in New Issue
Block a user