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,6 +1,8 @@
optimization speed;
vertex {
passes {
pass0 {
vertex {
vk BEGIN
#include "common.hlsl"
@ -18,12 +20,12 @@ VSOutput VsMain(VSInput input, uint vertexIndex : SV_VertexID) {
return output;
}
END
}
}
fragment {
fragment {
vk BEGIN
struct PSOutput {
float4 Color : SV_TARGET0;
float4 Color : SV_TARGET0;
};
PSOutput PsMain(void) {
@ -35,4 +37,6 @@ PSOutput PsMain(void) {
return output;
}
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>
typedef struct {
rt_attribute_binding *uniform_bindings;
rt_attribute_binding *storage_bindings;
rt_attribute_binding *texture_bindings;
rt_resource shaders[3];
char *shader_names[3];
unsigned int shader_count;
@ -36,96 +32,17 @@ typedef struct {
uint16_t texture_binding_count;
} rt_parsed_pipeline_data;
typedef struct {
unsigned int pass_count;
rt_parsed_pipeline_data pipelines[RT_MAX_SUBRESOURCES];
} rt_parsed_effect_data;
enum {
RT_SHADER_NOT_PRESENT = RT_ASSET_PROCESSING_FAILED + 1
};
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,
rt_shader_stage stage,
rt_shader_optimization_level optimization,
@ -300,27 +217,13 @@ ParseOptimizationLevel(rt_parse_state *state, unsigned int root_list, const char
return optimization_level;
}
static rt_result ParsePipelineFile(rt_file_id fid,
const char *text,
size_t length,
static rt_result ParsePipeline(rt_parse_state *state,
unsigned int root_list,
const char *file_path,
rt_shader_optimization_level optimization,
rt_parsed_pipeline_data *pipeline,
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) {
goto out;
}
/* We allow the pipeline file to overwrite the optimization level */
rt_shader_optimization_level optimization =
ParseOptimizationLevel(&state, root_list, file_path);
rt_result result = RT_SUCCESS;
rt_shader_type type = RT_SHADER_TYPE_INVALID;
if (strcmp(rt_Renderer.s, "vk") == 0)
@ -333,7 +236,7 @@ static rt_result ParsePipelineFile(rt_file_id fid,
}
/* Process shader stages */
result = ParseShader(&state,
result = ParseShader(state,
root_list,
"vertex",
file_path,
@ -353,7 +256,7 @@ static rt_result ParsePipelineFile(rt_file_id fid,
}
result = RT_SUCCESS;
result = ParseShader(&state,
result = ParseShader(state,
root_list,
"fragment",
file_path,
@ -373,7 +276,7 @@ static rt_result ParsePipelineFile(rt_file_id fid,
}
result = RT_SUCCESS;
result = ParseShader(&state,
result = ParseShader(state,
root_list,
"compute",
file_path,
@ -393,59 +296,84 @@ static rt_result ParsePipelineFile(rt_file_id fid,
}
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:
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_loaded_asset asset = LoadAsset(file);
if (!asset.buffer)
return RT_UNKNOWN_ERROR;
rt_parsed_pipeline_data pipeline;
memset(&pipeline, 0, sizeof(pipeline));
rt_result result = ParsePipelineFile(file, asset.buffer, asset.size, &pipeline, arena);
rt_parsed_effect_data effect;
memset(&effect, 0, sizeof(effect));
rt_result result = ParseEffect(file, asset.buffer, asset.size, &effect, arena);
if (result != RT_SUCCESS)
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};
result = rtCreateResources(pipeline.shader_count,
pipeline.shader_names,
@ -460,10 +388,7 @@ RT_ASSET_PROCESSOR_FN(PipelineProcessor) {
memcpy(pipeline_resource.dependencies, shader_resources, sizeof(shader_resources));
pipeline_resource.subresource_count = 0;
size_t data_size =
sizeof(rt_pipeline_info) + sizeof(rt_attribute_binding) * (pipeline.texture_binding_count +
pipeline.uniform_binding_count +
pipeline.storage_binding_count);
size_t data_size = sizeof(rt_pipeline_info);
pipeline_resource.data = rtArenaPush(arena, data_size);
if (!pipeline_resource.data) {
result = RT_OUT_OF_MEMORY;
@ -480,21 +405,6 @@ RT_ASSET_PROCESSOR_FN(PipelineProcessor) {
info->compute_shader = (pipeline.compute_shader != UINT_MAX)
? shader_resources[pipeline.compute_shader]
: 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;
const char *name = rtGetFilePath(file);
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));
*new_resource_count = 1 + pipeline.shader_count;
}
}
out:
rtLog("AC", "Released %p", asset.buffer);
rtReleaseBuffer(asset.buffer, asset.size);

View File

@ -33,8 +33,8 @@ asset_compiler = static_library('asset_compiler',
'asset_compiler.c',
'description_parser.c',
'effect_processor.c',
'framegraph_processor.c',
'pipeline_processor.c',
'shader_compiler.c',
sources : ac_sources,
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_lib = library('rtgfx',
# Project Sources
'effect.h',
'gfx.h',
'renderer_api.h',
'render_list.h',

View File

@ -59,20 +59,14 @@ typedef enum {
RT_TRANSFER_QUEUE,
} rt_gpu_queue;
typedef struct {
rt_resource_id vertex_shader;
rt_resource_id fragment_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;
#if 0
/* Attributes are used to bind buffers (or textures) to symbolic values.
* 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
@ -93,6 +87,8 @@ typedef struct {
rt_attribute_value value;
} rt_attribute_binding;
#endif
typedef enum {
RT_SHADER_TYPE_INVALID,
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) {
case RT_RESOURCE_PIPELINE: {
static const char *binding_str[RT_ATTRIBUTE_VALUE_count] = {"<UNDEFINED>",
"MaterialAlbedo",
"MaterialNormal"};
const rt_pipeline_info *pipeline = resource->data;
rtLog("RESMGR", " pipeline data:");
rtLog("RESMGR", " vertex shader: %llx", pipeline->vertex_shader);
rtLog("RESMGR", " fragment shader: %llx", pipeline->fragment_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;
case RT_RESOURCE_SHADER: {
static const char *stype_str[RT_SHADER_TYPE_count] = {"<INVALID>", "Vulkan"};

View File

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