153 lines
5.4 KiB
C++
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;
|
|
}
|