rtengine/src/renderer/dx11/render_graph.cpp
Kevin Trogant 6052f35485 Determine pass execution levels
Useful for (once we have a job system) executing render passes in
parallel
2024-05-06 12:21:18 +02:00

153 lines
5.4 KiB
C++

#include "gfx/renderer_api.h"
#include "renderer/common/common_render_graph.h"
#include "device_objects.hpp"
#include "gpu.hpp"
static rt_render_target_handle CreateRenderTarget(const rt_physical_render_target_info *rtinfo) {
return rtCreateRenderTarget({.format = rtinfo->format,
.width = rtinfo->width,
.height = rtinfo->height,
.name = rtinfo->name});
}
static int RequireExplicitSynchronization() {
return 0;
}
extern "C" rt_render_graph_builder RT_RENDERER_API_FN(CreateRenderGraphBuilder)(void) {
rt_render_graph_builder_platform_callbacks cbs{};
cbs.CreateRenderTarget = CreateRenderTarget;
cbs.RequireExplicitSynchronization = RequireExplicitSynchronization;
return rtCreateRenderGraphBuilder(&cbs);
}
extern "C" void RT_RENDERER_API_FN(DestroyRenderGraphBuilder)(rt_render_graph_builder *builder) {
rtDestroyRenderGraphBuilder(builder);
}
static rt_result ExecutePass(rt_render_pass *pass, rt_command_buffer_handle cmdbuf_handle) {
rt_command_buffer *cmd = rtGetCommandBuffer(cmdbuf_handle);
if (!RT_VERIFY(cmd))
return RT_INVALID_VALUE;
if (cmd->annotation) {
WCHAR wname[128];
if (rtUTF8ToWStr(pass->name, wname, sizeof(wname)) == RT_SUCCESS)
cmd->annotation->BeginEvent(wname);
}
// Setup rtvs
ID3D11RenderTargetView *rtvs[4];
ID3D11DepthStencilView *dsv = nullptr;
for (uint32_t i = 0; i < pass->color_output_count; ++i) {
rt_render_target *rt = rtGetRenderTarget(pass->color_outputs[i]);
if (!RT_VERIFY(rt))
return RT_INVALID_VALUE;
RT_ASSERT(rt->IsColorRenderTarget(), "Needs to provide a valid color render target");
rtvs[i] = rt->rtv;
if (pass->color_loads[i] == RT_PASS_LOAD_MODE_CLEAR) {
FLOAT color[4] = {
pass->color_clear_values[i].r,
pass->color_clear_values[i].g,
pass->color_clear_values[i].b,
pass->color_clear_values[i].a,
};
cmd->context->ClearRenderTargetView(rt->rtv, color);
}
}
rt_render_target *dsvrt = rtGetRenderTarget(pass->depth_stencil);
if (dsvrt) {
RT_ASSERT(dsvrt->IsDepthStencilTarget(),
"Need to provide a valid depth stencil render target");
dsv = dsvrt->dsv;
if (pass->depth_stencil_load == RT_PASS_LOAD_MODE_CLEAR)
cmd->context->ClearDepthStencilView(
dsv,
(dsvrt->HasStencilComponent()) ? D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL
: D3D11_CLEAR_DEPTH,
pass->depth_stencil_clear_value.depth,
static_cast<UINT8>(pass->depth_stencil_clear_value.stencil));
}
cmd->context->OMSetRenderTargets(static_cast<UINT>(pass->color_output_count), rtvs, dsv);
rt_result res = RT_VERIFY(pass->Execute)(cmdbuf_handle, nullptr, 0, pass->user_data);
if (cmd->annotation) {
cmd->annotation->EndEvent();
}
return res;
}
static bool IsCopyResourcePossible(const rt_render_target *backbuffer) {
DXGI_SWAP_CHAIN_DESC scd;
g_gpu.swap_chain.swap_chain->GetDesc(&scd);
D3D11_TEXTURE2D_DESC td;
backbuffer->texture->GetDesc(&td);
// This is more strict than necessary, because the formats could also be from the same group
return scd.BufferDesc.Width == td.Width && scd.BufferDesc.Height == td.Height &&
scd.SampleDesc.Count == td.SampleDesc.Count && scd.BufferDesc.Format == td.Format;
}
extern "C" rt_result RT_RENDERER_API_FN(ExecuteRenderGraph)(rt_render_graph *render_graph) {
rt_temp_arena temp = rtGetTemporaryArena(NULL, 0);
if (!temp.arena)
return RT_OUT_OF_MEMORY;
// Alloc a command buffer for every pass
rt_command_buffer_handle *cmdbufs =
RT_ARENA_PUSH_ARRAY(temp.arena, rt_command_buffer_handle, render_graph->pass_count);
rt_result res = rtAllocCommandBuffers(render_graph->pass_count, cmdbufs);
if (res != RT_SUCCESS) {
rtReturnTemporaryArena(temp);
return res;
}
for (uint32_t i = 0; i < render_graph->pass_count; ++i) {
rt_render_pass *pass = &render_graph->passes[i];
res = ExecutePass(pass, cmdbufs[i]);
if (res != RT_SUCCESS)
break;
}
if (res == RT_SUCCESS) {
res = rtSubmitCommandBuffers(render_graph->pass_count, cmdbufs);
}
// Copy backbuffer to swapchain
rt_render_target *backbuffer =
rtGetRenderTarget(render_graph->render_targets[render_graph->backbuffer_index]);
if (!backbuffer) {
rtReturnTemporaryArena(temp);
return RT_INVALID_VALUE;
}
ID3D11Texture2D *frame_buffer;
if (FAILED(g_gpu.swap_chain.swap_chain->GetBuffer(0, IID_PPV_ARGS(&frame_buffer)))) {
rtReportError("dx11", "Failed to retrieve the backbuffer.");
rtReturnTemporaryArena(temp);
return RT_UNKNOWN_ERROR;
}
if (IsCopyResourcePossible(backbuffer)) {
g_gpu.device_context->CopyResource(frame_buffer, backbuffer->texture);
} else {
// NOTE(Kevin): The most flexible solution would probably be a fullscreen tri draw
// that implements a blit.
// Another idea would be a compute shader that does a copy&filter but that requires more work
RT_NOT_IMPLEMENTED;
}
rtReturnTemporaryArena(temp);
return res;
}