Multiple asset processors
- Added a (stub) for shader files
This commit is contained in:
parent
9f3f40249f
commit
ae50c45a18
@ -112,6 +112,7 @@ if vk_dep.found()
|
||||
static_renderer_lib = vk_renderer_lib
|
||||
endif
|
||||
|
||||
|
||||
# Asset Compiler Tool
|
||||
executable('assetc',
|
||||
'src/tools/assetc/processing.h',
|
||||
@ -119,9 +120,12 @@ executable('assetc',
|
||||
|
||||
'src/tools/assetc/assetc.c',
|
||||
'src/tools/assetc/processor.c',
|
||||
'src/tools/assetc/pipeline_processor.c',
|
||||
'src/tools/assetc/shader_processor.c',
|
||||
'src/tools/assetc/utils.c',
|
||||
'src/tools/assetc/uidmap.c',
|
||||
include_directories : incdir,
|
||||
dependencies : [shaderc_dep],
|
||||
link_with : [runtime_lib],
|
||||
win_subsystem : 'console')
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
vertex shader/cell_vert.glsl;
|
||||
fragment shader/cell_frag.glsl;
|
||||
vertex shader/cell_vert.shader;
|
||||
fragment shader/cell_frag.shader;
|
||||
|
||||
texture_bindings {
|
||||
0 MATERIAL_ALBEDO;
|
4
shader/cell_vert.shader
Normal file
4
shader/cell_vert.shader
Normal file
@ -0,0 +1,4 @@
|
||||
#define VY_SHADER_TYPE_VERT
|
||||
#ifdef VY_SHADER_VK
|
||||
|
||||
#endif
|
@ -6,4 +6,6 @@
|
||||
/* Unique identifier for an asset. */
|
||||
typedef uint32_t vy_uid;
|
||||
|
||||
#define VY_INVALID_UID 0
|
||||
|
||||
#endif
|
@ -79,6 +79,7 @@ VY_DLLEXPORT vy_file_id vyAddFileFromSpan(vy_text_span path) {
|
||||
break;
|
||||
}
|
||||
memcpy(_file_tab.names + _file_tab.name_head, path.start, slen);
|
||||
_file_tab.names[_file_tab.name_head + slen - 1] = '\0';
|
||||
_file_tab.name_offsets[at] = _file_tab.name_head;
|
||||
_file_tab.ids[at] = fid;
|
||||
_file_tab.name_head += slen;
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
/* used to identify a file (XXH3 hash of the path) */
|
||||
typedef uint64_t vy_file_id;
|
||||
#define VY_INVALID_FILE_ID 0
|
||||
|
||||
VY_DLLEXPORT vy_result vyInitFileTab(unsigned int max_files);
|
||||
|
||||
|
@ -47,16 +47,4 @@ typedef struct {
|
||||
vy_attribute_value value;
|
||||
} vy_attribute_binding;
|
||||
|
||||
typedef struct {
|
||||
vy_attribute_binding *uniform_bindings;
|
||||
vy_attribute_binding *storage_bindings;
|
||||
vy_attribute_binding *texture_bindings;
|
||||
|
||||
vy_gfx_pipeline_handle pipeline;
|
||||
|
||||
unsigned int uniform_binding_count;
|
||||
unsigned int storage_binding_count;
|
||||
unsigned int texture_binding_count;
|
||||
} vy_shader;
|
||||
|
||||
#endif
|
||||
|
@ -5,6 +5,11 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
extern vy_result vyProcessPipelineFile(vy_file_id file,
|
||||
void *buffer,
|
||||
size_t size,
|
||||
vy_processor_output *output);
|
||||
|
||||
extern vy_result vyProcessShaderFile(vy_file_id file,
|
||||
void *buffer,
|
||||
size_t size,
|
||||
@ -22,6 +27,10 @@ int main(int argc, char **argv) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
vyInitUIDMap();
|
||||
|
||||
if (vyAddAssetProcessor(".pipeline", vyProcessPipelineFile) != VY_SUCCESS)
|
||||
return 1;
|
||||
if (vyAddAssetProcessor(".shader", vyProcessShaderFile) != VY_SUCCESS)
|
||||
return 1;
|
||||
|
||||
@ -29,7 +38,7 @@ int main(int argc, char **argv) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
vyAddFileToProcessingQueue(vyAddFile("shader\\cell.shader"));
|
||||
vyAddFileToProcessingQueue(vyAddFile("shader\\cell.pipeline"));
|
||||
while (1)
|
||||
_sleep(10);
|
||||
|
||||
|
538
src/tools/assetc/pipeline_processor.c
Normal file
538
src/tools/assetc/pipeline_processor.c
Normal file
@ -0,0 +1,538 @@
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "runtime/aio.h"
|
||||
#include "runtime/gfx.h"
|
||||
#include "runtime/handles.h"
|
||||
#include "runtime/runtime.h"
|
||||
|
||||
#include "processing.h"
|
||||
|
||||
typedef enum {
|
||||
VY_STMT_FORM_VALUE,
|
||||
VY_STMT_FORM_LIST,
|
||||
} vy_stmt_form;
|
||||
|
||||
typedef struct {
|
||||
unsigned int first;
|
||||
unsigned int count;
|
||||
} vy_parsed_stmt_list;
|
||||
|
||||
typedef struct {
|
||||
vy_stmt_form form;
|
||||
vy_text_span attribute;
|
||||
union {
|
||||
vy_text_span value;
|
||||
unsigned int list_index;
|
||||
};
|
||||
/* For lists */
|
||||
unsigned int next;
|
||||
} vy_parsed_stmt;
|
||||
|
||||
typedef struct {
|
||||
const char *file;
|
||||
const char *text;
|
||||
size_t at;
|
||||
size_t length;
|
||||
int line;
|
||||
|
||||
vy_parsed_stmt *statements;
|
||||
unsigned int statement_count;
|
||||
unsigned int statement_capacity;
|
||||
|
||||
vy_parsed_stmt_list *statement_lists;
|
||||
unsigned int statement_list_count;
|
||||
unsigned int statement_list_capacity;
|
||||
} vy_parse_state;
|
||||
|
||||
static bool IsAllowedChar(char c) {
|
||||
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') ||
|
||||
(c >= 'A' && c <= 'Z') || (c == '.') || (c == '_') || (c == '/');
|
||||
}
|
||||
|
||||
static bool IsWhitespace(char c) {
|
||||
return c == ' ' || c == '\t' || c == '\n' || c == '\r';
|
||||
}
|
||||
|
||||
static void SkipWhitespace(vy_parse_state *state) {
|
||||
while (state->at < state->length && IsWhitespace(state->text[state->at])) {
|
||||
if (state->text[state->at] == '\n')
|
||||
++state->line;
|
||||
++state->at;
|
||||
}
|
||||
}
|
||||
|
||||
static bool ParseAttribute(vy_parse_state *state, vy_text_span *_name) {
|
||||
vy_text_span name;
|
||||
name.start = &state->text[state->at];
|
||||
name.length = 0;
|
||||
while (state->at < state->length && !IsWhitespace(state->text[state->at])) {
|
||||
if (IsAllowedChar(state->text[state->at])) {
|
||||
++state->at;
|
||||
++name.length;
|
||||
} else {
|
||||
vyReportError("GFX",
|
||||
"%s:%d Unexpected character %c",
|
||||
state->file,
|
||||
state->line,
|
||||
state->text[state->at]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
*_name = name;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ParseValue(vy_parse_state *state, vy_text_span *_value) {
|
||||
vy_text_span value;
|
||||
value.start = &state->text[state->at];
|
||||
value.length = 0;
|
||||
while (state->at < state->length && !IsWhitespace(state->text[state->at]) &&
|
||||
state->text[state->at] != ';') {
|
||||
if (IsAllowedChar(state->text[state->at])) {
|
||||
++state->at;
|
||||
++value.length;
|
||||
} else {
|
||||
vyReportError("GFX",
|
||||
"%s:%d Unexpected character %c",
|
||||
state->file,
|
||||
state->line,
|
||||
state->text[state->at]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
*_value = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ParseStmtList(vy_parse_state *state, unsigned int *list_index);
|
||||
|
||||
static bool ParseStmt(vy_parse_state *state, unsigned int *stmt_index) {
|
||||
vy_parsed_stmt stmt;
|
||||
stmt.next = UINT_MAX; /* end of list */
|
||||
|
||||
SkipWhitespace(state);
|
||||
if (!ParseAttribute(state, &stmt.attribute))
|
||||
return false;
|
||||
SkipWhitespace(state);
|
||||
|
||||
if (state->at == state->length) {
|
||||
vyReportError("GFX",
|
||||
"%s:%d Expected either a value or '{'",
|
||||
state->file,
|
||||
state->line);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (state->text[state->at] == '{') {
|
||||
/* Consume '{' */
|
||||
++state->at;
|
||||
|
||||
stmt.form = VY_STMT_FORM_LIST;
|
||||
if (!ParseStmtList(state, &stmt.list_index))
|
||||
return false;
|
||||
|
||||
/* Consume '}' */
|
||||
if (state->at < state->length && state->text[state->at] == '}')
|
||||
++state->at;
|
||||
} else {
|
||||
stmt.form = VY_STMT_FORM_VALUE;
|
||||
if (!ParseValue(state, &stmt.value))
|
||||
return false;
|
||||
|
||||
/* Consume ';' */
|
||||
if (state->at < state->length && state->text[state->at] == ';')
|
||||
++state->at;
|
||||
}
|
||||
|
||||
SkipWhitespace(state);
|
||||
|
||||
/* Add statement to array */
|
||||
if (state->statement_count == state->statement_capacity) {
|
||||
unsigned int cap = (state->statement_capacity > 0)
|
||||
? state->statement_capacity * 2
|
||||
: 64;
|
||||
vy_parsed_stmt *temp =
|
||||
realloc(state->statements, sizeof(vy_parsed_stmt) * cap);
|
||||
if (!temp) {
|
||||
vyReportError("GFX",
|
||||
"While parsing %s: Out of memory\n",
|
||||
state->file);
|
||||
return false;
|
||||
}
|
||||
state->statements = temp;
|
||||
state->statement_capacity = cap;
|
||||
}
|
||||
state->statements[state->statement_count] = stmt;
|
||||
*stmt_index = state->statement_count++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ParseStmtList(vy_parse_state *state, unsigned int *list_index) {
|
||||
vy_parsed_stmt_list list;
|
||||
list.first = state->statement_count;
|
||||
list.count = 0;
|
||||
|
||||
unsigned int last = UINT_MAX;
|
||||
|
||||
while (state->at < state->length && state->text[state->at] != '}') {
|
||||
unsigned int stmt;
|
||||
if (!ParseStmt(state, &stmt))
|
||||
return false;
|
||||
|
||||
if (last != UINT_MAX)
|
||||
state->statements[last].next = stmt;
|
||||
last = stmt;
|
||||
|
||||
++list.count;
|
||||
}
|
||||
|
||||
/* Add list to array */
|
||||
if (state->statement_list_count == state->statement_list_capacity) {
|
||||
unsigned int cap = (state->statement_list_capacity > 0)
|
||||
? state->statement_list_capacity * 2
|
||||
: 64;
|
||||
vy_parsed_stmt_list *temp =
|
||||
realloc(state->statement_lists, sizeof(vy_parsed_stmt_list) * cap);
|
||||
if (!temp) {
|
||||
vyReportError("GFX",
|
||||
"While parsing %s: Out of memory\n",
|
||||
state->file);
|
||||
return false;
|
||||
}
|
||||
state->statement_lists = temp;
|
||||
state->statement_list_capacity = cap;
|
||||
}
|
||||
state->statement_lists[state->statement_list_count] = list;
|
||||
*list_index = state->statement_list_count++;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void DbgPrintShaderFile(const vy_parse_state *state,
|
||||
unsigned int list_index,
|
||||
unsigned int indent) {
|
||||
assert(list_index < state->statement_list_count);
|
||||
const vy_parsed_stmt_list *list = &state->statement_lists[list_index];
|
||||
|
||||
unsigned int stmt_index = list->first;
|
||||
for (unsigned int i = 0; i < list->count; ++i) {
|
||||
const vy_parsed_stmt *stmt = &state->statements[stmt_index];
|
||||
for (unsigned int j = 0; j < indent; ++j)
|
||||
printf(" ");
|
||||
printf("%.*s: ", stmt->attribute.length, stmt->attribute.start);
|
||||
if (stmt->form == VY_STMT_FORM_VALUE) {
|
||||
printf("%.*s\n", stmt->value.length, stmt->value.start);
|
||||
} else {
|
||||
printf("{\n");
|
||||
DbgPrintShaderFile(state, stmt->list_index, indent + 2);
|
||||
printf("}\n");
|
||||
}
|
||||
stmt_index = stmt->next;
|
||||
}
|
||||
assert(stmt_index == UINT_MAX || stmt_index == 0);
|
||||
}
|
||||
|
||||
static bool CompareSpanToString(vy_text_span span, const char *cmp) {
|
||||
size_t cmp_len = strlen(cmp);
|
||||
if (cmp_len != (size_t)span.length)
|
||||
return false;
|
||||
for (size_t i = 0; i < cmp_len; ++i) {
|
||||
if (span.start[i] != cmp[i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static const vy_parsed_stmt *FindStatement(const vy_parse_state *state,
|
||||
unsigned int list_index,
|
||||
const char *attribute) {
|
||||
if (list_index >= state->statement_list_count)
|
||||
return NULL;
|
||||
const vy_parsed_stmt_list *list = &state->statement_lists[list_index];
|
||||
unsigned int stmt_index = list->first;
|
||||
for (unsigned int i = 0; i < list->count; ++i) {
|
||||
const vy_parsed_stmt *stmt = &state->statements[stmt_index];
|
||||
if (CompareSpanToString(stmt->attribute, attribute))
|
||||
return stmt;
|
||||
stmt_index = stmt->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool ParseBindingIndex(vy_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 {
|
||||
vyReportError("GFX",
|
||||
"Unexpected non-digit character in binding index");
|
||||
return false;
|
||||
}
|
||||
--at;
|
||||
exp *= 10;
|
||||
}
|
||||
*index = n;
|
||||
return true;
|
||||
}
|
||||
|
||||
static vy_attribute_value ParseBindingValue(vy_text_span span) {
|
||||
if (CompareSpanToString(span, "MATERIAL_ALBEDO")) {
|
||||
return VY_ATTRIBUTE_VALUE_MATERIAL_ALBEDO;
|
||||
} else if (CompareSpanToString(span, "MATERIAL_NORMAL")) {
|
||||
return VY_ATTRIBUTE_VALUE_MATERIAL_NORMAL;
|
||||
}
|
||||
vyReportError("GFX",
|
||||
"Unsupported binding value %*.s",
|
||||
span.length,
|
||||
span.start);
|
||||
return VY_ATTRIBUTE_VALUE_UNDEFINED;
|
||||
}
|
||||
|
||||
static bool ParseBindings(vy_parse_state *state,
|
||||
unsigned int root_list,
|
||||
const char *name,
|
||||
const char *file_path,
|
||||
vy_attribute_binding **p_bindings,
|
||||
unsigned int *p_binding_count) {
|
||||
const vy_parsed_stmt *bindings = FindStatement(state, root_list, name);
|
||||
if (bindings) {
|
||||
if (bindings->form != VY_STMT_FORM_LIST) {
|
||||
vyReportError("GFX",
|
||||
"Expected list of bindings as the value of "
|
||||
"\"%s\" in %s",
|
||||
name,
|
||||
file_path);
|
||||
return false;
|
||||
}
|
||||
const vy_parsed_stmt_list *binding_list =
|
||||
&state->statement_lists[bindings->list_index];
|
||||
vy_attribute_binding *shader_bindings =
|
||||
malloc(sizeof(vy_attribute_binding) * binding_list->count);
|
||||
if (!bindings) {
|
||||
vyReportError("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 vy_parsed_stmt *stmt = &state->statements[stmt_index];
|
||||
if (!ParseBindingIndex(stmt->attribute,
|
||||
&shader_bindings[i].index)) {
|
||||
free(shader_bindings);
|
||||
return false;
|
||||
}
|
||||
|
||||
shader_bindings[i].value = ParseBindingValue(stmt->value);
|
||||
if (shader_bindings[i].value == VY_ATTRIBUTE_VALUE_UNDEFINED) {
|
||||
free(shader_bindings);
|
||||
return false;
|
||||
}
|
||||
stmt_index = stmt->next;
|
||||
}
|
||||
|
||||
*p_bindings = shader_bindings;
|
||||
*p_binding_count = binding_count;
|
||||
return true;
|
||||
} else {
|
||||
*p_bindings = NULL;
|
||||
*p_binding_count = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
VY_SHADER_STAGE_BIT_begin,
|
||||
|
||||
VY_SHADER_STAGE_BIT_VERTEX = 1,
|
||||
VY_SHADER_STAGE_BIT_FRAGMENT = 2,
|
||||
VY_SHADER_STAGE_BIT_COMPUTE = 3,
|
||||
|
||||
VY_SHADER_STAGE_BIT_count,
|
||||
};
|
||||
#define VY_SSBIT(n) (1 << (n))
|
||||
|
||||
typedef struct {
|
||||
vy_attribute_binding *uniform_bindings;
|
||||
vy_attribute_binding *storage_bindings;
|
||||
vy_attribute_binding *texture_bindings;
|
||||
|
||||
/* Toggels shader stages on or off */
|
||||
uint32_t shader_stage_bitmask;
|
||||
vy_uid vertex_shader;
|
||||
vy_uid fragment_shader;
|
||||
vy_uid compute_shader;
|
||||
|
||||
/* TODO Fixed function settings */
|
||||
|
||||
/* Sampler settings */
|
||||
|
||||
unsigned int uniform_binding_count;
|
||||
unsigned int storage_binding_count;
|
||||
unsigned int texture_binding_count;
|
||||
} vy_pipeline_data;
|
||||
|
||||
static vy_result ParseShader(vy_parse_state *state,
|
||||
unsigned int root_list,
|
||||
const char *name,
|
||||
const char *file_path,
|
||||
vy_uid *p_shader_uid) {
|
||||
const vy_parsed_stmt *stmt = FindStatement(state, root_list, name);
|
||||
if (stmt) {
|
||||
if (stmt->form != VY_STMT_FORM_VALUE) {
|
||||
vyReportError("GFX",
|
||||
"Expected a file name as the value of "
|
||||
"\"%s\" in %s",
|
||||
name,
|
||||
file_path);
|
||||
return VY_PROCESSING_FAILED;
|
||||
}
|
||||
vy_file_id shader_file = vyAddFileFromSpan(stmt->value);
|
||||
vy_uid uid = vyLookupUID(shader_file);
|
||||
if (uid == VY_INVALID_UID) {
|
||||
/* Add the shader file to processing and wait until its done */
|
||||
if (vyAddFileToProcessingQueue(shader_file) != VY_SUCCESS)
|
||||
return VY_PROCESSING_FAILED;
|
||||
return VY_PROCESSING_TRY_AGAIN;
|
||||
}
|
||||
|
||||
*p_shader_uid = uid;
|
||||
return VY_SUCCESS;
|
||||
}
|
||||
return VY_PROCESSING_FAILED;
|
||||
}
|
||||
|
||||
static vy_result ParsePipelineFile(vy_file_id fid,
|
||||
const char *text,
|
||||
size_t length,
|
||||
vy_pipeline_data *pipeline) {
|
||||
/* This is the grammar for pipeline files:
|
||||
* <stmt-list> ::= <stmt>*
|
||||
* <stmt> ::= <attribute> ( ( <value> ';' ) | ( '{' <stmt-list> '}' ) )
|
||||
* <attribute> ::= [:alnum:]*
|
||||
* <value>:: = [:alnum:]* */
|
||||
const char *file_path = vyGetFilePath(fid);
|
||||
vy_parse_state state = {.text = text,
|
||||
.at = 0,
|
||||
.length = length,
|
||||
.line = 1,
|
||||
.file = file_path,
|
||||
.statements = NULL,
|
||||
.statement_lists = NULL,
|
||||
.statement_capacity = 0,
|
||||
.statement_list_capacity = 0};
|
||||
|
||||
vy_result result = VY_SUCCESS;
|
||||
unsigned int root_list = 0;
|
||||
if (!ParseStmtList(&state, &root_list)) {
|
||||
result = false;
|
||||
goto out;
|
||||
}
|
||||
|
||||
DbgPrintShaderFile(&state, root_list, 0);
|
||||
|
||||
/* Process shader stages */
|
||||
pipeline->shader_stage_bitmask = 0;
|
||||
vy_result shader_result;
|
||||
if ((shader_result = ParseShader(&state,
|
||||
root_list,
|
||||
"vertex",
|
||||
file_path,
|
||||
&pipeline->vertex_shader)) == VY_SUCCESS) {
|
||||
|
||||
pipeline->shader_stage_bitmask |= VY_SHADER_STAGE_BIT_VERTEX;
|
||||
} else if (shader_result == VY_PROCESSING_TRY_AGAIN) {
|
||||
result = VY_PROCESSING_TRY_AGAIN;
|
||||
goto out;
|
||||
}
|
||||
if ((shader_result = ParseShader(&state,
|
||||
root_list,
|
||||
"fragment",
|
||||
file_path,
|
||||
&pipeline->vertex_shader)) == VY_SUCCESS) {
|
||||
|
||||
pipeline->shader_stage_bitmask |= VY_SHADER_STAGE_BIT_FRAGMENT;
|
||||
} else if (shader_result == VY_PROCESSING_TRY_AGAIN) {
|
||||
result = VY_PROCESSING_TRY_AGAIN;
|
||||
goto out;
|
||||
}
|
||||
if ((shader_result = ParseShader(&state,
|
||||
root_list,
|
||||
"compute",
|
||||
file_path,
|
||||
&pipeline->vertex_shader)) == VY_SUCCESS) {
|
||||
|
||||
pipeline->shader_stage_bitmask |= VY_SHADER_STAGE_BIT_COMPUTE;
|
||||
} else if (shader_result == VY_PROCESSING_TRY_AGAIN) {
|
||||
result = VY_PROCESSING_TRY_AGAIN;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* 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 = VY_AIO_STATE_FAILED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!ParseBindings(&state,
|
||||
root_list,
|
||||
"uniform_bindings",
|
||||
file_path,
|
||||
&pipeline->uniform_bindings,
|
||||
&pipeline->uniform_binding_count)) {
|
||||
result = VY_AIO_STATE_FAILED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!ParseBindings(&state,
|
||||
root_list,
|
||||
"storage_bindings",
|
||||
file_path,
|
||||
&pipeline->storage_bindings,
|
||||
&pipeline->storage_binding_count)) {
|
||||
result = VY_AIO_STATE_FAILED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
free(state.statements);
|
||||
free(state.statement_lists);
|
||||
return result;
|
||||
}
|
||||
|
||||
vy_result vyProcessPipelineFile(vy_file_id file,
|
||||
void *buffer,
|
||||
size_t size,
|
||||
vy_processor_output *output) {
|
||||
vy_pipeline_data tmp;
|
||||
vy_result result = ParsePipelineFile(file, buffer, size, &tmp);
|
||||
if (result == VY_SUCCESS) {
|
||||
|
||||
}
|
||||
return result;
|
||||
}
|
@ -21,7 +21,11 @@ vy_result vyAddAssetProcessor(const char *file_extension, vy_processor_fn fn);
|
||||
vy_result vyAddFileToProcessingQueue(vy_file_id file);
|
||||
|
||||
vy_result vyStartProcessing(void);
|
||||
|
||||
void vyStopProcessing(void);
|
||||
|
||||
|
||||
void vyInitUIDMap(void);
|
||||
void vyAddUIDMapping(vy_file_id fid, vy_uid uid);
|
||||
vy_uid vyLookupUID(vy_file_id fid);
|
||||
|
||||
#endif
|
||||
|
@ -114,7 +114,7 @@ static void ProcessLoadedFile(vy_file_processing_queue_entry entry, void *buffer
|
||||
entry.turn);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
}
|
||||
vyLog("ASSETC", "No asset processor for file: %s", path);
|
||||
@ -201,6 +201,7 @@ static void ProcessingThread(void *_param) {
|
||||
vyLog("ASSETC",
|
||||
"Loading file %s failed.",
|
||||
vyGetFilePath(submitted_entries[i].fid));
|
||||
vyReleaseBuffer(submitted_buffers[i], submitted_sizes[i]);
|
||||
PopAndSwapSubmittedData(i,
|
||||
&submitted_outstanding,
|
||||
submitted_entries,
|
||||
@ -213,6 +214,7 @@ static void ProcessingThread(void *_param) {
|
||||
vyLog("ASSETC",
|
||||
"Got invalid AIO handle for file: %s",
|
||||
vyGetFilePath(submitted_entries[i].fid));
|
||||
vyReleaseBuffer(submitted_buffers[i], submitted_sizes[i]);
|
||||
PopAndSwapSubmittedData(i,
|
||||
&submitted_outstanding,
|
||||
submitted_entries,
|
||||
@ -225,6 +227,7 @@ static void ProcessingThread(void *_param) {
|
||||
ProcessLoadedFile(submitted_entries[i],
|
||||
submitted_buffers[i],
|
||||
submitted_sizes[i]);
|
||||
vyReleaseBuffer(submitted_buffers[i], submitted_sizes[i]);
|
||||
PopAndSwapSubmittedData(i,
|
||||
&submitted_outstanding,
|
||||
submitted_entries,
|
||||
|
@ -1,445 +1,16 @@
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "runtime/aio.h"
|
||||
#include "runtime/gfx.h"
|
||||
#include "runtime/handles.h"
|
||||
#include "runtime/runtime.h"
|
||||
|
||||
#include "processing.h"
|
||||
|
||||
typedef enum {
|
||||
VY_STMT_FORM_VALUE,
|
||||
VY_STMT_FORM_LIST,
|
||||
} vy_stmt_form;
|
||||
|
||||
typedef struct {
|
||||
unsigned int first;
|
||||
unsigned int count;
|
||||
} vy_parsed_stmt_list;
|
||||
|
||||
typedef struct {
|
||||
vy_stmt_form form;
|
||||
vy_text_span attribute;
|
||||
union {
|
||||
vy_text_span value;
|
||||
unsigned int list_index;
|
||||
};
|
||||
/* For lists */
|
||||
unsigned int next;
|
||||
} vy_parsed_stmt;
|
||||
|
||||
typedef struct {
|
||||
const char *file;
|
||||
const char *text;
|
||||
size_t at;
|
||||
size_t length;
|
||||
int line;
|
||||
|
||||
vy_parsed_stmt *statements;
|
||||
unsigned int statement_count;
|
||||
unsigned int statement_capacity;
|
||||
|
||||
vy_parsed_stmt_list *statement_lists;
|
||||
unsigned int statement_list_count;
|
||||
unsigned int statement_list_capacity;
|
||||
} vy_parse_state;
|
||||
|
||||
static bool IsAllowedChar(char c) {
|
||||
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') ||
|
||||
(c >= 'A' && c <= 'Z') || (c == '.') || (c == '_') || (c == '/');
|
||||
}
|
||||
|
||||
static bool IsWhitespace(char c) {
|
||||
return c == ' ' || c == '\t' || c == '\n' || c == '\r';
|
||||
}
|
||||
|
||||
static void SkipWhitespace(vy_parse_state *state) {
|
||||
while (state->at < state->length && IsWhitespace(state->text[state->at])) {
|
||||
if (state->text[state->at] == '\n')
|
||||
++state->line;
|
||||
++state->at;
|
||||
}
|
||||
}
|
||||
|
||||
static bool ParseAttribute(vy_parse_state *state, vy_text_span *_name) {
|
||||
vy_text_span name;
|
||||
name.start = &state->text[state->at];
|
||||
name.length = 0;
|
||||
while (state->at < state->length && !IsWhitespace(state->text[state->at])) {
|
||||
if (IsAllowedChar(state->text[state->at])) {
|
||||
++state->at;
|
||||
++name.length;
|
||||
} else {
|
||||
vyReportError("GFX",
|
||||
"%s:%d Unexpected character %c",
|
||||
state->file,
|
||||
state->line,
|
||||
state->text[state->at]);
|
||||
return false;
|
||||
extern vy_result vyProcessShaderFile(vy_file_id file,
|
||||
void *buffer,
|
||||
size_t size,
|
||||
vy_processor_output *output) {
|
||||
/* Scan the first line for the shader type */
|
||||
char *text = buffer;
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
if (text[i] == '\n' || text[i] == '\r') {
|
||||
vyLog("ASSETC", "First Line");
|
||||
}
|
||||
}
|
||||
*_name = name;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ParseValue(vy_parse_state *state, vy_text_span *_value) {
|
||||
vy_text_span value;
|
||||
value.start = &state->text[state->at];
|
||||
value.length = 0;
|
||||
while (state->at < state->length && !IsWhitespace(state->text[state->at]) &&
|
||||
state->text[state->at] != ';') {
|
||||
if (IsAllowedChar(state->text[state->at])) {
|
||||
++state->at;
|
||||
++value.length;
|
||||
} else {
|
||||
vyReportError("GFX",
|
||||
"%s:%d Unexpected character %c",
|
||||
state->file,
|
||||
state->line,
|
||||
state->text[state->at]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
*_value = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ParseStmtList(vy_parse_state *state, unsigned int *list_index);
|
||||
|
||||
static bool ParseStmt(vy_parse_state *state, unsigned int *stmt_index) {
|
||||
vy_parsed_stmt stmt;
|
||||
stmt.next = UINT_MAX; /* end of list */
|
||||
|
||||
SkipWhitespace(state);
|
||||
if (!ParseAttribute(state, &stmt.attribute))
|
||||
return false;
|
||||
SkipWhitespace(state);
|
||||
|
||||
if (state->at == state->length) {
|
||||
vyReportError("GFX",
|
||||
"%s:%d Expected either a value or '{'",
|
||||
state->file,
|
||||
state->line);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (state->text[state->at] == '{') {
|
||||
/* Consume '{' */
|
||||
++state->at;
|
||||
|
||||
stmt.form = VY_STMT_FORM_LIST;
|
||||
if (!ParseStmtList(state, &stmt.list_index))
|
||||
return false;
|
||||
|
||||
/* Consume '}' */
|
||||
if (state->at < state->length && state->text[state->at] == '}')
|
||||
++state->at;
|
||||
} else {
|
||||
stmt.form = VY_STMT_FORM_VALUE;
|
||||
if (!ParseValue(state, &stmt.value))
|
||||
return false;
|
||||
|
||||
/* Consume ';' */
|
||||
if (state->at < state->length && state->text[state->at] == ';')
|
||||
++state->at;
|
||||
}
|
||||
|
||||
SkipWhitespace(state);
|
||||
|
||||
/* Add statement to array */
|
||||
if (state->statement_count == state->statement_capacity) {
|
||||
unsigned int cap = (state->statement_capacity > 0)
|
||||
? state->statement_capacity * 2
|
||||
: 64;
|
||||
vy_parsed_stmt *temp =
|
||||
realloc(state->statements, sizeof(vy_parsed_stmt) * cap);
|
||||
if (!temp) {
|
||||
vyReportError("GFX",
|
||||
"While parsing %s: Out of memory\n",
|
||||
state->file);
|
||||
return false;
|
||||
}
|
||||
state->statements = temp;
|
||||
state->statement_capacity = cap;
|
||||
}
|
||||
state->statements[state->statement_count] = stmt;
|
||||
*stmt_index = state->statement_count++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ParseStmtList(vy_parse_state *state, unsigned int *list_index) {
|
||||
vy_parsed_stmt_list list;
|
||||
list.first = state->statement_count;
|
||||
list.count = 0;
|
||||
|
||||
unsigned int last = UINT_MAX;
|
||||
|
||||
while (state->at < state->length && state->text[state->at] != '}') {
|
||||
unsigned int stmt;
|
||||
if (!ParseStmt(state, &stmt))
|
||||
return false;
|
||||
|
||||
if (last != UINT_MAX)
|
||||
state->statements[last].next = stmt;
|
||||
last = stmt;
|
||||
|
||||
++list.count;
|
||||
}
|
||||
|
||||
/* Add list to array */
|
||||
if (state->statement_list_count == state->statement_list_capacity) {
|
||||
unsigned int cap = (state->statement_list_capacity > 0)
|
||||
? state->statement_list_capacity * 2
|
||||
: 64;
|
||||
vy_parsed_stmt_list *temp =
|
||||
realloc(state->statement_lists, sizeof(vy_parsed_stmt_list) * cap);
|
||||
if (!temp) {
|
||||
vyReportError("GFX",
|
||||
"While parsing %s: Out of memory\n",
|
||||
state->file);
|
||||
return false;
|
||||
}
|
||||
state->statement_lists = temp;
|
||||
state->statement_list_capacity = cap;
|
||||
}
|
||||
state->statement_lists[state->statement_list_count] = list;
|
||||
*list_index = state->statement_list_count++;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void DbgPrintShaderFile(const vy_parse_state *state,
|
||||
unsigned int list_index,
|
||||
unsigned int indent) {
|
||||
assert(list_index < state->statement_list_count);
|
||||
const vy_parsed_stmt_list *list = &state->statement_lists[list_index];
|
||||
|
||||
unsigned int stmt_index = list->first;
|
||||
for (unsigned int i = 0; i < list->count; ++i) {
|
||||
const vy_parsed_stmt *stmt = &state->statements[stmt_index];
|
||||
for (unsigned int j = 0; j < indent; ++j)
|
||||
printf(" ");
|
||||
printf("%.*s: ", stmt->attribute.length, stmt->attribute.start);
|
||||
if (stmt->form == VY_STMT_FORM_VALUE) {
|
||||
printf("%.*s\n", stmt->value.length, stmt->value.start);
|
||||
} else {
|
||||
printf("{\n");
|
||||
DbgPrintShaderFile(state, stmt->list_index, indent + 2);
|
||||
printf("}\n");
|
||||
}
|
||||
stmt_index = stmt->next;
|
||||
}
|
||||
assert(stmt_index == UINT_MAX || stmt_index == 0);
|
||||
}
|
||||
|
||||
static bool CompareSpanToString(vy_text_span span, const char *cmp) {
|
||||
size_t cmp_len = strlen(cmp);
|
||||
if (cmp_len != (size_t)span.length)
|
||||
return false;
|
||||
for (size_t i = 0; i < cmp_len; ++i) {
|
||||
if (span.start[i] != cmp[i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static const vy_parsed_stmt *FindStatement(const vy_parse_state *state,
|
||||
unsigned int list_index,
|
||||
const char *attribute) {
|
||||
if (list_index >= state->statement_list_count)
|
||||
return NULL;
|
||||
const vy_parsed_stmt_list *list = &state->statement_lists[list_index];
|
||||
unsigned int stmt_index = list->first;
|
||||
for (unsigned int i = 0; i < list->count; ++i) {
|
||||
const vy_parsed_stmt *stmt = &state->statements[stmt_index];
|
||||
if (CompareSpanToString(stmt->attribute, attribute))
|
||||
return stmt;
|
||||
stmt_index = stmt->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool ParseBindingIndex(vy_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 {
|
||||
vyReportError("GFX",
|
||||
"Unexpected non-digit character in binding index");
|
||||
return false;
|
||||
}
|
||||
--at;
|
||||
exp *= 10;
|
||||
}
|
||||
*index = n;
|
||||
return true;
|
||||
}
|
||||
|
||||
static vy_attribute_value ParseBindingValue(vy_text_span span) {
|
||||
if (CompareSpanToString(span, "MATERIAL_ALBEDO")) {
|
||||
return VY_ATTRIBUTE_VALUE_MATERIAL_ALBEDO;
|
||||
} else if (CompareSpanToString(span, "MATERIAL_NORMAL")) {
|
||||
return VY_ATTRIBUTE_VALUE_MATERIAL_NORMAL;
|
||||
}
|
||||
vyReportError("GFX",
|
||||
"Unsupported binding value %*.s",
|
||||
span.length,
|
||||
span.start);
|
||||
return VY_ATTRIBUTE_VALUE_UNDEFINED;
|
||||
}
|
||||
|
||||
static bool ParseBindings(vy_parse_state *state,
|
||||
unsigned int root_list,
|
||||
const char *name,
|
||||
const char *file_path,
|
||||
vy_attribute_binding **p_bindings,
|
||||
unsigned int *p_binding_count) {
|
||||
const vy_parsed_stmt *bindings = FindStatement(state, root_list, name);
|
||||
if (bindings) {
|
||||
if (bindings->form != VY_STMT_FORM_LIST) {
|
||||
vyReportError("GFX",
|
||||
"Expected list of bindings as the value of "
|
||||
"\"%s\" in %s",
|
||||
name,
|
||||
file_path);
|
||||
return false;
|
||||
}
|
||||
const vy_parsed_stmt_list *binding_list =
|
||||
&state->statement_lists[bindings->list_index];
|
||||
vy_attribute_binding *shader_bindings =
|
||||
malloc(sizeof(vy_attribute_binding) * binding_list->count);
|
||||
if (!bindings) {
|
||||
vyReportError("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 vy_parsed_stmt *stmt = &state->statements[stmt_index];
|
||||
if (!ParseBindingIndex(stmt->attribute,
|
||||
&shader_bindings[i].index)) {
|
||||
free(shader_bindings);
|
||||
return false;
|
||||
}
|
||||
|
||||
shader_bindings[i].value = ParseBindingValue(stmt->value);
|
||||
if (shader_bindings[i].value == VY_ATTRIBUTE_VALUE_UNDEFINED) {
|
||||
free(shader_bindings);
|
||||
return false;
|
||||
}
|
||||
stmt_index = stmt->next;
|
||||
}
|
||||
|
||||
*p_bindings = shader_bindings;
|
||||
*p_binding_count = binding_count;
|
||||
return true;
|
||||
} else {
|
||||
*p_bindings = NULL;
|
||||
*p_binding_count = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
ParseShaderFile(vy_file_id fid, const char *text, size_t length, vy_shader *shader) {
|
||||
/* This is the grammar for shader files:
|
||||
* <stmt-list> ::= <stmt>*
|
||||
* <stmt> ::= <attribute> ( ( <value> ';' ) | ( '{' <stmt-list> '}' ) )
|
||||
* <attribute> ::= [:alnum:]*
|
||||
* <value>:: = [:alnum:]* */
|
||||
const char *file_path = vyGetFilePath(fid);
|
||||
vy_parse_state state = {.text = text,
|
||||
.at = 0,
|
||||
.length = length,
|
||||
.line = 1,
|
||||
.file = file_path,
|
||||
.statements = NULL,
|
||||
.statement_lists = NULL,
|
||||
.statement_capacity = 0,
|
||||
.statement_list_capacity = 0};
|
||||
|
||||
bool result = true;
|
||||
unsigned int root_list = 0;
|
||||
if (!ParseStmtList(&state, &root_list)) {
|
||||
result = false;
|
||||
goto out;
|
||||
}
|
||||
|
||||
DbgPrintShaderFile(&state, root_list, 0);
|
||||
|
||||
#if 0
|
||||
shader->pipeline = CreatePipeline(&state, file_path, root_list);
|
||||
if (!VY_IS_HANDLE_VALID(shader->pipeline)) {
|
||||
result = false;
|
||||
goto out;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Process bindings */
|
||||
shader->texture_bindings = NULL;
|
||||
shader->texture_binding_count = 0;
|
||||
shader->uniform_bindings = NULL;
|
||||
shader->uniform_binding_count = 0;
|
||||
shader->storage_bindings = NULL;
|
||||
shader->storage_binding_count = 0;
|
||||
|
||||
if (!ParseBindings(&state,
|
||||
root_list,
|
||||
"texture_bindings",
|
||||
file_path,
|
||||
&shader->texture_bindings,
|
||||
&shader->texture_binding_count)) {
|
||||
result = false;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!ParseBindings(&state,
|
||||
root_list,
|
||||
"uniform_bindings",
|
||||
file_path,
|
||||
&shader->uniform_bindings,
|
||||
&shader->uniform_binding_count)) {
|
||||
result = false;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!ParseBindings(&state,
|
||||
root_list,
|
||||
"storage_bindings",
|
||||
file_path,
|
||||
&shader->storage_bindings,
|
||||
&shader->storage_binding_count)) {
|
||||
result = false;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
free(state.statements);
|
||||
free(state.statement_lists);
|
||||
return result;
|
||||
}
|
||||
|
||||
vy_result vyProcessShaderFile(vy_file_id file,
|
||||
void *buffer,
|
||||
size_t size,
|
||||
vy_processor_output *output) {
|
||||
vy_shader tmp;
|
||||
if (ParseShaderFile(file, buffer, size, &tmp)) {
|
||||
return VY_SUCCESS;
|
||||
} else {
|
||||
return VY_PROCESSING_FAILED;
|
||||
}
|
||||
return VY_SUCCESS;
|
||||
}
|
57
src/tools/assetc/uidmap.c
Normal file
57
src/tools/assetc/uidmap.c
Normal file
@ -0,0 +1,57 @@
|
||||
#include "processing.h"
|
||||
|
||||
#include "runtime/threading.h"
|
||||
|
||||
#define MAP_SIZE 2048
|
||||
typedef struct {
|
||||
vy_file_id fids[MAP_SIZE];
|
||||
vy_uid uids[MAP_SIZE];
|
||||
|
||||
unsigned int used_slots;
|
||||
} vy_uid_map;
|
||||
|
||||
static vy_uid_map _map;
|
||||
static vy_mutex *_guard;
|
||||
|
||||
void vyInitUIDMap(void) {
|
||||
_guard = vyCreateMutex();
|
||||
}
|
||||
|
||||
vy_uid vyLookupUID(vy_file_id fid) {
|
||||
vyLockMutex(_guard);
|
||||
unsigned int i = 0;
|
||||
vy_uid result = VY_INVALID_UID;
|
||||
while (i < MAP_SIZE) {
|
||||
unsigned int slot = (fid + i) % MAP_SIZE;
|
||||
if (_map.fids[slot] == fid) {
|
||||
_map.fids[slot] = fid;
|
||||
result = _map.uids[slot];
|
||||
break;
|
||||
} else if (_map.fids[slot] == VY_INVALID_FILE_ID) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
vyUnlockMutex(_guard);
|
||||
return result;
|
||||
}
|
||||
|
||||
void vyAddUIDMapping(vy_file_id fid, vy_uid uid) {
|
||||
vyLockMutex(_guard);
|
||||
float fill_rate = (float)_map.used_slots / MAP_SIZE;
|
||||
if (fill_rate >= .5f) {
|
||||
vyLog("ASSETC", "UID map is above 50% filled.");
|
||||
}
|
||||
unsigned int i = 0;
|
||||
while (i < MAP_SIZE) {
|
||||
unsigned int slot = (fid + i) % MAP_SIZE;
|
||||
if (_map.fids[slot] == 0) {
|
||||
_map.fids[slot] = fid;
|
||||
_map.uids[slot] = uid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == MAP_SIZE) {
|
||||
vyReportError("ASSETC", "Failed to insert entry into UID map.");
|
||||
}
|
||||
vyUnlockMutex(_guard);
|
||||
}
|
Loading…
Reference in New Issue
Block a user