Merge pull request #749 from bjornbytes/shader-rework

Shader Rework
This commit is contained in:
Bjorn 2024-02-26 15:35:39 -08:00 committed by GitHub
commit 73eae189ae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 673 additions and 514 deletions

View File

@ -261,7 +261,6 @@ if config.modules.graphics and config.glslang then
glslang_cflags += '-fno-rtti'
glslang_cflags += '-Ideps/glslang'
glslang_lflags += '-shared'
glslang_src += 'deps/glslang/OGLCompilersDLL/*.cpp'
glslang_src += 'deps/glslang/glslang/CInterface/*.cpp'
glslang_src += 'deps/glslang/glslang/MachineIndependent/*.cpp'
glslang_src += 'deps/glslang/glslang/MachineIndependent/preprocessor/*.cpp'
@ -468,11 +467,12 @@ vert = 'etc/shaders/*.vert'
frag = 'etc/shaders/*.frag'
comp = 'etc/shaders/*.comp'
glslang_flags += '--quiet'
glslang_flags += config.debug and '-gVS' or ''
glslang_flags += '--target-env vulkan1.1'
function compileShaders(stage)
pattern = 'etc/shaders/*.' .. stage
glslang_flags += '--quiet'
glslang_flags += config.debug and '-gVS' or ''
glslang_flags += '--target-env vulkan1.1'
tup.foreach_rule(pattern, 'glslangValidator $(glslang_flags) --vn lovr_shader_%B_' .. stage .. ' -o %o %f', '%f.h')
end

2
deps/glslang vendored

@ -1 +1 @@
Subproject commit e7d1279bc02891585c96479987c0f3a11cc042b9
Subproject commit 545226e46f85f24d3d447a812d3156787ca879d7

View File

@ -2,7 +2,7 @@
#extension GL_EXT_multiview : require
#extension GL_GOOGLE_include_directive : require
#include "lovr.glsl"
#include "shaders/lovr.glsl"
vec4 lovrmain() {
float y = UV.y;

BIN
etc/nogame/logo.spv Normal file

Binary file not shown.

View File

@ -9,7 +9,7 @@ function lovr.load()
lovr.graphics.setBackgroundColor(0x20232c)
end
logo = lovr.graphics.newShader('unlit', 'logo')
logo = lovr.graphics.newShader('unlit', 'logo.spv')
end
function lovr.draw(pass)

View File

@ -10,7 +10,6 @@
#include "shaders/animator.comp.h"
#include "shaders/blender.comp.h"
#include "shaders/tallymerge.comp.h"
#include "shaders/logo.frag.h"
#include "shaders/lovr.glsl.h"
@ -19,3 +18,5 @@
#define LOCATION_UV 12
#define LOCATION_COLOR 13
#define LOCATION_TANGENT 14
#define LAST_BUILTIN_BINDING 3

View File

@ -63,6 +63,10 @@ layout(set = 1, binding = 4) uniform texture2D RoughnessTexture;
layout(set = 1, binding = 5) uniform texture2D ClearcoatTexture;
layout(set = 1, binding = 6) uniform texture2D OcclusionTexture;
layout(set = 1, binding = 7) uniform texture2D NormalTexture;
layout(push_constant) uniform PushConstants {
uint DrawID;
};
#endif
// Attributes
@ -116,7 +120,7 @@ layout(location = 14) in vec4 Tangent;
#define BaseInstance gl_BaseInstance
#define BaseVertex gl_BaseVertex
#define DrawIndex gl_DrawIndex
#define InstanceIndex (gl_InstanceIndex - gl_BaseInstance)
#define InstanceIndex gl_InstanceIndex
#define PointSize gl_PointSize
#define Position gl_Position
#define VertexIndex gl_VertexIndex
@ -140,12 +144,7 @@ layout(location = 14) in vec4 Tangent;
// Helpers
#define Constants layout(push_constant) uniform PushConstants
#ifdef GL_COMPUTE_SHADER
#define var(x) layout(set = 0, binding = x)
#else
#define var(x) layout(set = 2, binding = x)
#endif
#define Constants uniform DefaultUniformBlock
#ifndef GL_COMPUTE_SHADER
#define Projection Cameras[ViewIndex].projection
@ -156,7 +155,6 @@ layout(location = 14) in vec4 Tangent;
#endif
#ifdef GL_VERTEX_SHADER
#define DrawID gl_BaseInstance
#define Transform mat4(Draws[DrawID].transform)
#define NormalMatrix (cofactor3(Draws[DrawID].transform))
#define PassColor Draws[DrawID].color

View File

@ -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[];

View File

@ -58,7 +58,6 @@ StringEntry lovrDefaultShader[] = {
[SHADER_EQUIRECT] = ENTRY("equirect"),
[SHADER_FILL_2D] = ENTRY("fill"),
[SHADER_FILL_ARRAY] = ENTRY("fillarray"),
[SHADER_LOGO] = ENTRY("logo"),
{ 0 }
};
@ -146,6 +145,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"),
@ -978,94 +983,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 = 2;
index = 3;
}
lovrGraphicsCompileShader(source, compiled, info.stageCount, luax_readfile);
arr_t(ShaderFlag) flags;
arr_init(&flags, realloc);
@ -1088,6 +1087,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);
@ -1101,8 +1104,12 @@ 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);
for (uint32_t i = 0; i < info.stageCount; i++) {
if (shouldFree[i]) free((void*) source[i].code);
if (source[i].code != compiled[i].code) free((void*) compiled[i].code);
}
arr_free(&flags);
return 1;
}

