diff --git a/docs/used/SRP_URP_RenderGraphImportedSurfaceMultiViewPlan_2026-04-22_完成归档.md b/docs/used/SRP_URP_RenderGraphImportedSurfaceMultiViewPlan_2026-04-22_完成归档.md new file mode 100644 index 00000000..379b5226 --- /dev/null +++ b/docs/used/SRP_URP_RenderGraphImportedSurfaceMultiViewPlan_2026-04-22_完成归档.md @@ -0,0 +1,93 @@ +# SRP / URP Render Graph Imported Surface Multi-View Plan + +时间:2026-04-22 + +## 背景 + +上一阶段已经打通了 managed fullscreen/custom pass 的额外纹理绑定链路,也补上了 transient depth 的 SRV 创建。 + +但 render graph 对 imported texture 的建模仍然是不完整的: + +1. `RenderGraph::TextureResource` 只保存了一个 `importedView` +2. `RenderGraphRuntimeResources::ResolveTextureView(...)` 对 imported texture 不区分请求的 view type,直接返回这一份 view +3. imported color / depth 一旦进入 graph,就无法稳定地在不同阶段之间切换 `RTV / DSV / SRV` +4. graph-managed imported surface 在 fullscreen/custom pass 中采样 imported depth / color 时,仍然可能拿到错误类型的 view + +这会导致 render graph 表面上已经接管 imported surface 的生命周期与状态调度,但在真正执行时还没有形成“纹理资源 + 多视图解析”的闭环。 + +## 本阶段目标 + +把 imported texture 从“只有一个盲视图指针”升级成“保留底层纹理资源,并能按需解析不同 view type”的运行时模型。 + +阶段完成后应满足: + +1. imported texture 能从 `RHIResourceView` 反查到底层 `RHITexture` +2. render graph 能为 imported texture 按请求解析 `RenderTarget / DepthStencil / ShaderResource / UnorderedAccess` +3. 对 graph-owned imported transitions,不再依赖导入时碰巧传进来的那一个 view 类型 +4. managed fullscreen/custom pass 采样 imported surface 时,color / depth 都能走统一的 `ResolveTextureView(..., ShaderResource)` 路径 +5. `XCEditor` Debug 构建通过,old editor 冒烟至少 10-15 秒并出现 `SceneReady` + +## 范围 + +本阶段只收口 imported texture multi-view 运行时能力,不做: + +- deferred pipeline +- shadow 系统迁移 +- editor 侧功能扩展 +- imported buffer multi-view 建模 +- RenderSurface 大规模重做 + +## 实施步骤 + +### 1. 抬升 texture-backed view 抽象 + +在 `RHIResourceView` 抽象层增加统一接口,让上层可以判断一个 view 是否绑定到某个 `RHITexture`。 + +要求: + +- `RHIResourceView` 暴露 `GetTextureResource()` +- Vulkan / OpenGL 返回各自已经持有的 texture 指针 +- D3D12 纹理视图补齐 `D3D12Texture*` 存储,并通过该接口返回 +- buffer view 继续返回空指针 + +### 2. 扩展 render graph imported texture 元数据 + +在 render graph builder / compiler / runtime 之间,把 imported texture 的“底层纹理资源”一起传下去,而不是只传一份 primary view。 + +要求: + +- `RenderGraph::TextureResource` 记录 imported texture 指针 +- `CompiledRenderGraph::CompiledTexture` 同步保存该信息 +- compiler 对 graph-owned imported transitions 增加更严格校验: + `graphOwnsTransitions == true` 时必须导入 texture-backed view + +### 3. 重做 imported texture runtime view 解析 + +把 imported texture 的运行时解析从“固定返回 importedView”改成: + +1. 如果 primary imported view 类型正好匹配请求,则直接返回 primary view +2. 如果类型不匹配,但有底层 imported texture,则按需创建目标 view +3. 运行时缓存这些按需创建的派生 view,并在 graph 执行结束后释放 + +要求: + +- `ResolveTextureView(handle, viewType)` 对 imported / transient 路径统一 +- 派生 view 创建走 `RHIDevice`,不允许在 render graph 里做后端特化 cast +- depth texture 不创建 RTV,color texture 不创建 DSV +- 失败时返回空指针,由上层执行链路报错 + +### 4. 完成验证与归档 + +执行以下验证闭环: + +1. `cmake --build . --config Debug --target XCEditor` +2. 运行 old editor 冒烟 15 秒 +3. 检查 `editor/bin/Debug/editor.log` 出现 `SceneReady` +4. plan 归档到 `docs/used` +5. 使用规范 Conventional Commit 提交并推送 + +## 风险与边界 + +1. swapchain backbuffer 是否支持 SRV,取决于后端和资源创建方式;本阶段不为此额外改造 swapchain 资源描述 +2. 如果某个 imported view 不是 texture-backed view,本阶段不会伪造多视图能力 +3. 本阶段先解决“可正确建模并解析 imported texture view”的根问题,不扩展更高层 surface API diff --git a/engine/include/XCEngine/RHI/D3D12/D3D12ResourceView.h b/engine/include/XCEngine/RHI/D3D12/D3D12ResourceView.h index 630dadb0..05e15461 100644 --- a/engine/include/XCEngine/RHI/D3D12/D3D12ResourceView.h +++ b/engine/include/XCEngine/RHI/D3D12/D3D12ResourceView.h @@ -6,6 +6,7 @@ #include "../RHIResourceView.h" #include "../RHIEnums.h" +#include "D3D12Texture.h" #include "D3D12Enums.h" using Microsoft::WRL::ComPtr; @@ -15,7 +16,6 @@ namespace RHI { class D3D12DescriptorHeap; class D3D12Buffer; - class D3D12ResourceView : public RHIResourceView { public: D3D12ResourceView(); @@ -30,8 +30,10 @@ public: ResourceViewType GetViewType() const override { return m_viewType; } ResourceViewDimension GetDimension() const override { return m_dimension; } Format GetFormat() const override { return m_format; } + RHITexture* GetTextureResource() const override { return m_texture; } void SetOwnedHeap(std::unique_ptr heap); + void SetTextureResource(D3D12Texture* texture) { m_texture = texture; } void InitializeAsRenderTarget(ID3D12Device* device, ID3D12Resource* resource, const D3D12_RENDER_TARGET_VIEW_DESC* desc, @@ -86,6 +88,7 @@ private: ResourceViewDimension m_dimension; D3D12_CPU_DESCRIPTOR_HANDLE m_handle; ID3D12Resource* m_resource; + D3D12Texture* m_texture; D3D12DescriptorHeap* m_heap; uint32_t m_slotIndex; std::unique_ptr m_ownedHeap; diff --git a/engine/include/XCEngine/RHI/OpenGL/OpenGLResourceView.h b/engine/include/XCEngine/RHI/OpenGL/OpenGLResourceView.h index 57c24c73..5a7fad3c 100644 --- a/engine/include/XCEngine/RHI/OpenGL/OpenGLResourceView.h +++ b/engine/include/XCEngine/RHI/OpenGL/OpenGLResourceView.h @@ -1,6 +1,7 @@ #pragma once #include "XCEngine/RHI/OpenGL/OpenGLFramebuffer.h" +#include "XCEngine/RHI/OpenGL/OpenGLTexture.h" #include "XCEngine/RHI/RHIResourceView.h" #include "XCEngine/RHI/RHITypes.h" @@ -71,7 +72,8 @@ public: uint32_t GetBufferSize() const { return m_bufferSize; } uint32_t GetBufferStride() const { return m_bufferStride; } const FramebufferAttachment& GetFramebufferAttachment() const { return m_framebufferAttachment; } - const OpenGLTexture* GetTextureResource() const { return m_texture; } + RHITexture* GetTextureResource() const override { return m_texture; } + OpenGLTexture* GetOpenGLTextureResource() const { return m_texture; } private: ResourceViewType m_viewType; diff --git a/engine/include/XCEngine/RHI/RHIResourceView.h b/engine/include/XCEngine/RHI/RHIResourceView.h index 7ab160fd..a55679a6 100644 --- a/engine/include/XCEngine/RHI/RHIResourceView.h +++ b/engine/include/XCEngine/RHI/RHIResourceView.h @@ -6,6 +6,8 @@ namespace XCEngine { namespace RHI { +class RHITexture; + class RHIResourceView { public: virtual ~RHIResourceView() = default; @@ -19,6 +21,9 @@ public: virtual ResourceViewType GetViewType() const = 0; virtual ResourceViewDimension GetDimension() const = 0; virtual Format GetFormat() const = 0; + virtual RHITexture* GetTextureResource() const { + return nullptr; + } }; class RHIVertexBufferView : public RHIResourceView { diff --git a/engine/include/XCEngine/RHI/Vulkan/VulkanResourceView.h b/engine/include/XCEngine/RHI/Vulkan/VulkanResourceView.h index e4b881fd..f5488582 100644 --- a/engine/include/XCEngine/RHI/Vulkan/VulkanResourceView.h +++ b/engine/include/XCEngine/RHI/Vulkan/VulkanResourceView.h @@ -2,13 +2,12 @@ #include "XCEngine/RHI/RHIResourceView.h" #include "XCEngine/RHI/Vulkan/VulkanCommon.h" +#include "XCEngine/RHI/Vulkan/VulkanTexture.h" namespace XCEngine { namespace RHI { class VulkanBuffer; -class VulkanTexture; - class VulkanResourceView : public RHIResourceView { public: VulkanResourceView() = default; @@ -30,6 +29,7 @@ public: ResourceViewType GetViewType() const override { return m_viewType; } ResourceViewDimension GetDimension() const override { return m_dimension; } Format GetFormat() const override { return m_format; } + RHITexture* GetTextureResource() const override { return m_texture; } VulkanTexture* GetTexture() const { return m_texture; } VkImageView GetImageView() const { return m_imageView; } diff --git a/engine/include/XCEngine/Rendering/Graph/RenderGraph.h b/engine/include/XCEngine/Rendering/Graph/RenderGraph.h index 188c7767..11f7d1d7 100644 --- a/engine/include/XCEngine/Rendering/Graph/RenderGraph.h +++ b/engine/include/XCEngine/Rendering/Graph/RenderGraph.h @@ -32,6 +32,7 @@ private: RenderGraphTextureDesc desc = {}; RenderGraphTextureKind kind = RenderGraphTextureKind::Transient; RHI::RHIResourceView* importedView = nullptr; + RHI::RHITexture* importedTexture = nullptr; RenderGraphImportedTextureOptions importedOptions = {}; }; diff --git a/engine/include/XCEngine/Rendering/Graph/RenderGraphCompiler.h b/engine/include/XCEngine/Rendering/Graph/RenderGraphCompiler.h index 2bfb047b..5d3a8698 100644 --- a/engine/include/XCEngine/Rendering/Graph/RenderGraphCompiler.h +++ b/engine/include/XCEngine/Rendering/Graph/RenderGraphCompiler.h @@ -48,6 +48,7 @@ private: RenderGraphTextureDesc desc = {}; RenderGraphTextureKind kind = RenderGraphTextureKind::Transient; RHI::RHIResourceView* importedView = nullptr; + RHI::RHITexture* importedTexture = nullptr; RenderGraphImportedTextureOptions importedOptions = {}; }; diff --git a/engine/include/XCEngine/Rendering/Graph/RenderGraphTypes.h b/engine/include/XCEngine/Rendering/Graph/RenderGraphTypes.h index dfe446a3..3c1ab073 100644 --- a/engine/include/XCEngine/Rendering/Graph/RenderGraphTypes.h +++ b/engine/include/XCEngine/Rendering/Graph/RenderGraphTypes.h @@ -104,7 +104,7 @@ struct RenderGraphTextureTransitionPlan { struct RenderGraphExecutionContext { const RenderContext& renderContext; - const RenderGraphRuntimeResources* runtimeResources = nullptr; + RenderGraphRuntimeResources* runtimeResources = nullptr; RHI::RHIResourceView* ResolveTextureView( RenderGraphTextureHandle handle, diff --git a/engine/src/RHI/D3D12/D3D12Device.cpp b/engine/src/RHI/D3D12/D3D12Device.cpp index 5b431b06..3914ab3b 100644 --- a/engine/src/RHI/D3D12/D3D12Device.cpp +++ b/engine/src/RHI/D3D12/D3D12Device.cpp @@ -1597,6 +1597,7 @@ RHIResourceView* D3D12Device::CreateRenderTargetView(RHITexture* texture, const } view->InitializeAsRenderTarget(m_device.Get(), resource, &rtvDesc, heap.get(), 0); + view->SetTextureResource(d3d12Texture); view->SetOwnedHeap(std::move(heap)); return view; @@ -1619,6 +1620,7 @@ RHIResourceView* D3D12Device::CreateDepthStencilView(RHITexture* texture, const } view->InitializeAsDepthStencil(m_device.Get(), resource, &dsvDesc, heap.get(), 0); + view->SetTextureResource(d3d12Texture); view->SetOwnedHeap(std::move(heap)); return view; } @@ -1738,6 +1740,7 @@ RHIResourceView* D3D12Device::CreateShaderResourceView(RHITexture* texture, cons } view->InitializeAsShaderResource(m_device.Get(), resource, &srvDesc, heap.get(), 0); + view->SetTextureResource(d3d12Texture); if (IsDepthFormat(format)) { view->SetFormat(format); } @@ -1815,6 +1818,7 @@ RHIResourceView* D3D12Device::CreateUnorderedAccessView(RHITexture* texture, con } view->InitializeAsUnorderedAccess(m_device.Get(), resource, &uavDesc, heap.get(), 0); + view->SetTextureResource(d3d12Texture); view->SetOwnedHeap(std::move(heap)); return view; } diff --git a/engine/src/RHI/D3D12/D3D12ResourceView.cpp b/engine/src/RHI/D3D12/D3D12ResourceView.cpp index 47c44625..d87b51a1 100644 --- a/engine/src/RHI/D3D12/D3D12ResourceView.cpp +++ b/engine/src/RHI/D3D12/D3D12ResourceView.cpp @@ -111,6 +111,7 @@ D3D12ResourceView::D3D12ResourceView() , m_dimension(ResourceViewDimension::Unknown) , m_handle({0}) , m_resource(nullptr) + , m_texture(nullptr) , m_heap(nullptr) , m_slotIndex(0) , m_ownedHeap(nullptr) @@ -127,6 +128,7 @@ D3D12ResourceView::~D3D12ResourceView() { void D3D12ResourceView::Shutdown() { m_handle = {}; m_resource = nullptr; + m_texture = nullptr; if (m_ownedHeap) { m_ownedHeap->Shutdown(); m_ownedHeap.reset(); diff --git a/engine/src/RHI/OpenGL/OpenGLCommandList.cpp b/engine/src/RHI/OpenGL/OpenGLCommandList.cpp index 1c7d690d..385f5332 100644 --- a/engine/src/RHI/OpenGL/OpenGLCommandList.cpp +++ b/engine/src/RHI/OpenGL/OpenGLCommandList.cpp @@ -52,7 +52,7 @@ bool GetOpenGLClearTargetExtent(const OpenGLResourceView* view, GLsizei& width, return false; } - const OpenGLTexture* texture = view->GetTextureResource(); + const OpenGLTexture* texture = view->GetOpenGLTextureResource(); if (texture == nullptr) { return false; } @@ -631,7 +631,7 @@ void OpenGLCommandList::TransitionBarrier(RHIResourceView* resource, ResourceSta (void)stateBefore; if (resource != nullptr && resource->IsValid()) { OpenGLResourceView* view = static_cast(resource); - OpenGLTexture* texture = const_cast(view->GetTextureResource()); + OpenGLTexture* texture = view->GetOpenGLTextureResource(); if (texture != nullptr) { texture->SetState(stateAfter); } @@ -750,7 +750,7 @@ void OpenGLCommandList::SetRenderTargets(uint32_t count, RHIResourceView** rende for (uint32_t i = 0; i < count; ++i) { auto* view = static_cast(renderTargets[i]); - const OpenGLTexture* texture = view != nullptr ? view->GetTextureResource() : nullptr; + const OpenGLTexture* texture = view != nullptr ? view->GetOpenGLTextureResource() : nullptr; if (texture != nullptr) { width = texture->GetWidth(); height = texture->GetHeight(); @@ -760,7 +760,7 @@ void OpenGLCommandList::SetRenderTargets(uint32_t count, RHIResourceView** rende if ((width == 0 || height == 0) && depthStencil != nullptr) { auto* dsv = static_cast(depthStencil); - const OpenGLTexture* depthTexture = dsv != nullptr ? dsv->GetTextureResource() : nullptr; + const OpenGLTexture* depthTexture = dsv != nullptr ? dsv->GetOpenGLTextureResource() : nullptr; if (depthTexture != nullptr) { width = depthTexture->GetWidth(); height = depthTexture->GetHeight(); @@ -782,7 +782,7 @@ void OpenGLCommandList::SetRenderTargets(uint32_t count, RHIResourceView** rende } else { ReleaseComposedFramebuffer(); OpenGLResourceView* view = static_cast(renderTargets[0]); - const OpenGLTexture* texture = view != nullptr ? view->GetTextureResource() : nullptr; + const OpenGLTexture* texture = view != nullptr ? view->GetOpenGLTextureResource() : nullptr; if (texture != nullptr) { m_currentRenderTargetWidth = texture->GetWidth(); m_currentRenderTargetHeight = texture->GetHeight(); @@ -795,7 +795,7 @@ void OpenGLCommandList::SetRenderTargets(uint32_t count, RHIResourceView** rende ReleaseComposedFramebuffer(); OpenGLResourceView* dsv = static_cast(depthStencil); if (dsv && dsv->IsValid()) { - const OpenGLTexture* depthTexture = dsv->GetTextureResource(); + const OpenGLTexture* depthTexture = dsv->GetOpenGLTextureResource(); if (depthTexture != nullptr) { m_currentRenderTargetWidth = depthTexture->GetWidth(); m_currentRenderTargetHeight = depthTexture->GetHeight(); @@ -1056,8 +1056,8 @@ void OpenGLCommandList::CopyResource(RHIResourceView* dst, RHIResourceView* src) GLuint dstTex = dstView->GetTexture(); GLuint srcTex = srcView->GetTexture(); - const OpenGLTexture* dstTexture = dstView->GetTextureResource(); - const OpenGLTexture* srcTexture = srcView->GetTextureResource(); + const OpenGLTexture* dstTexture = dstView->GetOpenGLTextureResource(); + const OpenGLTexture* srcTexture = srcView->GetOpenGLTextureResource(); if (dstTex && srcTex && dstTexture != nullptr && srcTexture != nullptr) { const GLuint srcTarget = ToOpenGL(srcTexture->GetOpenGLType()); const GLuint dstTarget = ToOpenGL(dstTexture->GetOpenGLType()); diff --git a/engine/src/RHI/OpenGL/OpenGLDescriptorSet.cpp b/engine/src/RHI/OpenGL/OpenGLDescriptorSet.cpp index d12729ad..7c11b8ca 100644 --- a/engine/src/RHI/OpenGL/OpenGLDescriptorSet.cpp +++ b/engine/src/RHI/OpenGL/OpenGLDescriptorSet.cpp @@ -196,7 +196,7 @@ void OpenGLDescriptorSet::Update(uint32_t offset, RHIResourceView* view) { } binding->textureIds[0] = glView->GetTexture(); - if (const OpenGLTexture* texture = glView->GetTextureResource()) { + if (const OpenGLTexture* texture = glView->GetOpenGLTextureResource()) { binding->textureTargets[0] = static_cast(ToOpenGL(texture->GetOpenGLType())); } } diff --git a/engine/src/Rendering/Graph/RenderGraph.cpp b/engine/src/Rendering/Graph/RenderGraph.cpp index 75daab98..7c64b895 100644 --- a/engine/src/Rendering/Graph/RenderGraph.cpp +++ b/engine/src/Rendering/Graph/RenderGraph.cpp @@ -87,6 +87,8 @@ RenderGraphTextureHandle RenderGraphBuilder::ImportTexture( resource.desc = desc; resource.kind = RenderGraphTextureKind::Imported; resource.importedView = importedView; + resource.importedTexture = + importedView != nullptr ? importedView->GetTextureResource() : nullptr; resource.importedOptions = importedOptions; m_graph.m_textures.push_back(resource); diff --git a/engine/src/Rendering/Graph/RenderGraphCompiler.cpp b/engine/src/Rendering/Graph/RenderGraphCompiler.cpp index 56b71ba0..4df32bdf 100644 --- a/engine/src/Rendering/Graph/RenderGraphCompiler.cpp +++ b/engine/src/Rendering/Graph/RenderGraphCompiler.cpp @@ -115,9 +115,9 @@ bool RenderGraphCompiler::Compile( if (texture.kind == RenderGraphTextureKind::Imported && texture.importedOptions.graphOwnsTransitions && - texture.importedView == nullptr) { + texture.importedTexture == nullptr) { WriteError( - Containers::String("RenderGraph imported texture requires a valid view when graph owns transitions: ") + + Containers::String("RenderGraph imported texture requires a texture-backed view when graph owns transitions: ") + texture.name, outErrorMessage); return false; @@ -237,6 +237,7 @@ bool RenderGraphCompiler::Compile( compiledTexture.desc = graph.m_textures[textureIndex].desc; compiledTexture.kind = graph.m_textures[textureIndex].kind; compiledTexture.importedView = graph.m_textures[textureIndex].importedView; + compiledTexture.importedTexture = graph.m_textures[textureIndex].importedTexture; compiledTexture.importedOptions = graph.m_textures[textureIndex].importedOptions; outCompiledGraph.m_textures.push_back(std::move(compiledTexture)); diff --git a/engine/src/Rendering/Graph/RenderGraphExecutor.cpp b/engine/src/Rendering/Graph/RenderGraphExecutor.cpp index fb835728..10ebedbc 100644 --- a/engine/src/Rendering/Graph/RenderGraphExecutor.cpp +++ b/engine/src/Rendering/Graph/RenderGraphExecutor.cpp @@ -40,6 +40,26 @@ RenderGraphTextureViewType ResolveBarrierViewType(RHI::ResourceStates state) { : RenderGraphTextureViewType::ShaderResource; } +RHI::ResourceViewType ResolveRequestedResourceViewType( + RenderGraphTextureViewType viewType) { + switch (viewType) { + case RenderGraphTextureViewType::RenderTarget: + return RHI::ResourceViewType::RenderTarget; + case RenderGraphTextureViewType::DepthStencil: + return RHI::ResourceViewType::DepthStencil; + case RenderGraphTextureViewType::UnorderedAccess: + return RHI::ResourceViewType::UnorderedAccess; + case RenderGraphTextureViewType::ShaderResource: + default: + return RHI::ResourceViewType::ShaderResource; + } +} + +bool IsTextureViewDimension(RHI::ResourceViewDimension dimension) { + return dimension != RHI::ResourceViewDimension::Unknown && + !RHI::IsBufferResourceViewDimension(dimension); +} + } // namespace class RenderGraphRuntimeResources { @@ -62,7 +82,10 @@ public: for (size_t textureIndex = 0u; textureIndex < m_graph.m_textures.size(); ++textureIndex) { const CompiledRenderGraph::CompiledTexture& texture = m_graph.m_textures[textureIndex]; const RenderGraphTextureLifetime& lifetime = m_graph.m_textureLifetimes[textureIndex]; + TextureAllocation& allocation = m_textureAllocations[textureIndex]; if (texture.kind == RenderGraphTextureKind::Imported) { + allocation.texture = texture.importedTexture; + allocation.primaryImportedView = texture.importedView; m_textureStates[textureIndex] = texture.importedOptions.initialState; } @@ -105,29 +128,23 @@ public: RHI::RHIResourceView* ResolveTextureView( RenderGraphTextureHandle handle, - RenderGraphTextureViewType viewType) const { + RenderGraphTextureViewType viewType, + const RenderContext& renderContext) { if (!handle.IsValid() || handle.index >= m_graph.m_textures.size()) { return nullptr; } const CompiledRenderGraph::CompiledTexture& texture = m_graph.m_textures[handle.index]; + TextureAllocation& allocation = m_textureAllocations[handle.index]; if (texture.kind == RenderGraphTextureKind::Imported) { - return texture.importedView; + return ResolveImportedTextureView( + texture, + allocation, + viewType, + renderContext); } - const TextureAllocation& allocation = m_textureAllocations[handle.index]; - switch (viewType) { - case RenderGraphTextureViewType::RenderTarget: - return allocation.renderTargetView; - case RenderGraphTextureViewType::DepthStencil: - return allocation.depthStencilView; - case RenderGraphTextureViewType::ShaderResource: - return allocation.shaderResourceView; - case RenderGraphTextureViewType::UnorderedAccess: - return allocation.unorderedAccessView; - default: - return nullptr; - } + return ResolveCachedView(allocation, viewType); } bool TryGetTextureDesc( @@ -158,7 +175,6 @@ public: const CompiledRenderGraph::CompiledTexture& texture = m_graph.m_textures[textureIndex]; if (texture.kind != RenderGraphTextureKind::Imported || !texture.importedOptions.graphOwnsTransitions || - texture.importedView == nullptr || textureIndex >= m_graph.m_textureLifetimes.size() || !m_graph.m_textureLifetimes[textureIndex].used || !IsGraphManagedTransientState(texture.importedOptions.finalState)) { @@ -208,8 +224,197 @@ private: RHI::RHIResourceView* depthStencilView = nullptr; RHI::RHIResourceView* shaderResourceView = nullptr; RHI::RHIResourceView* unorderedAccessView = nullptr; + RHI::RHIResourceView* primaryImportedView = nullptr; + bool ownsTexture = false; }; + static RHI::RHIResourceView* ResolveCachedView( + const TextureAllocation& allocation, + RenderGraphTextureViewType viewType) { + switch (viewType) { + case RenderGraphTextureViewType::RenderTarget: + return allocation.renderTargetView; + case RenderGraphTextureViewType::DepthStencil: + return allocation.depthStencilView; + case RenderGraphTextureViewType::ShaderResource: + return allocation.shaderResourceView; + case RenderGraphTextureViewType::UnorderedAccess: + return allocation.unorderedAccessView; + default: + return nullptr; + } + } + + static RHI::RHIResourceView*& ResolveCachedViewSlot( + TextureAllocation& allocation, + RenderGraphTextureViewType viewType) { + switch (viewType) { + case RenderGraphTextureViewType::RenderTarget: + return allocation.renderTargetView; + case RenderGraphTextureViewType::DepthStencil: + return allocation.depthStencilView; + case RenderGraphTextureViewType::ShaderResource: + return allocation.shaderResourceView; + case RenderGraphTextureViewType::UnorderedAccess: + default: + return allocation.unorderedAccessView; + } + } + + static RHI::ResourceViewDimension ResolveDefaultImportedViewDimension( + const CompiledRenderGraph::CompiledTexture& texture) { + const RHI::TextureType textureType = + static_cast(texture.desc.textureType); + const bool isMultisampled = texture.desc.sampleCount > 1u; + + switch (textureType) { + case RHI::TextureType::Texture1D: + return RHI::ResourceViewDimension::Texture1D; + case RHI::TextureType::Texture2DArray: + return isMultisampled + ? RHI::ResourceViewDimension::Texture2DMSArray + : RHI::ResourceViewDimension::Texture2DArray; + case RHI::TextureType::Texture3D: + return RHI::ResourceViewDimension::Texture3D; + case RHI::TextureType::TextureCube: + return RHI::ResourceViewDimension::TextureCube; + case RHI::TextureType::TextureCubeArray: + return RHI::ResourceViewDimension::TextureCubeArray; + case RHI::TextureType::Texture2D: + default: + return isMultisampled + ? RHI::ResourceViewDimension::Texture2DMS + : RHI::ResourceViewDimension::Texture2D; + } + } + + static RHI::Format ResolveImportedViewFormat( + const CompiledRenderGraph::CompiledTexture& texture, + const TextureAllocation& allocation) { + if (allocation.primaryImportedView != nullptr && + allocation.primaryImportedView->GetFormat() != RHI::Format::Unknown) { + return allocation.primaryImportedView->GetFormat(); + } + + if (allocation.texture != nullptr && + allocation.texture->GetFormat() != RHI::Format::Unknown) { + return allocation.texture->GetFormat(); + } + + return static_cast(texture.desc.format); + } + + static RHI::ResourceViewDimension ResolveImportedViewDimension( + const CompiledRenderGraph::CompiledTexture& texture, + const TextureAllocation& allocation) { + if (allocation.primaryImportedView != nullptr && + IsTextureViewDimension(allocation.primaryImportedView->GetDimension())) { + return allocation.primaryImportedView->GetDimension(); + } + + return ResolveDefaultImportedViewDimension(texture); + } + + static bool CanCreateImportedView( + const CompiledRenderGraph::CompiledTexture& texture, + RenderGraphTextureViewType viewType) { + const bool isDepthTexture = + IsDepthFormat(static_cast(texture.desc.format)); + switch (viewType) { + case RenderGraphTextureViewType::RenderTarget: + return !isDepthTexture; + case RenderGraphTextureViewType::DepthStencil: + return isDepthTexture; + case RenderGraphTextureViewType::ShaderResource: + return true; + case RenderGraphTextureViewType::UnorderedAccess: + return !isDepthTexture; + default: + return false; + } + } + + RHI::RHIResourceView* ResolveImportedTextureView( + const CompiledRenderGraph::CompiledTexture& texture, + TextureAllocation& allocation, + RenderGraphTextureViewType viewType, + const RenderContext& renderContext) { + if (allocation.primaryImportedView != nullptr && + allocation.primaryImportedView->GetViewType() == + ResolveRequestedResourceViewType(viewType) && + allocation.primaryImportedView->IsValid()) { + return allocation.primaryImportedView; + } + + RHI::RHIResourceView*& cachedView = + ResolveCachedViewSlot(allocation, viewType); + if (cachedView != nullptr) { + return cachedView; + } + + if (!CanCreateImportedView(texture, viewType) || + allocation.texture == nullptr || + renderContext.device == nullptr) { + return nullptr; + } + + cachedView = CreateImportedTextureView( + texture, + allocation, + viewType, + renderContext); + return cachedView; + } + + static void DestroyOwnedView(RHI::RHIResourceView*& view) { + if (view == nullptr) { + return; + } + + view->Shutdown(); + delete view; + view = nullptr; + } + + RHI::RHIResourceView* CreateImportedTextureView( + const CompiledRenderGraph::CompiledTexture& texture, + const TextureAllocation& allocation, + RenderGraphTextureViewType viewType, + const RenderContext& renderContext) { + if (renderContext.device == nullptr || allocation.texture == nullptr) { + return nullptr; + } + + RHI::ResourceViewDesc viewDesc = {}; + const RHI::Format viewFormat = + ResolveImportedViewFormat(texture, allocation); + if (viewFormat != RHI::Format::Unknown) { + viewDesc.format = static_cast(viewFormat); + } + viewDesc.dimension = ResolveImportedViewDimension(texture, allocation); + + switch (viewType) { + case RenderGraphTextureViewType::RenderTarget: + return renderContext.device->CreateRenderTargetView( + allocation.texture, + viewDesc); + case RenderGraphTextureViewType::DepthStencil: + return renderContext.device->CreateDepthStencilView( + allocation.texture, + viewDesc); + case RenderGraphTextureViewType::ShaderResource: + return renderContext.device->CreateShaderResourceView( + allocation.texture, + viewDesc); + case RenderGraphTextureViewType::UnorderedAccess: + return renderContext.device->CreateUnorderedAccessView( + allocation.texture, + viewDesc); + default: + return nullptr; + } + } + bool ShouldGraphManageTransitions(RenderGraphTextureHandle handle) const { if (!handle.IsValid() || handle.index >= m_graph.m_textures.size()) { return false; @@ -240,8 +445,21 @@ private: return true; } - RHI::RHIResourceView* resourceView = - ResolveTextureView(handle, ResolveBarrierViewType(targetState)); + const CompiledRenderGraph::CompiledTexture& texture = + m_graph.m_textures[handle.index]; + TextureAllocation& allocation = m_textureAllocations[handle.index]; + RHI::RHIResourceView* resourceView = nullptr; + if (texture.kind == RenderGraphTextureKind::Imported && + allocation.primaryImportedView != nullptr && + allocation.primaryImportedView->GetTextureResource() != nullptr && + allocation.primaryImportedView->IsValid()) { + resourceView = allocation.primaryImportedView; + } else { + resourceView = ResolveTextureView( + handle, + ResolveBarrierViewType(targetState), + renderContext); + } if (resourceView == nullptr) { if (outErrorMessage != nullptr) { *outErrorMessage = @@ -260,35 +478,19 @@ private: } static void DestroyTextureAllocation(TextureAllocation& allocation) { - if (allocation.renderTargetView != nullptr) { - allocation.renderTargetView->Shutdown(); - delete allocation.renderTargetView; - allocation.renderTargetView = nullptr; - } + DestroyOwnedView(allocation.renderTargetView); + DestroyOwnedView(allocation.depthStencilView); + DestroyOwnedView(allocation.shaderResourceView); + DestroyOwnedView(allocation.unorderedAccessView); - if (allocation.depthStencilView != nullptr) { - allocation.depthStencilView->Shutdown(); - delete allocation.depthStencilView; - allocation.depthStencilView = nullptr; - } - - if (allocation.shaderResourceView != nullptr) { - allocation.shaderResourceView->Shutdown(); - delete allocation.shaderResourceView; - allocation.shaderResourceView = nullptr; - } - - if (allocation.unorderedAccessView != nullptr) { - allocation.unorderedAccessView->Shutdown(); - delete allocation.unorderedAccessView; - allocation.unorderedAccessView = nullptr; - } - - if (allocation.texture != nullptr) { + if (allocation.ownsTexture && allocation.texture != nullptr) { allocation.texture->Shutdown(); delete allocation.texture; - allocation.texture = nullptr; } + + allocation.texture = nullptr; + allocation.primaryImportedView = nullptr; + allocation.ownsTexture = false; } bool TextureRequiresUnorderedAccess(Core::uint32 textureIndex) const { @@ -327,6 +529,7 @@ private: DestroyTextureAllocation(allocation); return false; } + allocation.ownsTexture = true; RHI::ResourceViewDesc viewDesc = {}; viewDesc.format = texture.desc.format; @@ -392,7 +595,10 @@ RHI::RHIResourceView* RenderGraphExecutionContext::ResolveTextureView( RenderGraphTextureHandle handle, RenderGraphTextureViewType viewType) const { return runtimeResources != nullptr - ? runtimeResources->ResolveTextureView(handle, viewType) + ? runtimeResources->ResolveTextureView( + handle, + viewType, + renderContext) : nullptr; }