mirror of https://github.com/bjornbytes/lovr.git
Shader rework;
- Undeprecate ShaderType (it's good actually, kinda like a pipeline bind point and more specific than having lovr trying to make sense of a random set of stages). - Use a default uniform block instead of push constants. The Constants macro now maps to a uniform block declaration. - It is no longer possible to create a shader by giving a single DefaultShader string (you can still give a per-stage DefaultShader though). - lovr.graphics.compileShader now takes all stages instead of a single stage. In general we need all stages when compiling GLSL because default uniforms require linking across stages.
This commit is contained in:
parent
8b78a4d3b5
commit
875a7f8237
|
@ -140,7 +140,7 @@ layout(location = 14) in vec4 Tangent;
|
|||
|
||||
// Helpers
|
||||
|
||||
#define Constants layout(push_constant) uniform PushConstants
|
||||
#define Constants uniform DefaultUniformBlock
|
||||
#ifdef GL_COMPUTE_SHADER
|
||||
#define var(x) layout(set = 0, binding = x)
|
||||
#else
|
||||
|
|
|
@ -51,6 +51,7 @@ extern StringEntry lovrPassType[];
|
|||
extern StringEntry lovrPermission[];
|
||||
extern StringEntry lovrSampleFormat[];
|
||||
extern StringEntry lovrShaderStage[];
|
||||
extern StringEntry lovrShaderType[];
|
||||
extern StringEntry lovrShapeType[];
|
||||
extern StringEntry lovrSmoothMode[];
|
||||
extern StringEntry lovrStackType[];
|
||||
|
|
|
@ -146,6 +146,12 @@ StringEntry lovrShaderStage[] = {
|
|||
{ 0 }
|
||||
};
|
||||
|
||||
StringEntry lovrShaderType[] = {
|
||||
[SHADER_GRAPHICS] = ENTRY("graphics"),
|
||||
[SHADER_COMPUTE] = ENTRY("compute"),
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
StringEntry lovrStackType[] = {
|
||||
[STACK_TRANSFORM] = ENTRY("transform"),
|
||||
[STACK_STATE] = ENTRY("state"),
|
||||
|
@ -946,94 +952,88 @@ static int l_lovrGraphicsNewSampler(lua_State* L) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
static ShaderSource luax_checkshadersource(lua_State* L, int index, ShaderStage stage, bool* allocated) {
|
||||
ShaderSource source;
|
||||
static ShaderSource luax_checkshadersource(lua_State* L, int index, ShaderStage stage, bool* shouldFree) {
|
||||
*shouldFree = false;
|
||||
|
||||
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;
|
||||
return (ShaderSource) { stage, string, length };
|
||||
} else {
|
||||
for (int i = 0; lovrDefaultShader[i].length; i++) {
|
||||
if (lovrDefaultShader[i].length == length && !memcmp(lovrDefaultShader[i].string, string, length)) {
|
||||
*allocated = false;
|
||||
return lovrGraphicsGetDefaultShaderSource(i, stage);
|
||||
}
|
||||
}
|
||||
|
||||
source.code = luax_readfile(string, &source.size);
|
||||
size_t size;
|
||||
void* code = luax_readfile(string, &size);
|
||||
|
||||
if (source.code) {
|
||||
*allocated = true;
|
||||
if (code) {
|
||||
*shouldFree = true;
|
||||
return (ShaderSource) { stage, code, size };
|
||||
} else {
|
||||
luaL_argerror(L, index, "single-line string was not filename or DefaultShader");
|
||||
}
|
||||
}
|
||||
} else if (lua_isuserdata(L, index)) {
|
||||
Blob* blob = luax_checktype(L, index, Blob);
|
||||
source.code = blob->data;
|
||||
source.size = blob->size;
|
||||
*allocated = false;
|
||||
return (ShaderSource) { stage, blob->data, blob->size };
|
||||
} else {
|
||||
*allocated = false;
|
||||
return lovrGraphicsGetDefaultShaderSource(SHADER_UNLIT, stage);
|
||||
luax_typeerror(L, index, "string, Blob, or DefaultShader");
|
||||
}
|
||||
|
||||
ShaderSource bytecode = lovrGraphicsCompileShader(stage, &source, luax_readfile);
|
||||
|
||||
if (bytecode.code != source.code) {
|
||||
if (*allocated) free((void*) source.code);
|
||||
*allocated = true;
|
||||
return bytecode;
|
||||
}
|
||||
|
||||
return source;
|
||||
return (ShaderSource) { 0 };
|
||||
}
|
||||
|
||||
static int l_lovrGraphicsCompileShader(lua_State* L) {
|
||||
ShaderStage stage = luax_checkenum(L, 1, ShaderStage, NULL);
|
||||
bool allocated;
|
||||
ShaderSource spirv = luax_checkshadersource(L, 2, stage, &allocated);
|
||||
Blob* blob = lovrBlobCreate((void*) spirv.code, spirv.size, "Compiled Shader Code");
|
||||
luax_pushtype(L, Blob, blob);
|
||||
lovrRelease(blob, lovrBlobDestroy);
|
||||
return 1;
|
||||
ShaderSource inputs[2], outputs[2];
|
||||
bool shouldFree[2];
|
||||
uint32_t count;
|
||||
|
||||
if (lua_gettop(L) == 1) {
|
||||
inputs[0] = luax_checkshadersource(L, 1, STAGE_COMPUTE, &shouldFree[0]);
|
||||
count = 1;
|
||||
} else {
|
||||
inputs[0] = luax_checkshadersource(L, 1, STAGE_VERTEX, &shouldFree[0]);
|
||||
inputs[1] = luax_checkshadersource(L, 2, STAGE_FRAGMENT, &shouldFree[1]);
|
||||
count = 2;
|
||||
}
|
||||
|
||||
lovrGraphicsCompileShader(inputs, outputs, count, luax_readfile);
|
||||
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
if (shouldFree[i] && outputs[i].code != inputs[i].code) free((void*) inputs[i].code);
|
||||
Blob* blob = lovrBlobCreate((void*) outputs[i].code, outputs[i].size, "Shader code");
|
||||
luax_pushtype(L, Blob, blob);
|
||||
lovrRelease(blob, lovrBlobDestroy);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int l_lovrGraphicsNewShader(lua_State* L) {
|
||||
ShaderInfo info = { 0 };
|
||||
bool allocated[2];
|
||||
ShaderSource source[2], compiled[2];
|
||||
ShaderInfo info = { .stages = compiled };
|
||||
bool shouldFree[2] = { 0 };
|
||||
int index;
|
||||
|
||||
// If there's only one source given, it could be a DefaultShader or a compute shader
|
||||
if (lua_gettop(L) == 1 || (lua_istable(L, 2) && luax_len(L, 2) == 0)) {
|
||||
if (lua_type(L, 1) == LUA_TSTRING) {
|
||||
size_t length;
|
||||
const char* string = lua_tolstring(L, 1, &length);
|
||||
for (int i = 0; lovrDefaultShader[i].length; i++) {
|
||||
if (lovrDefaultShader[i].length == length && !memcmp(lovrDefaultShader[i].string, string, length)) {
|
||||
info.source[STAGE_VERTEX] = lovrGraphicsGetDefaultShaderSource(i, STAGE_VERTEX);
|
||||
info.source[STAGE_FRAGMENT] = lovrGraphicsGetDefaultShaderSource(i, STAGE_FRAGMENT);
|
||||
allocated[0] = false;
|
||||
allocated[1] = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!info.source[0].code) {
|
||||
info.source[STAGE_COMPUTE] = luax_checkshadersource(L, 1, STAGE_COMPUTE, &allocated[0]);
|
||||
}
|
||||
|
||||
if (lua_gettop(L) == 1 || lua_istable(L, 2)) {
|
||||
info.type = SHADER_COMPUTE;
|
||||
source[0] = luax_checkshadersource(L, 1, STAGE_COMPUTE, &shouldFree[0]);
|
||||
info.stageCount = 1;
|
||||
index = 2;
|
||||
} else {
|
||||
info.source[STAGE_VERTEX] = luax_checkshadersource(L, 1, STAGE_VERTEX, &allocated[0]);
|
||||
info.source[STAGE_FRAGMENT] = luax_checkshadersource(L, 2, STAGE_FRAGMENT, &allocated[1]);
|
||||
info.type = SHADER_GRAPHICS;
|
||||
source[0] = luax_checkshadersource(L, 1, STAGE_VERTEX, &shouldFree[0]);
|
||||
source[1] = luax_checkshadersource(L, 2, STAGE_FRAGMENT, &shouldFree[1]);
|
||||
info.stageCount = source[1].code ? 2 : 1;
|
||||
index = 3;
|
||||
}
|
||||
|
||||
lovrGraphicsCompileShader(source, compiled, info.stageCount, luax_readfile);
|
||||
|
||||
arr_t(ShaderFlag) flags;
|
||||
arr_init(&flags, realloc);
|
||||
|
||||
|
@ -1056,6 +1056,10 @@ static int l_lovrGraphicsNewShader(lua_State* L) {
|
|||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, index, "type");
|
||||
info.type = lua_isnil(L, -1) ? info.type : luax_checkenum(L, -1, ShaderType, NULL);
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, index, "label");
|
||||
info.label = lua_tostring(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
@ -1069,8 +1073,8 @@ static int l_lovrGraphicsNewShader(lua_State* L) {
|
|||
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);
|
||||
if (shouldFree[0] && source[0].code != compiled[0].code) free((void*) source[0].code);
|
||||
if (shouldFree[1] && source[1].code != compiled[1].code) free((void*) source[1].code);
|
||||
arr_free(&flags);
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -33,6 +33,13 @@ static int l_lovrShaderClone(lua_State* L) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int l_lovrShaderGetType(lua_State* L) {
|
||||
Shader* shader = luax_checktype(L, 1, Shader);
|
||||
const ShaderInfo* info = lovrShaderGetInfo(shader);
|
||||
luax_pushenum(L, ShaderType, info->type);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_lovrShaderHasStage(lua_State* L) {
|
||||
Shader* shader = luax_checktype(L, 1, Shader);
|
||||
ShaderStage stage = luax_checkenum(L, 2, ShaderStage, NULL);
|
||||
|
@ -86,18 +93,12 @@ static int l_lovrShaderGetBufferFormat(lua_State* L) {
|
|||
return 2;
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
static int l_lovrShaderGetType(lua_State* L) {
|
||||
lua_pushstring(L, lovrShaderHasStage(luax_checktype(L, 1, Shader), STAGE_COMPUTE) ? "compute" : "graphics");
|
||||
return 1;
|
||||
}
|
||||
|
||||
const luaL_Reg lovrShader[] = {
|
||||
{ "clone", l_lovrShaderClone },
|
||||
{ "getType", l_lovrShaderGetType },
|
||||
{ "hasStage", l_lovrShaderHasStage },
|
||||
{ "hasAttribute", l_lovrShaderHasAttribute },
|
||||
{ "getWorkgroupSize", l_lovrShaderGetWorkgroupSize },
|
||||
{ "getBufferFormat", l_lovrShaderGetBufferFormat },
|
||||
{ "getType", l_lovrShaderGetType }, // Deprecated
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#define MAX_CUSTOM_ATTRIBUTES 10
|
||||
#define LAYOUT_BUILTIN 0
|
||||
#define LAYOUT_MATERIAL 1
|
||||
#define LAYOUT_UNIFORMS 2
|
||||
#define FLOAT_BITS(f) ((union { float f; uint32_t u; }) { f }).u
|
||||
|
||||
typedef struct {
|
||||
|
@ -94,9 +95,9 @@ struct Sampler {
|
|||
};
|
||||
|
||||
enum {
|
||||
VERTEX = (1 << STAGE_VERTEX),
|
||||
FRAGMENT = (1 << STAGE_FRAGMENT),
|
||||
COMPUTE = (1 << STAGE_COMPUTE)
|
||||
FLAG_VERTEX = (1 << 0),
|
||||
FLAG_FRAGMENT = (1 << 1),
|
||||
FLAG_COMPUTE = (1 << 2)
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
|
@ -129,12 +130,12 @@ struct Shader {
|
|||
uint32_t textureMask;
|
||||
uint32_t samplerMask;
|
||||
uint32_t storageMask;
|
||||
uint32_t constantSize;
|
||||
uint32_t constantCount;
|
||||
uint32_t uniformSize;
|
||||
uint32_t uniformCount;
|
||||
uint32_t stageMask;
|
||||
ShaderAttribute* attributes;
|
||||
ShaderResource* resources;
|
||||
DataField* constants;
|
||||
DataField* uniforms;
|
||||
DataField* fields;
|
||||
uint32_t flagCount;
|
||||
uint32_t overrideCount;
|
||||
|
@ -353,7 +354,7 @@ enum {
|
|||
|
||||
enum {
|
||||
DIRTY_BINDINGS = (1 << 0),
|
||||
DIRTY_CONSTANTS = (1 << 1),
|
||||
DIRTY_UNIFORMS = (1 << 1),
|
||||
DIRTY_CAMERA = (1 << 2),
|
||||
NEEDS_VIEW_CULL = (1 << 3)
|
||||
};
|
||||
|
@ -441,7 +442,8 @@ typedef struct {
|
|||
Shader* shader;
|
||||
gpu_bundle_info* bundleInfo;
|
||||
gpu_bundle* bundle;
|
||||
void* constants;
|
||||
gpu_buffer* uniformBuffer;
|
||||
uint32_t uniformOffset;
|
||||
union {
|
||||
struct {
|
||||
uint32_t x;
|
||||
|
@ -471,9 +473,10 @@ typedef struct {
|
|||
gpu_bundle_info* bundleInfo;
|
||||
gpu_pipeline* pipeline;
|
||||
gpu_bundle* bundle;
|
||||
void* constants;
|
||||
gpu_buffer* vertexBuffer;
|
||||
gpu_buffer* indexBuffer;
|
||||
gpu_buffer* uniformBuffer;
|
||||
uint32_t uniformOffset;
|
||||
union {
|
||||
struct {
|
||||
uint32_t start;
|
||||
|
@ -521,9 +524,10 @@ struct Pass {
|
|||
Pipeline* pipeline;
|
||||
uint32_t transformIndex;
|
||||
uint32_t pipelineIndex;
|
||||
char* constants;
|
||||
gpu_binding* bindings;
|
||||
uint32_t bindingMask;
|
||||
void* uniforms;
|
||||
uint32_t uniformSize;
|
||||
uint32_t computeCount;
|
||||
Compute* computes;
|
||||
uint32_t drawCount;
|
||||
|
@ -710,6 +714,13 @@ bool lovrGraphicsInit(GraphicsConfig* config) {
|
|||
size_t materialLayout = getLayout(materialSlots, COUNTOF(materialSlots));
|
||||
if (materialLayout != LAYOUT_MATERIAL) lovrUnreachable();
|
||||
|
||||
gpu_slot uniformSlots[] = {
|
||||
{ 0, GPU_SLOT_UNIFORM_BUFFER_DYNAMIC, GPU_STAGE_GRAPHICS | GPU_STAGE_COMPUTE }
|
||||
};
|
||||
|
||||
size_t uniformLayout = getLayout(uniformSlots, COUNTOF(uniformSlots));
|
||||
if (uniformLayout != LAYOUT_UNIFORMS) lovrUnreachable();
|
||||
|
||||
float data[] = { 0.f, 0.f, 0.f, 0.f, 1.f, 1.f, 1.f, 1.f };
|
||||
|
||||
state.defaultBuffer = lovrBufferCreate(&(BufferInfo) {
|
||||
|
@ -1035,7 +1046,9 @@ static void recordComputePass(Pass* pass, gpu_stream* stream) {
|
|||
|
||||
gpu_pipeline* pipeline = NULL;
|
||||
gpu_bundle_info* bundleInfo = NULL;
|
||||
void* constants = NULL;
|
||||
gpu_bundle* uniformBundle = NULL;
|
||||
gpu_buffer* uniformBuffer = NULL;
|
||||
uint32_t uniformOffset = 0;
|
||||
|
||||
gpu_compute_begin(stream);
|
||||
|
||||
|
@ -1053,9 +1066,19 @@ static void recordComputePass(Pass* pass, gpu_stream* stream) {
|
|||
gpu_bind_bundles(stream, compute->shader->gpu, &bundle, 0, 1, NULL, 0);
|
||||
}
|
||||
|
||||
if (compute->constants && compute->constants != constants) {
|
||||
gpu_push_constants(stream, compute->shader->gpu, compute->constants, compute->shader->constantSize);
|
||||
constants = compute->constants;
|
||||
if (compute->uniformBuffer != uniformBuffer || compute->uniformOffset != uniformOffset) {
|
||||
if (compute->uniformBuffer != uniformBuffer) {
|
||||
uniformBundle = getBundle(LAYOUT_UNIFORMS, &(gpu_binding) {
|
||||
.number = 0,
|
||||
.type = GPU_SLOT_UNIFORM_BUFFER_DYNAMIC,
|
||||
.buffer.object = compute->uniformBuffer,
|
||||
.buffer.extent = compute->shader->uniformSize
|
||||
}, 1);
|
||||
}
|
||||
|
||||
gpu_bind_bundles(stream, compute->shader->gpu, &uniformBundle, 1, 1, &compute->uniformOffset, 1);
|
||||
uniformBuffer = compute->uniformBuffer;
|
||||
uniformOffset = compute->uniformOffset;
|
||||
}
|
||||
|
||||
if (compute->flags & COMPUTE_INDIRECT) {
|
||||
|
@ -1366,13 +1389,14 @@ static void recordRenderPass(Pass* pass, gpu_stream* stream) {
|
|||
Material* material = NULL;
|
||||
gpu_buffer* vertexBuffer = NULL;
|
||||
gpu_buffer* indexBuffer = NULL;
|
||||
void* constants = NULL;
|
||||
gpu_buffer* uniformBuffer = NULL;
|
||||
uint32_t uniformOffset = 0;
|
||||
gpu_bundle* uniformBundle = NULL;
|
||||
|
||||
gpu_bind_vertex_buffers(stream, &state.defaultBuffer->gpu, &state.defaultBuffer->base, 1, 1);
|
||||
|
||||
for (uint32_t i = 0; i < activeDrawCount; i++) {
|
||||
Draw* draw = &pass->draws[activeDraws[i]];
|
||||
bool constantsDirty = draw->constants != constants;
|
||||
|
||||
if (pass->tally.buffer && draw->tally != tally) {
|
||||
if (tally != ~0u) gpu_tally_finish(stream, pass->tally.gpu, tally * canvas->views);
|
||||
|
@ -1385,22 +1409,37 @@ static void recordRenderPass(Pass* pass, gpu_stream* stream) {
|
|||
pipeline = draw->pipeline;
|
||||
}
|
||||
|
||||
if ((i & 0xff) == 0 || draw->camera != cameraIndex || constantsDirty) {
|
||||
if ((i & 0xff) == 0 || draw->camera != cameraIndex) {
|
||||
uint32_t dynamicOffsets[] = { draw->camera * canvas->views * sizeof(Camera), (i >> 8) * 256 * sizeof(DrawData) };
|
||||
gpu_bind_bundles(stream, draw->shader->gpu, &builtinBundle, 0, 1, dynamicOffsets, COUNTOF(dynamicOffsets));
|
||||
cameraIndex = draw->camera;
|
||||
}
|
||||
|
||||
if (draw->material != material || constantsDirty) {
|
||||
if (draw->material != material) {
|
||||
gpu_bind_bundles(stream, draw->shader->gpu, &draw->material->bundle, 1, 1, NULL, 0);
|
||||
material = draw->material;
|
||||
}
|
||||
|
||||
if (draw->bundle && (draw->bundle != bundle || constantsDirty)) {
|
||||
if (draw->bundle && (draw->bundle != bundle)) {
|
||||
gpu_bind_bundles(stream, draw->shader->gpu, &draw->bundle, 2, 1, NULL, 0);
|
||||
bundle = draw->bundle;
|
||||
}
|
||||
|
||||
if (draw->uniformBuffer != uniformBuffer || draw->uniformOffset != uniformOffset) {
|
||||
if (draw->uniformBuffer != uniformBuffer) {
|
||||
uniformBundle = getBundle(LAYOUT_UNIFORMS, &(gpu_binding) {
|
||||
.number = 0,
|
||||
.type = GPU_SLOT_UNIFORM_BUFFER_DYNAMIC,
|
||||
.buffer.object = draw->uniformBuffer,
|
||||
.buffer.extent = draw->shader->uniformSize
|
||||
}, 1);
|
||||
}
|
||||
|
||||
gpu_bind_bundles(stream, draw->shader->gpu, &uniformBundle, 3, 1, &draw->uniformOffset, 1);
|
||||
uniformBuffer = draw->uniformBuffer;
|
||||
uniformOffset = draw->uniformOffset;
|
||||
}
|
||||
|
||||
if (draw->vertexBuffer && draw->vertexBuffer != vertexBuffer) {
|
||||
gpu_bind_vertex_buffers(stream, &draw->vertexBuffer, NULL, 0, 1);
|
||||
vertexBuffer = draw->vertexBuffer;
|
||||
|
@ -1412,11 +1451,6 @@ static void recordRenderPass(Pass* pass, gpu_stream* stream) {
|
|||
indexBuffer = draw->indexBuffer;
|
||||
}
|
||||
|
||||
if (draw->constants && constantsDirty) {
|
||||
gpu_push_constants(stream, draw->shader->gpu, draw->constants, draw->shader->constantSize);
|
||||
constants = draw->constants;
|
||||
}
|
||||
|
||||
if (draw->flags & DRAW_INDIRECT) {
|
||||
if (draw->indexBuffer) {
|
||||
gpu_draw_indirect_indexed(stream, draw->indirect.buffer, draw->indirect.offset, draw->indirect.count, draw->indirect.stride);
|
||||
|
@ -2534,15 +2568,9 @@ static glsl_include_result_t* includer(void* cb, const char* path, const char* i
|
|||
}
|
||||
#endif
|
||||
|
||||
ShaderSource lovrGraphicsCompileShader(ShaderStage stage, ShaderSource* source, ShaderIncluder* io) {
|
||||
uint32_t magic = 0x07230203;
|
||||
|
||||
if (source->size % 4 == 0 && source->size >= 4 && !memcmp(source->code, &magic, 4)) {
|
||||
return *source;
|
||||
}
|
||||
|
||||
void lovrGraphicsCompileShader(ShaderSource* stages, ShaderSource* outputs, uint32_t stageCount, ShaderIncluder* io) {
|
||||
#ifdef LOVR_USE_GLSLANG
|
||||
const glslang_stage_t stages[] = {
|
||||
const glslang_stage_t stageMap[] = {
|
||||
[STAGE_VERTEX] = GLSLANG_STAGE_VERTEX,
|
||||
[STAGE_FRAGMENT] = GLSLANG_STAGE_FRAGMENT,
|
||||
[STAGE_COMPUTE] = GLSLANG_STAGE_COMPUTE
|
||||
|
@ -2560,66 +2588,91 @@ ShaderSource lovrGraphicsCompileShader(ShaderStage stage, ShaderSource* source,
|
|||
"#extension GL_EXT_samplerless_texture_functions : require\n"
|
||||
"#extension GL_GOOGLE_include_directive : require\n";
|
||||
|
||||
const char* strings[] = {
|
||||
prefix,
|
||||
(const char*) etc_shaders_lovr_glsl,
|
||||
"#line 1\n",
|
||||
source->code
|
||||
};
|
||||
glslang_program_t* program = NULL;
|
||||
glslang_shader_t* shaders[2] = { 0 };
|
||||
|
||||
lovrCheck(source->size <= INT_MAX, "Shader is way too big");
|
||||
|
||||
int lengths[] = {
|
||||
-1,
|
||||
etc_shaders_lovr_glsl_len,
|
||||
-1,
|
||||
(int) source->size
|
||||
};
|
||||
|
||||
const glslang_resource_t* resource = glslang_default_resource();
|
||||
|
||||
glslang_input_t input = {
|
||||
.language = GLSLANG_SOURCE_GLSL,
|
||||
.stage = stages[stage],
|
||||
.client = GLSLANG_CLIENT_VULKAN,
|
||||
.client_version = GLSLANG_TARGET_VULKAN_1_1,
|
||||
.target_language = GLSLANG_TARGET_SPV,
|
||||
.target_language_version = GLSLANG_TARGET_SPV_1_3,
|
||||
.strings = strings,
|
||||
.lengths = lengths,
|
||||
.string_count = COUNTOF(strings),
|
||||
.default_version = 460,
|
||||
.default_profile = GLSLANG_NO_PROFILE,
|
||||
.resource = resource,
|
||||
.callbacks.include_local = includer,
|
||||
.callbacks_ctx = (void*) io
|
||||
};
|
||||
|
||||
glslang_shader_t* shader = glslang_shader_create(&input);
|
||||
|
||||
int options = 0;
|
||||
options |= GLSLANG_SHADER_AUTO_MAP_BINDINGS;
|
||||
options |= GLSLANG_SHADER_AUTO_MAP_LOCATIONS;
|
||||
options |= GLSLANG_SHADER_VULKAN_RULES_RELAXED;
|
||||
|
||||
glslang_shader_set_options(shader, options);
|
||||
|
||||
if (!glslang_shader_preprocess(shader, &input)) {
|
||||
lovrThrow("Could not preprocess %s shader:\n%s", stageNames[stage], glslang_shader_get_info_log(shader));
|
||||
return (ShaderSource) { NULL, 0 };
|
||||
if (stageCount > COUNTOF(shaders)) {
|
||||
lovrUnreachable();
|
||||
}
|
||||
|
||||
if (!glslang_shader_parse(shader, &input)) {
|
||||
lovrThrow("Could not parse %s shader:\n%s", stageNames[stage], glslang_shader_get_info_log(shader));
|
||||
return (ShaderSource) { NULL, 0 };
|
||||
for (uint32_t i = 0; i < stageCount; i++) {
|
||||
ShaderSource* source = &stages[i];
|
||||
|
||||
// It's okay to pass precompiled SPIR-V here, and it will be returned unchanged. However, it's
|
||||
// dangerous to mix SPIR-V and GLSL because then glslang won't perform cross-stage linking,
|
||||
// which means that e.g. the default uniform block might be different for each stage. This
|
||||
// isn't a problem when using the default shaders since they don't use uniforms.
|
||||
uint32_t magic = 0x07230203;
|
||||
if (source->size % 4 == 0 && source->size >= 4 && !memcmp(source->code, &magic, 4)) {
|
||||
outputs[i] = stages[i];
|
||||
continue;
|
||||
} else if (!program) {
|
||||
program = glslang_program_create();
|
||||
}
|
||||
|
||||
const char* strings[] = {
|
||||
prefix,
|
||||
(const char*) etc_shaders_lovr_glsl,
|
||||
"#line 1\n",
|
||||
source->code
|
||||
};
|
||||
|
||||
lovrCheck(source->size <= INT_MAX, "Shader is way too big");
|
||||
|
||||
int lengths[] = {
|
||||
-1,
|
||||
etc_shaders_lovr_glsl_len,
|
||||
-1,
|
||||
(int) source->size
|
||||
};
|
||||
|
||||
const glslang_resource_t* resource = glslang_default_resource();
|
||||
|
||||
glslang_input_t input = {
|
||||
.language = GLSLANG_SOURCE_GLSL,
|
||||
.stage = stageMap[source->stage],
|
||||
.client = GLSLANG_CLIENT_VULKAN,
|
||||
.client_version = GLSLANG_TARGET_VULKAN_1_1,
|
||||
.target_language = GLSLANG_TARGET_SPV,
|
||||
.target_language_version = GLSLANG_TARGET_SPV_1_3,
|
||||
.strings = strings,
|
||||
.lengths = lengths,
|
||||
.string_count = COUNTOF(strings),
|
||||
.default_version = 460,
|
||||
.default_profile = GLSLANG_NO_PROFILE,
|
||||
.forward_compatible = true,
|
||||
.resource = resource,
|
||||
.callbacks.include_local = includer,
|
||||
.callbacks_ctx = (void*) io
|
||||
};
|
||||
|
||||
shaders[i] = glslang_shader_create(&input);
|
||||
|
||||
int options = 0;
|
||||
options |= GLSLANG_SHADER_AUTO_MAP_BINDINGS;
|
||||
options |= GLSLANG_SHADER_AUTO_MAP_LOCATIONS;
|
||||
options |= GLSLANG_SHADER_VULKAN_RULES_RELAXED;
|
||||
|
||||
glslang_shader_set_options(shaders[i], options);
|
||||
|
||||
if (!glslang_shader_preprocess(shaders[i], &input)) {
|
||||
lovrThrow("Could not preprocess %s shader:\n%s", stageNames[source->stage], glslang_shader_get_info_log(shaders[i]));
|
||||
}
|
||||
|
||||
if (!glslang_shader_parse(shaders[i], &input)) {
|
||||
lovrThrow("Could not parse %s shader:\n%s", stageNames[source->stage], glslang_shader_get_info_log(shaders[i]));
|
||||
}
|
||||
|
||||
glslang_program_add_shader(program, shaders[i]);
|
||||
}
|
||||
|
||||
glslang_program_t* program = glslang_program_create();
|
||||
glslang_program_add_shader(program, shader);
|
||||
// We might not need to do anything if all the inputs were already SPIR-V
|
||||
if (!program) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!glslang_program_link(program, 0)) {
|
||||
lovrThrow("Could not link shader:\n%s", glslang_program_get_info_log(program));
|
||||
return (ShaderSource) { NULL, 0 };
|
||||
}
|
||||
|
||||
glslang_program_map_io(program);
|
||||
|
@ -2630,25 +2683,36 @@ ShaderSource lovrGraphicsCompileShader(ShaderStage stage, ShaderSource* source,
|
|||
spvOptions.generate_debug_info = true;
|
||||
spvOptions.emit_nonsemantic_shader_debug_info = true;
|
||||
spvOptions.emit_nonsemantic_shader_debug_source = true;
|
||||
glslang_program_add_source_text(program, stages[stage], source->code, source->size);
|
||||
}
|
||||
|
||||
glslang_program_SPIRV_generate_with_options(program, stages[stage], &spvOptions);
|
||||
for (uint32_t i = 0; i < stageCount; i++) {
|
||||
if (!shaders[i]) continue;
|
||||
|
||||
void* words = glslang_program_SPIRV_get_ptr(program);
|
||||
size_t size = glslang_program_SPIRV_get_size(program) * 4;
|
||||
ShaderSource* source = &stages[i];
|
||||
|
||||
void* data = malloc(size);
|
||||
lovrAssert(data, "Out of memory");
|
||||
memcpy(data, words, size);
|
||||
if (state.config.debug && state.features.shaderDebug) {
|
||||
glslang_program_add_source_text(program, stageMap[source->stage], source->code, source->size);
|
||||
}
|
||||
|
||||
glslang_program_SPIRV_generate_with_options(program, stageMap[source->stage], &spvOptions);
|
||||
|
||||
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);
|
||||
|
||||
outputs[i].stage = source->stage;
|
||||
outputs[i].code = data;
|
||||
outputs[i].size = size;
|
||||
|
||||
glslang_shader_delete(shaders[i]);
|
||||
}
|
||||
|
||||
glslang_program_delete(program);
|
||||
glslang_shader_delete(shader);
|
||||
|
||||
return (ShaderSource) { data, size };
|
||||
#else
|
||||
lovrThrow("Could not compile shader: No shader compiler available");
|
||||
return (ShaderSource) { NULL, 0 };
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -2679,7 +2743,7 @@ static void lovrShaderInit(Shader* shader) {
|
|||
}
|
||||
}
|
||||
|
||||
if (shader->stageMask & COMPUTE) {
|
||||
if (shader->info.type == SHADER_COMPUTE) {
|
||||
gpu_compute_pipeline_info pipelineInfo = {
|
||||
.shader = shader->gpu,
|
||||
.flags = shader->flags,
|
||||
|
@ -2694,47 +2758,47 @@ static void lovrShaderInit(Shader* shader) {
|
|||
}
|
||||
|
||||
ShaderSource lovrGraphicsGetDefaultShaderSource(DefaultShader type, ShaderStage stage) {
|
||||
const ShaderSource sources[][STAGE_COUNT] = {
|
||||
const ShaderSource sources[][3] = {
|
||||
[SHADER_UNLIT] = {
|
||||
[STAGE_VERTEX] = { lovr_shader_unlit_vert, sizeof(lovr_shader_unlit_vert) },
|
||||
[STAGE_FRAGMENT] = { lovr_shader_unlit_frag, sizeof(lovr_shader_unlit_frag) }
|
||||
[STAGE_VERTEX] = { STAGE_VERTEX, lovr_shader_unlit_vert, sizeof(lovr_shader_unlit_vert) },
|
||||
[STAGE_FRAGMENT] = { STAGE_FRAGMENT, lovr_shader_unlit_frag, sizeof(lovr_shader_unlit_frag) }
|
||||
},
|
||||
[SHADER_NORMAL] = {
|
||||
[STAGE_VERTEX] = { lovr_shader_unlit_vert, sizeof(lovr_shader_unlit_vert) },
|
||||
[STAGE_FRAGMENT] = { lovr_shader_normal_frag, sizeof(lovr_shader_normal_frag) }
|
||||
[STAGE_VERTEX] = { STAGE_VERTEX, lovr_shader_unlit_vert, sizeof(lovr_shader_unlit_vert) },
|
||||
[STAGE_FRAGMENT] = { STAGE_FRAGMENT, lovr_shader_normal_frag, sizeof(lovr_shader_normal_frag) }
|
||||
},
|
||||
[SHADER_FONT] = {
|
||||
[STAGE_VERTEX] = { lovr_shader_unlit_vert, sizeof(lovr_shader_unlit_vert) },
|
||||
[STAGE_FRAGMENT] = { lovr_shader_font_frag, sizeof(lovr_shader_font_frag) }
|
||||
[STAGE_VERTEX] = { STAGE_VERTEX, lovr_shader_unlit_vert, sizeof(lovr_shader_unlit_vert) },
|
||||
[STAGE_FRAGMENT] = { STAGE_FRAGMENT, lovr_shader_font_frag, sizeof(lovr_shader_font_frag) }
|
||||
},
|
||||
[SHADER_CUBEMAP] = {
|
||||
[STAGE_VERTEX] = { lovr_shader_cubemap_vert, sizeof(lovr_shader_cubemap_vert) },
|
||||
[STAGE_FRAGMENT] = { lovr_shader_cubemap_frag, sizeof(lovr_shader_cubemap_frag) }
|
||||
[STAGE_VERTEX] = { STAGE_VERTEX, lovr_shader_cubemap_vert, sizeof(lovr_shader_cubemap_vert) },
|
||||
[STAGE_FRAGMENT] = { STAGE_FRAGMENT, lovr_shader_cubemap_frag, sizeof(lovr_shader_cubemap_frag) }
|
||||
},
|
||||
[SHADER_EQUIRECT] = {
|
||||
[STAGE_VERTEX] = { lovr_shader_cubemap_vert, sizeof(lovr_shader_cubemap_vert) },
|
||||
[STAGE_FRAGMENT] = { lovr_shader_equirect_frag, sizeof(lovr_shader_equirect_frag) }
|
||||
[STAGE_VERTEX] = { STAGE_VERTEX, lovr_shader_cubemap_vert, sizeof(lovr_shader_cubemap_vert) },
|
||||
[STAGE_FRAGMENT] = { STAGE_FRAGMENT, lovr_shader_equirect_frag, sizeof(lovr_shader_equirect_frag) }
|
||||
},
|
||||
[SHADER_FILL_2D] = {
|
||||
[STAGE_VERTEX] = { lovr_shader_fill_vert, sizeof(lovr_shader_fill_vert) },
|
||||
[STAGE_FRAGMENT] = { lovr_shader_unlit_frag, sizeof(lovr_shader_unlit_frag) }
|
||||
[STAGE_VERTEX] = { STAGE_VERTEX, lovr_shader_fill_vert, sizeof(lovr_shader_fill_vert) },
|
||||
[STAGE_FRAGMENT] = { STAGE_FRAGMENT, lovr_shader_unlit_frag, sizeof(lovr_shader_unlit_frag) }
|
||||
},
|
||||
[SHADER_FILL_ARRAY] = {
|
||||
[STAGE_VERTEX] = { lovr_shader_fill_vert, sizeof(lovr_shader_fill_vert) },
|
||||
[STAGE_FRAGMENT] = { lovr_shader_fill_array_frag, sizeof(lovr_shader_fill_array_frag) }
|
||||
[STAGE_VERTEX] = { STAGE_VERTEX, lovr_shader_fill_vert, sizeof(lovr_shader_fill_vert) },
|
||||
[STAGE_FRAGMENT] = { STAGE_FRAGMENT, lovr_shader_fill_array_frag, sizeof(lovr_shader_fill_array_frag) }
|
||||
},
|
||||
[SHADER_LOGO] = {
|
||||
[STAGE_VERTEX] = { lovr_shader_unlit_vert, sizeof(lovr_shader_unlit_vert) },
|
||||
[STAGE_FRAGMENT] = { lovr_shader_logo_frag, sizeof(lovr_shader_logo_frag) }
|
||||
[STAGE_VERTEX] = { STAGE_VERTEX, lovr_shader_unlit_vert, sizeof(lovr_shader_unlit_vert) },
|
||||
[STAGE_FRAGMENT] = { STAGE_FRAGMENT, lovr_shader_logo_frag, sizeof(lovr_shader_logo_frag) }
|
||||
},
|
||||
[SHADER_ANIMATOR] = {
|
||||
[STAGE_COMPUTE] = { lovr_shader_animator_comp, sizeof(lovr_shader_animator_comp) }
|
||||
[STAGE_COMPUTE] = { STAGE_COMPUTE, lovr_shader_animator_comp, sizeof(lovr_shader_animator_comp) }
|
||||
},
|
||||
[SHADER_BLENDER] = {
|
||||
[STAGE_COMPUTE] = { lovr_shader_blender_comp, sizeof(lovr_shader_blender_comp) }
|
||||
[STAGE_COMPUTE] = { STAGE_COMPUTE, lovr_shader_blender_comp, sizeof(lovr_shader_blender_comp) }
|
||||
},
|
||||
[SHADER_TALLY_MERGE] = {
|
||||
[STAGE_COMPUTE] = { lovr_shader_tallymerge_comp, sizeof(lovr_shader_tallymerge_comp) }
|
||||
[STAGE_COMPUTE] = { STAGE_COMPUTE, lovr_shader_tallymerge_comp, sizeof(lovr_shader_tallymerge_comp) }
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -2751,14 +2815,22 @@ Shader* lovrGraphicsGetDefaultShader(DefaultShader type) {
|
|||
case SHADER_BLENDER:
|
||||
case SHADER_TALLY_MERGE:
|
||||
return state.defaultShaders[type] = lovrShaderCreate(&(ShaderInfo) {
|
||||
.source[STAGE_COMPUTE] = lovrGraphicsGetDefaultShaderSource(type, STAGE_COMPUTE),
|
||||
.type = SHADER_COMPUTE,
|
||||
.stages = (ShaderSource[1]) {
|
||||
lovrGraphicsGetDefaultShaderSource(type, STAGE_COMPUTE)
|
||||
},
|
||||
.stageCount = 1,
|
||||
.flags = &(ShaderFlag) { NULL, 0, state.device.subgroupSize },
|
||||
.flagCount = 1
|
||||
});
|
||||
default:
|
||||
return state.defaultShaders[type] = lovrShaderCreate(&(ShaderInfo) {
|
||||
.source[STAGE_VERTEX] = lovrGraphicsGetDefaultShaderSource(type, STAGE_VERTEX),
|
||||
.source[STAGE_FRAGMENT] = lovrGraphicsGetDefaultShaderSource(type, STAGE_FRAGMENT)
|
||||
.type = SHADER_GRAPHICS,
|
||||
.stages = (ShaderSource[2]) {
|
||||
lovrGraphicsGetDefaultShaderSource(type, STAGE_VERTEX),
|
||||
lovrGraphicsGetDefaultShaderSource(type, STAGE_FRAGMENT)
|
||||
},
|
||||
.stageCount = 2
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -2767,24 +2839,25 @@ Shader* lovrShaderCreate(const ShaderInfo* info) {
|
|||
Shader* shader = calloc(1, sizeof(Shader) + gpu_sizeof_shader());
|
||||
lovrAssert(shader, "Out of memory");
|
||||
|
||||
for (uint32_t i = 0; i < COUNTOF(info->source); i++) {
|
||||
if (info->source[i].code) {
|
||||
shader->stageMask |= (1 << i);
|
||||
}
|
||||
size_t stack = tempPush(&state.allocator);
|
||||
|
||||
for (uint32_t i = 0; i < info->stageCount; i++) {
|
||||
shader->stageMask |= (1 << info->stages[i].stage);
|
||||
}
|
||||
|
||||
ShaderStage stages[2];
|
||||
uint32_t stageCount = 0;
|
||||
if (info->type == SHADER_GRAPHICS) {
|
||||
uint32_t mask = FLAG_VERTEX | FLAG_FRAGMENT;
|
||||
lovrCheck(shader->stageMask & FLAG_VERTEX, "Graphics shaders must have a vertex stage");
|
||||
lovrCheck((shader->stageMask & mask) == mask, "Graphics shaders can only have vertex and pixel stages");
|
||||
} else if (info->type == SHADER_COMPUTE) {
|
||||
lovrCheck(shader->stageMask == FLAG_COMPUTE, "Compute shaders can only have a compute stage");
|
||||
}
|
||||
|
||||
if (shader->stageMask == VERTEX) {
|
||||
stages[stageCount++] = STAGE_VERTEX;
|
||||
} else if (shader->stageMask == (VERTEX | FRAGMENT)) {
|
||||
stages[stageCount++] = STAGE_VERTEX;
|
||||
stages[stageCount++] = STAGE_FRAGMENT;
|
||||
} else if (shader->stageMask == COMPUTE) {
|
||||
stages[stageCount++] = STAGE_COMPUTE;
|
||||
} else {
|
||||
lovrThrow("Invalid combination of shader stages given. Expected vertex, vertex + pixel, or compute");
|
||||
// Copy the source, because we perform edits on the SPIR-V and the input might be readonly memory
|
||||
void* source[2];
|
||||
for (uint32_t i = 0; i < info->stageCount; i++) {
|
||||
source[i] = tempAlloc(&state.allocator, info->stages[i].size);
|
||||
memcpy(source[i], info->stages[i].code, info->stages[i].size);
|
||||
}
|
||||
|
||||
// Parse SPIR-V
|
||||
|
@ -2794,8 +2867,8 @@ Shader* lovrShaderCreate(const ShaderInfo* info) {
|
|||
uint32_t maxSpecConstants = 0;
|
||||
uint32_t maxFields = 0;
|
||||
uint32_t maxChars = 0;
|
||||
for (uint32_t i = 0; i < stageCount; i++) {
|
||||
result = spv_parse(info->source[stages[i]].code, info->source[stages[i]].size, &spv[i]);
|
||||
for (uint32_t i = 0; i < info->stageCount; i++) {
|
||||
result = spv_parse(source[i], info->stages[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)");
|
||||
|
||||
|
@ -2806,7 +2879,7 @@ Shader* lovrShaderCreate(const ShaderInfo* info) {
|
|||
spv[i].fields = tempAlloc(&state.allocator, spv[i].fieldCount * sizeof(spv_field));
|
||||
memset(spv[i].fields, 0, spv[i].fieldCount * sizeof(spv_field));
|
||||
|
||||
result = spv_parse(info->source[stages[i]].code, info->source[stages[i]].size, &spv[i]);
|
||||
result = spv_parse(source[i], info->stages[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);
|
||||
|
@ -2832,7 +2905,7 @@ Shader* lovrShaderCreate(const ShaderInfo* info) {
|
|||
lovrAssert(shader->flags && shader->flagLookup, "Out of memory");
|
||||
|
||||
// Stage-specific metadata
|
||||
if (shader->stageMask & COMPUTE) {
|
||||
if (info->type == SHADER_COMPUTE) {
|
||||
memcpy(shader->workgroupSize, spv[0].workgroupSize, 3 * sizeof(uint32_t));
|
||||
lovrCheck(shader->workgroupSize[0] <= state.limits.workgroupSize[0], "Shader workgroup size exceeds the 'workgroupSize' limit");
|
||||
lovrCheck(shader->workgroupSize[1] <= state.limits.workgroupSize[1], "Shader workgroup size exceeds the 'workgroupSize' limit");
|
||||
|
@ -2850,12 +2923,13 @@ Shader* lovrShaderCreate(const ShaderInfo* info) {
|
|||
}
|
||||
}
|
||||
|
||||
uint32_t userSet = shader->stageMask & COMPUTE ? 0 : 2;
|
||||
uint32_t resourceSet = info->type == SHADER_COMPUTE ? 0 : 2;
|
||||
uint32_t uniformSet = info->type == SHADER_COMPUTE ? 1 : 3;
|
||||
uint32_t lastResourceCount = 0;
|
||||
|
||||
// Resources
|
||||
for (uint32_t s = 0; s < stageCount; s++, lastResourceCount = shader->resourceCount) {
|
||||
ShaderStage stage = stages[s];
|
||||
for (uint32_t s = 0; s < info->stageCount; s++, lastResourceCount = shader->resourceCount) {
|
||||
ShaderStage stage = info->stages[s].stage;
|
||||
for (uint32_t i = 0; i < spv[s].resourceCount; i++) {
|
||||
spv_resource* resource = &spv[s].resources[i];
|
||||
uint32_t* set = (uint32_t*) resource->set;
|
||||
|
@ -2865,7 +2939,7 @@ Shader* lovrShaderCreate(const ShaderInfo* info) {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!(*set == userSet || (*set == 0 && *binding > LAST_BUILTIN_BINDING))) {
|
||||
if (!(*set == resourceSet || (*set == 0 && *binding > LAST_BUILTIN_BINDING))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -2905,7 +2979,7 @@ Shader* lovrShaderCreate(const ShaderInfo* info) {
|
|||
lovrCheck(other->type == resourceTypes[resource->type], "Shader variable '%s' is declared in multiple shader stages with different types", resource->name);
|
||||
slots[j].stages |= stageMap[stage];
|
||||
shader->resources[j].phase |= stagePhase[stage];
|
||||
*set = userSet;
|
||||
*set = resourceSet;
|
||||
*binding = shader->resources[j].binding;
|
||||
skip = true;
|
||||
break;
|
||||
|
@ -2916,12 +2990,24 @@ Shader* lovrShaderCreate(const ShaderInfo* info) {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(resource->name, "gl_DefaultUniformBlock") || !strcmp(resource->name, "DefaultUniformBlock")) {
|
||||
spv_field* block = resource->bufferFields;
|
||||
shader->uniformSize = block->elementSize;
|
||||
shader->uniformCount = block->fieldCount;
|
||||
shader->uniforms = shader->fields + ((s == 1 ? spv[0].fieldCount : 0) + (block->fields - spv[s].fields));
|
||||
*set = uniformSet;
|
||||
*binding = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t index = shader->resourceCount++;
|
||||
|
||||
lovrAssert(index < MAX_SHADER_RESOURCES, "Shader resource count exceeds resourcesPerShader limit (%d)", MAX_SHADER_RESOURCES);
|
||||
|
||||
*set = userSet;
|
||||
*binding = index;
|
||||
if (*set != resourceSet) {
|
||||
*set = resourceSet;
|
||||
*binding = index;
|
||||
}
|
||||
|
||||
slots[index] = (gpu_slot) {
|
||||
.number = index,
|
||||
|
@ -2978,7 +3064,7 @@ Shader* lovrShaderCreate(const ShaderInfo* info) {
|
|||
|
||||
// Fields
|
||||
char* name = shader->names;
|
||||
for (uint32_t s = 0; s < stageCount; s++) {
|
||||
for (uint32_t s = 0; s < info->stageCount; s++) {
|
||||
for (uint32_t i = 0; i < spv[s].fieldCount; i++) {
|
||||
static const DataType dataTypes[] = {
|
||||
[SPV_B32] = TYPE_U32,
|
||||
|
@ -3032,24 +3118,8 @@ Shader* lovrShaderCreate(const ShaderInfo* info) {
|
|||
}
|
||||
}
|
||||
|
||||
// Push constant fields (use the biggest struct that actually exists, if any)
|
||||
spv_field* c1 = spv[0].pushConstants;
|
||||
spv_field* c2 = spv[1].pushConstants;
|
||||
|
||||
if (c1 && (!c2 || c1->elementSize > c2->elementSize)) {
|
||||
shader->constants = shader->fields + (c1->fields - spv[0].fields);
|
||||
shader->constantCount = c1->fieldCount;
|
||||
shader->constantSize = c1->elementSize;
|
||||
} else if (c2) {
|
||||
shader->constants = shader->fields + spv[0].fieldCount + (c2->fields - spv[1].fields);
|
||||
shader->constantCount = c2->fieldCount;
|
||||
shader->constantSize = c2->elementSize;
|
||||
}
|
||||
|
||||
lovrCheck(shader->constantSize <= state.limits.pushConstantSize, "Shader push constants block is too big");
|
||||
|
||||
// Specialization constants
|
||||
for (uint32_t s = 0; s < stageCount; s++) {
|
||||
for (uint32_t s = 0; s < info->stageCount; s++) {
|
||||
for (uint32_t i = 0; i < spv[s].specConstantCount; i++) {
|
||||
spv_spec_constant* constant = &spv[s].specConstants[i];
|
||||
|
||||
|
@ -3096,28 +3166,44 @@ Shader* lovrShaderCreate(const ShaderInfo* info) {
|
|||
}
|
||||
}
|
||||
|
||||
// Push constants
|
||||
uint32_t pushConstantSize = 0;
|
||||
for (uint32_t i = 0; i < info->stageCount; i++) {
|
||||
if (spv[i].pushConstants) {
|
||||
pushConstantSize = MAX(pushConstantSize, spv[i].pushConstants->elementSize);
|
||||
}
|
||||
}
|
||||
|
||||
shader->ref = 1;
|
||||
shader->gpu = (gpu_shader*) (shader + 1);
|
||||
shader->info = *info;
|
||||
shader->layout = getLayout(slots, shader->resourceCount);
|
||||
|
||||
gpu_shader_info gpu = {
|
||||
.vertex = { info->source[STAGE_VERTEX].code, info->source[STAGE_VERTEX].size },
|
||||
.fragment = { info->source[STAGE_FRAGMENT].code, info->source[STAGE_FRAGMENT].size },
|
||||
.compute = { info->source[STAGE_COMPUTE].code, info->source[STAGE_COMPUTE].size },
|
||||
.pushConstantSize = shader->constantSize,
|
||||
.pushConstantSize = pushConstantSize,
|
||||
.label = info->label
|
||||
};
|
||||
|
||||
if (shader->stageMask & (VERTEX | FRAGMENT)) {
|
||||
for (uint32_t i = 0; i < info->stageCount; i++) {
|
||||
switch (info->stages[i].stage) {
|
||||
case STAGE_VERTEX: gpu.vertex = (gpu_shader_stage) { .code = source[i], .length = info->stages[i].size }; break;
|
||||
case STAGE_FRAGMENT: gpu.fragment = (gpu_shader_stage) { .code = source[i], .length = info->stages[i].size }; break;
|
||||
case STAGE_COMPUTE: gpu.compute = (gpu_shader_stage) { .code = source[i], .length = info->stages[i].size }; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
if (info->type == SHADER_GRAPHICS) {
|
||||
gpu.layouts[0] = state.layouts.data[LAYOUT_BUILTIN].gpu;
|
||||
gpu.layouts[1] = state.layouts.data[LAYOUT_MATERIAL].gpu;
|
||||
}
|
||||
|
||||
gpu.layouts[userSet] = shader->resourceCount > 0 ? state.layouts.data[shader->layout].gpu : NULL;
|
||||
gpu.layouts[resourceSet] = state.layouts.data[shader->layout].gpu;
|
||||
if (shader->uniformSize > 0) gpu.layouts[uniformSet] = state.layouts.data[LAYOUT_UNIFORMS].gpu;
|
||||
|
||||
gpu_shader_init(shader->gpu, &gpu);
|
||||
lovrShaderInit(shader);
|
||||
tempPop(&state.allocator, stack);
|
||||
return shader;
|
||||
}
|
||||
|
||||
|
@ -3137,13 +3223,13 @@ Shader* lovrShaderClone(Shader* parent, ShaderFlag* flags, uint32_t count) {
|
|||
shader->textureMask = parent->textureMask;
|
||||
shader->samplerMask = parent->samplerMask;
|
||||
shader->storageMask = parent->storageMask;
|
||||
shader->constantSize = parent->constantSize;
|
||||
shader->constantCount = parent->constantCount;
|
||||
shader->uniformSize = parent->uniformSize;
|
||||
shader->uniformCount = parent->uniformCount;
|
||||
shader->resourceCount = parent->resourceCount;
|
||||
shader->flagCount = parent->flagCount;
|
||||
shader->attributes = parent->attributes;
|
||||
shader->resources = parent->resources;
|
||||
shader->constants = parent->constants;
|
||||
shader->uniforms = parent->uniforms;
|
||||
shader->fields = parent->fields;
|
||||
shader->names = parent->names;
|
||||
shader->flags = malloc(shader->flagCount * sizeof(gpu_shader_flag));
|
||||
|
@ -5142,11 +5228,12 @@ void lovrPassReset(Pass* pass) {
|
|||
pass->allocator.cursor = 0;
|
||||
pass->access[ACCESS_RENDER] = NULL;
|
||||
pass->access[ACCESS_COMPUTE] = NULL;
|
||||
pass->flags = DIRTY_BINDINGS | DIRTY_CONSTANTS;
|
||||
pass->flags = DIRTY_BINDINGS;
|
||||
pass->transform = lovrPassAllocate(pass, TRANSFORM_STACK_SIZE * 16 * sizeof(float));
|
||||
pass->pipeline = lovrPassAllocate(pass, PIPELINE_STACK_SIZE * sizeof(Pipeline));
|
||||
pass->constants = lovrPassAllocate(pass, state.limits.pushConstantSize);
|
||||
pass->bindings = lovrPassAllocate(pass, 32 * sizeof(gpu_binding));
|
||||
pass->uniforms = NULL;
|
||||
pass->uniformSize = 0;
|
||||
pass->computeCount = 0;
|
||||
pass->computes = NULL;
|
||||
pass->drawCount = 0;
|
||||
|
@ -5573,8 +5660,8 @@ void lovrPassSetShader(Pass* pass, Shader* shader) {
|
|||
Shader* previous = pass->pipeline->shader;
|
||||
if (shader == previous) return;
|
||||
|
||||
bool fromCompute = previous && (previous->stageMask & COMPUTE);
|
||||
bool toCompute = shader && (shader->stageMask & COMPUTE);
|
||||
bool fromCompute = previous && previous->info.type == SHADER_COMPUTE;
|
||||
bool toCompute = shader && shader->info.type == SHADER_COMPUTE;
|
||||
|
||||
if (fromCompute ^ toCompute) {
|
||||
pass->bindingMask = 0;
|
||||
|
@ -5645,8 +5732,12 @@ void lovrPassSetShader(Pass* pass, Shader* shader) {
|
|||
pass->pipeline->lastVertexBuffer = NULL;
|
||||
}
|
||||
|
||||
if (shader && shader->constantSize > 0 && (!previous || previous->constantSize != shader->constantSize)) {
|
||||
pass->flags |= DIRTY_CONSTANTS;
|
||||
if (shader && shader->uniformSize > pass->uniformSize) {
|
||||
void* uniforms = lovrPassAllocate(pass, shader->uniformSize);
|
||||
if (pass->uniforms) memcpy(uniforms, pass->uniforms, pass->uniformSize);
|
||||
pass->uniformSize = shader->uniformSize;
|
||||
pass->uniforms = uniforms;
|
||||
pass->flags |= DIRTY_UNIFORMS;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5779,11 +5870,11 @@ void lovrPassSendData(Pass* pass, const char* name, size_t length, void** data,
|
|||
lovrCheck(shader, "A Shader must be active to send data to it");
|
||||
|
||||
uint32_t hash = (uint32_t) hash64(name, length);
|
||||
for (uint32_t i = 0; i < shader->constantCount; i++) {
|
||||
if (shader->constants[i].hash == hash) {
|
||||
*data = (char*) pass->constants + shader->constants[i].offset;
|
||||
*format = &shader->constants[i];
|
||||
pass->flags |= DIRTY_CONSTANTS;
|
||||
for (uint32_t i = 0; i < shader->uniformCount; i++) {
|
||||
if (shader->uniforms[i].hash == hash) {
|
||||
*data = (char*) pass->uniforms + shader->uniforms[i].offset;
|
||||
*format = &shader->uniforms[i];
|
||||
pass->flags |= DIRTY_UNIFORMS;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -5892,7 +5983,7 @@ static void lovrPassResolvePipeline(Pass* pass, DrawInfo* info, Draw* draw, Draw
|
|||
}
|
||||
}
|
||||
|
||||
static void lovrPassResolveBuffers(Pass* pass, DrawInfo* info, Draw* draw) {
|
||||
static void lovrPassResolveVertices(Pass* pass, DrawInfo* info, Draw* draw) {
|
||||
CachedShape* cached = info->hash ? &pass->geocache[info->hash & (COUNTOF(pass->geocache) - 1)] : NULL;
|
||||
|
||||
if (cached && cached->hash == info->hash) {
|
||||
|
@ -5977,19 +6068,11 @@ static gpu_bundle_info* lovrPassResolveBindings(Pass* pass, Shader* shader, gpu_
|
|||
return bundle;
|
||||
}
|
||||
|
||||
static void* lovrPassResolveConstants(Pass* pass, Shader* shader, void* previous) {
|
||||
if (shader->constantSize == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (~pass->flags & DIRTY_CONSTANTS) {
|
||||
return previous;
|
||||
}
|
||||
|
||||
void* constants = lovrPassAllocate(pass, shader->constantSize);
|
||||
memcpy(constants, pass->constants, shader->constantSize);
|
||||
pass->flags &= ~DIRTY_CONSTANTS;
|
||||
return constants;
|
||||
static void lovrPassResolveUniforms(Pass* pass, Shader* shader, gpu_buffer** buffer, uint32_t* offset) {
|
||||
BufferView view = lovrPassGetBuffer(pass, shader->uniformSize, state.limits.uniformBufferAlign);
|
||||
memcpy(view.pointer, pass->uniforms, shader->uniformSize);
|
||||
*buffer = view.buffer;
|
||||
*offset = view.offset;
|
||||
}
|
||||
|
||||
void lovrPassDraw(Pass* pass, DrawInfo* info) {
|
||||
|
@ -6001,7 +6084,7 @@ void lovrPassDraw(Pass* pass, DrawInfo* info) {
|
|||
pass->draws = draws;
|
||||
}
|
||||
|
||||
Draw* prev = pass->drawCount > 0 ? &pass->draws[pass->drawCount - 1] : NULL;
|
||||
Draw* previous = pass->drawCount > 0 ? &pass->draws[pass->drawCount - 1] : NULL;
|
||||
Draw* draw = &pass->draws[pass->drawCount++];
|
||||
|
||||
draw->flags = 0;
|
||||
|
@ -6010,7 +6093,7 @@ void lovrPassDraw(Pass* pass, DrawInfo* info) {
|
|||
pass->flags &= ~DIRTY_CAMERA;
|
||||
|
||||
draw->shader = pass->pipeline->shader ? pass->pipeline->shader : lovrGraphicsGetDefaultShader(info->shader);
|
||||
lovrCheck(draw->shader->stageMask & (VERTEX | FRAGMENT), "Tried to draw while a compute shader is active");
|
||||
lovrCheck(draw->shader->info.type == SHADER_GRAPHICS, "Tried to draw while a compute shader is active");
|
||||
lovrRetain(draw->shader);
|
||||
|
||||
draw->material = info->material;
|
||||
|
@ -6023,11 +6106,17 @@ void lovrPassDraw(Pass* pass, DrawInfo* info) {
|
|||
draw->instances = MAX(info->instances, 1);
|
||||
draw->baseVertex = info->baseVertex;
|
||||
|
||||
lovrPassResolvePipeline(pass, info, draw, prev);
|
||||
lovrPassResolveBuffers(pass, info, draw);
|
||||
lovrPassResolvePipeline(pass, info, draw, previous);
|
||||
lovrPassResolveVertices(pass, info, draw);
|
||||
draw->bundleInfo = lovrPassResolveBindings(pass, draw->shader, previous ? previous->bundleInfo : NULL);
|
||||
|
||||
draw->bundleInfo = lovrPassResolveBindings(pass, draw->shader, prev ? prev->bundleInfo : NULL);
|
||||
draw->constants = lovrPassResolveConstants(pass, draw->shader, prev ? prev->constants : NULL);
|
||||
if (pass->flags & DIRTY_UNIFORMS) {
|
||||
lovrPassResolveUniforms(pass, draw->shader, &draw->uniformBuffer, &draw->uniformOffset);
|
||||
pass->flags &= ~DIRTY_UNIFORMS;
|
||||
} else {
|
||||
draw->uniformBuffer = previous ? previous->uniformBuffer : NULL;
|
||||
draw->uniformOffset = previous ? previous->uniformOffset : 0;
|
||||
}
|
||||
|
||||
if (pass->pipeline->viewCull && info->bounds) {
|
||||
memcpy(draw->bounds, info->bounds, sizeof(draw->bounds));
|
||||
|
@ -7081,7 +7170,7 @@ void lovrPassMeshIndirect(Pass* pass, Buffer* vertices, Buffer* indices, Buffer*
|
|||
pass->draws = draws;
|
||||
}
|
||||
|
||||
Draw* prev = pass->drawCount > 0 ? &pass->draws[pass->drawCount - 1] : NULL;
|
||||
Draw* previous = pass->drawCount > 0 ? &pass->draws[pass->drawCount - 1] : NULL;
|
||||
Draw* draw = &pass->draws[pass->drawCount++];
|
||||
|
||||
draw->flags = DRAW_INDIRECT;
|
||||
|
@ -7101,11 +7190,17 @@ void lovrPassMeshIndirect(Pass* pass, Buffer* vertices, Buffer* indices, Buffer*
|
|||
draw->indirect.count = count;
|
||||
draw->indirect.stride = stride;
|
||||
|
||||
lovrPassResolvePipeline(pass, &info, draw, prev);
|
||||
lovrPassResolveBuffers(pass, &info, draw);
|
||||
lovrPassResolvePipeline(pass, &info, draw, previous);
|
||||
lovrPassResolveVertices(pass, &info, draw);
|
||||
draw->bundleInfo = lovrPassResolveBindings(pass, shader, previous ? previous->bundleInfo : NULL);
|
||||
|
||||
draw->bundleInfo = lovrPassResolveBindings(pass, shader, prev ? prev->bundleInfo : NULL);
|
||||
draw->constants = lovrPassResolveConstants(pass, shader, prev ? prev->constants : NULL);
|
||||
if (pass->flags & DIRTY_UNIFORMS) {
|
||||
lovrPassResolveUniforms(pass, shader, &draw->uniformBuffer, &draw->uniformOffset);
|
||||
pass->flags &= ~DIRTY_UNIFORMS;
|
||||
} else {
|
||||
draw->uniformBuffer = previous ? previous->uniformBuffer : NULL;
|
||||
draw->uniformOffset = previous ? previous->uniformOffset : 0;
|
||||
}
|
||||
|
||||
mat4_init(draw->transform, pass->transform);
|
||||
memcpy(draw->color, pass->pipeline->color, 4 * sizeof(float));
|
||||
|
@ -7150,7 +7245,7 @@ void lovrPassCompute(Pass* pass, uint32_t x, uint32_t y, uint32_t z, Buffer* ind
|
|||
Compute* compute = &pass->computes[pass->computeCount++];
|
||||
Shader* shader = pass->pipeline->shader;
|
||||
|
||||
lovrCheck(shader->stageMask == COMPUTE, "To run a compute shader, a compute shader must be active");
|
||||
lovrCheck(shader->info.type == SHADER_COMPUTE, "To run a compute shader, a compute shader must be active");
|
||||
lovrCheck(x <= state.limits.workgroupCount[0], "Compute %s count exceeds workgroupCount limit", "x");
|
||||
lovrCheck(y <= state.limits.workgroupCount[1], "Compute %s count exceeds workgroupCount limit", "y");
|
||||
lovrCheck(z <= state.limits.workgroupCount[2], "Compute %s count exceeds workgroupCount limit", "z");
|
||||
|
@ -7160,7 +7255,14 @@ void lovrPassCompute(Pass* pass, uint32_t x, uint32_t y, uint32_t z, Buffer* ind
|
|||
lovrRetain(shader);
|
||||
|
||||
compute->bundleInfo = lovrPassResolveBindings(pass, shader, previous ? previous->bundleInfo : NULL);
|
||||
compute->constants = lovrPassResolveConstants(pass, shader, previous ? previous->constants : NULL);
|
||||
|
||||
if (pass->flags & DIRTY_UNIFORMS) {
|
||||
lovrPassResolveUniforms(pass, shader, &compute->uniformBuffer, &compute->uniformOffset);
|
||||
pass->flags &= ~DIRTY_UNIFORMS;
|
||||
} else {
|
||||
compute->uniformBuffer = previous ? previous->uniformBuffer : NULL;
|
||||
compute->uniformOffset = previous ? previous->uniformOffset : 0;
|
||||
}
|
||||
|
||||
if (indirect) {
|
||||
compute->flags |= COMPUTE_INDIRECT;
|
||||
|
|
|
@ -294,14 +294,19 @@ typedef enum {
|
|||
DEFAULT_SHADER_COUNT
|
||||
} DefaultShader;
|
||||
|
||||
typedef enum {
|
||||
SHADER_GRAPHICS,
|
||||
SHADER_COMPUTE
|
||||
} ShaderType;
|
||||
|
||||
typedef enum {
|
||||
STAGE_VERTEX,
|
||||
STAGE_FRAGMENT,
|
||||
STAGE_COMPUTE,
|
||||
STAGE_COUNT
|
||||
STAGE_COMPUTE
|
||||
} ShaderStage;
|
||||
|
||||
typedef struct {
|
||||
ShaderStage stage;
|
||||
const void* code;
|
||||
size_t size;
|
||||
} ShaderSource;
|
||||
|
@ -313,15 +318,17 @@ typedef struct {
|
|||
} ShaderFlag;
|
||||
|
||||
typedef struct {
|
||||
ShaderSource source[STAGE_COUNT];
|
||||
uint32_t flagCount;
|
||||
ShaderType type;
|
||||
ShaderSource* stages;
|
||||
uint32_t stageCount;
|
||||
ShaderFlag* flags;
|
||||
uint32_t flagCount;
|
||||
const char* label;
|
||||
} ShaderInfo;
|
||||
|
||||
typedef void* ShaderIncluder(const char* filename, size_t* bytesRead);
|
||||
|
||||
ShaderSource lovrGraphicsCompileShader(ShaderStage stage, ShaderSource* source, ShaderIncluder* includer);
|
||||
void lovrGraphicsCompileShader(ShaderSource* stages, ShaderSource* outputs, uint32_t count, ShaderIncluder* includer);
|
||||
ShaderSource lovrGraphicsGetDefaultShaderSource(DefaultShader type, ShaderStage stage);
|
||||
Shader* lovrGraphicsGetDefaultShader(DefaultShader type);
|
||||
Shader* lovrShaderCreate(const ShaderInfo* info);
|
||||
|
|
Loading…
Reference in New Issue