Start work on effect files

Extends pipeline files
This commit is contained in:
Kevin Trogant 2024-02-29 16:43:27 +01:00
parent 36e2314f35
commit 41fe5426b8
9 changed files with 177 additions and 269 deletions

View File

@ -1,5 +1,7 @@
optimization speed; optimization speed;
passes {
pass0 {
vertex { vertex {
vk BEGIN vk BEGIN
#include "common.hlsl" #include "common.hlsl"
@ -36,3 +38,5 @@ PSOutput PsMain(void) {
} }
END END
} }
}
}

5
assets/test.effect Normal file
View File

@ -0,0 +1,5 @@
passes {
pass0 {
pipeline test/shader/static_object.pipeline;
}
}

View File

@ -15,10 +15,6 @@
#include <string.h> #include <string.h>
typedef struct { typedef struct {
rt_attribute_binding *uniform_bindings;
rt_attribute_binding *storage_bindings;
rt_attribute_binding *texture_bindings;
rt_resource shaders[3]; rt_resource shaders[3];
char *shader_names[3]; char *shader_names[3];
unsigned int shader_count; unsigned int shader_count;
@ -36,96 +32,17 @@ typedef struct {
uint16_t texture_binding_count; uint16_t texture_binding_count;
} rt_parsed_pipeline_data; } rt_parsed_pipeline_data;
typedef struct {
unsigned int pass_count;
rt_parsed_pipeline_data pipelines[RT_MAX_SUBRESOURCES];
} rt_parsed_effect_data;
enum { enum {
RT_SHADER_NOT_PRESENT = RT_ASSET_PROCESSING_FAILED + 1 RT_SHADER_NOT_PRESENT = RT_ASSET_PROCESSING_FAILED + 1
}; };
extern RT_DLLIMPORT rt_cvar rt_Renderer; extern RT_DLLIMPORT rt_cvar rt_Renderer;
static bool ParseBindingIndex(rt_text_span span, unsigned int *index) {
if (span.length == 0)
return false;
int at = (int)span.length - 1;
unsigned int exp = 1;
unsigned int n = 0;
while (at >= 0) {
if (span.start[at] >= '0' && span.start[at] <= '9') {
unsigned int digit = (unsigned int)(span.start[at] - '0');
n += digit * exp;
} else {
rtReportError("GFX", "Unexpected non-digit character in binding index");
return false;
}
--at;
exp *= 10;
}
*index = n;
return true;
}
static rt_attribute_value ParseBindingValue(rt_text_span span) {
if (rtCompareSpanToString(span, "MATERIAL_ALBEDO") == 0) {
return RT_ATTRIBUTE_VALUE_MATERIAL_ALBEDO;
} else if (rtCompareSpanToString(span, "MATERIAL_NORMAL") == 0) {
return RT_ATTRIBUTE_VALUE_MATERIAL_NORMAL;
}
rtReportError("GFX", "Unsupported binding value %*.s", span.length, span.start);
return RT_ATTRIBUTE_VALUE_UNDEFINED;
}
static bool ParseBindings(rt_parse_state *state,
unsigned int root_list,
const char *name,
const char *file_path,
rt_attribute_binding **p_bindings,
uint16_t *p_binding_count) {
const rt_parsed_stmt *bindings = rtFindStatement(state, root_list, name);
if (bindings) {
if (bindings->form != RT_STMT_FORM_LIST) {
rtReportError("GFX",
"Expected list of bindings as the value of "
"\"%s\" in %s",
name,
file_path);
return false;
}
const rt_parsed_stmt_list *binding_list = &state->statement_lists[bindings->list_index];
rt_attribute_binding *shader_bindings =
rtAllocBuffer(sizeof(rt_attribute_binding) * binding_list->count);
if (!bindings) {
rtReportError("GFX", "Out of memory");
return false;
}
unsigned int binding_count = binding_list->count;
unsigned int stmt_index = binding_list->first;
for (unsigned int i = 0; i < binding_list->count; ++i) {
const rt_parsed_stmt *stmt = &state->statements[stmt_index];
if (!ParseBindingIndex(stmt->attribute, &shader_bindings[i].index)) {
rtReleaseBuffer(shader_bindings,
sizeof(rt_attribute_binding) * binding_list->count);
return false;
}
shader_bindings[i].value = ParseBindingValue(stmt->value);
if (shader_bindings[i].value == RT_ATTRIBUTE_VALUE_UNDEFINED) {
rtReleaseBuffer(shader_bindings,
sizeof(rt_attribute_binding) * binding_list->count);
return false;
}
stmt_index = stmt->next;
}
*p_bindings = shader_bindings;
*p_binding_count = (uint16_t)binding_count;
return true;
} else {
*p_bindings = NULL;
*p_binding_count = 0;
return true;
}
}
static char *GenerateShaderName(rt_shader_type type, static char *GenerateShaderName(rt_shader_type type,
rt_shader_stage stage, rt_shader_stage stage,
rt_shader_optimization_level optimization, rt_shader_optimization_level optimization,
@ -300,27 +217,13 @@ ParseOptimizationLevel(rt_parse_state *state, unsigned int root_list, const char
return optimization_level; return optimization_level;
} }
static rt_result ParsePipelineFile(rt_file_id fid, static rt_result ParsePipeline(rt_parse_state *state,
const char *text, unsigned int root_list,
size_t length, const char *file_path,
rt_shader_optimization_level optimization,
rt_parsed_pipeline_data *pipeline, rt_parsed_pipeline_data *pipeline,
rt_arena *arena) { rt_arena *arena) {
/* This is the grammar for pipeline files: rt_result result = RT_SUCCESS;
* <stmt-list> ::= <stmt>*
* <stmt> ::= <attribute> ( ( <value> ';' ) | ( '{' <stmt-list> '}' ) )
* <attribute> ::= [:alnum:]*
* <value>:: = [:alnum:]* */
const char *file_path = rtGetFilePath(fid);
rt_parse_state state;
unsigned int root_list;
rt_result result = rtParseDescription(text, length, file_path, &root_list, &state, arena);
if (result != RT_SUCCESS) {
goto out;
}
/* We allow the pipeline file to overwrite the optimization level */
rt_shader_optimization_level optimization =
ParseOptimizationLevel(&state, root_list, file_path);
rt_shader_type type = RT_SHADER_TYPE_INVALID; rt_shader_type type = RT_SHADER_TYPE_INVALID;
if (strcmp(rt_Renderer.s, "vk") == 0) if (strcmp(rt_Renderer.s, "vk") == 0)
@ -333,7 +236,7 @@ static rt_result ParsePipelineFile(rt_file_id fid,
} }
/* Process shader stages */ /* Process shader stages */
result = ParseShader(&state, result = ParseShader(state,
root_list, root_list,
"vertex", "vertex",
file_path, file_path,
@ -353,7 +256,7 @@ static rt_result ParsePipelineFile(rt_file_id fid,
} }
result = RT_SUCCESS; result = RT_SUCCESS;
result = ParseShader(&state, result = ParseShader(state,
root_list, root_list,
"fragment", "fragment",
file_path, file_path,
@ -373,7 +276,7 @@ static rt_result ParsePipelineFile(rt_file_id fid,
} }
result = RT_SUCCESS; result = RT_SUCCESS;
result = ParseShader(&state, result = ParseShader(state,
root_list, root_list,
"compute", "compute",
file_path, file_path,
@ -393,59 +296,84 @@ static rt_result ParsePipelineFile(rt_file_id fid,
} }
result = RT_SUCCESS; result = RT_SUCCESS;
/* Process bindings */
pipeline->texture_bindings = NULL;
pipeline->texture_binding_count = 0;
pipeline->uniform_bindings = NULL;
pipeline->uniform_binding_count = 0;
pipeline->storage_bindings = NULL;
pipeline->storage_binding_count = 0;
if (!ParseBindings(&state,
root_list,
"texture_bindings",
file_path,
&pipeline->texture_bindings,
&pipeline->texture_binding_count)) {
result = RT_ASSET_PROCESSING_FAILED;
goto out;
}
if (!ParseBindings(&state,
root_list,
"uniform_bindings",
file_path,
&pipeline->uniform_bindings,
&pipeline->uniform_binding_count)) {
result = RT_ASSET_PROCESSING_FAILED;
goto out;
}
if (!ParseBindings(&state,
root_list,
"storage_bindings",
file_path,
&pipeline->storage_bindings,
&pipeline->storage_binding_count)) {
result = RT_ASSET_PROCESSING_FAILED;
goto out;
}
out: out:
return result; return result;
} }
static rt_result ParseEffect(rt_file_id fid,
const char *text,
size_t length,
rt_parsed_effect_data *effect,
rt_arena *arena) {
/* This is the grammar for pipeline files:
* <stmt-list> ::= <stmt>*
* <stmt> ::= <attribute> ( ( <value> ';' ) | ( '{' <stmt-list> '}' ) )
* <attribute> ::= [:alnum:]*
* <value>:: = [:alnum:]* */
const char *file_path = rtGetFilePath(fid);
rt_parse_state state;
unsigned int root_list;
rt_result result = rtParseDescription(text, length, file_path, &root_list, &state, arena);
if (result != RT_SUCCESS) {
return result;
}
memset(effect, 0, sizeof(*effect));
rt_shader_optimization_level optimization =
ParseOptimizationLevel(&state, root_list, file_path);
const rt_parsed_stmt *passes_stmt = rtFindStatement(&state, root_list, "passes");
if (!passes_stmt) {
rtLog("AC", "Did not find passes list in %s.", file_path);
return RT_INVALID_VALUE;
}
const rt_parsed_stmt_list *passes_list = &state.statement_lists[passes_stmt->list_index];
if (passes_list->count > RT_MAX_SUBRESOURCES) {
rtLog("AC",
"Too many passes in 'passes'. Maximum supported number is %u. In %s.",
RT_MAX_SUBRESOURCES,
file_path);
return RT_INVALID_VALUE;
}
const rt_parsed_stmt *pass_stmt = &state.statements[passes_list->first];
for (unsigned int i = 0; i < passes_list->count;
++i, pass_stmt = &state.statements[pass_stmt->next]) {
if (pass_stmt->form != RT_STMT_FORM_LIST) {
rtLog("AC",
"Expected a list as the value of passes.%*s in %s",
pass_stmt->attribute.length,
pass_stmt->attribute.start,
file_path);
return RT_INVALID_VALUE;
}
result = ParsePipeline(&state,
pass_stmt->list_index,
file_path,
optimization,
&effect->pipelines[i],
arena);
if (result != RT_SUCCESS)
return result;
}
return result;
}
RT_ASSET_PROCESSOR_FN(PipelineProcessor) { RT_ASSET_PROCESSOR_FN(PipelineProcessor) {
rt_loaded_asset asset = LoadAsset(file); rt_loaded_asset asset = LoadAsset(file);
if (!asset.buffer) if (!asset.buffer)
return RT_UNKNOWN_ERROR; return RT_UNKNOWN_ERROR;
rt_parsed_pipeline_data pipeline; rt_parsed_effect_data effect;
memset(&pipeline, 0, sizeof(pipeline)); memset(&effect, 0, sizeof(effect));
rt_result result = ParsePipelineFile(file, asset.buffer, asset.size, &pipeline, arena); rt_result result = ParseEffect(file, asset.buffer, asset.size, &effect, arena);
if (result != RT_SUCCESS) if (result != RT_SUCCESS)
goto out; goto out;
for (unsigned int i = 0; i < effect.pass_count; ++i) {
rt_parsed_pipeline_data pipeline;
memcpy(&pipeline, &effect.pipelines[i], sizeof(pipeline));
rt_resource_id shader_resources[3] = {0}; rt_resource_id shader_resources[3] = {0};
result = rtCreateResources(pipeline.shader_count, result = rtCreateResources(pipeline.shader_count,
pipeline.shader_names, pipeline.shader_names,
@ -460,10 +388,7 @@ RT_ASSET_PROCESSOR_FN(PipelineProcessor) {
memcpy(pipeline_resource.dependencies, shader_resources, sizeof(shader_resources)); memcpy(pipeline_resource.dependencies, shader_resources, sizeof(shader_resources));
pipeline_resource.subresource_count = 0; pipeline_resource.subresource_count = 0;
size_t data_size = size_t data_size = sizeof(rt_pipeline_info);
sizeof(rt_pipeline_info) + sizeof(rt_attribute_binding) * (pipeline.texture_binding_count +
pipeline.uniform_binding_count +
pipeline.storage_binding_count);
pipeline_resource.data = rtArenaPush(arena, data_size); pipeline_resource.data = rtArenaPush(arena, data_size);
if (!pipeline_resource.data) { if (!pipeline_resource.data) {
result = RT_OUT_OF_MEMORY; result = RT_OUT_OF_MEMORY;
@ -480,21 +405,6 @@ RT_ASSET_PROCESSOR_FN(PipelineProcessor) {
info->compute_shader = (pipeline.compute_shader != UINT_MAX) info->compute_shader = (pipeline.compute_shader != UINT_MAX)
? shader_resources[pipeline.compute_shader] ? shader_resources[pipeline.compute_shader]
: RT_INVALID_RESOURCE_ID; : RT_INVALID_RESOURCE_ID;
rt_attribute_binding *uniform_bindings = (rt_attribute_binding *)(info + 1);
if (pipeline.uniform_binding_count > 0) {
memcpy(uniform_bindings, pipeline.uniform_bindings, pipeline.uniform_binding_count);
rtSetRelptr(&info->uniform_bindings, uniform_bindings);
}
rt_attribute_binding *texture_bindings = (uniform_bindings + pipeline.uniform_binding_count);
if (pipeline.texture_binding_count > 0) {
memcpy(texture_bindings, pipeline.texture_bindings, pipeline.texture_binding_count);
rtSetRelptr(&info->texture_bindings, texture_bindings);
}
rt_attribute_binding *storage_bindings = (texture_bindings + pipeline.texture_binding_count);
if (pipeline.texture_binding_count > 0) {
memcpy(storage_bindings, pipeline.storage_bindings, pipeline.storage_binding_count);
rtSetRelptr(&info->storage_bindings, storage_bindings);
}
rt_resource_id pipeline_id; rt_resource_id pipeline_id;
const char *name = rtGetFilePath(file); const char *name = rtGetFilePath(file);
result = rtCreateResources(1, &name, &pipeline_resource, &pipeline_id); result = rtCreateResources(1, &name, &pipeline_resource, &pipeline_id);
@ -503,6 +413,7 @@ RT_ASSET_PROCESSOR_FN(PipelineProcessor) {
memcpy(&new_resources[1], shader_resources, sizeof(shader_resources)); memcpy(&new_resources[1], shader_resources, sizeof(shader_resources));
*new_resource_count = 1 + pipeline.shader_count; *new_resource_count = 1 + pipeline.shader_count;
} }
}
out: out:
rtLog("AC", "Released %p", asset.buffer); rtLog("AC", "Released %p", asset.buffer);
rtReleaseBuffer(asset.buffer, asset.size); rtReleaseBuffer(asset.buffer, asset.size);

View File

@ -33,8 +33,8 @@ asset_compiler = static_library('asset_compiler',
'asset_compiler.c', 'asset_compiler.c',
'description_parser.c', 'description_parser.c',
'effect_processor.c',
'framegraph_processor.c', 'framegraph_processor.c',
'pipeline_processor.c',
'shader_compiler.c', 'shader_compiler.c',
sources : ac_sources, sources : ac_sources,
include_directories : engine_incdir, include_directories : engine_incdir,

25
src/gfx/effect.h Normal file
View File

@ -0,0 +1,25 @@
#ifndef RT_GFX_EFFECT_H
#define RT_GFX_EFFECT_H
/* A effect lists the passes during which an object needs to be rendered
* and a pipeline for each pass.
* The effect also defines the required vertex layout per pass.
*/
#include "gfx.h"
#include "runtime/resources.h"
typedef struct {
/* Id of the render pass during which this effect pass is run. */
rt_render_pass_id pass_id;
rt_resource_id vertex_shader;
rt_resource_id fragment_shader;
rt_resource_id compute_shader;
} rt_effect_pass_info;
typedef struct {
} rt_effect_info;
#endif

View File

@ -1,6 +1,7 @@
gfx_deps = [thread_dep, m_dep] gfx_deps = [thread_dep, m_dep]
gfx_lib = library('rtgfx', gfx_lib = library('rtgfx',
# Project Sources # Project Sources
'effect.h',
'gfx.h', 'gfx.h',
'renderer_api.h', 'renderer_api.h',
'render_list.h', 'render_list.h',

View File

@ -59,20 +59,14 @@ typedef enum {
RT_TRANSFER_QUEUE, RT_TRANSFER_QUEUE,
} rt_gpu_queue; } rt_gpu_queue;
typedef struct { typedef struct {
rt_resource_id vertex_shader; rt_resource_id vertex_shader;
rt_resource_id fragment_shader; rt_resource_id fragment_shader;
rt_resource_id compute_shader; rt_resource_id compute_shader;
rt_relptr texture_bindings;
rt_relptr uniform_bindings;
rt_relptr storage_bindings;
uint16_t texture_binding_count;
uint16_t uniform_binding_count;
uint16_t storage_binding_count;
} rt_pipeline_info; } rt_pipeline_info;
#if 0
/* Attributes are used to bind buffers (or textures) to symbolic values. /* Attributes are used to bind buffers (or textures) to symbolic values.
* For example, an attribute might be bound to "CELL_GRID", which would be * For example, an attribute might be bound to "CELL_GRID", which would be
* replaced with the (at the time of the invoke) grid buffer of the current * replaced with the (at the time of the invoke) grid buffer of the current
@ -93,6 +87,8 @@ typedef struct {
rt_attribute_value value; rt_attribute_value value;
} rt_attribute_binding; } rt_attribute_binding;
#endif
typedef enum { typedef enum {
RT_SHADER_TYPE_INVALID, RT_SHADER_TYPE_INVALID,
RT_SHADER_TYPE_VULKAN, RT_SHADER_TYPE_VULKAN,

View File

@ -957,47 +957,11 @@ RT_DLLEXPORT void rDebugLogResource(rt_resource_id id, const rt_resource *resour
} }
switch (resource->type) { switch (resource->type) {
case RT_RESOURCE_PIPELINE: { case RT_RESOURCE_PIPELINE: {
static const char *binding_str[RT_ATTRIBUTE_VALUE_count] = {"<UNDEFINED>",
"MaterialAlbedo",
"MaterialNormal"};
const rt_pipeline_info *pipeline = resource->data; const rt_pipeline_info *pipeline = resource->data;
rtLog("RESMGR", " pipeline data:"); rtLog("RESMGR", " pipeline data:");
rtLog("RESMGR", " vertex shader: %llx", pipeline->vertex_shader); rtLog("RESMGR", " vertex shader: %llx", pipeline->vertex_shader);
rtLog("RESMGR", " fragment shader: %llx", pipeline->fragment_shader); rtLog("RESMGR", " fragment shader: %llx", pipeline->fragment_shader);
rtLog("RESMGR", " compute shader: %llx", pipeline->compute_shader); rtLog("RESMGR", " compute shader: %llx", pipeline->compute_shader);
rtLog("RESMGR", " uniform bindings:");
const rt_attribute_binding *uniform_bindings =
rtResolveConstRelptr(&pipeline->uniform_bindings);
for (uint32_t i = 0; i < pipeline->uniform_binding_count; ++i) {
rtLog("RESMGR",
" - %u : %s",
uniform_bindings[i].index,
(uniform_bindings[i].value < RT_ATTRIBUTE_VALUE_count)
? binding_str[uniform_bindings[i].value]
: "<INVALID>");
}
rtLog("RESMGR", " texture bindings:");
const rt_attribute_binding *texture_bindings =
rtResolveConstRelptr(&pipeline->texture_bindings);
for (uint32_t i = 0; i < pipeline->texture_binding_count; ++i) {
rtLog("RESMGR",
" - %u : %s",
texture_bindings[i].index,
(texture_bindings[i].value < RT_ATTRIBUTE_VALUE_count)
? binding_str[texture_bindings[i].value]
: "<INVALID>");
}
rtLog("RESMGR", " storage bindings:");
const rt_attribute_binding *storage_bindings =
rtResolveConstRelptr(&pipeline->storage_bindings);
for (uint32_t i = 0; i < pipeline->storage_binding_count; ++i) {
rtLog("RESMGR",
" - %u : %s",
storage_bindings[i].index,
(storage_bindings[i].value < RT_ATTRIBUTE_VALUE_count)
? binding_str[storage_bindings[i].value]
: "<INVALID>");
}
} break; } break;
case RT_RESOURCE_SHADER: { case RT_RESOURCE_SHADER: {
static const char *stype_str[RT_SHADER_TYPE_count] = {"<INVALID>", "Vulkan"}; static const char *stype_str[RT_SHADER_TYPE_count] = {"<INVALID>", "Vulkan"};

View File

@ -37,6 +37,8 @@ typedef enum {
RT_RESOURCE_FRAMEGRAPH, RT_RESOURCE_FRAMEGRAPH,
RT_RESOURCE_EFFECT,
RT_RESOURCE_TYPE_count, RT_RESOURCE_TYPE_count,
} rt_resource_type; } rt_resource_type;