Merge branch 'main' into f/framegraph-v2

This commit is contained in:
Kevin Trogant 2024-04-04 20:46:03 +02:00
commit df50759303
28 changed files with 1503 additions and 79 deletions

View File

@ -14,6 +14,25 @@ struct VSOutput
float4 Pos : SV_POSITION;
};
VSOutput VsMain(VSInput input, uint vertexIndex : SV_VertexID) {
VSOutput output = (VSOutput)0;
return output;
}
END
dx11 BEGIN
#include "common.hlsl"
struct VSInput
{
};
struct VSOutput
{
float4 Pos : SV_POSITION;
};
VSOutput VsMain(VSInput input, uint vertexIndex : SV_VertexID) {
VSOutput output = (VSOutput)0;
return output;
@ -27,6 +46,21 @@ struct PSOutput {
float4 Color : SV_TARGET0;
};
PSOutput PsMain(void) {
PSOutput output = (PSOutput)0;
output.Color[0] = 0;
output.Color[1] = 0;
output.Color[2] = 0;
output.Color[3] = 0;
return output;
}
END
dx11 BEGIN
struct PSOutput {
float4 Color : SV_TARGET0;
};
PSOutput PsMain(void) {
PSOutput output = (PSOutput)0;
output.Color[0] = 0;

View File

@ -2,7 +2,7 @@ project('rtengine', ['c', 'cpp'],
default_options: ['buildtype=debug',
'b_sanitize=address',
'c_std=c17',
'cpp_std=c++14',
'cpp_std=c++20',
'warning_level=3',
'werror=true',
'b_vscrt=static_from_buildtype',
@ -85,6 +85,8 @@ if get_option('default_library') == 'static'
engine_link_libs = [runtime_lib, gfx_lib, app_lib, vk_renderer_lib]
elif get_option('static_renderer') == 'null'
engine_link_libs = [runtime_lib, gfx_lib, app_lib, null_renderer_lib]
elif get_option('static_renderer') == 'dx11'
engine_link_libs = [runtime_lib, gfx_lib, app_lib, dx11_renderer_lib]
else
error('Invalid static_renderer option ', get_option('static_renderer'))
endif

View File

@ -2,5 +2,6 @@ option('static_renderer', type : 'string', value : 'vk', description : 'Name of
option('use_xlib', type : 'boolean', value : false, description : 'Use Xlib for window creation under linux')
option('error_report_debugbreak', type : 'boolean', value : true, description : 'Debugbreak in ReportError')
option('enable_dxc_shader_compiler', type : 'boolean', value : true, description : 'Enables building the dxc-based shader compiler.')
option('enable_dx11_shader_compiler', type : 'boolean', value : true, description : 'Enables building the dx11-bases shader compiler.')
option('game_as_subdir', type : 'boolean', value : false, description : 'If true, adds the directory "src/game" to the build.')
option('build_dx11', type : 'boolean', value : true, description : 'Enables/disables the build of the dx11 renderer.')

View File

@ -0,0 +1,89 @@
#include <d3dcompiler.h>
#include "shader_compiler.h"
extern "C" rt_shader_bytecode CompileDX11Shader(rt_shader_stage stage,
rt_shader_optimization_level optimization,
rt_text_span code,
const char *file_path,
rt_arena *arena) {
rt_shader_bytecode bytecode = {};
D3D_SHADER_MACRO defines[] = {
{"RT_DX11", "1"},
{ NULL, NULL},
};
LPCSTR target = NULL;
LPCSTR entrypoint = NULL;
switch (stage) {
case RT_SHADER_STAGE_VERTEX:
target = "vs_5_0";
entrypoint = "VsMain";
break;
case RT_SHADER_STAGE_FRAGMENT:
target = "ps_5_0";
entrypoint = "PsMain";
break;
case RT_SHADER_STAGE_COMPUTE:
target = "cs_5_0";
entrypoint = "CsMain";
break;
default:
rtLog("AC", "Tried to compile an invalid shader stage %u for %s", stage, file_path);
return bytecode;
}
UINT flags = D3DCOMPILE_PARTIAL_PRECISION | D3DCOMPILE_WARNINGS_ARE_ERRORS;
switch (optimization) {
case RT_SHADER_OPTIMIZATION_NONE:
flags |= D3DCOMPILE_SKIP_OPTIMIZATION | D3DCOMPILE_OPTIMIZATION_LEVEL0;
break;
case RT_SHADER_OPTIMIZATION_SIZE:
flags |= D3DCOMPILE_OPTIMIZATION_LEVEL2;
break;
case RT_SHADER_OPTIMIZATION_SPEED:
flags |= D3DCOMPILE_OPTIMIZATION_LEVEL3;
break;
}
#ifdef RT_DEBUG
flags |= D3DCOMPILE_DEBUG;
#endif
ID3DBlob *bytes = nullptr, *error = nullptr;
if (FAILED(D3DCompile2(code.start,
code.length,
file_path,
defines,
D3D_COMPILE_STANDARD_FILE_INCLUDE,
entrypoint,
target,
flags,
0,
0,
nullptr,
0,
&bytes,
&error))) {
if (error) {
rtLog("AC",
"Shader compilation failed (%s): %s",file_path,
(const char *)error->GetBufferPointer());
error->Release();
return bytecode;
} else {
rtLog("AC", "Shader compilation failed (%s): NO ERROR INFORMATION", file_path);
return bytecode;
}
}
bytecode.bytes = rtArenaPush(arena, bytes->GetBufferSize());
if (bytecode.bytes) {
bytecode.len = bytes->GetBufferSize();
memcpy(bytecode.bytes, bytes->GetBufferPointer(), bytecode.len);
} else {
rtLog("AC", "Out of memory while compiling %s", file_path);
}
bytes->Release();
return bytecode;
}

View File

@ -62,6 +62,9 @@ static char *GenerateShaderName(rt_shader_type type,
case RT_SHADER_TYPE_VULKAN:
type_str = ":vk";
break;
case RT_SHADER_TYPE_DX11:
type_str = ":dx11";
break;
default:
return NULL;
}
@ -135,6 +138,8 @@ static rt_result ParseShader(rt_parse_state *state,
rt_shader_type in_file_type = RT_SHADER_TYPE_INVALID;
if (rtCompareSpanToString(shader->attribute, "vk") == 0) {
in_file_type = RT_SHADER_TYPE_VULKAN;
} else if (rtCompareSpanToString(shader->attribute, "dx11") == 0) {
in_file_type = RT_SHADER_TYPE_DX11;
} else {
rtReportError("GFX",
"Invalid renderer backend"
@ -230,6 +235,8 @@ static rt_result ParsePipeline(rt_parse_state *state,
rt_shader_type type = RT_SHADER_TYPE_INVALID;
if (strcmp(rt_Renderer.s, "vk") == 0)
type = RT_SHADER_TYPE_VULKAN;
else if (strcmp(rt_Renderer.s, "dx11") == 0)
type = RT_SHADER_TYPE_DX11;
if (type == RT_SHADER_TYPE_INVALID) {
result = RT_ASSET_PROCESSING_FAILED;

View File

@ -39,6 +39,13 @@ if get_option('enable_dxc_shader_compiler')
ac_cargs += '-DRT_BUILD_DXC_SHADER_COMPILER'
endif
if get_option('enable_dx11_shader_compiler')
ac_cargs += '-DRT_BUILD_DX11_SHADER_COMPILER'
ac_sources += ['dx11_shader_compiler.cpp']
d3dcompiler_dep = declare_dependency(link_args : ['-lD3DCompiler'])
ac_deps += d3dcompiler_dep
endif
asset_compiler = static_library('asset_compiler',
'asset_compiler.h',
'description_parser.h',

View File

@ -8,6 +8,14 @@ extern rt_shader_bytecode CompileVulkanShader(rt_shader_stage stage,
rt_arena *arena);
#endif
#ifdef RT_BUILD_DX11_SHADER_COMPILER
extern rt_shader_bytecode CompileDX11Shader(rt_shader_stage stage,
rt_shader_optimization_level optimization,
rt_text_span code,
const char *file_path,
rt_arena *arena);
#endif
static rt_shader_bytecode CompileNullShader(rt_shader_stage stage,
rt_shader_optimization_level optimization,
rt_text_span code,
@ -24,8 +32,11 @@ static rt_shader_bytecode CompileNullShader(rt_shader_stage stage,
};
}
typedef rt_shader_bytecode
shader_compiler_fn(rt_shader_stage, rt_shader_optimization_level, rt_text_span, const char *, rt_arena *);
typedef rt_shader_bytecode shader_compiler_fn(rt_shader_stage,
rt_shader_optimization_level,
rt_text_span,
const char *,
rt_arena *);
static shader_compiler_fn *_compiler_funcs[RT_SHADER_TYPE_count] = {
CompileNullShader,
@ -36,6 +47,11 @@ static shader_compiler_fn *_compiler_funcs[RT_SHADER_TYPE_count] = {
CompileNullShader,
#endif
#ifdef RT_BUILD_DX11_SHADER_COMPILER
CompileDX11Shader,
#else
CompileNullShader,
#endif
};
rt_shader_bytecode CompileShader(rt_shader_type type,

View File

@ -13,29 +13,7 @@ void RegisterCVars(void) {
static rt_framegraph *_framegraph;
static void PassPrepare(rt_render_pass_id pass,
const rt_render_target_write *writes,
uint32_t write_count,
const rt_render_target_read *reads,
uint32_t read_count) {
// rtLog("GAME", "Prepare pass %x", pass);
}
static void PassExecute(rt_render_pass_id pass,
const rt_render_target_write *writes,
uint32_t write_count,
const rt_render_target_read *reads,
uint32_t read_count) {
// rtLog("GAME", "Execute pass %x", pass);
}
static void PassFinalize(rt_render_pass_id pass,
const rt_render_target_write *writes,
uint32_t write_count,
const rt_render_target_read *reads,
uint32_t read_count) {
// rtLog("GAME", "Finalize pass %x", pass);
}
static rt_render_target_handle _rt;
/* Called after the runtime has finished its initialization and before entering the main-loop*/
void Init(void) {
@ -44,56 +22,13 @@ void Init(void) {
rtWaitForAssetProcessing();
rt_temp_arena temp = rtGetTemporaryArena(NULL, 0);
#if 0
rt_resource_id resid = rtGetResourceID("assets/forward.framegraph");
size_t size = rtGetResourceSize(resid);
rt_resource *res = rtArenaPush(temp.arena, size);
rtGetResource(resid, res);
_framegraph = rtCreateFramegraph(res->data);
rt_render_pass_bind_fns bind = {.Execute = PassExecute,
.Prepare = PassPrepare,
.Finalize = PassFinalize};
rtBindRenderPass(_framegraph, rtCalculateRenderPassID("forward", sizeof("forward") - 1), &bind);
#else
rt_resource_id resid = rtGetResourceID("assets/test.framegraph");
size_t size = rtGetResourceSize(resid);
rt_resource *res = rtArenaPush(temp.arena, size);
rtGetResource(resid, res);
_framegraph = rtCreateFramegraph(res->data);
rt_render_pass_bind_fns bind = {.Execute = PassExecute,
.Prepare = PassPrepare,
.Finalize = PassFinalize};
rtBindRenderPass(_framegraph, rtCalculateRenderPassID("pass0", sizeof("pass0") - 1), &bind);
rtBindRenderPass(_framegraph, rtCalculateRenderPassID("pass1", sizeof("pass1") - 1), &bind);
rt_v2 vertices[] = {
{ 0, 0.5},
{ 0.5, -0.5},
{-0.5, -0.5}
};
rt_buffer_info info = {
.type = RT_BUFFER_TYPE_VERTEX,
.usage = RT_BUFFER_USAGE_STATIC,
.size = sizeof(vertices),
.data = vertices,
};
rt_buffer_handle buf;
g_renderer.CreateBuffers(1, &info, &buf);
#endif
_rt = g_renderer.GetSwapchainRenderTarget();
}
/* Called after exiting the main-loop and before the runtime starts its shutdown */
void Shutdown(void) {
rtLog("GAME", "Shutdown");
rtShutdownAssetCompiler();
rtDestroyFramegraph(_framegraph);
}
void Update(unsigned int frame_id) {
@ -101,5 +36,20 @@ void Update(unsigned int frame_id) {
}
void Render(unsigned int frame_id) {
rtExecuteFramegraph(_framegraph, frame_id);
rt_alloc_command_buffer_info info = {RT_GRAPHICS_QUEUE};
rt_command_buffer_handle cmd;
g_renderer.AllocCommandBuffers(1, &info, &cmd);
rt_cmd_begin_pass_info pass_info = {
.color_buffer_count = 1,
.color_buffers = {_rt},
.color_buffer_loads = {RT_PASS_LOAD_MODE_CLEAR},
.color_buffer_clear_values = {{.color = {1.f, 0.f, 0.f, 1.f}}},
.name = "testme",
};
g_renderer.CmdBeginPass(cmd, &pass_info);
g_renderer.CmdEndPass(cmd);
rt_submit_command_buffers_info submit = {.command_buffer_count = 1, .command_buffers = &cmd};
g_renderer.SubmitCommandBuffers(RT_GRAPHICS_QUEUE, &submit);
}

View File

@ -20,6 +20,9 @@ extern "C" {
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
#elif defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable : 4201) /* anonymous struct */
#endif
typedef union {
float v[4];
@ -32,6 +35,8 @@ typedef union {
} rt_color;
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic pop
#elif defined(_MSC_VER)
#pragma warning(pop)
#endif
/* NOTE(kevin): When you add a value here, you need to handle them in
@ -59,6 +64,10 @@ typedef enum {
RT_PIXEL_FORMAT_count,
} rt_pixel_format;
RT_INLINE int rtIsDepthFormat(rt_pixel_format format) {
return format == RT_PIXEL_FORMAT_DEPTH24_STENCIL8 || format == RT_PIXEL_FORMAT_DEPTH32;
}
/* In renderer_api.h -> Not necessary for almost all gfx usage */
typedef struct rt_renderer_init_info_s rt_renderer_init_info;

View File

@ -25,7 +25,7 @@ static bool _renderer_loaded = false;
RT_DLLEXPORT
RT_CVAR_S(rt_Renderer,
"Select the render backend. Available options: [vk, null], Default: vk",
"vk");
"dx11");
#ifdef RT_STATIC_LIB
extern void RT_RENDERER_API_FN(RegisterCVars)(void);

View File

@ -17,7 +17,7 @@ extern "C" {
/* Handles for backend objects */
#define RT_RENDERER_BACKEND_HANDLE_MAX_INDEX ((1u<<24)-1)
#define RT_RENDER_BACKEND_HANDLE_MAX_INDEX ((1u<<24)-1)
#define RT_RENDER_BACKEND_HANDLE_MAX_VERSION 255
#define RT_RENDER_BACKEND_HANDLE(name) \
@ -87,6 +87,7 @@ typedef struct {
typedef enum {
RT_SHADER_TYPE_INVALID,
RT_SHADER_TYPE_VULKAN,
RT_SHADER_TYPE_DX11,
RT_SHADER_TYPE_count,
} rt_shader_type;

View File

@ -6,3 +6,4 @@ subdir('app_framework')
# Renderer libs
subdir('renderer/vk')
subdir('renderer/null')
subdir('renderer/dx11')

View File

@ -0,0 +1,146 @@
#include <d3d11.h>
#include <d3d11_1.h>
#include "gfx/renderer_api.h"
#include "runtime/config.h"
#include "runtime/handles.h"
#include "runtime/threading_helpers.hpp"
#include "device_objects.hpp"
#include "gpu.hpp"
RT_CVAR_I(rt_Dx11MaxBuffers,
"Maximum number of simultaneously existing buffers. Default: 4096",
4096);
static rt_buffer *_buffers;
static rt_buffer *_first_free;
static rt_mutex *_lock;
rt_result InitBufferManagement() {
_buffers =
reinterpret_cast<rt_buffer *>(calloc((size_t)rt_Dx11MaxBuffers.i, sizeof(rt_buffer)));
if (!_buffers) {
return RT_OUT_OF_MEMORY;
}
_lock = rtCreateMutex();
if (!_lock) {
free(_buffers);
return RT_UNKNOWN_ERROR;
}
_first_free = _buffers + 2;
for (int i = 0; i < rt_Dx11MaxBuffers.i; ++i) {
_buffers[i].next_free = &_buffers[i + 1];
}
return RT_SUCCESS;
}
void ShutdownBufferManagement() {
for (int i = 0; i < rt_Dx11MaxBuffers.i; ++i) {
if (_buffers[i].buffer)
_buffers[i].buffer->Release();
}
free(_buffers);
rtDestroyMutex(_lock);
}
rt_buffer *rtGetBuffer(rt_buffer_handle handle) {
if (!RT_IS_HANDLE_VALID(handle) || (int)handle.index >= rt_Dx11MaxBuffers.i)
return nullptr;
auto lg = rtAutoLock(_lock);
if (handle.version != _buffers[handle.index].version)
return nullptr;
return &_buffers[handle.index];
}
extern "C" rt_result RT_RENDERER_API_FN(CreateBuffers)(uint32_t count,
const rt_buffer_info *info,
rt_buffer_handle *p_buffers) {
for (uint32_t i = 0; i < count; ++i) {
rtLockMutex(_lock);
rt_buffer *slot = _first_free;
if (slot)
_first_free = slot->next_free;
rtUnlockMutex(_lock);
if (!slot) {
rtLog("dx11", "Failed to allocate a command buffer slot.");
rtLockMutex(_lock);
for (uint32_t j = 0; j < i; ++j) {
rt_buffer *s = &_buffers[p_buffers[j].index];
s->next_free = _first_free;
_first_free = s;
_first_free = s;
}
rtUnlockMutex(_lock);
return RT_OUT_OF_MEMORY;
}
D3D11_USAGE usage = D3D11_USAGE_DEFAULT;
if (info[i].usage == RT_BUFFER_USAGE_STATIC) {
usage = D3D11_USAGE_IMMUTABLE;
} else if (info[i].usage == RT_BUFFER_USAGE_DYNAMIC) {
usage = D3D11_USAGE_DEFAULT;
} else if (info[i].usage == RT_BUFFER_USAGE_TRANSIENT) {
usage = D3D11_USAGE_DYNAMIC;
}
UINT bind_flags = D3D11_BIND_UNORDERED_ACCESS;
if (info[i].type == RT_BUFFER_TYPE_VERTEX)
bind_flags = D3D11_BIND_VERTEX_BUFFER;
else if (info[i].type == RT_BUFFER_TYPE_INDEX)
bind_flags = D3D11_BIND_INDEX_BUFFER;
else if (info[i].type == RT_BUFFER_TYPE_UNIFORM)
bind_flags = D3D11_BIND_CONSTANT_BUFFER;
else if (info[i].type == RT_BUFFER_TYPE_STORAGE)
bind_flags = D3D11_BIND_UNORDERED_ACCESS;
D3D11_BUFFER_DESC desc = {};
desc.ByteWidth = static_cast<UINT>(((info[i].size + 15) / 16) * 16);
desc.Usage = usage;
desc.BindFlags = bind_flags;
desc.CPUAccessFlags = 0;
desc.MiscFlags = 0;
desc.StructureByteStride = 1;
D3D11_SUBRESOURCE_DATA data;
data.pSysMem = info->data;
data.SysMemPitch = 0;
data.SysMemSlicePitch = 0;
if (FAILED(
g_gpu.device->CreateBuffer(&desc, info[i].data ? &data : nullptr, &slot->buffer))) {
rtLog("dx11", "Failed to create a deferred context.");
auto lock_guard = rtAutoLock(_lock);
for (uint32_t j = 0; j < i; ++j) {
rt_buffer *s = &_buffers[p_buffers[j].index];
s->next_free = _first_free;
_first_free = s;
}
return RT_UNKNOWN_ERROR;
}
slot->version = (slot->version + 1) % RT_RENDER_BACKEND_HANDLE_MAX_VERSION;
const uint32_t index = (uint32_t)(slot - _buffers);
p_buffers[i].version = slot->version;
p_buffers[i].index = index;
}
return RT_SUCCESS;
}
extern "C" void RT_RENDERER_API_FN(DestroyBuffers)(uint32_t count, rt_buffer_handle *buffers) {
for (uint32_t i = 0; i < count; ++i) {
if (!RT_IS_HANDLE_VALID(buffers[i]) || (int)buffers[i].index >= rt_Dx11MaxBuffers.i)
continue;
auto lg = rtAutoLock(_lock);
if (buffers[i].version != _buffers[buffers[i].index].version)
continue;
_buffers[buffers[i].index].buffer->Release();
_buffers[buffers[i].index].next_free = _first_free;
_first_free = &_buffers[buffers[i].index];
}
}

View File

@ -0,0 +1,140 @@
#include <d3d11.h>
#include <d3d11_1.h>
#include "gfx/renderer_api.h"
#include "runtime/config.h"
#include "runtime/handles.h"
#include "runtime/threading.h"
#include "runtime/threading_helpers.hpp"
#include "device_objects.hpp"
#include "gpu.hpp"
RT_CVAR_I(rt_Dx11MaxCommandBuffers,
"Maximum number of simultaneously created command buffers. Default: 1024",
1024);
static rt_command_buffer *_buffers;
static rt_command_buffer *_first_free;
static rt_mutex *_lock;
rt_result InitCommandBufferManagement() {
_buffers = reinterpret_cast<rt_command_buffer *>(
calloc((size_t)rt_Dx11MaxCommandBuffers.i, sizeof(rt_command_buffer)));
if (!_buffers)
return RT_OUT_OF_MEMORY;
_first_free = &_buffers[1];
_lock = rtCreateMutex();
if (!_lock) {
free(_buffers);
return RT_UNKNOWN_ERROR;
}
for (int i = 0; i < rt_Dx11MaxCommandBuffers.i - 1; ++i) {
_buffers[i].next_free = &_buffers[i + 1];
}
return RT_SUCCESS;
}
void ShutdownCommandBufferManagement() {
for (int i = 0; i < rt_Dx11MaxCommandBuffers.i; ++i) {
if (_buffers[i].context)
_buffers[i].context->Release();
}
free(_buffers);
_buffers = nullptr;
}
extern "C" rt_result
RT_RENDERER_API_FN(AllocCommandBuffers)(uint32_t count,
const rt_alloc_command_buffer_info *,
rt_command_buffer_handle *p_command_buffers) {
for (uint32_t i = 0; i < count; ++i) {
rtLockMutex(_lock);
rt_command_buffer *slot = _first_free;
if (slot)
_first_free = slot->next_free;
rtUnlockMutex(_lock);
if (!slot) {
rtLog("dx11", "Failed to allocate a command buffer slot.");
rtLockMutex(_lock);
for (uint32_t j = 0; j < i; ++j) {
rt_command_buffer *s = &_buffers[p_command_buffers[j].index];
s->next_free = _first_free;
_first_free = s;
}
rtUnlockMutex(_lock);
return RT_OUT_OF_MEMORY;
}
if (!slot->context) {
if (FAILED(g_gpu.device->CreateDeferredContext1(0, &slot->context))) {
rtLog("dx11", "Failed to create a deferred context.");
auto lock_guard = rtAutoLock(_lock);
for (uint32_t j = 0; j < i; ++j) {
rt_command_buffer *s = &_buffers[p_command_buffers[j].index];
s->next_free = _first_free;
_first_free = s;
}
return RT_UNKNOWN_ERROR;
}
#ifdef RT_DEBUG
if (FAILED(slot->context->QueryInterface(IID_PPV_ARGS(&slot->annotation)))) {
rtLog("dx11", "Failed to retrieve the annotation interface.");
slot->annotation = nullptr;
}
#endif
} else {
slot->context->ClearState();
}
slot->version = (slot->version + 1) % RT_RENDER_BACKEND_HANDLE_MAX_VERSION;
const uint32_t index = (uint32_t)(slot - _buffers);
p_command_buffers[i].version = slot->version;
p_command_buffers[i].index = index;
}
return RT_SUCCESS;
}
extern "C" rt_result
RT_RENDERER_API_FN(SubmitCommandBuffers)(rt_gpu_queue, const rt_submit_command_buffers_info *info) {
// TODO: Handle semaphores
// Submit the command lists to the gpu
for (uint32_t i = 0; i < info->command_buffer_count; ++i) {
rt_command_buffer *cmdbuf = &_buffers[info->command_buffers[i].index];
if (cmdbuf->version != info->command_buffers[i].version) {
rtLog("dx11", "Tried to submit an invalid command buffer (version mismatch)");
return RT_INVALID_VALUE;
}
ID3D11CommandList *cmdlist;
if (FAILED(cmdbuf->context->FinishCommandList(FALSE, &cmdlist))) {
rtLog("dx11", "FinishCommandList failed");
return RT_UNKNOWN_ERROR;
}
rtLockMutex(g_gpu.context_lock);
g_gpu.device_context->ExecuteCommandList(cmdlist, FALSE);
rtUnlockMutex(g_gpu.context_lock);
rtLockMutex(_lock);
cmdbuf->next_free = _first_free;
_first_free = cmdbuf;
rtUnlockMutex(_lock);
}
return RT_SUCCESS;
}
rt_command_buffer *rtGetCommandBuffer(rt_command_buffer_handle handle) {
if (!RT_IS_HANDLE_VALID(handle) || (int)handle.index >= rt_Dx11MaxCommandBuffers.i)
return nullptr;
auto lg = rtAutoLock(_lock);
if (handle.version != _buffers[handle.index].version)
return nullptr;
return &_buffers[handle.index];
}

View File

@ -0,0 +1,87 @@
#include <d3d11.h>
#include <d3d11_1.h>
#include "gfx/renderer_api.h"
#include "device_objects.hpp"
#include "gpu.hpp"
extern "C" void RT_RENDERER_API_FN(CmdBeginPass)(rt_command_buffer_handle cmdhandle,
const rt_cmd_begin_pass_info *info) {
rt_command_buffer *cmd = rtGetCommandBuffer(cmdhandle);
if (!RT_VERIFY(cmd))
return;
if (cmd->annotation) {
WCHAR wname[128];
if (rtUTF8ToWStr(info->name, wname, sizeof(wname)) == RT_SUCCESS)
cmd->annotation->BeginEvent(wname);
}
// Setup rtvs
ID3D11RenderTargetView *rtvs[4];
ID3D11DepthStencilView *dsv = nullptr;
for (uint32_t i = 0; i < info->color_buffer_count; ++i) {
rt_render_target *rt = rtGetRenderTarget(info->color_buffers[i]);
if (!RT_VERIFY(rt))
return;
RT_ASSERT(rt->IsColorRenderTarget(), "Needs to provide a valid color render target");
rtvs[i] = rt->rtv;
if (info->color_buffer_loads[i] == RT_PASS_LOAD_MODE_CLEAR) {
FLOAT color[4] = {
info->color_buffer_clear_values[i].color.r,
info->color_buffer_clear_values[i].color.g,
info->color_buffer_clear_values[i].color.b,
info->color_buffer_clear_values[i].color.a,
};
cmd->context->ClearRenderTargetView(rt->rtv, color);
}
}
rt_render_target *dsvrt = rtGetRenderTarget(info->depth_stencil_buffer);
if (dsvrt) {
RT_ASSERT(dsvrt->IsDepthStencilTarget(),
"Need to provide a valid depth stencil render target");
dsv = dsvrt->dsv;
if (info->depth_stencil_buffer_load == RT_PASS_LOAD_MODE_CLEAR)
cmd->context->ClearDepthStencilView(
dsv,
(dsvrt->HasStencilComponent()) ? D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL
: D3D11_CLEAR_DEPTH,
info->depth_stencil_buffer_clear_value.depth_stencil.depth,
static_cast<UINT8>(info->depth_stencil_buffer_clear_value.depth_stencil.stencil));
}
cmd->context->OMSetRenderTargets(static_cast<UINT>(info->color_buffer_count), rtvs, dsv);
}
extern "C" void RT_RENDERER_API_FN(CmdEndPass)(rt_command_buffer_handle cmdhandle) {
rt_command_buffer *cmd = rtGetCommandBuffer(cmdhandle);
if (!RT_VERIFY(cmd))
return;
if (cmd->annotation) {
cmd->annotation->EndEvent();
}
}
extern "C" void RT_RENDERER_API_FN(CmdTransitionRenderTarget)(rt_command_buffer_handle cmdhandle,
rt_render_target_handle target,
rt_render_target_state state) {
rt_command_buffer *cmd = rtGetCommandBuffer(cmdhandle);
if (!RT_VERIFY(cmd))
return;
RT_UNUSED(target);
RT_UNUSED(state);
}
extern "C" void
RT_RENDERER_API_FN(CmdFlushRenderTargetWrite)(rt_command_buffer_handle cmdhandle,
rt_render_target_handle render_target) {
rt_command_buffer *cmd = rtGetCommandBuffer(cmdhandle);
if (!RT_VERIFY(cmd))
return;
RT_UNUSED(render_target);
}

View File

@ -0,0 +1,81 @@
#ifndef RT_DX11_DEVICE_OBJECTS_HPP
#define RT_DX11_DEVICE_OBJECTS_HPP
// Types containing various api objects
#include <stdint.h>
#include <d3d11.h>
#include <d3d11_1.h>
#include "runtime/runtime.h"
struct rt_render_target {
// Only one of these should be valid
ID3D11RenderTargetView *rtv;
ID3D11DepthStencilView *dsv;
ID3D11Texture2D *texture;
rt_pixel_format format;
uint32_t version;
rt_render_target *next_free;
RT_INLINE bool HasStencilComponent() const {
return format == RT_PIXEL_FORMAT_DEPTH24_STENCIL8;
}
RT_INLINE bool IsColorRenderTarget() const {
RT_ASSERT(!(rtv != nullptr && dsv != nullptr),
"A render target should not contain a render target and a depth stencil view");
return rtv != nullptr;
}
RT_INLINE bool IsDepthStencilTarget() const {
RT_ASSERT(!(rtv != nullptr && dsv != nullptr),
"A render target should not contain a render target and a depth stencil view");
return dsv != nullptr;
}
};
struct rt_command_buffer {
// Only created once and then re-used.
ID3D11DeviceContext1 *context;
ID3DUserDefinedAnnotation *annotation;
uint32_t version;
rt_command_buffer *next_free;
};
struct rt_buffer {
ID3D11Buffer *buffer;
rt_buffer_type type;
rt_buffer_usage usage;
uint32_t version;
rt_buffer *next_free;
};
struct rt_pipeline {
ID3D11InputLayout *input_layout;
ID3D11VertexShader *vertex_shader;
ID3D11PixelShader *pixel_shader;
ID3D11ComputeShader *compute_shader;
rt_pipeline *next_free;
uint32_t version;
RT_INLINE bool IsComputePipeline() const {
RT_ASSERT(!(compute_shader && (vertex_shader || pixel_shader)),
"A pipeline should contain either a compute shader or graphics shaders.");
return compute_shader != nullptr;
}
};
rt_render_target *rtGetRenderTarget(rt_render_target_handle handle);
rt_command_buffer *rtGetCommandBuffer(rt_command_buffer_handle handle);
rt_buffer *rtGetBuffer(rt_buffer_handle handle);
rt_pipeline *rtGetPipeline(rt_pipeline_handle handle);
#endif

41
src/renderer/dx11/gpu.hpp Normal file
View File

@ -0,0 +1,41 @@
#ifndef RT_DX11_GPU_HPP
#define RT_DX11_GPU_HPP
#include <wrl.h>
#include <d3d11.h>
#include <d3d11_1.h>
#include <dxgi1_3.h>
#include "runtime/threading.h"
#include "gfx/renderer_api.h"
// Smart pointer for COM-Objects
template<typename T>
using ComPtr = Microsoft::WRL::ComPtr<T>;
struct rt_swap_chain {
ComPtr<IDXGISwapChain1> swap_chain;
ComPtr<ID3D11RenderTargetView> rtv;
};
struct rt_gpu {
ComPtr<ID3D11Device1> device;
ComPtr<ID3D11DeviceContext1> device_context;
ComPtr<IDXGIFactory2> dxgi_factory;
rt_swap_chain swap_chain;
rt_mutex *context_lock;
D3D_FEATURE_LEVEL feature_level;
D3D11_FEATURE_DATA_THREADING threading_support;
};
#ifndef DONT_DEFINE_GPU_GLOBAL
extern rt_gpu g_gpu;
#endif
DXGI_FORMAT rtConvertPixelFormat(rt_pixel_format format);
#endif

View File

@ -0,0 +1,32 @@
#include "gpu.hpp"
DXGI_FORMAT rtConvertPixelFormat(rt_pixel_format format) {
switch (format) {
case RT_PIXEL_FORMAT_INVALID:
return DXGI_FORMAT_UNKNOWN;
case RT_PIXEL_FORMAT_R8G8B8A8_UNORM:
return DXGI_FORMAT_R8G8B8A8_UNORM;
case RT_PIXEL_FORMAT_B8G8R8A8_UNORM:
return DXGI_FORMAT_B8G8R8A8_UNORM;
case RT_PIXEL_FORMAT_R8G8B8A8_SRGB:
return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
case RT_PIXEL_FORMAT_B8G8R8A8_SRGB:
return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB;
case RT_PIXEL_FORMAT_R8G8B8_UNORM:
return DXGI_FORMAT_R8G8B8A8_UNORM;
case RT_PIXEL_FORMAT_B8G8R8_UNORM:
return DXGI_FORMAT_B8G8R8X8_UNORM;
case RT_PIXEL_FORMAT_R8G8B8_SRGB:
return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
case RT_PIXEL_FORMAT_B8G8R8_SRGB:
return DXGI_FORMAT_B8G8R8X8_UNORM_SRGB;
case RT_PIXEL_FORMAT_DEPTH24_STENCIL8:
return DXGI_FORMAT_D24_UNORM_S8_UINT;
case RT_PIXEL_FORMAT_DEPTH32:
return DXGI_FORMAT_D32_FLOAT;
default:
return DXGI_FORMAT_UNKNOWN;
}
}

283
src/renderer/dx11/init.cpp Normal file
View File

@ -0,0 +1,283 @@
#ifndef _WIN32
#pragma warning Building DX11 on non - windows is probably a mistake
#endif
#include <d3d11.h>
#include <dxgi1_3.h>
#include <wrl.h>
#include "gfx/renderer_api.h"
#include "runtime/config.h"
#define DONT_DEFINE_RENDERER_GLOBAL
#include "gpu.hpp"
RT_CVAR_S(
rt_Dx11AdapterName,
"Name of the adapter that should be used for device creation. Default: \"\" (Use default)",
"");
RT_CVAR_I(rt_Dx11VSync, "Enable vsync. Default: 1", 1);
RT_CVAR_I(rt_Dx11MaxSubmittedCommandBuffers,
"Maximum number of submitted command buffers per frame. Default: 1024",
1024);
extern rt_cvar rt_Dx11MaxCommandBuffers;
rt_gpu g_gpu;
extern "C" void RT_RENDERER_API_FN(RegisterCVars)(void) {
rtRegisterCVAR(&rt_Dx11AdapterName);
rtRegisterCVAR(&rt_Dx11VSync);
rtRegisterCVAR(&rt_Dx11MaxCommandBuffers);
}
static rt_swap_chain CreateSwapChain(HWND hwnd) {
rt_swap_chain swc;
DXGI_SWAP_CHAIN_DESC1 desc;
desc.Width = 0; // use window width
desc.Height = 0; // use window height
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // can't specify _SRGB here when using
// DXGI_SWAP_EFFECT_FLIP_* ...;
desc.Stereo = FALSE;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
desc.BufferCount = 2;
desc.Scaling = DXGI_SCALING_STRETCH;
desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
desc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
desc.Flags = 0;
if (FAILED(g_gpu.dxgi_factory->CreateSwapChainForHwnd(g_gpu.device.Get(),
hwnd,
&desc,
nullptr,
nullptr,
&swc.swap_chain))) {
rtReportError("dx11", "Failed to create the swap chain.");
return swc;
}
ID3D11Texture2D *frame_buffer;
if (FAILED(swc.swap_chain->GetBuffer(0, IID_PPV_ARGS(&frame_buffer)))) {
rtReportError("dx11", "Failed to retrieve the backbuffer.");
swc.swap_chain.Reset();
return swc;
}
D3D11_RENDER_TARGET_VIEW_DESC rtv_desc = {};
rtv_desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB;
rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
if (FAILED(g_gpu.device->CreateRenderTargetView(frame_buffer, &rtv_desc, &swc.rtv))) {
rtReportError("dx11", "Failed to create the render target view for the backbuffer.");
swc.swap_chain.Reset();
return swc;
}
return swc;
}
static IDXGIAdapter *RetrieveSelectedAdapter(void) {
ComPtr<IDXGIFactory2> factory;
if (FAILED(CreateDXGIFactory2(0, IID_PPV_ARGS(&factory)))) {
return NULL;
}
UINT i = 0;
IDXGIAdapter *adapter;
while (factory->EnumAdapters(i, &adapter) == S_OK) {
++i;
DXGI_ADAPTER_DESC desc;
adapter->GetDesc(&desc);
char utf8_desc[256];
rtWStrToUTF8(desc.Description, utf8_desc, 256);
if (strncmp(utf8_desc, rt_Dx11AdapterName.s, 256) == 0)
return adapter;
}
return NULL;
}
extern rt_result InitCommandBufferManagement();
extern void ShutdownCommandBufferManagement();
extern rt_result InitRenderTargetManagement();
extern void ShutdownRenderTargetManagement();
extern rt_result InitBufferManagement();
extern void ShutdownBufferManagement();
extern "C" rt_result RT_RENDERER_API_FN(Init)(const rt_renderer_init_info *info) {
constexpr D3D_FEATURE_LEVEL feature_levels[] = {D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0};
UINT device_flags = 0;
#ifdef RT_DEBUG
device_flags |= D3D11_CREATE_DEVICE_DEBUG;
#endif
IDXGIAdapter *selected_adapter = RetrieveSelectedAdapter();
ID3D11Device *base_device;
ID3D11DeviceContext *base_context;
if (FAILED(D3D11CreateDevice(selected_adapter,
D3D_DRIVER_TYPE_HARDWARE,
nullptr,
device_flags,
feature_levels,
RT_ARRAY_COUNT(feature_levels),
D3D11_SDK_VERSION,
&base_device,
&g_gpu.feature_level,
&base_context))) {
rtLog("dx11", "Feature level 11.1 creation failed, retrying with feature level 11.0");
if (FAILED(D3D11CreateDevice(selected_adapter,
D3D_DRIVER_TYPE_HARDWARE,
nullptr,
device_flags,
&feature_levels[1],
RT_ARRAY_COUNT(feature_levels) - 1,
D3D11_SDK_VERSION,
&base_device,
&g_gpu.feature_level,
&base_context))) {
rtReportError("dx11", "Failed to create the d3d11 device.");
return RT_UNKNOWN_ERROR;
}
}
if (FAILED(base_device->QueryInterface(IID_PPV_ARGS(&g_gpu.device)))) {
rtReportError("dx11", "Failed to query the D3D11Device1 interface.");
return RT_UNKNOWN_ERROR;
}
if (FAILED(base_context->QueryInterface(IID_PPV_ARGS(&g_gpu.device_context)))) {
rtReportError("dx11", "Failed to query the D3D11DeviceContext1 interface.");
return RT_UNKNOWN_ERROR;
}
IDXGIDevice1 *dxgi_device;
if (FAILED(g_gpu.device->QueryInterface(&dxgi_device))) {
rtReportError("dx11", "Failed to query the DXGIDevice1 interface.");
return RT_UNKNOWN_ERROR;
}
IDXGIAdapter *adapter;
if (FAILED(dxgi_device->GetAdapter(&adapter))) {
rtReportError("dx11", "Failed to retrieve the dxgi adapter.");
return RT_UNKNOWN_ERROR;
}
if (FAILED(adapter->GetParent(IID_PPV_ARGS(&g_gpu.dxgi_factory)))) {
rtReportError("dx11", "Failed to retrieve the dxgi factory.");
return RT_UNKNOWN_ERROR;
}
g_gpu.device->CheckFeatureSupport(D3D11_FEATURE_THREADING,
&g_gpu.threading_support,
sizeof(g_gpu.threading_support));
g_gpu.swap_chain = CreateSwapChain(info->hWnd);
g_gpu.context_lock = rtCreateMutex();
rt_result res = InitCommandBufferManagement();
if (res != RT_SUCCESS)
return res;
res = InitRenderTargetManagement();
if (res != RT_SUCCESS)
return res;
res = InitBufferManagement();
if (res != RT_SUCCESS)
return res;
return RT_SUCCESS;
}
extern "C" void RT_RENDERER_API_FN(Shutdown)(void) {
ShutdownBufferManagement();
ShutdownRenderTargetManagement();
ShutdownCommandBufferManagement();
rtDestroyMutex(g_gpu.context_lock);
g_gpu.swap_chain.rtv.Reset();
g_gpu.swap_chain.swap_chain.Reset();
g_gpu.dxgi_factory.Reset();
g_gpu.device.Reset();
}
extern "C" unsigned int RT_RENDERER_API_FN(GetMaxFramesInFlight)(void) {
// TODO: Verify this.
return 1;
}
extern "C" void RT_RENDERER_API_FN(BeginFrame)(unsigned int frame_id) {
RT_UNUSED(frame_id);
FLOAT clear_color[4] = {
0,
0,
0,
0,
};
rtLockMutex(g_gpu.context_lock);
g_gpu.device_context->ClearRenderTargetView(g_gpu.swap_chain.rtv.Get(), clear_color);
rtUnlockMutex(g_gpu.context_lock);
}
extern "C" void RT_RENDERER_API_FN(EndFrame)(unsigned int frame_id) {
RT_UNUSED(frame_id);
rtLockMutex(g_gpu.context_lock);
UINT sync_interval = rt_Dx11VSync.i ? 1 : 0;
g_gpu.swap_chain.swap_chain->Present(sync_interval, 0);
rtUnlockMutex(g_gpu.context_lock);
}
// Copied from null. Delete once no longer needed
extern "C" {
#define RETURN_HANDLE_STUB2(type, initial) \
static unsigned int s_next = (initial); \
s_next = (s_next + 1) % RT_RENDER_BACKEND_HANDLE_MAX_INDEX; \
type h = { \
1, \
s_next, \
}; \
return h;
#define RETURN_HANDLE_STUB(type) RETURN_HANDLE_STUB2(type, 1)
#define RETURN_HANDLE_ARRAY_STUB2(out, count, initial) \
static unsigned int s_next = (initial); \
for (uint32_t i = 0; i < (count); ++i) { \
(out)[i].index = (s_next++) % RT_RENDER_BACKEND_HANDLE_MAX_INDEX; \
(out)[i].version = 1; \
}
#define RETURN_HANDLE_ARRAY_STUB(out, count) RETURN_HANDLE_ARRAY_STUB2(out, count, 1)
rt_result RT_RENDERER_API_FN(CreateSemaphores)(uint32_t count,
const rt_gpu_semaphore_info *info,
rt_gpu_semaphore_handle *p_semaphores) {
RT_UNUSED(info);
RETURN_HANDLE_ARRAY_STUB2(p_semaphores, count, 3)
return RT_SUCCESS;
}
void RT_RENDERER_API_FN(DestroySemaphores)(uint32_t count, rt_gpu_semaphore_handle *semaphores) {
RT_UNUSED(count);
RT_UNUSED(semaphores);
}
/* NOTE(Kevin): It might become necessary to actually track the value, to correctly simulate gpu
* behaviour */
uint64_t RT_RENDERER_API_FN(GetSemaphoreValue)(rt_gpu_semaphore_handle sem) {
RT_UNUSED(sem);
return 0;
}
rt_gpu_semaphore_handle RT_RENDERER_API_FN(GetSwapchainAvailableSemaphore)(void) {
return {1, 1};
}
rt_gpu_semaphore_handle RT_RENDERER_API_FN(GetRenderFinishedSemaphore)(void) {
return {1, 2};
}
}

View File

@ -0,0 +1,25 @@
if get_option('build_dx11')
dx11_dep = declare_dependency(link_args: ['-ld3d11', '-ldxgi', '-lwinmm', '-ldxguid'])
dx11_renderer_lib = library('rtdx11',
# Project Sources
'device_objects.hpp',
'gpu.hpp',
'buffers.cpp',
'commands.cpp',
'command_buffers.cpp',
'helpers.cpp',
'init.cpp',
'render_targets.cpp',
dependencies : [m_dep, windowing_dep, dx11_dep],
include_directories : [engine_incdir, contrib_incdir],
link_with : [runtime_lib],
cpp_pch : 'pch/dx11_pch.h',
override_options : ['b_sanitize=none'],
install : true)
engine_libs += dx11_renderer_lib
engine_lib_paths += dx11_renderer_lib.full_path()
endif

View File

@ -0,0 +1,5 @@
// DX11 headers
#include <wrl.h>
#include <d3d11.h>
#include <d3d11_1.h>
#include <dxgi1_3.h>

View File

@ -0,0 +1,217 @@
#include <d3d11.h>
#include <d3d11_1.h>
#include "gfx/effect.h"
#include "gfx/renderer_api.h"
#include "runtime/config.h"
#include "runtime/handles.h"
#include "runtime/mem_arena.h"
#include "runtime/threading_helpers.hpp"
#include "device_objects.hpp"
#include "gpu.hpp"
RT_CVAR_I(rt_Dx11MaxPipelines,
"Maximum number of simultaneously existing pipelines. Default: 128",
128);
static rt_pipeline *_pipelines;
static rt_pipeline *_first_free;
static rt_mutex *_lock;
rt_result InitPipelineManagement() {
_pipelines =
reinterpret_cast<rt_pipeline *>(calloc((size_t)rt_Dx11MaxPipelines.i, sizeof(rt_pipeline)));
if (!_pipelines)
return RT_OUT_OF_MEMORY;
_first_free = _pipelines + 1;
for (int i = 0; i < rt_Dx11MaxPipelines.i - 1; ++i)
_pipelines[i].next_free = &_pipelines[i + 1];
_lock = rtCreateMutex();
if (!_lock) {
free(_pipelines);
return RT_UNKNOWN_ERROR;
}
return RT_SUCCESS;
}
void ShutdownPipelineManagement() {
for (int i = 0; i < rt_Dx11MaxPipelines.i; ++i) {
if (_pipelines[i].compute_shader)
_pipelines[i].compute_shader->Release();
if (_pipelines[i].vertex_shader)
_pipelines[i].vertex_shader->Release();
if (_pipelines[i].pixel_shader)
_pipelines[i].pixel_shader->Release();
if (_pipelines[i].input_layout)
_pipelines[i].input_layout->Release();
}
free(_pipelines);
rtDestroyMutex(_lock);
}
rt_result GetShader(rt_resource_id id, rt_shader_info **p_shader, rt_arena *arena) {
size_t shader_size = rtGetResourceSize(id);
if (shader_size == 0)
return RT_INVALID_VALUE;
void *buffer = rtArenaPush(arena, shader_size);
if (!buffer)
return RT_OUT_OF_MEMORY;
rt_result res = rtGetResource(id, buffer);
if (res != RT_SUCCESS) {
rtArenaPop(arena, shader_size);
return res;
}
rt_resource *resource = reinterpret_cast<rt_resource *>(buffer);
RT_ASSERT(resource->type == RT_RESOURCE_SHADER, "Expected a shader");
*p_shader = reinterpret_cast<rt_shader_info *>(resource->data);
return RT_SUCCESS;
}
extern "C" rt_pipeline_handle RT_RENDERER_API_FN(CompilePipeline)(const rt_pipeline_info *info) {
rt_pipeline *slot = nullptr;
{
auto lg = rtAutoLock(_lock);
slot = _first_free;
if (slot)
_first_free = slot->next_free;
}
if (!slot) {
rtLog("dx11", "Could not create pipeline, because no slots are available.");
return RT_INVALID_HANDLE;
}
rt_temp_arena temp = rtGetTemporaryArena(NULL, 0);
if (info->vertex_shader != RT_INVALID_RESOURCE_ID) {
rt_shader_info *vs;
if (GetShader(info->vertex_shader, &vs, temp.arena) != RT_SUCCESS) {
rtReportError("dx11", "Could not retrieve vertex shader data.");
auto lg = rtAutoLock(_lock);
slot->next_free = _first_free;
_first_free = slot;
return RT_INVALID_HANDLE;
}
void *bytecode = rtResolveRelptr(&vs->bytecode);
if (FAILED(g_gpu.device->CreateVertexShader(bytecode,
vs->bytecode_length,
NULL,
&slot->vertex_shader))) {
rtReportError("dx11", "Vertex shader creation failed");
auto lg = rtAutoLock(_lock);
slot->next_free = _first_free;
_first_free = slot;
return RT_INVALID_HANDLE;
}
// TODO: effects should specify the expected vertex layout
// For now, we use a default
/* clang-format off */
D3D11_INPUT_ELEMENT_DESC default_layout[] = {
{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
{"NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0},
{"TANGENT", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0},
{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0},
};
/* clang-format on */
if (FAILED(g_gpu.device->CreateInputLayout(default_layout,
RT_ARRAY_COUNT(default_layout),
bytecode,
vs->bytecode_length,
&slot->input_layout))) {
rtReportError("dx11", "Failed to create the vertex layout.");
auto lg = rtAutoLock(_lock);
slot->next_free = _first_free;
_first_free = slot;
return RT_INVALID_HANDLE;
}
}
if (info->fragment_shader != RT_INVALID_RESOURCE_ID) {
rt_shader_info *vs;
if (GetShader(info->fragment_shader, &vs, temp.arena) != RT_SUCCESS) {
rtReportError("dx11", "Could not retrieve fragment shader data.");
auto lg = rtAutoLock(_lock);
slot->next_free = _first_free;
_first_free = slot;
return RT_INVALID_HANDLE;
}
void *bytecode = rtResolveRelptr(&vs->bytecode);
if (FAILED(g_gpu.device->CreatePixelShader(bytecode,
vs->bytecode_length,
NULL,
&slot->pixel_shader))) {
rtReportError("dx11", "Fragment shader creation failed");
auto lg = rtAutoLock(_lock);
slot->next_free = _first_free;
_first_free = slot;
return RT_INVALID_HANDLE;
}
}
if (info->compute_shader != RT_INVALID_RESOURCE_ID) {
rt_shader_info *vs;
if (GetShader(info->compute_shader, &vs, temp.arena) != RT_SUCCESS) {
rtReportError("dx11", "Could not retrieve compute shader data.");
auto lg = rtAutoLock(_lock);
slot->next_free = _first_free;
_first_free = slot;
return RT_INVALID_HANDLE;
}
void *bytecode = rtResolveRelptr(&vs->bytecode);
if (FAILED(g_gpu.device->CreateComputeShader(bytecode,
vs->bytecode_length,
NULL,
&slot->compute_shader))) {
rtReportError("dx11", "Compute shader creation failed");
auto lg = rtAutoLock(_lock);
slot->next_free = _first_free;
_first_free = slot;
return RT_INVALID_HANDLE;
}
}
slot->version = (slot->version + 1) % RT_RENDER_BACKEND_HANDLE_MAX_VERSION;
uint32_t index = static_cast<uint32_t>(slot - _pipelines);
return {slot->version, index};
}
extern "C" void RT_RENDERER_API_FN(DestroyPipeline)(rt_pipeline_handle handle) {
if (!RT_IS_HANDLE_VALID(handle) || (int)handle.index >= rt_Dx11MaxPipelines.i)
return;
auto lg = rtAutoLock(_lock);
if (handle.version != _pipelines[handle.index].version)
return;
if (_pipelines[handle.index].compute_shader)
_pipelines[handle.index].compute_shader->Release();
if (_pipelines[handle.index].vertex_shader)
_pipelines[handle.index].vertex_shader->Release();
if (_pipelines[handle.index].pixel_shader)
_pipelines[handle.index].pixel_shader->Release();
if (_pipelines[handle.index].input_layout)
_pipelines[handle.index].input_layout->Release();
_pipelines[handle.index].next_free = _first_free;
_pipelines[handle.index].version =
(_pipelines[handle.index].version + 1) % RT_RENDER_BACKEND_HANDLE_MAX_VERSION;
_first_free = &_pipelines[handle.index];
}
rt_pipeline *rtGetPipeline(rt_pipeline_handle handle) {
if (!RT_IS_HANDLE_VALID(handle) || (int)handle.index >= rt_Dx11MaxPipelines.i)
return nullptr;
auto lg = rtAutoLock(_lock);
if (handle.version != _pipelines[handle.index].version)
return nullptr;
return &_pipelines[handle.index];
}

View File

@ -0,0 +1,173 @@
#include <d3d11.h>
#include <d3d11_1.h>
#include "gfx/renderer_api.h"
#include "runtime/config.h"
#include "runtime/handles.h"
#include "runtime/threading_helpers.hpp"
#include "device_objects.hpp"
#include "gpu.hpp"
RT_CVAR_I(rt_Dx11MaxRenderTargets,
"Maximum number of simultaneously existing render targets. Default: 128",
128);
static rt_render_target *_render_targets;
static rt_render_target *_first_free;
static rt_mutex *_lock;
rt_result InitRenderTargetManagement() {
_render_targets = reinterpret_cast<rt_render_target *>(
calloc((size_t)rt_Dx11MaxRenderTargets.i, sizeof(rt_render_target)));
if (!_render_targets) {
return RT_OUT_OF_MEMORY;
}
_lock = rtCreateMutex();
if (!_lock) {
free(_render_targets);
return RT_UNKNOWN_ERROR;
}
_render_targets[1].rtv = g_gpu.swap_chain.rtv.Get();
_render_targets[1].format = RT_PIXEL_FORMAT_B8G8R8A8_SRGB;
_render_targets[1].version = 1;
_first_free = _render_targets + 2;
for (int i = 0; i < rt_Dx11MaxRenderTargets.i; ++i) {
_render_targets[i].next_free = &_render_targets[i + 1];
}
return RT_SUCCESS;
}
void ShutdownRenderTargetManagement() {
// Swapchain rtv in slot 1 will be released elsewhere
for (int i = 2; i < rt_Dx11MaxRenderTargets.i; ++i) {
if (_render_targets[i].rtv)
_render_targets[i].rtv->Release();
if (_render_targets[i].dsv)
_render_targets[i].dsv->Release();
if (_render_targets[i].texture)
_render_targets[i].texture->Release();
}
free(_render_targets);
rtDestroyMutex(_lock);
}
extern "C" rt_render_target_handle
RT_RENDERER_API_FN(CreateRenderTarget)(const rt_render_target_info *info) {
rt_render_target *slot = nullptr;
{
auto lock_guard = rtAutoLock(_lock);
slot = _first_free;
_first_free = slot->next_free;
}
if (!slot) {
rtLog("dx11",
"Could not create a new render target, because all available slots are currently in "
"use.");
return RT_INVALID_HANDLE;
}
slot->format = info->format;
if (!rtIsDepthFormat(info->format)) {
D3D11_TEXTURE2D_DESC tex_desc = {};
tex_desc.Width = info->width;
tex_desc.Height = info->height;
tex_desc.MipLevels = 1;
tex_desc.ArraySize = 1;
tex_desc.Format = rtConvertPixelFormat(info->format);
tex_desc.SampleDesc.Count = 1;
tex_desc.SampleDesc.Quality = 0;
tex_desc.Usage = D3D11_USAGE_DEFAULT; // read and write
tex_desc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
tex_desc.CPUAccessFlags = 0; // none
tex_desc.MiscFlags = 0;
if (FAILED(g_gpu.device->CreateTexture2D(&tex_desc, nullptr, &slot->texture))) {
rtLog("dx11", "Failed to create backing texture for render target %s", info->name);
auto lg = rtAutoLock(_lock);
slot->next_free = _first_free;
_first_free = slot;
return RT_INVALID_HANDLE;
}
D3D11_RENDER_TARGET_VIEW_DESC rtv_desc = {};
rtv_desc.Format = rtConvertPixelFormat(info->format);
rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
rtv_desc.Texture2D.MipSlice = 0;
if (FAILED(g_gpu.device->CreateRenderTargetView(slot->texture, &rtv_desc, &slot->rtv))) {
slot->texture->Release();
rtLog("dx11",
"Failed to create the render target view for render target %s",
info->name);
auto lg = rtAutoLock(_lock);
slot->next_free = _first_free;
_first_free = slot;
return RT_INVALID_HANDLE;
}
slot->version = (slot->version + 1) % RT_RENDER_BACKEND_HANDLE_MAX_VERSION;
uint32_t index = static_cast<uint32_t>(slot - _render_targets);
return {.version = slot->version, .index = index};
} else {
D3D11_TEXTURE2D_DESC tex_desc = {};
tex_desc.Width = info->width;
tex_desc.Height = info->height;
tex_desc.MipLevels = 1;
tex_desc.ArraySize = 1;
tex_desc.Format = rtConvertPixelFormat(info->format);
tex_desc.SampleDesc.Count = 1;
tex_desc.SampleDesc.Quality = 0;
tex_desc.Usage = D3D11_USAGE_DEFAULT; // read and write
tex_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE;
tex_desc.CPUAccessFlags = 0; // none
tex_desc.MiscFlags = 0;
if (FAILED(g_gpu.device->CreateTexture2D(&tex_desc, nullptr, &slot->texture))) {
rtLog("dx11", "Failed to create backing texture for render target %s", info->name);
auto lg = rtAutoLock(_lock);
slot->next_free = _first_free;
_first_free = slot;
return RT_INVALID_HANDLE;
}
D3D11_DEPTH_STENCIL_VIEW_DESC dsv_desc = {};
dsv_desc.Format = rtConvertPixelFormat(info->format);
dsv_desc.Flags = 0;
dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
dsv_desc.Texture2D.MipSlice = 0;
if (FAILED(g_gpu.device->CreateDepthStencilView(slot->texture, &dsv_desc, &slot->dsv))) {
slot->texture->Release();
rtLog("dx11",
"Failed to create the depth stencil view for render target %s",
info->name);
auto lg = rtAutoLock(_lock);
slot->next_free = _first_free;
_first_free = slot;
return RT_INVALID_HANDLE;
}
slot->version = (slot->version + 1) % RT_RENDER_BACKEND_HANDLE_MAX_VERSION;
uint32_t index = static_cast<uint32_t>(slot - _render_targets);
return {.version = slot->version, .index = index};
}
}
extern "C" rt_render_target_handle RT_RENDERER_API_FN(GetSwapchainRenderTarget)(void) {
return {1, 1};
}
extern "C" void RT_RENDERER_API_FN(DestroyRenderTarget)(rt_render_target_handle handle) {
RT_UNUSED(handle);
}
rt_render_target *rtGetRenderTarget(rt_render_target_handle handle) {
if (!RT_IS_HANDLE_VALID(handle) || (int)handle.index >= rt_Dx11MaxRenderTargets.i)
return nullptr;
auto lg = rtAutoLock(_lock);
if (_render_targets[handle.index].version != handle.version)
return nullptr;
return &_render_targets[handle.index];
}

View File

@ -6,14 +6,14 @@
#define RETURN_HANDLE_STUB2(type, initial) \
static unsigned int s_next = (initial); \
return (type) { .index = (s_next++) % RT_RENDERER_BACKEND_HANDLE_MAX_INDEX, .version = 1 }
return (type) { .index = (s_next++) % RT_RENDER_BACKEND_HANDLE_MAX_INDEX, .version = 1 }
#define RETURN_HANDLE_STUB(type) RETURN_HANDLE_STUB2(type, 1)
#define RETURN_HANDLE_ARRAY_STUB2(out, count, initial) \
static unsigned int s_next = (initial); \
for (uint32_t i = 0; i < (count); ++i) { \
(out)[i].index = (s_next++) % RT_RENDERER_BACKEND_HANDLE_MAX_INDEX; \
(out)[i].index = (s_next++) % RT_RENDER_BACKEND_HANDLE_MAX_INDEX; \
(out)[i].version = 1; \
}

View File

@ -19,6 +19,7 @@ runtime_lib = library('rt',
'resources.h',
'runtime.h',
'threading.h',
'threading_helpers.hpp',
'aio.c',
'assert.c',

View File

@ -5,6 +5,9 @@
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
#elif defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable : 4201) /* anonymous struct */
#endif
struct vec2 {
@ -44,6 +47,8 @@ typedef struct {
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic pop
#elif defined(_MSC_VER)
#pragma warning(pop)
#endif
#endif

View File

@ -121,8 +121,18 @@ RT_DLLEXPORT int rtAssertHandler(const char *expr, const char *msg, const char *
} \
} \
} while (0)
#define RT_ASSERT_ALWAYS_EVAL(x, msg) RT_ASSERT(x, msg)
// Asserts if p is "false", evaluates to p
// NOTE that this will evaluate p multiple times!
#define RT_VERIFY(p) \
((!p) ? (RT_DEBUGBREAK, rtAssertHandler(#p, "Verify failed", __FILE__, __LINE__), p) : p)
#else
#define RT_ASSERT(x, msg) RT_UNUSED(x)
#define RT_ASSERT_ALWAYS_EVAL(x, msg) (x)
#define RT_VERIFY(p) (p)
#endif
enum {

View File

@ -0,0 +1,61 @@
#ifndef RT_THREADING_HELPERS_HPP
#define RT_THREADING_HELPERS_HPP
// C++ wrappers around threading functions/types
#ifndef __cplusplus
#error This file must only be used from C++ code
#endif
#include "threading.h"
template <typename _LockType, void (*_AcquireFunc)(_LockType *), void (*_ReleaseFunc)(_LockType *)>
class rt_generic_auto_lock {
public:
explicit rt_generic_auto_lock(_LockType *lock) : m_lock(lock) {
_AcquireFunc(lock);
}
~rt_generic_auto_lock() {
_ReleaseFunc(m_lock);
}
rt_generic_auto_lock(const rt_generic_auto_lock &) = delete;
const rt_generic_auto_lock &operator=(const rt_generic_auto_lock &) = delete;
private:
_LockType *m_lock;
};
namespace __rt_auto_lock_mutex_wrappers {
RT_INLINE void Lock(rt_mutex *mtx) {
RT_ASSERT_ALWAYS_EVAL(rtLockMutex(mtx), "Lock mutex failed!");
}
RT_INLINE void Unlock(rt_mutex *mtx) {
RT_ASSERT_ALWAYS_EVAL(rtUnlockMutex(mtx), "Unlock mutex failed!");
}
} // namespace __rt_auto_lock_mutex_wrappers
using rt_mutex_auto_lock = rt_generic_auto_lock<rt_mutex,
__rt_auto_lock_mutex_wrappers::Lock,
__rt_auto_lock_mutex_wrappers::Unlock>;
using rt_read_auto_lock = rt_generic_auto_lock<rt_rwlock, rtLockRead, rtUnlockRead>;
using rt_write_auto_lock = rt_generic_auto_lock<rt_rwlock, rtLockWrite, rtUnlockWrite>;
RT_INLINE rt_mutex_auto_lock rtAutoLock(rt_mutex *mutex) {
return rt_mutex_auto_lock(mutex);
}
RT_INLINE rt_read_auto_lock rtAutoLock(rt_rwlock *rw) {
return rt_read_auto_lock(rw);
}
RT_INLINE rt_write_auto_lock rtAutoWriteLock(rt_rwlock *rw) {
return rt_write_auto_lock(rw);
}
#endif