Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ set(GRAPHICS_DIRECTX12_SOURCES
src/Graphics/DirectX12/DirectX12Renderer.cpp
src/Graphics/DirectX12/DirectX12Shader.cpp
src/Graphics/DirectX12/DirectX12Texture.cpp
src/Graphics/DirectX12/DirectX12UploadRing.cpp
)

set(LIGHTING_SOURCES
Expand Down
26 changes: 15 additions & 11 deletions include/private/Graphics/DirectX12/DirectX12Renderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,22 @@
#ifndef _DIRECTX12RENDERER_H
#define _DIRECTX12RENDERER_H

#include "Graphics/Common/Renderer.hpp"
#include <backends/imgui_impl_dx12.h>
#include <d3d12.h>
#include <d3dcompiler.h>
#include <dxgi1_4.h>
#include <imgui.h>
#include <wrl/client.h>

#include <Core/Window.hpp>

#include "Graphics/Common/RenderContext.hpp"
#include "Graphics/Common/Renderer.hpp"
#include "Graphics/Common/ResourceManager.hpp"
#include "Graphics/DirectX12/DirectX12Buffer.hpp"
#include "Graphics/DirectX12/DirectX12Shader.hpp"
#include "Graphics/DirectX12/DirectX12Texture.hpp"
#include <Core/Window.hpp>
#include <d3d12.h>
#include <d3dcompiler.h>
#include <dxgi1_4.h>
#include <wrl/client.h>
#include <imgui.h>
#include <backends/imgui_impl_dx12.h>
#include "Graphics/DirectX12/DirectX12UploadRing.hpp"

namespace Sleak {
namespace RenderEngine {
Expand Down Expand Up @@ -177,6 +180,9 @@ class ENGINE_API DirectX12Renderer : public Renderer, public RenderContext {
/// Builds the line-topology PSO used for debug line rendering.
bool CreateDebugLinePipelineState();

// Transient per-frame constant data (per-draw shadow transforms)
DirectX12UploadRing m_uploadRing;

// Light/fog constant buffer (persistently-mapped upload heap)
Microsoft::WRL::ComPtr<ID3D12Resource> m_lightUBO;
void* m_lightUBOMapped = nullptr;
Expand All @@ -193,9 +199,7 @@ class ENGINE_API DirectX12Renderer : public Renderer, public RenderContext {
float m_lightVP[16] = {};
float m_pendingLightVP[16] = {};
bool m_hasPendingLightVP = false;
bool m_inShadowPass = false;
Microsoft::WRL::ComPtr<ID3D12Resource> m_shadowTransformCB;
void* m_shadowTransformMapped = nullptr;
bool m_inShadowPass = false;
// Depth-only PSO for shadow pass (no PS, no RTV, CULL_NONE)
Microsoft::WRL::ComPtr<ID3D12PipelineState> m_shadowPassPSO;
Microsoft::WRL::ComPtr<ID3DBlob> m_cachedVSBlob; // saved for shadow PSO
Expand Down
67 changes: 67 additions & 0 deletions include/private/Graphics/DirectX12/DirectX12UploadRing.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#ifndef _DIRECTX12_UPLOAD_RING_HPP_
#define _DIRECTX12_UPLOAD_RING_HPP_

#include <d3d12.h>
#include <wrl/client.h>

#include <cstdint>
#include <vector>

namespace Sleak {
namespace RenderEngine {

/// Per-frame linear allocator over upload heap pages for transient GPU data.
/// Each frame in flight owns its own pages, which are rewound by BeginFrame.
class DirectX12UploadRing {
public:
/// CPU write pointer and matching GPU address of one allocation.
struct Allocation {
void* cpu = nullptr;
D3D12_GPU_VIRTUAL_ADDRESS gpu = 0;
};

DirectX12UploadRing() = default;
~DirectX12UploadRing() { Release(); }

DirectX12UploadRing(const DirectX12UploadRing&) = delete;
DirectX12UploadRing& operator=(const DirectX12UploadRing&) = delete;

/// Stores the device and sizes the ring. Pages are created on demand.
void Initialize(ID3D12Device* device, uint32_t frameCount,
uint64_t pageSize);

/// Rewinds the frame's pages. The GPU must be done with that frame.
void BeginFrame(uint32_t frameIndex);

/// Returns 256-byte aligned memory valid until the frame is reused.
bool Allocate(uint64_t size, Allocation& out);

/// Unmaps and frees every page. The GPU must be idle.
void Release();

private:
struct Page {
Microsoft::WRL::ComPtr<ID3D12Resource> resource;
uint8_t* cpu = nullptr;
D3D12_GPU_VIRTUAL_ADDRESS gpu = 0;
uint64_t size = 0;
};

struct Frame {
std::vector<Page> pages;
size_t page = 0;
uint64_t offset = 0;
};

bool CreatePage(uint64_t size, Page& out);

ID3D12Device* m_device = nullptr;
std::vector<Frame> m_frames;
uint32_t m_current = 0;
uint64_t m_pageSize = 0;
};

} // namespace RenderEngine
} // namespace Sleak

#endif // _DIRECTX12_UPLOAD_RING_HPP_
30 changes: 7 additions & 23 deletions src/Graphics/DirectX12/DirectX12Renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ bool DirectX12Renderer::Initialize() {
if (!CreateRenderTargetViews()) return false;
if (!CreateDepthStencilView()) return false;
if (!CreateFence()) return false;
m_uploadRing.Initialize(device.Get(), FrameCount, 64 * 1024);
if (!CreateRootSignature()) return false;
if (!CreateSharedSrvHeap()) return false;
// PSO is created lazily when CreateShader() is called
Expand Down Expand Up @@ -643,6 +644,7 @@ void DirectX12Renderer::BeginRender() {

// Process deferred GPU resource deletions now that GPU is idle
DirectX12Buffer::ProcessDeferredCleanup();
m_uploadRing.BeginFrame(frameIndex);

// Reset the command allocator and command list for this frame
commandAllocators[frameIndex]->Reset();
Expand Down Expand Up @@ -853,6 +855,7 @@ void DirectX12Renderer::Cleanup() {
}
m_lightUBO.Reset();
m_lightUBOCreated = false;
m_uploadRing.Release();

imguiSrvHeap.Reset();
m_sharedSrvHeap.Reset();
Expand Down Expand Up @@ -1051,29 +1054,10 @@ void DirectX12Renderer::BindConstantBuffer(RefPtr<BufferBase> buffer,
}
memcpy(&shadowPC[16], srcWorld, sizeof(float) * 16);

// Create dedicated shadow transform CB on first use
if (!m_shadowTransformCB) {
const UINT cbSize = 256; // 256-byte aligned
D3D12_HEAP_PROPERTIES heapProps = {};
heapProps.Type = D3D12_HEAP_TYPE_UPLOAD;
D3D12_RESOURCE_DESC desc = {};
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
desc.Width = cbSize;
desc.Height = 1;
desc.DepthOrArraySize = 1;
desc.MipLevels = 1;
desc.Format = DXGI_FORMAT_UNKNOWN;
desc.SampleDesc.Count = 1;
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
device->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &desc,
D3D12_RESOURCE_STATE_GENERIC_READ, nullptr,
IID_PPV_ARGS(&m_shadowTransformCB));
m_shadowTransformCB->Map(0, nullptr, &m_shadowTransformMapped);
}

memcpy(m_shadowTransformMapped, shadowPC, sizeof(shadowPC));
commandList->SetGraphicsRootConstantBufferView(
slot, m_shadowTransformCB->GetGPUVirtualAddress());
DirectX12UploadRing::Allocation alloc;
if (!m_uploadRing.Allocate(sizeof(shadowPC), alloc)) return;
memcpy(alloc.cpu, shadowPC, sizeof(shadowPC));
commandList->SetGraphicsRootConstantBufferView(slot, alloc.gpu);
return;
}

Expand Down
118 changes: 118 additions & 0 deletions src/Graphics/DirectX12/DirectX12UploadRing.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
#include <Core/Logger.hpp>
#include <Graphics/DirectX12/DirectX12UploadRing.hpp>
#include <utility>

namespace Sleak {
namespace RenderEngine {

namespace {
constexpr uint64_t kAlignment = D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT;

uint64_t AlignUp(uint64_t value) {
return (value + kAlignment - 1) & ~(kAlignment - 1);
}
} // namespace

void DirectX12UploadRing::Initialize(ID3D12Device* device, uint32_t frameCount,
uint64_t pageSize) {
Release();
m_device = device;
m_pageSize = AlignUp(pageSize);
m_frames.resize(frameCount);
m_current = 0;
}

void DirectX12UploadRing::BeginFrame(uint32_t frameIndex) {
if (frameIndex >= m_frames.size()) return;
m_current = frameIndex;
m_frames[frameIndex].page = 0;
m_frames[frameIndex].offset = 0;
}

bool DirectX12UploadRing::Allocate(uint64_t size, Allocation& out) {
if (!m_device || m_current >= m_frames.size() || size == 0) return false;

const uint64_t aligned = AlignUp(size);
Frame& frame = m_frames[m_current];

while (frame.page < frame.pages.size()) {
Page& page = frame.pages[frame.page];
if (frame.offset + aligned <= page.size) {
out.cpu = page.cpu + frame.offset;
out.gpu = page.gpu + frame.offset;
frame.offset += aligned;
return true;
}
++frame.page;
frame.offset = 0;
}

Page page;
if (!CreatePage(aligned > m_pageSize ? aligned : m_pageSize, page))
return false;
frame.pages.push_back(std::move(page));
frame.page = frame.pages.size() - 1;

Page& fresh = frame.pages.back();
out.cpu = fresh.cpu;
out.gpu = fresh.gpu;
frame.offset = aligned;
return true;
}

void DirectX12UploadRing::Release() {
for (Frame& frame : m_frames) {
for (Page& page : frame.pages) {
if (page.resource && page.cpu) page.resource->Unmap(0, nullptr);
}
frame.pages.clear();
frame.page = 0;
frame.offset = 0;
}
m_frames.clear();
m_device = nullptr;
m_current = 0;
}

bool DirectX12UploadRing::CreatePage(uint64_t size, Page& out) {
D3D12_HEAP_PROPERTIES heapProps = {};
heapProps.Type = D3D12_HEAP_TYPE_UPLOAD;

D3D12_RESOURCE_DESC desc = {};
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
desc.Width = size;
desc.Height = 1;
desc.DepthOrArraySize = 1;
desc.MipLevels = 1;
desc.Format = DXGI_FORMAT_UNKNOWN;
desc.SampleDesc.Count = 1;
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;

HRESULT hr = m_device->CreateCommittedResource(
&heapProps, D3D12_HEAP_FLAG_NONE, &desc,
D3D12_RESOURCE_STATE_GENERIC_READ, nullptr,
IID_PPV_ARGS(&out.resource));
if (FAILED(hr)) {
SLEAK_ERROR("Failed to create upload ring page! HRESULT: 0x{:08X}",
static_cast<unsigned int>(hr));
return false;
}
out.resource->SetName(L"UploadRingPage");

D3D12_RANGE noRead = {0, 0};
void* mapped = nullptr;
hr = out.resource->Map(0, &noRead, &mapped);
if (FAILED(hr)) {
SLEAK_ERROR("Failed to map upload ring page! HRESULT: 0x{:08X}",
static_cast<unsigned int>(hr));
out.resource.Reset();
return false;
}
out.cpu = static_cast<uint8_t*>(mapped);
out.gpu = out.resource->GetGPUVirtualAddress();
out.size = size;
return true;
}

} // namespace RenderEngine
} // namespace Sleak
Loading