validate dx12 root signature presence. create debug log arena before registering callback

This commit is contained in:
jacob 2025-06-08 18:47:25 -05:00
parent c9cd9d0b18
commit 54a8a45835
8 changed files with 252 additions and 88 deletions

View File

@ -1,4 +1,4 @@
#include "shaders/common.hlsl"
#include "gpu/common.hlsl"
struct instance {
float2x3 xf;
@ -17,17 +17,17 @@ struct constants {
* Root Signature
* ========================== */
#define SIG \
#define ROOTSIG \
"CBV(b0), " \
"DescriptorTable(SRV(t0), SRV(t1)), " \
"DescriptorTable(Sampler(s0))"
cbuffer c : register(b0)
cbuffer cbuff : register(b0)
{
struct constants g_constants;
};
StructuredBuffer<instance> g_instance_buffer : register(t0);
StructuredBuffer<struct instance> g_instances : register(t0);
Texture2D g_texture : register(t1);
@ -57,15 +57,15 @@ struct vs_output {
DECL(float4, tint_lin);
};
[RootSignature(SIG)]
vs_output vs_main(uint instance_id : SV_InstanceID, uint vertex_id : SV_VertexID)
[RootSignature(ROOTSIG)]
struct vs_output vs(uint instance_id : SV_InstanceID, uint vertex_id : SV_VertexID)
{
instance instance = g_instance_buffer[g_constants.instance_offset + instance_id];
struct instance instance = g_instances[g_constants.instance_offset + instance_id];
float2 vert = g_quad_verts[vertex_id];
float2 uv_factor = g_uv_factors[vertex_id];
float2 world_pos = mul(instance.xf, float3(vert, 1)).xy;
vs_output output;
struct vs_output output;
output.screen_pos = mul(g_constants.projection, float4(world_pos, 0, 1));
output.uv = instance.uv0 + uv_factor * (instance.uv1 - instance.uv0);
output.tint_lin = linear_from_srgb32(instance.tint_srgb);
@ -77,8 +77,8 @@ vs_output vs_main(uint instance_id : SV_InstanceID, uint vertex_id : SV_VertexID
* Pixel shader
* ========================== */
[RootSignature(SIG)]
float4 ps_main(vs_output input) : SV_TARGET
[RootSignature(ROOTSIG)]
float4 ps(struct vs_output input) : SV_TARGET
{
float4 color = g_texture.Sample(g_sampler, input.uv) * input.tint_lin;
return color;

View File

@ -80,7 +80,7 @@
#define DX12_TEST 0
#define DX12_TEST 1

View File

@ -43,20 +43,23 @@
# define DX12_SHADER_DEBUG 0
#endif
enum pipeline_desc_flags {
PIPELINE_DESC_FLAG_NONE = 0,
PIPELINE_DESC_FLAG_VS = (1 << 0),
PIPELINE_DESC_FLAG_PS = (1 << 1)
struct shader_desc {
char *file;
char *func;
};
struct pipeline_desc {
char *shader;
char *name;
struct shader_desc vs;
struct shader_desc ps;
u32 flags;
};
struct pipeline {
struct pipeline_desc desc;
ID3D12PipelineState *pso;
ID3D12RootSignature *rs;
};
struct pipeline_result {
@ -72,6 +75,7 @@ struct pipeline_error {
enum dx12_handle_kind {
DX12_HANDLE_KIND_NONE,
DX12_HANDLE_KIND_TEXTURE,
DX12_HANDLE_KIND_COMMAND_LIST,
NUM_DX12_HANDLE_KINDS
};
@ -216,6 +220,22 @@ INTERNAL struct dx12_handle_entry *handle_get_entry(struct gpu_handle handle, st
return res;
}
INTERNAL void *handle_get_data(struct gpu_handle handle, enum dx12_handle_kind kind)
{
void *data = NULL;
struct sys_lock lock = sys_mutex_lock_s(&G.handle_entries_mutex);
{
struct dx12_handle_entry *entry = handle_get_entry(handle, &lock);
data = entry->data;
#if RTC
/* Handle should match expected kind */
ASSERT(entry->kind == kind);
#endif
}
sys_mutex_unlock(&lock);
return data;
}
/* TODO: The GPU api should ensure that resources freed by the caller will not cause issues on the GPU (via fencing),
* however the caller is responsible for managing resource lifetimes on the CPU side (e.g. using sprites w/ sprite scopes
* to ensure freed textures aren't being used in pending command lists. */
@ -315,7 +335,6 @@ INTERNAL void dx12_init_base(struct sys_window *window)
if (SUCCEEDED(hr)) {
DXGI_ADAPTER_DESC1 desc;
IDXGIAdapter1_GetDesc1(adapter, &desc);
if (!(desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)) {
if (first_gpu_name.len == 0) {
first_gpu_name = string_from_wstr_no_limit(scratch.arena, desc.Description);
}
@ -325,7 +344,6 @@ INTERNAL void dx12_init_base(struct sys_window *window)
adapter = NULL;
break;
}
}
ID3D12Device_Release(device);
IDXGIAdapter1_Release(adapter);
adapter = NULL;
@ -556,9 +574,9 @@ INTERNAL void dx12_init_pipelines(void)
struct pipeline_desc pipeline_descs[] = {
/* Texture pipeline */
{
.shader = "shaders/texture.hlsl",
.flags = PIPELINE_DESC_FLAG_VS |
PIPELINE_DESC_FLAG_PS
.name = "texture",
.vs = { "gpu/texture.hlsl", "vs" },
.ps = { "gpu/texture.hlsl", "ps" }
}
};
@ -660,7 +678,8 @@ struct shader_compile_task_arg {
/* In */
enum shader_compile_task_kind kind;
struct pipeline *pipeline;
struct resource *src_res;
struct shader_desc shader_desc;
struct resource *shader_res;
/* Out */
b32 success;
@ -673,24 +692,22 @@ INTERNAL WORK_TASK_FUNC_DEF(shader_compile_task, comp_arg_raw)
{
__prof;
struct shader_compile_task_arg *comp_arg = (struct shader_compile_task_arg *)comp_arg_raw;
struct pipeline *pipeline = comp_arg->pipeline;
struct string shader_name = string_from_cstr_no_limit(pipeline->desc.shader);
enum shader_compile_task_kind kind = comp_arg->kind;
struct resource *src_res = comp_arg->src_res;
struct pipeline *pipeline = comp_arg->pipeline;
struct shader_desc shader_desc = comp_arg->shader_desc;
struct resource *shader_res = comp_arg->shader_res;
struct arena_temp scratch = scratch_begin_no_conflict();
{
b32 success = false;
ID3DBlob *blob = NULL;
ID3DBlob *error_blob = NULL;
struct dx12_include_handler include_handler = dx12_include_handler_alloc(pipeline);
if (resource_exists(src_res)) {
#if 0
#if RESOURCE_RELOADING
shader_reset_includes(shader_desc);
#endif
#endif
struct string file_name = string_from_cstr_no_limit(shader_desc.file);
struct string func_name = string_from_cstr_no_limit(shader_desc.func);
if (resource_exists(shader_res)) {
struct dx12_include_handler include_handler = dx12_include_handler_alloc(pipeline);
u32 d3d_compile_flags = 0;
#if DX12_SHADER_DEBUG
@ -701,56 +718,33 @@ INTERNAL WORK_TASK_FUNC_DEF(shader_compile_task, comp_arg_raw)
/* Compile shader */
{
struct string shader_src = resource_get_data(src_res);
logf_info("Compiling shader \"%F\"", FMT_STR(shader_name));
struct string shader_src = resource_get_data(shader_res);
logf_info("Compiling shader \"%F:%F\"", FMT_STR(file_name), FMT_STR(func_name));
/* Compile shader */
struct string friendly_name = string_cat(scratch.arena, LIT("res/"), shader_name);
struct string friendly_name = string_cat(scratch.arena, LIT("res/"), file_name);
char *friendly_name_cstr = cstr_from_string(scratch.arena, friendly_name);
char *entry_point = NULL;
char *target = NULL;
switch (kind) {
case SHADER_COMPILE_TASK_KIND_VS:
{
entry_point = "vs_main";
target = "vs_5_1";
} break;
case SHADER_COMPILE_TASK_KIND_PS:
{
entry_point = "ps_main";
target = "ps_5_1";
} break;
}
HRESULT hr = D3DCompile(shader_src.text, shader_src.len, friendly_name_cstr, NULL, (ID3DInclude *)&include_handler, entry_point, target, d3d_compile_flags, 0, &blob, &error_blob);
HRESULT hr = D3DCompile(shader_src.text, shader_src.len, friendly_name_cstr, NULL, (ID3DInclude *)&include_handler, shader_desc.func, target, d3d_compile_flags, 0, &blob, &error_blob);
success = SUCCEEDED(hr) && !error_blob;
}
#if 0
if (success) {
/* Get number of device layout elements from NULL terminated array */
u32 elem_count = 0;
for (; elem_count < ARRAY_COUNT(shader_desc->input_layout_desc); ++elem_count) {
const D3D11_INPUT_ELEMENT_DESC *d = &shader_desc->input_layout_desc[elem_count];
if (d->SemanticName == NULL) {
break;
}
}
/* Create device layout */
if (elem_count > 0) {
HRESULT hr = ID3D11Device_CreateInputLayout(G.dev, shader_desc->input_layout_desc, elem_count, ID3D10Blob_GetBufferPointer(vs_blob), ID3D10Blob_GetBufferSize(vs_blob), &shader->input_layout);
if (!SUCCEEDED(hr)) {
success = false;
error_str = LIT("Failed to create input layout");
}
}
}
#endif
dx12_include_handler_release(&include_handler);
}
#if 0
if (success) {
logf_success("Finished compiling shader \"%F\" in %F seconds", FMT_STR(shader_name), FMT_FLOAT(SECONDS_FROM_NS(sys_time_ns() - start_ns)));
logf_success("Finished compiling shader \"%F\" in %F seconds", FMT_STR(src_name), FMT_FLOAT(SECONDS_FROM_NS(sys_time_ns() - start_ns)));
}
#endif
@ -762,7 +756,6 @@ INTERNAL WORK_TASK_FUNC_DEF(shader_compile_task, comp_arg_raw)
shader->valid = true;
#endif
dx12_include_handler_release(&include_handler);
}
scratch_end(scratch);
}
@ -786,39 +779,100 @@ INTERNAL WORK_TASK_FUNC_DEF(pipeline_load_task, load_arg_raw)
struct arena_temp scratch = scratch_begin_no_conflict();
{
struct string shader_name = string_from_cstr_no_limit(desc.shader);
logf_info("Loading pipeline from shader '%F'", FMT_STR(shader_name));
struct resource src_res = resource_open(shader_name);
struct string pipeline_name = string_from_cstr_no_limit(desc.name);
logf_info("Loading pipeline \"%F\"", FMT_STR(pipeline_name));
HRESULT hr = 0;
struct string error_str = LIT("Unknown error");
struct string vs_filename = string_from_cstr_no_limit(desc.vs.file);
struct string ps_filename = string_from_cstr_no_limit(desc.ps.file);
b32 ps_res_is_shared = string_eq(vs_filename, ps_filename);
struct resource vs_res = resource_open(vs_filename);
struct resource ps_res = vs_res;
if (!ps_res_is_shared) {
ps_res = resource_open(ps_filename);
}
struct shader_compile_task_arg vs = ZI;
vs.kind = SHADER_COMPILE_TASK_KIND_VS;
vs.src_res = &src_res;
vs.pipeline = pipeline;
vs.shader_desc = desc.vs;
vs.shader_res = &vs_res;
struct shader_compile_task_arg ps = ZI;
ps.kind = SHADER_COMPILE_TASK_KIND_PS;
ps.src_res = &src_res;
ps.pipeline = pipeline;
ps.shader_desc = desc.ps;
ps.shader_res = &ps_res;
/* Compile shaders */
struct work_slate ws = work_slate_begin();
if (desc.flags & PIPELINE_DESC_FLAG_VS) {
work_slate_push_task(&ws, shader_compile_task, &vs);
}
if (desc.flags & PIPELINE_DESC_FLAG_PS) {
work_slate_push_task(&ws, shader_compile_task, &ps);
}
struct work_handle work = work_slate_end_and_help(&ws, WORK_PRIORITY_NORMAL);
work_wait(work);
/* FIXME: Validate root signature blob exists in bytecode */
b32 success = vs.success && ps.success;
/* Get root signature blob
* NOTE: This isn't necessary for creating the RS (since it can just
* take the shader blob), however we'd like to verify that the root
* signature exists and matches between shaders. */
ID3D10Blob *rs_blob = NULL;
if (success) {
__profscope(Validate root signatures);
char *vs_rs_data = NULL;
char *ps_rs_data = NULL;
u32 vs_rs_data_len = 0;
u32 ps_rs_data_len = 0;
ID3D10Blob *vs_rs_blob = NULL;
ID3D10Blob *ps_rs_blob = NULL;
D3DGetBlobPart(ID3D10Blob_GetBufferPointer(vs.blob), ID3D10Blob_GetBufferSize(vs.blob), D3D_BLOB_ROOT_SIGNATURE, 0, &vs_rs_blob);
D3DGetBlobPart(ID3D10Blob_GetBufferPointer(ps.blob), ID3D10Blob_GetBufferSize(ps.blob), D3D_BLOB_ROOT_SIGNATURE, 0, &ps_rs_blob);
if (vs_rs_blob) {
vs_rs_data = ID3D10Blob_GetBufferPointer(vs_rs_blob);
vs_rs_data_len = ID3D10Blob_GetBufferSize(vs_rs_blob);
}
if (ps_rs_blob) {
ps_rs_data = ID3D10Blob_GetBufferPointer(ps_rs_blob);
ps_rs_data_len = ID3D10Blob_GetBufferSize(ps_rs_blob);
}
u32 cmp_len = min_u32(vs_rs_data_len, ps_rs_data_len);
if (vs_rs_data_len == 0) {
success = false;
error_str = LIT("Vertex shader is missing root signature");
} else if (ps_rs_data_len == 0) {
success = false;
error_str = LIT("Pixel shader is missing root signature");
} else if (!MEMEQ(vs_rs_data, ps_rs_data, cmp_len)) {
success = false;
error_str = LIT("Root signature mismatch between vertex and pixel shader");
} else {
rs_blob = vs_rs_blob;
}
if (ps_rs_blob) {
ID3D10Blob_Release(ps_rs_blob);
}
}
/* Create root signature */
ID3D12RootSignature *rs = NULL;
if (success) {
__profscope(Create root signature);
hr = ID3D12Device_CreateRootSignature(G.device, 0, ID3D10Blob_GetBufferPointer(rs_blob), ID3D10Blob_GetBufferSize(rs_blob), &IID_ID3D12RootSignature, (void **)&rs);
if (FAILED(hr)) {
error_str = LIT("Failed to create root signature");
success = false;
}
}
/* Create PSO */
ID3D12PipelineState *pso = NULL;
if (success) {
/* Default rasterizer state */
__profscope(Create PSO);
D3D12_RASTERIZER_DESC raster_desc = {
.FillMode = D3D12_FILL_MODE_SOLID,
.CullMode = D3D12_CULL_MODE_BACK,
@ -855,7 +909,7 @@ INTERNAL WORK_TASK_FUNC_DEF(pipeline_load_task, load_arg_raw)
/* PSO */
D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc = { 0 };
pso_desc.pRootSignature = NULL; /* Use embedded root signature */
pso_desc.pRootSignature = rs;
if (vs.success) {
pso_desc.VS.pShaderBytecode = ID3D10Blob_GetBufferPointer(vs.blob);
pso_desc.VS.BytecodeLength = ID3D10Blob_GetBufferSize(vs.blob);
@ -873,11 +927,10 @@ INTERNAL WORK_TASK_FUNC_DEF(pipeline_load_task, load_arg_raw)
pso_desc.NumRenderTargets = 1;
pso_desc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
pso_desc.SampleDesc.Count = 1;
HRESULT hr = ID3D12Device_CreateGraphicsPipelineState(G.device, &pso_desc, &IID_ID3D12PipelineState, (void **)&pso);
hr = ID3D12Device_CreateGraphicsPipelineState(G.device, &pso_desc, &IID_ID3D12PipelineState, (void **)&pso);
if (FAILED(hr)) {
error_str = LIT("Failed to create pipeline state object");
success = false;
ASSERT(false);
}
}
@ -901,7 +954,15 @@ INTERNAL WORK_TASK_FUNC_DEF(pipeline_load_task, load_arg_raw)
}
pipeline->pso = pso;
pipeline->rs = rs;
resource_close(&vs_res);
if (!ps_res_is_shared) {
resource_close(&ps_res);
}
if (rs_blob) {
ID3D10Blob_Release(rs_blob);
}
if (vs.blob) {
ID3D10Blob_Release(vs.blob);
}
@ -914,7 +975,6 @@ INTERNAL WORK_TASK_FUNC_DEF(pipeline_load_task, load_arg_raw)
if (ps.error_blob) {
ID3D10Blob_Release(ps.error_blob);
}
resource_close(&src_res);
}
scratch_end(scratch);
}
@ -1022,11 +1082,114 @@ struct gpu_handle gpu_dispatch_state_alloc(void)
return res;
}
#if 0
void gpu_dispatch(struct gpu_handle gpu_dispatch_state, struct gpu_dispatch_params params, struct gpu_handle gpu_command_list)
{
(UNUSED)gpu_dispatch_state;
(UNUSED)params;
struct pipeline_scope *pipeline_scope = pipeline_scope_begin();
struct dx12_command_list *command_list = handle_get_data(gpu_command_list, DX12_HANDLE_KIND_COMMAND_LIST);
/* Texture pass */
{
struct pipeline *pipeline = dx12_get_pipeline(pipeline_scope, LIT("gpu/texture.hlsl"));
}
pipeline_scope_end(pipeline_scope);
#if 0
__prof;
struct sprite_scope *sprite_scope = sprite_scope_begin();
struct dx11_dispatch_state *state = (struct dx11_dispatch_state *)gpu_dispatch_state.v;
struct rect viewport = params.draw_target_viewport;
/* Set viewport */
D3D11_VIEWPORT d3d11_viewport = ZI;
d3d11_viewport.Width = viewport.width;
d3d11_viewport.Height = viewport.height;
d3d11_viewport.MinDepth = 0.0f;
d3d11_viewport.MaxDepth = 1.0f;
d3d11_viewport.TopLeftX = viewport.x;
d3d11_viewport.TopLeftY = viewport.y;
ID3D11DeviceContext_RSSetViewports(G.devcon, 1, &d3d11_viewport);
struct dx12_buffer *final_tex = (struct dx12_buffer *)params.draw_target.v;
struct v2i32 final_tex_size = final_tex->size;
/* Texture pass */
{
__profscope(Texture pass);
struct dx11_shader *shader = &G.shaders[DX11_SHADER_KIND_TEXTURE];
if (shader->valid) {
struct dx12_buffer *texture = NULL;
if (cmd->texture.texture.v) {
/* Load texture if handle is set */
texture = (struct dx12_buffer *)cmd->texture.texture.v;
} else if (cmd->texture.sprite.hash) {
/* Otherwise load sprite */
struct sprite_texture *sprite_texture = sprite_texture_from_tag_async(sprite_scope, cmd->texture.sprite);
if (sprite_texture->loaded) {
texture = (struct dx12_buffer *)sprite_texture->texture.v;
}
}
if (texture && texture->srv) {
struct dx11_buffer *instance_buffer = list->buffers.texture.instance_buffer;
u32 instance_offset = cmd->texture.instance_offset;
u32 instance_count = cmd->texture.instance_count;
/* Bind shader */
ID3D11DeviceContext_VSSetShader(G.devcon, shader->vs, 0, 0);
ID3D11DeviceContext_PSSetShader(G.devcon, shader->ps, 0, 0);
/* Fill & bind constant buffer */
{
struct dx11_texture_uniform *uniform = dx11_buffer_push(constant_buffer, sizeof(struct dx11_texture_uniform));
uniform->vp = vp_matrix;
uniform->instance_offset = instance_offset;
dx11_buffer_submit(constant_buffer);
}
ID3D11DeviceContext_VSSetConstantBuffers(G.devcon, 0, 1, &constant_buffer->gpu_buffer);
ID3D11DeviceContext_PSSetConstantBuffers(G.devcon, 0, 1, &constant_buffer->gpu_buffer);
/* Bind dummy vertex buffer */
u32 zero = 0;
ID3D11DeviceContext_IASetVertexBuffers(G.devcon, 0, 1, &G.dummy_vertex_buffer->gpu_buffer, &zero, &zero);
ID3D11DeviceContext_IASetIndexBuffer(G.devcon, G.quad_index_buffer->gpu_buffer, DXGI_FORMAT_R16_UINT, zero);
/* Bind SRVs */
ID3D11ShaderResourceView *srvs[] = { instance_buffer->srv, texture->srv };
ID3D11DeviceContext_VSSetShaderResources(G.devcon, 0, ARRAY_COUNT(srvs), srvs);
ID3D11DeviceContext_PSSetShaderResources(G.devcon, 0, ARRAY_COUNT(srvs), srvs);
/* Bind RTVs */
ID3D11RenderTargetView *rtvs[] = { final_tex->rtv };
ID3D11DeviceContext_OMSetRenderTargets(G.devcon, ARRAY_COUNT(rtvs), rtvs, NULL);
/* Draw */
ID3D11DeviceContext_IASetPrimitiveTopology(G.devcon, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ID3D11DeviceContext_DrawIndexedInstanced(G.devcon, 6, instance_count, 0, 0, 0);
/* Unbind */
dx11_unbind(DX11_UNBIND_VS | DX11_UNBIND_PS | DX11_UNBIND_CBUFF | DX11_UNBIND_VBUFF | DX11_UNBIND_IBUFF | DX11_UNBIND_SRV | DX11_UNBIND_RTV);
}
}
}
sprite_scope_end(sprite_scope);
#endif
}
#else
void gpu_dispatch(struct gpu_handle gpu_dispatch_state, struct gpu_dispatch_params params)
{
(UNUSED)gpu_dispatch_state;
(UNUSED)params;
(UNUSED)handle_get_data;
}
#endif
/* ========================== *
* Backbuffer
@ -1065,6 +1228,7 @@ void gpu_swap_backbuffer(i32 vsync)
#endif

View File

@ -264,10 +264,10 @@ struct user_startup_receipt user_startup(struct work_startup_receipt *work_sr,
G.user_dispatch_state = gpu_dispatch_state_alloc();
G.backbuffer_dispatch_state = gpu_dispatch_state_alloc();
//log_register_callback(debug_console_log_callback, LOG_LEVEL_SUCCESS);
log_register_callback(debug_console_log_callback, LOG_LEVEL_DEBUG);
G.console_logs_mutex = sys_mutex_alloc();
G.console_logs_arena = arena_alloc(GIGABYTE(64));
//log_register_callback(debug_console_log_callback, LOG_LEVEL_SUCCESS);
log_register_callback(debug_console_log_callback, LOG_LEVEL_DEBUG);
G.window = window;
sys_window_register_event_callback(G.window, &window_event_callback);