View File

@ -643,15 +643,9 @@ static int l_lovrPassSetWireframe(lua_State* L) {
static int l_lovrPassSend(lua_State* L) {
Pass* pass = luax_checktype(L, 1, Pass);
const char* name = NULL;
size_t length = 0;
uint32_t slot = ~0u;
switch (lua_type(L, 2)) {
case LUA_TSTRING: name = lua_tolstring(L, 2, &length); break;
case LUA_TNUMBER: slot = lua_tointeger(L, 2) - 1; break;
default: return luax_typeerror(L, 2, "string or number");
}
size_t length;
const char* name = lua_tolstring(L, 2, &length);
if (lua_isnoneornil(L, 3)) {
return luax_typeerror(L, 3, "Buffer, Texture, Sampler, number, vector, table, or boolean");
@ -662,27 +656,27 @@ static int l_lovrPassSend(lua_State* L) {
if (buffer) {
uint32_t offset = lua_tointeger(L, 4);
uint32_t extent = lua_tointeger(L, 5);
lovrPassSendBuffer(pass, name, length, slot, buffer, offset, extent);
lovrPassSendBuffer(pass, name, length, buffer, offset, extent);
return 0;
}
Texture* texture = luax_totype(L, 3, Texture);
if (texture) {
lovrPassSendTexture(pass, name, length, slot, texture);
lovrPassSendTexture(pass, name, length, texture);
return 0;
}
Sampler* sampler = luax_totype(L, 3, Sampler);
if (sampler) {
lovrPassSendSampler(pass, name, length, slot, sampler);
lovrPassSendSampler(pass, name, length, sampler);
return 0;
}
void* pointer;
DataField* format;
lovrPassSendData(pass, name, length, slot, &pointer, &format);
lovrPassSendData(pass, name, length, &pointer, &format);
char* data = pointer;
// Coerce booleans since they aren't supported in buffer formats

View File

@ -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 }
};

View File

@ -246,16 +246,16 @@ void gpu_layout_destroy(gpu_layout* layout);
// Shader
typedef struct {
uint32_t stage;
const void* code;
size_t length;
} gpu_shader_stage;
} gpu_shader_source;
typedef struct {
gpu_shader_stage vertex;
gpu_shader_stage fragment;
gpu_shader_stage compute;
gpu_layout* layouts[4];
uint32_t stageCount;
gpu_shader_source* stages;
uint32_t pushConstantSize;
gpu_layout* layouts[4];
const char* label;
} gpu_shader_info;

View File

@ -991,31 +991,28 @@ void gpu_layout_destroy(gpu_layout* layout) {
// Shader
bool gpu_shader_init(gpu_shader* shader, gpu_shader_info* info) {
struct { VkShaderStageFlags flags; gpu_shader_stage* source; } stages[] = {
{ VK_SHADER_STAGE_VERTEX_BIT, &info->vertex },
{ VK_SHADER_STAGE_FRAGMENT_BIT, &info->fragment },
{ VK_SHADER_STAGE_COMPUTE_BIT, &info->compute }
};
uint32_t stageCount = 0;
VkShaderStageFlags stageFlags = 0;
for (uint32_t i = 0; i < COUNTOF(stages); i++) {
if (!stages[i].source->code) continue;
for (uint32_t i = 0; i < info->stageCount; i++) {
switch (info->stages[i].stage) {
case GPU_STAGE_VERTEX: stageFlags |= VK_SHADER_STAGE_VERTEX_BIT; break;
case GPU_STAGE_FRAGMENT: stageFlags |= VK_SHADER_STAGE_FRAGMENT_BIT; break;
case GPU_STAGE_COMPUTE: stageFlags |= VK_SHADER_STAGE_COMPUTE_BIT; break;
default: return false;
}
}
for (uint32_t i = 0; i < info->stageCount; i++) {
VkShaderModuleCreateInfo moduleInfo = {
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
.codeSize = stages[i].source->length,
.pCode = stages[i].source->code
.codeSize = info->stages[i].length,
.pCode = info->stages[i].code
};
VK(vkCreateShaderModule(state.device, &moduleInfo, NULL, &shader->handles[stageCount]), "Failed to load shader") {
VK(vkCreateShaderModule(state.device, &moduleInfo, NULL, &shader->handles[i]), "Failed to load shader") {
return false;
}
nickname(shader->handles[i], VK_OBJECT_TYPE_SHADER_MODULE, info->label);
stageFlags |= stages[i].flags;
stageCount++;
}
VkDescriptorSetLayout layouts[4];
@ -1594,7 +1591,7 @@ bool gpu_pipeline_init_graphics(gpu_pipeline* pipeline, gpu_pipeline_info* info)
.pData = (const void*) constants
};
uint32_t stageCount = info->shader->handles[1] && info->pass->colorCount > 0 ? 2 : 1;
uint32_t stageCount = info->shader->handles[1] ? 2 : 1;
VkPipelineShaderStageCreateInfo shaders[2] = {
{

View File

@ -20,8 +20,7 @@ typedef union {
uint16_t name;
} attribute;
struct {
uint8_t set;
uint8_t binding;
uint16_t decoration;
uint16_t name;
} variable;
struct {
@ -220,8 +219,12 @@ static spv_result spv_parse_decoration(spv_context* spv, const uint32_t* op, spv
case 1: spv->cache[id].flag.number = op[3]; break; // SpecID
case 6: spv->cache[id].type.arrayStride = op[3]; break; // ArrayStride (overrides name)
case 30: spv->cache[id].attribute.location = op[3]; break; // Location
case 33: spv->cache[id].variable.binding = op[3]; break; // Binding
case 34: spv->cache[id].variable.set = op[3]; break; // Set
case 33:
case 34:
if (spv->cache[id].variable.decoration == 0xffff) {
spv->cache[id].variable.decoration = op - spv->words;
}
break;
default: break;
}
@ -352,11 +355,8 @@ static spv_result spv_parse_variable(spv_context* spv, const uint32_t* op, spv_i
return spv_parse_field(spv, type, info->pushConstants, info);
}
uint32_t set = spv->cache[variableId].variable.set;
uint32_t binding = spv->cache[variableId].variable.binding;
// Ignore output variables (storageClass 3) and anything without a set/binding decoration
if (storageClass == 3 || set == 0xff || binding == 0xff) {
if (storageClass == 3 || spv->cache[variableId].variable.decoration == 0xffff) {
return SPV_OK;
}
@ -374,8 +374,38 @@ static spv_result spv_parse_variable(spv_context* spv, const uint32_t* op, spv_i
spv_resource* resource = &info->resources[info->resourceCount++];
resource->set = set;
resource->binding = binding;
// Resolve the set/binding pointers. The cache stores the index of the first set/binding
// decoration word, we need to search for the "other" one.
const uint32_t* word = spv->words + spv->cache[variableId].variable.decoration;
bool set = word[2] == 34;
uint32_t other = set ? 33 : 34;
if (set) {
resource->set = &word[3];
resource->binding = NULL;
} else {
resource->set = NULL;
resource->binding = &word[3];
}
for (;;) {
const uint32_t* next = word + OP_LENGTH(word);
if (word == next || next > spv->edge || (OP_CODE(next) != 71 && OP_CODE(next) != 72)) {
break;
}
word = next;
if (OP_CODE(word) == 71 && word[1] == variableId && word[2] == other) {
if (set) {
resource->binding = &word[3];
} else {
resource->set = &word[3];
}
break;
}
}
// If it's an array, read the array size and unwrap the inner type
if (OP_CODE(type) == 28) { // OpTypeArray

View File

@ -79,10 +79,10 @@ enum {
};
typedef struct {
uint32_t set;
uint32_t binding;
uint32_t arraySize;
const uint32_t* set;
const uint32_t* binding;
const char* name;
uint32_t arraySize;
spv_resource_type type;
spv_texture_dimension dimension;
uint32_t textureFlags;

File diff suppressed because it is too large Load Diff

View File

@ -285,21 +285,25 @@ typedef enum {
SHADER_EQUIRECT,
SHADER_FILL_2D,
SHADER_FILL_ARRAY,
SHADER_LOGO,
SHADER_ANIMATOR,
SHADER_BLENDER,
SHADER_TALLY_MERGE,
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;
@ -311,15 +315,18 @@ 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;
bool isDefault;
} 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);
@ -606,10 +613,10 @@ void lovrPassSetViewCull(Pass* pass, bool enable);
void lovrPassSetWinding(Pass* pass, Winding winding);
void lovrPassSetWireframe(Pass* pass, bool wireframe);
void lovrPassSendBuffer(Pass* pass, const char* name, size_t length, uint32_t slot, Buffer* buffer, uint32_t offset, uint32_t extent);
void lovrPassSendTexture(Pass* pass, const char* name, size_t length, uint32_t slot, Texture* texture);
void lovrPassSendSampler(Pass* pass, const char* name, size_t length, uint32_t slot, Sampler* sampler);
void lovrPassSendData(Pass* pass, const char* name, size_t length, uint32_t slot, void** data, DataField** format);
void lovrPassSendBuffer(Pass* pass, const char* name, size_t length, Buffer* buffer, uint32_t offset, uint32_t extent);
void lovrPassSendTexture(Pass* pass, const char* name, size_t length, Texture* texture);
void lovrPassSendSampler(Pass* pass, const char* name, size_t length, Sampler* sampler);
void lovrPassSendData(Pass* pass, const char* name, size_t length, void** data, DataField** format);
void lovrPassPoints(Pass* pass, uint32_t count, float** vertices);
void lovrPassLine(Pass* pass, uint32_t count, float** vertices);