Compare commits

...

5 Commits

Author SHA1 Message Date
bjorn 72dd7ee0f2 Fix compute shader compilation; 2022-07-09 23:39:20 -07:00
bjorn 8e89026678 Shader:getLocalWorkgroupSize; 2022-07-09 23:39:03 -07:00
bjorn 886bfe18c1 Shader "improvements"; 2022-07-09 23:09:02 -07:00
bjorn c7f4e11b0b Improve shader errors; 2022-07-09 21:39:31 -07:00
bjorn e8a5f02e7b Start stats; 2022-07-09 21:19:51 -07:00
7 changed files with 179 additions and 82 deletions

View File

@ -62,7 +62,9 @@ layout(location = 0) out vec4 PixelColors[1];
layout(location = 10) out vec3 Normal;
layout(location = 11) out vec4 Color;
layout(location = 12) out vec2 UV;
#else
#endif
#ifdef GL_FRAGMENT_SHADER
layout(location = 10) in vec3 Normal;
layout(location = 11) in vec4 Color;
layout(location = 12) in vec2 UV;

View File

@ -61,6 +61,7 @@ StringEntry lovrCullMode[] = {
StringEntry lovrDefaultShader[] = {
[SHADER_UNLIT] = ENTRY("unlit"),
[SHADER_CUBE] = ENTRY("cube"),
[SHADER_PANO] = ENTRY("pano"),
[SHADER_FILL] = ENTRY("fill"),
[SHADER_FONT] = ENTRY("font"),
{ 0 }
@ -684,6 +685,17 @@ static int l_lovrGraphicsGetLimits(lua_State* L) {
return 1;
}
static int l_lovrGraphicsGetStats(lua_State* L) {
GraphicsStats stats;
lovrGraphicsGetStats(&stats);
lua_newtable(L);
lua_pushinteger(L, stats.pipelineSwitches), lua_setfield(L, -2, "pipelineSwitches");
lua_pushinteger(L, stats.bundleSwitches), lua_setfield(L, -2, "bundleSwitches");
return 1;
}
static int l_lovrGraphicsIsFormatSupported(lua_State* L) {
TextureFormat format = luax_checkenum(L, 1, TextureFormat, NULL);
uint32_t features = 0;
@ -1000,58 +1012,77 @@ static int l_lovrGraphicsNewSampler(lua_State* L) {
return 1;
}
static Blob* luax_checkshadercode(lua_State* L, int index, ShaderStage stage) {
Blob* source;
size_t length;
const char* string = lua_tolstring(L, index, &length);
if (string && memchr(string, '\n', MIN(256, length))) {
void* data = malloc(length);
lovrAssert(data, "Out of memory");
memcpy(data, string, length);
source = lovrBlobCreate(data, length, "Shader code");
static ShaderSource luax_checkshadercode(lua_State* L, int index, ShaderStage stage, bool* allocated) {
ShaderSource source;
if (lua_isstring(L, index)) {
size_t length;
const char* string = lua_tolstring(L, index, &length);
if (memchr(string, '\n', MIN(256, length))) {
source.code = string;
source.size = length;
*allocated = false;
} else {
source.code = luax_readfile(string, &source.size);
lovrAssert(source.code, "Could not read shader code from %s", string);
*allocated = true;
}
} else if (lua_istable(L, index)) {
int length = luax_len(L, index);
size_t size = length * 4;
uint32_t* words = malloc(size);
source.size = length * sizeof(uint32_t);
uint32_t* words = malloc(source.size);
lovrAssert(words, "Out of memory");
source = lovrBlobCreate(words, size, "SPIR-V code");
source.code = words;
*allocated = true;
for (int i = 0; i < length; i++) {
lua_rawgeti(L, index, i + 1);
words[i] = lua_tointeger(L, -1);
words[i] = luax_checku32(L, -1);
lua_pop(L, 1);
}
} else {
source = luax_readblob(L, index, "Shader");
} else if (lua_isuserdata(L, index)) {
Blob* blob = luax_checktype(L, index, Blob);
source.code = blob->data;
source.size = blob->size;
*allocated = false;
}
Blob* code = lovrGraphicsCompileShader(stage, source);
lovrAssert(code, "Could not compile shader");
lovrRelease(source, lovrBlobDestroy);
return code;
ShaderSource bytecode = lovrGraphicsCompileShader(stage, &source);
if (bytecode.code != source.code) {
if (*allocated) free((void*) source.code);
*allocated = true;
return bytecode;
}
return source;
}
static int l_lovrGraphicsCompileShader(lua_State* L) {
ShaderStage stage = luax_checkenum(L, 1, ShaderStage, NULL);
Blob* source = luax_checkshadercode(L, 2, stage);
Blob* output = lovrGraphicsCompileShader(stage, source);
luax_pushtype(L, Blob, output);
lovrRelease(output, lovrBlobDestroy);
lovrRelease(source, lovrBlobDestroy);
bool allocated;
ShaderSource spirv = luax_checkshadercode(L, 2, stage, &allocated);
if (!allocated) {
lua_settop(L, 2);
return 1;
} else {
Blob* blob = lovrBlobCreate((void*) spirv.code, spirv.size, "Compiled Shader Code");
luax_pushtype(L, Blob, blob);
}
return 1;
}
static int l_lovrGraphicsNewShader(lua_State* L) {
ShaderInfo info = { 0 };
bool allocated[2];
int index;
if (lua_gettop(L) == 1 || (lua_istable(L, 2) && luax_len(L, 2) == 0)) {
info.type = SHADER_COMPUTE;
info.stages[0] = luax_checkshadercode(L, 1, STAGE_COMPUTE);
info.source[0] = luax_checkshadercode(L, 1, STAGE_COMPUTE, &allocated[0]);
index = 2;
} else {
info.type = SHADER_GRAPHICS;
info.stages[0] = luax_checkshadercode(L, 1, STAGE_VERTEX);
info.stages[1] = luax_checkshadercode(L, 2, STAGE_FRAGMENT);
info.source[0] = luax_checkshadercode(L, 1, STAGE_VERTEX, &allocated[0]);
info.source[1] = luax_checkshadercode(L, 2, STAGE_FRAGMENT, &allocated[1]);
index = 3;
}
@ -1084,13 +1115,13 @@ static int l_lovrGraphicsNewShader(lua_State* L) {
info.flags = flags.data;
info.flagCount = flags.length;
Shader* shader = lovrShaderCreate(&info);
lovrRelease(info.stages[0], lovrBlobDestroy);
lovrRelease(info.stages[1], lovrBlobDestroy);
arr_free(&flags);
Shader* shader = lovrShaderCreate(&info);
luax_pushtype(L, Shader, shader);
lovrRelease(shader, lovrShaderDestroy);
if (allocated[0]) free((void*) info.source[0].code);
if (allocated[1]) free((void*) info.source[1].code);
arr_free(&flags);
return 1;
}
@ -1335,6 +1366,7 @@ static const luaL_Reg lovrGraphics[] = {
{ "getDevice", l_lovrGraphicsGetDevice },
{ "getFeatures", l_lovrGraphicsGetFeatures },
{ "getLimits", l_lovrGraphicsGetLimits },
{ "getStats", l_lovrGraphicsGetStats },
{ "isFormatSupported", l_lovrGraphicsIsFormatSupported },
{ "getBackground", l_lovrGraphicsGetBackground },
{ "setBackground", l_lovrGraphicsSetBackground },

View File

@ -64,10 +64,27 @@ static int l_lovrShaderHasAttribute(lua_State* L) {
return 1;
}
static int l_lovrShaderGetLocalWorkgroupSize(lua_State* L) {
Shader* shader = luax_checktype(L, 1, Shader);
if (!lovrShaderHasStage(shader, STAGE_COMPUTE)) {
lua_pushnil(L);
return 1;
}
uint32_t size[3];
lovrShaderGetLocalWorkgroupSize(shader, size);
lua_pushinteger(L, size[0]);
lua_pushinteger(L, size[1]);
lua_pushinteger(L, size[2]);
return 3;
}
const luaL_Reg lovrShader[] = {
{ "clone", l_lovrShaderClone },
{ "getType", l_lovrShaderGetType },
{ "hasStage", l_lovrShaderHasStage },
{ "hasAttribute", l_lovrShaderHasAttribute },
{ "getLocalWorkgroupSize", l_lovrShaderGetLocalWorkgroupSize },
{ NULL, NULL }
};

View File

@ -49,6 +49,7 @@ typedef struct {
#define OP_LENGTH(op) (op[0] >> 16)
static spv_result spv_parse_capability(spv_context* spv, const uint32_t* op, spv_info* info);
static spv_result spv_parse_execution_mode(spv_context* spv, const uint32_t* op, spv_info* info);
static spv_result spv_parse_name(spv_context* spv, const uint32_t* op, spv_info* info);
static spv_result spv_parse_decoration(spv_context* spv, const uint32_t* op, spv_info* info);
static spv_result spv_parse_type(spv_context* spv, const uint32_t* op, spv_info* info);
@ -104,6 +105,8 @@ spv_result spv_parse(const void* source, uint32_t size, spv_info* info) {
case 17: // OpCapability
result = spv_parse_capability(&spv, op, info);
break;
case 16: // OpExecutionMode
result = spv_parse_execution_mode(&spv, op, info);
case 5: // OpName
result = spv_parse_name(&spv, op, info);
break;
@ -179,6 +182,17 @@ static spv_result spv_parse_capability(spv_context* spv, const uint32_t* op, spv
return SPV_OK;
}
static spv_result spv_parse_execution_mode(spv_context* spv, const uint32_t* op, spv_info* info) {
if (OP_LENGTH(op) != 6 || op[2] != 17) { // LocalSize
return SPV_OK;
}
info->localWorkgroupSize[0] = op[3];
info->localWorkgroupSize[1] = op[4];
info->localWorkgroupSize[2] = op[5];
return SPV_OK;
}
static spv_result spv_parse_name(spv_context* spv, const uint32_t* op, spv_info* info) {
if (OP_LENGTH(op) < 3 || op[1] > spv->bound) {
return SPV_INVALID;

View File

@ -56,6 +56,7 @@ typedef struct {
typedef struct {
uint32_t version;
uint32_t localWorkgroupSize[3];
uint32_t featureCount;
uint32_t specConstantCount;
uint32_t pushConstantCount;

View File

@ -105,6 +105,7 @@ struct Shader {
ShaderInfo info;
uint32_t layout;
uint32_t computePipeline;
uint32_t localWorkgroupSize[3];
uint32_t bufferMask;
uint32_t textureMask;
uint32_t samplerMask;
@ -334,6 +335,7 @@ static struct {
gpu_device_info device;
gpu_features features;
gpu_limits limits;
GraphicsStats stats;
float background[4];
Texture* window;
Font* defaultFont;
@ -676,6 +678,10 @@ void lovrGraphicsGetLimits(GraphicsLimits* limits) {
limits->pointSize = state.limits.pointSize;
}
void lovrGraphicsGetStats(GraphicsStats* stats) {
*stats = state.stats;
}
bool lovrGraphicsIsFormatSupported(uint32_t format, uint32_t features) {
uint8_t supports = state.features.formats[format];
if (!features) return supports;
@ -900,6 +906,9 @@ void lovrGraphicsSubmit(Pass** passes, uint32_t count) {
cleanupPasses();
arr_clear(&state.passes);
state.stats.pipelineSwitches = 0;
state.stats.bundleSwitches = 0;
if (present) {
state.window->gpu = NULL;
state.window->renderView = NULL;
@ -1281,11 +1290,11 @@ const SamplerInfo* lovrSamplerGetInfo(Sampler* sampler) {
// Shader
Blob* lovrGraphicsCompileShader(ShaderStage stage, Blob* source) {
uint32_t spirv = 0x07230203;
ShaderSource lovrGraphicsCompileShader(ShaderStage stage, ShaderSource* source) {
uint32_t magic = 0x07230203;
if (source->size % 4 == 0 && source->size >= 4 && !memcmp(source->data, &spirv, 4)) {
return lovrRetain(source), source;
if (source->size % 4 == 0 && source->size >= 4 && !memcmp(source->code, &magic, 4)) {
return *source;
}
#ifdef LOVR_USE_GLSLANG
@ -1313,21 +1322,23 @@ Blob* lovrGraphicsCompileShader(ShaderStage stage, Blob* source) {
"Normal = normalize(NormalMatrix * VertexNormal);"
"UV = VertexUV;"
"Position = lovrmain();"
"}\n#line 0\n",
[STAGE_FRAGMENT] = "void main() { PixelColors[0] = lovrmain(); }\n#line 0\n",
[STAGE_COMPUTE] = "void main() { lovrmain(); }\n#line 0\n"
"}",
[STAGE_FRAGMENT] = "void main() { PixelColors[0] = lovrmain(); }",
[STAGE_COMPUTE] = "void main() { lovrmain(); }"
};
const char* strings[] = {
prefix,
(const char*) etc_shaders_lovr_glsl,
source->data,
"#line 1\n",
source->code,
suffixes[stage]
};
int lengths[] = {
-1,
etc_shaders_lovr_glsl_len,
-1,
source->size,
-1
};
@ -1352,38 +1363,39 @@ Blob* lovrGraphicsCompileShader(ShaderStage stage, Blob* source) {
glslang_shader_t* shader = glslang_shader_create(&input);
if (!glslang_shader_preprocess(shader, &input)) {
lovrLog(LOG_INFO, "GFX", "Could not preprocess %s shader:\n%s", stageNames[stage], glslang_shader_get_info_log(shader));
return NULL;
lovrThrow("Could not preprocess %s shader:\n%s", stageNames[stage], glslang_shader_get_info_log(shader));
return (ShaderSource) { NULL, 0 };
}
if (!glslang_shader_parse(shader, &input)) {
lovrLog(LOG_INFO, "GFX", "Could not parse %s shader:\n%s", stageNames[stage], glslang_shader_get_info_log(shader));
return NULL;
lovrThrow("Could not parse %s shader:\n%s", stageNames[stage], glslang_shader_get_info_log(shader));
return (ShaderSource) { NULL, 0 };
}
glslang_program_t* program = glslang_program_create();
glslang_program_add_shader(program, shader);
if (!glslang_program_link(program, 0)) {
lovrLog(LOG_INFO, "GFX", "Could not link shader:\n%s", glslang_program_get_info_log(program));
return NULL;
lovrThrow("Could not link shader:\n%s", glslang_program_get_info_log(program));
return (ShaderSource) { NULL, 0 };
}
glslang_program_SPIRV_generate(program, stages[stage]);
void* words = glslang_program_SPIRV_get_ptr(program);
size_t size = glslang_program_SPIRV_get_size(program) * 4;
void* data = malloc(size);
lovrAssert(data, "Out of memory");
memcpy(data, words, size);
Blob* blob = lovrBlobCreate(data, size, "SPIRV");
glslang_program_delete(program);
glslang_shader_delete(shader);
return blob;
return (ShaderSource) { data, size };
#else
return NULL;
lovrThrow("Could not compile shader: No shader compiler available");
return (ShaderSource) { NULL, 0 };
#endif
}
@ -1437,39 +1449,34 @@ Shader* lovrGraphicsGetDefaultShader(DefaultShader type) {
switch (type) {
case SHADER_UNLIT:
info.stages[0] = lovrBlobCreate((void*) lovr_shader_unlit_vert, sizeof(lovr_shader_unlit_vert), "Unlit Vertex Shader");
info.stages[1] = lovrBlobCreate((void*) lovr_shader_unlit_frag, sizeof(lovr_shader_unlit_frag), "Unlit Fragment Shader");
info.source[0] = (ShaderSource) { lovr_shader_unlit_vert, sizeof(lovr_shader_unlit_vert) };
info.source[1] = (ShaderSource) { lovr_shader_unlit_frag, sizeof(lovr_shader_unlit_frag) };
info.label = "unlit";
break;
case SHADER_CUBE:
info.stages[0] = lovrBlobCreate((void*) lovr_shader_cubemap_vert, sizeof(lovr_shader_cubemap_vert), "Cubemap Vertex Shader");
info.stages[1] = lovrBlobCreate((void*) lovr_shader_cubemap_frag, sizeof(lovr_shader_cubemap_frag), "Cubemap Fragment Shader");
info.source[0] = (ShaderSource) { lovr_shader_cubemap_vert, sizeof(lovr_shader_cubemap_vert) };
info.source[1] = (ShaderSource) { lovr_shader_cubemap_frag, sizeof(lovr_shader_cubemap_frag) };
info.label = "cubemap";
break;
case SHADER_PANO:
info.stages[0] = lovrBlobCreate((void*) lovr_shader_cubemap_vert, sizeof(lovr_shader_cubemap_vert), "Cubemap Vertex Shader");
info.stages[1] = lovrBlobCreate((void*) lovr_shader_equirect_frag, sizeof(lovr_shader_equirect_frag), "Equirect Fragment Shader");
info.source[0] = (ShaderSource) { lovr_shader_cubemap_vert, sizeof(lovr_shader_cubemap_vert) };
info.source[1] = (ShaderSource) { lovr_shader_equirect_frag, sizeof(lovr_shader_equirect_frag) };
info.label = "equirect";
break;
case SHADER_FILL:
info.stages[0] = lovrBlobCreate((void*) lovr_shader_fill_vert, sizeof(lovr_shader_fill_vert), "Fill Vertex Shader");
info.stages[1] = lovrBlobCreate((void*) lovr_shader_unlit_frag, sizeof(lovr_shader_unlit_frag), "Unlit Fragment Shader");
info.source[0] = (ShaderSource) { lovr_shader_fill_vert, sizeof(lovr_shader_fill_vert) };
info.source[1] = (ShaderSource) { lovr_shader_unlit_frag, sizeof(lovr_shader_unlit_frag) };
info.label = "fill";
break;
case SHADER_FONT:
info.stages[0] = lovrBlobCreate((void*) lovr_shader_unlit_vert, sizeof(lovr_shader_unlit_vert), "Unlit Vertex Shader");
info.stages[1] = lovrBlobCreate((void*) lovr_shader_font_frag, sizeof(lovr_shader_font_frag), "Font Fragment Shader");
info.source[0] = (ShaderSource) { lovr_shader_unlit_vert, sizeof(lovr_shader_unlit_vert) };
info.source[1] = (ShaderSource) { lovr_shader_font_frag, sizeof(lovr_shader_font_frag) };
info.label = "font";
break;
default: lovrUnreachable();
}
state.defaultShaders[type] = lovrShaderCreate(&info);
info.stages[0]->data = NULL;
info.stages[1]->data = NULL;
lovrRelease(info.stages[0], lovrBlobDestroy);
lovrRelease(info.stages[1], lovrBlobDestroy);
return state.defaultShaders[type];
return state.defaultShaders[type] = lovrShaderCreate(&info);
}
Shader* lovrShaderCreate(ShaderInfo* info) {
@ -1483,7 +1490,7 @@ Shader* lovrShaderCreate(ShaderInfo* info) {
spv_result result;
spv_info spv[2] = { 0 };
for (uint32_t i = 0; i < stageCount; i++) {
result = spv_parse(info->stages[i]->data, info->stages[i]->size, &spv[i]);
result = spv_parse(info->source[i].code, info->source[i].size, &spv[i]);
lovrCheck(result == SPV_OK, "Failed to load Shader: %s\n", spv_result_to_string(result));
lovrCheck(spv[i].version <= 0x00010300, "Invalid SPIR-V version (up to 1.3 is supported)");
@ -1493,12 +1500,16 @@ Shader* lovrShaderCreate(ShaderInfo* info) {
spv[i].attributes = tempAlloc(spv[i].attributeCount * sizeof(spv_attribute));
spv[i].resources = tempAlloc(spv[i].resourceCount * sizeof(spv_resource));
result = spv_parse(info->stages[i]->data, info->stages[i]->size, &spv[i]);
result = spv_parse(info->source[i].code, info->source[i].size, &spv[i]);
lovrCheck(result == SPV_OK, "Failed to load Shader: %s\n", spv_result_to_string(result));
checkShaderFeatures(spv[i].features, spv[i].featureCount);
}
if (info->type == SHADER_COMPUTE) {
memcpy(shader->localWorkgroupSize, spv[0].localWorkgroupSize, 3 * sizeof(uint32_t));
}
uint32_t constantStage = spv[0].pushConstantSize > spv[1].pushConstantSize ? 0 : 1;
uint32_t maxFlags = spv[0].specConstantCount + spv[1].specConstantCount;
shader->constantCount = spv[constantStage].pushConstantCount;
@ -1671,13 +1682,13 @@ Shader* lovrShaderCreate(ShaderInfo* info) {
shader->layout = getLayout(slots, shader->resourceCount);
gpu_shader_info gpu = {
.stages[0] = { info->stages[0]->data, info->stages[0]->size },
.stages[0] = { info->source[0].code, info->source[0].size },
.pushConstantSize = shader->constantSize,
.label = info->label
};
if (info->stages[1]) {
gpu.stages[1] = (gpu_shader_stage) { info->stages[1]->data, info->stages[1]->size };
if (info->source[1].code) {
gpu.stages[1] = (gpu_shader_stage) { info->source[1].code, info->source[1].size };
}
if (info->type == SHADER_GRAPHICS) {
@ -1758,6 +1769,10 @@ bool lovrShaderHasAttribute(Shader* shader, const char* name, uint32_t location)
return false;
}
void lovrShaderGetLocalWorkgroupSize(Shader* shader, uint32_t size[3]) {
memcpy(size, shader->localWorkgroupSize, 3 * sizeof(uint32_t));
}
// Material
Material* lovrMaterialCreate(MaterialInfo* info) {
@ -2598,15 +2613,12 @@ static void lovrModelReskin(Model* model) {
}
if (!state.animator) {
Blob* source = lovrBlobCreate((void*) lovr_shader_animator_comp, sizeof(lovr_shader_animator_comp), NULL);
state.animator = lovrShaderCreate(&(ShaderInfo) {
.type = SHADER_COMPUTE,
.stages[0] = source,
.source[0] = { lovr_shader_animator_comp, sizeof(lovr_shader_animator_comp) },
.flags = &(ShaderFlag) { "local_size_x_id", 0, state.device.subgroupSize },
.label = "Animator"
.label = "animator"
});
source->data = NULL;
lovrRelease(source, lovrBlobDestroy);
}
gpu_pipeline* pipeline = state.pipelines.data[state.animator->computePipeline];
@ -2650,6 +2662,8 @@ static void lovrModelReskin(Model* model) {
gpu_compute(state.stream, (skin->vertexCount + subgroupSize - 1) / subgroupSize, 1, 1);
gpu_compute_end(state.stream);
baseVertex += skin->vertexCount;
state.stats.pipelineSwitches++;
state.stats.bundleSwitches++;
}
state.hasReskin = true;
@ -3461,6 +3475,7 @@ static void flushPipeline(Pass* pass, Draw* draw, Shader* shader) {
}
gpu_bind_pipeline(pass->stream, state.pipelines.data[index], false);
state.stats.pipelineSwitches++;
pipeline->dirty = false;
}
@ -3492,6 +3507,7 @@ static void flushBindings(Pass* pass, Shader* shader) {
gpu_bundle* bundle = getBundle(shader->layout);
gpu_bundle_write(&bundle, &info, 1);
gpu_bind_bundle(pass->stream, shader->gpu, set, bundle, NULL, 0);
state.stats.bundleSwitches++;
}
static void flushBuiltins(Pass* pass, Draw* draw, Shader* shader) {
@ -3534,6 +3550,7 @@ static void flushBuiltins(Pass* pass, Draw* draw, Shader* shader) {
gpu_bundle* bundle = getBundle(state.builtinLayout);
gpu_bundle_write(&bundle, &bundleInfo, 1);
gpu_bind_bundle(pass->stream, shader->gpu, 0, bundle, NULL, 0);
state.stats.bundleSwitches++;
}
float m[16];
@ -3557,9 +3574,11 @@ static void flushBuiltins(Pass* pass, Draw* draw, Shader* shader) {
static void flushMaterial(Pass* pass, Draw* draw, Shader* shader) {
if (draw->material && draw->material != pass->pipeline->material) {
gpu_bind_bundle(pass->stream, shader->gpu, 1, draw->material->bundle, NULL, 0);
state.stats.bundleSwitches++;
pass->materialDirty = true;
} else if (pass->materialDirty) {
gpu_bind_bundle(pass->stream, shader->gpu, 1, pass->pipeline->material->bundle, NULL, 0);
state.stats.bundleSwitches++;
pass->materialDirty = false;
}
}
@ -4466,6 +4485,7 @@ void lovrPassCompute(Pass* pass, uint32_t x, uint32_t y, uint32_t z, Buffer* ind
if (pass->pipeline->dirty) {
gpu_bind_pipeline(pass->stream, pipeline, true);
state.stats.pipelineSwitches++;
pass->pipeline->dirty = false;
}
@ -4550,14 +4570,11 @@ void lovrPassCopyTallyToBuffer(Pass* pass, Tally* tally, Buffer* buffer, uint32_
}, 1);
if (!state.timeWizard) {
Blob* source = lovrBlobCreate((void*) lovr_shader_timewizard_comp, sizeof(lovr_shader_timewizard_comp), NULL);
state.timeWizard = lovrShaderCreate(&(ShaderInfo) {
.type = SHADER_COMPUTE,
.stages[0] = source,
.label = "Chronophage"
.source[0] = { lovr_shader_timewizard_comp, sizeof(lovr_shader_timewizard_comp) },
.label = "timewizard"
});
source->data = NULL;
lovrRelease(source, lovrBlobDestroy);
}
gpu_pipeline* pipeline = state.pipelines.data[state.timeWizard->computePipeline];
@ -4586,6 +4603,8 @@ void lovrPassCopyTallyToBuffer(Pass* pass, Tally* tally, Buffer* buffer, uint32_
gpu_push_constants(pass->stream, shader, &constants, sizeof(constants));
gpu_compute(pass->stream, (count + 31) / 32, 1, 1);
gpu_compute_end(pass->stream);
state.stats.pipelineSwitches++;
state.stats.bundleSwitches++;
trackBuffer(pass, buffer, GPU_PHASE_SHADER_COMPUTE, GPU_CACHE_STORAGE_WRITE);
} else {

View File

@ -72,6 +72,11 @@ typedef struct {
float pointSize;
} GraphicsLimits;
typedef struct {
uint32_t pipelineSwitches;
uint32_t bundleSwitches;
} GraphicsStats;
enum {
TEXTURE_FEATURE_SAMPLE = (1 << 0),
TEXTURE_FEATURE_FILTER = (1 << 1),
@ -89,6 +94,7 @@ void lovrGraphicsDestroy(void);
void lovrGraphicsGetDevice(GraphicsDevice* device);
void lovrGraphicsGetFeatures(GraphicsFeatures* features);
void lovrGraphicsGetLimits(GraphicsLimits* limits);
void lovrGraphicsGetStats(GraphicsStats* stats);
bool lovrGraphicsIsFormatSupported(uint32_t format, uint32_t features);
void lovrGraphicsGetBackground(float background[4]);
@ -271,6 +277,11 @@ typedef enum {
STAGE_COMPUTE
} ShaderStage;
typedef struct {
const void* code;
size_t size;
} ShaderSource;
typedef struct {
const char* name;
uint32_t id;
@ -279,13 +290,13 @@ typedef struct {
typedef struct {
ShaderType type;
struct Blob* stages[2];
ShaderSource source[2];
uint32_t flagCount;
ShaderFlag* flags;
const char* label;
} ShaderInfo;
struct Blob* lovrGraphicsCompileShader(ShaderStage stage, struct Blob* source);
ShaderSource lovrGraphicsCompileShader(ShaderStage stage, ShaderSource* source);
Shader* lovrGraphicsGetDefaultShader(DefaultShader type);
Shader* lovrShaderCreate(ShaderInfo* info);
Shader* lovrShaderClone(Shader* parent, ShaderFlag* flags, uint32_t count);
@ -293,6 +304,7 @@ void lovrShaderDestroy(void* ref);
const ShaderInfo* lovrShaderGetInfo(Shader* shader);
bool lovrShaderHasStage(Shader* shader, ShaderStage stage);
bool lovrShaderHasAttribute(Shader* shader, const char* name, uint32_t location);
void lovrShaderGetLocalWorkgroupSize(Shader* shader, uint32_t size[3]);
// Material