Skip to content

Commit 83ded53

Browse files
committed
gfx/render_graph and render updates (exclude markdown)
1 parent 28ea897 commit 83ded53

13 files changed

Lines changed: 396 additions & 61 deletions

.claude/settings.local.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,14 @@
1717
"Bash(cd E:/workspace/HitagiEngine && MSYSTEM= MINGW_PREFIX= MINGW_CHOST= xmake build -y debugger 2>&1)",
1818
"Bash(cd E:/workspace/HitagiEngine && MSYSTEM= MINGW_PREFIX= MINGW_CHOST= xmake build -y --rebuild debugger 2>&1)",
1919
"WebSearch",
20-
"WebFetch(domain:xmake.io)"
20+
"WebFetch(domain:xmake.io)",
21+
"WebFetch(domain:developercommunity.visualstudio.com)",
22+
"WebFetch(domain:devblogs.microsoft.com)",
23+
"WebFetch(domain:www.khronos.org)",
24+
"WebFetch(domain:docs.vulkan.org)",
25+
"WebFetch(domain:wccftech.com)",
26+
"WebFetch(domain:microsoft.github.io)",
27+
"WebFetch(domain:www.nuget.org)"
2128
]
2229
}
2330
}

.vscode/settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"xmake.compileCommandsDirectory": "${workspaceRoot}/build",
33
"C_Cpp.intelliSenseEngine": "Disabled",
44
"clangd.arguments": [
5-
"-j=8",
5+
"-j=16",
66
"--pch-storage=memory",
77
"--experimental-modules-support",
88
],

hitagi/gfx/dx12/dx12_command_queue.cppm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ module;
55
export module gfx.dx12:command_queue;
66
import std;
77
import gfx.base;
8+
import :types;
89
import :sync;
910

1011
using namespace Microsoft::WRL;

hitagi/gfx/mock/mock_resource.cppm

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,15 @@ struct MockSwapChain : public SwapChain {
2828
MockSwapChain(Device& device, SwapChainDesc desc) : SwapChain(device, std::move(desc)), texture(device, {}) {}
2929

3030
auto AcquireTextureForRendering() -> utils::optional_ref<Texture> final { return texture; }
31-
auto GetWidth() const noexcept -> std::uint32_t final { return 0; }
32-
auto GetHeight() const noexcept -> std::uint32_t final { return 0; }
31+
auto GetWidth() const noexcept -> std::uint32_t final { return width; }
32+
auto GetHeight() const noexcept -> std::uint32_t final { return height; }
3333
auto GetFormat() const noexcept -> Format final { return Format::UNKNOWN; }
3434
void Present() final {}
3535
void Resize() final {}
3636

37-
MockTexture texture;
37+
MockTexture texture;
38+
std::uint32_t width = 1;
39+
std::uint32_t height = 1;
3840
};
3941

4042
struct MockShader : public Shader {
@@ -101,7 +103,11 @@ struct MockCommandQueue : public CommandQueue {
101103
void Submit(
102104
std::span<const std::reference_wrapper<const CommandContext>> contexts,
103105
std::span<const FenceWaitInfo> wait_fences = {},
104-
std::span<const FenceSignalInfo> signal_fences = {}) final {}
106+
std::span<const FenceSignalInfo> signal_fences = {}) final {
107+
for (const auto& signal : signal_fences) {
108+
signal.fence.Signal(signal.value);
109+
}
110+
}
105111
void WaitIdle() final{};
106112
};
107113

hitagi/gfx/render_graph/pass_builder.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -760,10 +760,13 @@ void PresentPassBuilder::Finish() noexcept {
760760
}
761761
if (m_Invalid) return;
762762

763+
if (m_RenderGraph.m_PresentPassNode != nullptr) {
764+
Invalidate("Finish present pass failed: a present pass already exists in the render graph");
765+
return;
766+
}
767+
763768
PassBuilder::Finish();
764769
m_RenderGraph.m_PresentPassNode = pass;
765-
766-
m_Finished = true;
767770
}
768771

769772
} // namespace hitagi::rg

hitagi/gfx/render_graph/pass_node.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -229,17 +229,17 @@ void PassNode::ResourceBarrier() {
229229
if (device.device_type == gfx::Device::Type::DX12) {
230230
if (GetCommandType() == gfx::CommandType::Copy) {
231231
for (auto& buffer_barrier : m_GPUBufferBarriers) {
232-
if (buffer_barrier.src_access != gfx::BarrierAccess::CopySrc ||
233-
buffer_barrier.src_access != gfx::BarrierAccess::CopySrc) {
232+
if (buffer_barrier.src_access != gfx::BarrierAccess::CopySrc &&
233+
buffer_barrier.src_access != gfx::BarrierAccess::CopyDst) {
234234
buffer_barrier.src_access = gfx::BarrierAccess::None;
235235
}
236236
if (buffer_barrier.src_stage != gfx::PipelineStage::Copy) {
237237
buffer_barrier.src_stage = gfx::PipelineStage::None;
238238
}
239239
}
240240
for (auto& texture_barrier : m_TextureBarriers) {
241-
if (texture_barrier.src_access != gfx::BarrierAccess::CopySrc ||
242-
texture_barrier.src_access != gfx::BarrierAccess::CopySrc) {
241+
if (texture_barrier.src_access != gfx::BarrierAccess::CopySrc &&
242+
texture_barrier.src_access != gfx::BarrierAccess::CopyDst) {
243243
texture_barrier.src_access = gfx::BarrierAccess::None;
244244
}
245245
if (texture_barrier.src_stage != gfx::PipelineStage::Copy) {

hitagi/gfx/render_graph/render_graph.cpp

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,11 @@ auto RenderGraph::MoveFrom(RenderGraphNode::Type type, std::size_t resource_node
224224
return invalid_index;
225225
} break;
226226
}
227+
228+
if (!name.empty()) {
229+
m_BlackBoard[type].emplace(name, new_handle);
230+
}
231+
227232
return new_handle;
228233
}
229234

@@ -440,11 +445,134 @@ void RenderGraph::RetireNodes() noexcept {
440445
const auto& retired_resource = m_RetiredNodes.front();
441446
const auto& [fence, last_value] = retired_resource.last_fence_value;
442447
if (latest_fence_values.at(fence) >= last_value) {
448+
RecycleTransientResource(retired_resource.node.get());
443449
m_RetiredNodes.pop_front();
444450
} else {
445451
break;
446452
}
447453
}
454+
455+
EvictStalePoolEntries();
456+
}
457+
458+
static auto buffer_pool_key(const gfx::GPUBufferDesc& desc) -> std::size_t {
459+
return utils::combine_hash(std::array{
460+
utils::hash(desc.element_size),
461+
utils::hash(desc.element_count),
462+
utils::hash(desc.usages),
463+
});
464+
}
465+
466+
static bool buffer_pool_match(const gfx::GPUBufferDesc& a, const gfx::GPUBufferDesc& b) {
467+
return a.element_size == b.element_size &&
468+
a.element_count == b.element_count &&
469+
a.usages == b.usages;
470+
}
471+
472+
static auto texture_pool_key(const gfx::TextureDesc& desc) -> std::size_t {
473+
return utils::combine_hash(std::array{
474+
utils::hash(desc.width),
475+
utils::hash(desc.height),
476+
utils::hash(static_cast<std::uint16_t>(desc.depth)),
477+
utils::hash(static_cast<std::uint16_t>(desc.array_size)),
478+
utils::hash(desc.format),
479+
utils::hash(static_cast<std::uint16_t>(desc.mip_levels)),
480+
utils::hash(desc.usages),
481+
});
482+
}
483+
484+
static bool texture_pool_match(const gfx::TextureDesc& a, const gfx::TextureDesc& b) {
485+
return a.width == b.width &&
486+
a.height == b.height &&
487+
a.depth == b.depth &&
488+
a.array_size == b.array_size &&
489+
a.format == b.format &&
490+
a.mip_levels == b.mip_levels &&
491+
a.clear_value.has_value() == b.clear_value.has_value() &&
492+
a.usages == b.usages;
493+
}
494+
495+
auto RenderGraph::AcquireTransientBuffer(const gfx::GPUBufferDesc& desc) -> std::shared_ptr<gfx::GPUBuffer> {
496+
const auto key = buffer_pool_key(desc);
497+
auto range = m_TransientPool.buffers.equal_range(key);
498+
for (auto it = range.first; it != range.second; ++it) {
499+
if (buffer_pool_match(it->second.desc, desc)) {
500+
auto resource = std::move(it->second.resource);
501+
m_TransientPool.buffers.erase(it);
502+
m_Logger->trace("Reused transient buffer from pool: {}", desc.name);
503+
return resource;
504+
}
505+
}
506+
return m_Device.CreateGPUBuffer(desc);
507+
}
508+
509+
auto RenderGraph::AcquireTransientTexture(const gfx::TextureDesc& desc) -> std::shared_ptr<gfx::Texture> {
510+
const auto key = texture_pool_key(desc);
511+
auto range = m_TransientPool.textures.equal_range(key);
512+
for (auto it = range.first; it != range.second; ++it) {
513+
if (texture_pool_match(it->second.desc, desc)) {
514+
auto resource = std::move(it->second.resource);
515+
m_TransientPool.textures.erase(it);
516+
m_Logger->trace("Reused transient texture from pool: {}", desc.name);
517+
return resource;
518+
}
519+
}
520+
return m_Device.CreateTexture(desc);
521+
}
522+
523+
void RenderGraph::RecycleTransientResource(RenderGraphNode* node) noexcept {
524+
if (!node->IsResourceNode()) return;
525+
526+
auto* resource_node = static_cast<ResourceNode*>(node);
527+
if (resource_node->m_IsImported || !resource_node->m_Resource) return;
528+
529+
switch (node->GetType()) {
530+
case RenderGraphNode::Type::GPUBuffer: {
531+
auto* buffer_node = static_cast<GPUBufferNode*>(node);
532+
if (buffer_node->m_MoveFromNode || buffer_node->m_MoveToNode) return;
533+
auto resource = std::static_pointer_cast<gfx::GPUBuffer>(resource_node->m_Resource);
534+
m_TransientPool.buffers.emplace(
535+
buffer_pool_key(buffer_node->GetDesc()),
536+
TransientResourcePool::CachedBuffer{
537+
.desc = buffer_node->GetDesc(),
538+
.resource = std::move(resource),
539+
.last_used_frame = m_FrameIndex,
540+
});
541+
resource_node->m_Resource = nullptr;
542+
} break;
543+
case RenderGraphNode::Type::Texture: {
544+
auto* texture_node = static_cast<TextureNode*>(node);
545+
if (texture_node->m_MoveFromNode || texture_node->m_MoveToNode) return;
546+
auto resource = std::static_pointer_cast<gfx::Texture>(resource_node->m_Resource);
547+
m_TransientPool.textures.emplace(
548+
texture_pool_key(texture_node->GetDesc()),
549+
TransientResourcePool::CachedTexture{
550+
.desc = texture_node->GetDesc(),
551+
.resource = std::move(resource),
552+
.last_used_frame = m_FrameIndex,
553+
});
554+
resource_node->m_Resource = nullptr;
555+
} break;
556+
default:
557+
break;
558+
}
559+
}
560+
561+
void RenderGraph::EvictStalePoolEntries() noexcept {
562+
for (auto it = m_TransientPool.buffers.begin(); it != m_TransientPool.buffers.end();) {
563+
if (m_FrameIndex - it->second.last_used_frame > TransientResourcePool::max_unused_frames) {
564+
it = m_TransientPool.buffers.erase(it);
565+
} else {
566+
++it;
567+
}
568+
}
569+
for (auto it = m_TransientPool.textures.begin(); it != m_TransientPool.textures.end();) {
570+
if (m_FrameIndex - it->second.last_used_frame > TransientResourcePool::max_unused_frames) {
571+
it = m_TransientPool.textures.erase(it);
572+
} else {
573+
++it;
574+
}
575+
}
448576
}
449577

450578
auto RenderGraph::ToDot() const noexcept -> std::pmr::string {

hitagi/gfx/render_graph/render_graph.cppm

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ private:
6464
friend CopyPassBuilder;
6565
friend PresentPassBuilder;
6666
friend ResourceNode;
67+
friend GPUBufferNode;
68+
friend TextureNode;
6769
friend PassNode;
6870

6971
using ResourceDesc = std::variant<gfx::GPUBufferDesc, gfx::TextureDesc, gfx::SamplerDesc, gfx::RenderPipelineDesc, gfx::ComputePipelineDesc>;
@@ -88,6 +90,11 @@ private:
8890
void RetireNodes() noexcept;
8991
void Reset() noexcept;
9092

93+
auto AcquireTransientBuffer(const gfx::GPUBufferDesc& desc) -> std::shared_ptr<gfx::GPUBuffer>;
94+
auto AcquireTransientTexture(const gfx::TextureDesc& desc) -> std::shared_ptr<gfx::Texture>;
95+
void RecycleTransientResource(RenderGraphNode* node) noexcept;
96+
void EvictStalePoolEntries() noexcept;
97+
9198
gfx::Device& m_Device;
9299

93100
std::pmr::string m_Name;
@@ -113,6 +120,25 @@ private:
113120
FenceValue last_fence_value;
114121
};
115122
std::pmr::deque<RetiredNode> m_RetiredNodes;
123+
124+
struct TransientResourcePool {
125+
static constexpr std::uint64_t max_unused_frames = 3;
126+
127+
struct CachedBuffer {
128+
gfx::GPUBufferDesc desc;
129+
std::shared_ptr<gfx::GPUBuffer> resource;
130+
std::uint64_t last_used_frame = 0;
131+
};
132+
struct CachedTexture {
133+
gfx::TextureDesc desc;
134+
std::shared_ptr<gfx::Texture> resource;
135+
std::uint64_t last_used_frame = 0;
136+
};
137+
138+
std::unordered_multimap<std::size_t, CachedBuffer> buffers;
139+
std::unordered_multimap<std::size_t, CachedTexture> textures;
140+
};
141+
TransientResourcePool m_TransientPool;
116142
};
117143

118144
template <RenderGraphNode::Type T>

hitagi/gfx/render_graph/resource_node.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ void GPUBufferNode::Initialize() {
6161
m_Resource = m_MoveFromNode->m_Resource;
6262
}
6363
if (m_IsImported || m_Resource) return;
64-
m_Resource = m_RenderGraph->GetDevice().CreateGPUBuffer(m_Desc.value());
64+
m_Resource = m_RenderGraph->AcquireTransientBuffer(m_Desc.value());
6565
}
6666

6767
TextureNode::TextureNode(RenderGraph& render_graph, gfx::TextureDesc desc, std::string_view name)
@@ -109,7 +109,7 @@ void TextureNode::Initialize() {
109109
m_Resource = m_MoveFromNode->m_Resource;
110110
}
111111
if (m_IsImported || m_Resource) return;
112-
m_Resource = m_RenderGraph->GetDevice().CreateTexture(m_Desc.value());
112+
m_Resource = m_RenderGraph->AcquireTransientTexture(m_Desc.value());
113113
}
114114

115115
SamplerNode::SamplerNode(RenderGraph& render_graph, gfx::SamplerDesc desc, std::string_view name)

0 commit comments

Comments
 (0)