Compare commits

...

23 Commits

Author SHA1 Message Date
Bjorn 73eae189ae
Merge pull request #749 from bjornbytes/shader-rework
Shader Rework
2024-02-26 15:35:39 -08:00
bjorn 792fd75204 Texture view improvements;
- You can nest texture views (make a view of a view).
- You can do transfers on views (copies, clears, blits, etc.).
- You can give labels to views.
- API changes
  - Rename Texture:newView to lovr.graphics.newTextureView.
  - Constructor takes a table of options (order was too confusing).
  - Remove Texture:isView/getParent, since views can be used for everything now.
  - Better matches what LÖVE ended up doing.
- Fix bug where :getType on a view returned parent type.
2024-02-26 15:24:09 -08:00
bjorn ec380e0cfd rm pass uniformSize;
It's no longer necessary.
2024-02-25 14:58:11 -08:00
bjorn fa8ea6732b rm pass binding mask;
It's no longer necessary.
2024-02-25 14:57:27 -08:00
bjorn 466a052ded Cleanup; 2024-02-24 15:45:30 -08:00
bjorn 2fe5ba8f3b Ensure all binding fields are fully initialized; 2024-02-24 15:45:10 -08:00
bjorn ae19b7aad3 Fix resource binding numbers; 2024-02-24 15:33:09 -08:00
bjorn 652a074677 Update resource invalidation when switching shaders;
Previously, when switching shaders, resource bindings would be preserved
for resources with matching slots/types.  This doesn't make sense in a
world where binding numbers are internal.  Instead, match up resources
by name/type.

Additionally, rewire all the uniforms by name/size so uniforms with the
same name get preserved (name/type would be too hard for e.g. structs).
This seems like it would be horribly slow and may need to be optimized,
optional, or removed.

I didn't test any of this lol, but I will I promise.
2024-02-24 14:34:29 -08:00
bjorn bd83ad6eb4 Cleanup; 2024-02-24 11:49:11 -08:00
bjorn b63ec6e137 Graphics shader requires fragment stage for now;
The vertex-only shader requires too much juggling of shader stage flags
and pipeline layout compatibility when e.g. binding push constants and
descriptor sets.
2024-02-21 10:37:59 -08:00
bjorn 6a669bd967 rm var shader macro;
It is no longer necessary, and wasn't widely used/documented.
2024-02-21 10:28:30 -08:00
bjorn ada33c6620 Downgrade glslang to 13.0.0 with lovr cherrypicks; 2024-02-21 10:25:00 -08:00
bjorn cbc9312716 Fix memory leak; 2024-02-21 10:21:42 -08:00
bjorn 728b166d69 Fix binding numbers; 2024-02-21 10:21:42 -08:00
bjorn 64e253a8ef mv logo shader to nogame bundle; 2024-02-21 10:21:42 -08:00
bjorn 81ef58d032 Use push constant instead of BaseInstance for DrawID;
This is a little slower, but means indirect draws can use Transform and Color.

There are other solutions for this.  For example LÖVR could reserve
BaseInstance and use a compute shader to rewrite indirect buffers.

For now we choose to be correct and a little slower.
2024-02-21 10:21:42 -08:00
bjorn 875a7f8237 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.
2024-02-21 10:21:41 -08:00
bjorn 8b78a4d3b5 core/gpu: depth-only passes can include fragment shader; 2024-02-20 10:19:16 -08:00
bjorn d4e2736e0b Update glslang;
Includes a fix for generating SPIRV for multiple shader stages.

Also reverts a buggy upstream commit temporarily.
2024-02-20 10:17:20 -08:00
bjorn 45eba2fe85 rm ability to send resources by slot number; 2024-02-20 10:16:41 -08:00
bjorn 93348499e9 Binding numbers are assigned explicitly and merged across stages; 2024-02-20 10:16:41 -08:00
bjorn 035d359133 Add relaxed Vulkan flag; 2024-02-20 10:16:41 -08:00
bjorn f7c1d4cccb core/spv: return pointer to set/binding decorations instead of value; 2024-02-20 10:16:41 -08:00
18 changed files with 812 additions and 638 deletions

View File

@ -261,7 +261,6 @@ if config.modules.graphics and config.glslang then
glslang_cflags += '-fno-rtti' glslang_cflags += '-fno-rtti'
glslang_cflags += '-Ideps/glslang' glslang_cflags += '-Ideps/glslang'
glslang_lflags += '-shared' glslang_lflags += '-shared'
glslang_src += 'deps/glslang/OGLCompilersDLL/*.cpp'
glslang_src += 'deps/glslang/glslang/CInterface/*.cpp' glslang_src += 'deps/glslang/glslang/CInterface/*.cpp'
glslang_src += 'deps/glslang/glslang/MachineIndependent/*.cpp' glslang_src += 'deps/glslang/glslang/MachineIndependent/*.cpp'
glslang_src += 'deps/glslang/glslang/MachineIndependent/preprocessor/*.cpp' glslang_src += 'deps/glslang/glslang/MachineIndependent/preprocessor/*.cpp'
@ -468,11 +467,12 @@ vert = 'etc/shaders/*.vert'
frag = 'etc/shaders/*.frag' frag = 'etc/shaders/*.frag'
comp = 'etc/shaders/*.comp' comp = 'etc/shaders/*.comp'
glslang_flags += '--quiet'
glslang_flags += config.debug and '-gVS' or ''
glslang_flags += '--target-env vulkan1.1'
function compileShaders(stage) function compileShaders(stage)
pattern = 'etc/shaders/*.' .. 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') tup.foreach_rule(pattern, 'glslangValidator $(glslang_flags) --vn lovr_shader_%B_' .. stage .. ' -o %o %f', '%f.h')
end 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_EXT_multiview : require
#extension GL_GOOGLE_include_directive : require #extension GL_GOOGLE_include_directive : require
#include "lovr.glsl" #include "shaders/lovr.glsl"
vec4 lovrmain() { vec4 lovrmain() {
float y = UV.y; 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) lovr.graphics.setBackgroundColor(0x20232c)
end end
logo = lovr.graphics.newShader('unlit', 'logo') logo = lovr.graphics.newShader('unlit', 'logo.spv')
end end
function lovr.draw(pass) function lovr.draw(pass)

View File

@ -10,7 +10,6 @@
#include "shaders/animator.comp.h" #include "shaders/animator.comp.h"
#include "shaders/blender.comp.h" #include "shaders/blender.comp.h"
#include "shaders/tallymerge.comp.h" #include "shaders/tallymerge.comp.h"
#include "shaders/logo.frag.h"
#include "shaders/lovr.glsl.h" #include "shaders/lovr.glsl.h"
@ -19,3 +18,5 @@
#define LOCATION_UV 12 #define LOCATION_UV 12
#define LOCATION_COLOR 13 #define LOCATION_COLOR 13
#define LOCATION_TANGENT 14 #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 = 5) uniform texture2D ClearcoatTexture;
layout(set = 1, binding = 6) uniform texture2D OcclusionTexture; layout(set = 1, binding = 6) uniform texture2D OcclusionTexture;
layout(set = 1, binding = 7) uniform texture2D NormalTexture; layout(set = 1, binding = 7) uniform texture2D NormalTexture;
layout(push_constant) uniform PushConstants {
uint DrawID;
};
#endif #endif
// Attributes // Attributes
@ -116,7 +120,7 @@ layout(location = 14) in vec4 Tangent;
#define BaseInstance gl_BaseInstance #define BaseInstance gl_BaseInstance
#define BaseVertex gl_BaseVertex #define BaseVertex gl_BaseVertex
#define DrawIndex gl_DrawIndex #define DrawIndex gl_DrawIndex
#define InstanceIndex (gl_InstanceIndex - gl_BaseInstance) #define InstanceIndex gl_InstanceIndex
#define PointSize gl_PointSize #define PointSize gl_PointSize
#define Position gl_Position #define Position gl_Position
#define VertexIndex gl_VertexIndex #define VertexIndex gl_VertexIndex
@ -140,12 +144,7 @@ layout(location = 14) in vec4 Tangent;
// Helpers // 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
#define var(x) layout(set = 2, binding = x)
#endif
#ifndef GL_COMPUTE_SHADER #ifndef GL_COMPUTE_SHADER
#define Projection Cameras[ViewIndex].projection #define Projection Cameras[ViewIndex].projection
@ -156,7 +155,6 @@ layout(location = 14) in vec4 Tangent;
#endif #endif
#ifdef GL_VERTEX_SHADER #ifdef GL_VERTEX_SHADER
#define DrawID gl_BaseInstance
#define Transform mat4(Draws[DrawID].transform) #define Transform mat4(Draws[DrawID].transform)
#define NormalMatrix (cofactor3(Draws[DrawID].transform)) #define NormalMatrix (cofactor3(Draws[DrawID].transform))
#define PassColor Draws[DrawID].color #define PassColor Draws[DrawID].color

View File

@ -51,6 +51,7 @@ extern StringEntry lovrPassType[];
extern StringEntry lovrPermission[]; extern StringEntry lovrPermission[];
extern StringEntry lovrSampleFormat[]; extern StringEntry lovrSampleFormat[];
extern StringEntry lovrShaderStage[]; extern StringEntry lovrShaderStage[];
extern StringEntry lovrShaderType[];
extern StringEntry lovrShapeType[]; extern StringEntry lovrShapeType[];
extern StringEntry lovrSmoothMode[]; extern StringEntry lovrSmoothMode[];
extern StringEntry lovrStackType[]; extern StringEntry lovrStackType[];

View File

@ -58,7 +58,6 @@ StringEntry lovrDefaultShader[] = {
[SHADER_EQUIRECT] = ENTRY("equirect"), [SHADER_EQUIRECT] = ENTRY("equirect"),
[SHADER_FILL_2D] = ENTRY("fill"), [SHADER_FILL_2D] = ENTRY("fill"),
[SHADER_FILL_ARRAY] = ENTRY("fillarray"), [SHADER_FILL_ARRAY] = ENTRY("fillarray"),
[SHADER_LOGO] = ENTRY("logo"),
{ 0 } { 0 }
}; };
@ -146,6 +145,12 @@ StringEntry lovrShaderStage[] = {
{ 0 } { 0 }
}; };
StringEntry lovrShaderType[] = {
[SHADER_GRAPHICS] = ENTRY("graphics"),
[SHADER_COMPUTE] = ENTRY("compute"),
{ 0 }
};
StringEntry lovrStackType[] = { StringEntry lovrStackType[] = {
[STACK_TRANSFORM] = ENTRY("transform"), [STACK_TRANSFORM] = ENTRY("transform"),
[STACK_STATE] = ENTRY("state"), [STACK_STATE] = ENTRY("state"),
@ -873,6 +878,43 @@ static int l_lovrGraphicsNewTexture(lua_State* L) {
return 1; return 1;
} }
static int l_lovrGraphicsNewTextureView(lua_State* L) {
Texture* texture = luax_checktype(L, 1, Texture);
const TextureInfo* base = lovrTextureGetInfo(texture);
luaL_checktype(L, 2, LUA_TTABLE);
TextureViewInfo info = { 0 };
lua_getfield(L, 2, "type");
info.type = lua_isnil(L, -1) ? base->type : luax_checkenum(L, -1, TextureType, NULL);
lua_pop(L, 1);
lua_getfield(L, 2, "layer");
info.layerIndex = lua_isnil(L, -1) ? 0 : luax_checku32(L, -1) - 1;
lua_pop(L, 1);
lua_getfield(L, 2, "layercount");
info.layerCount = lua_isnil(L, -1) ? ~0u : luax_checku32(L, -1);
lua_pop(L, 1);
lua_getfield(L, 2, "mipmap");
info.levelIndex = lua_isnil(L, -1) ? 0 : luax_checku32(L, -1) - 1;
lua_pop(L, 1);
lua_getfield(L, 2, "mipmapcount");
info.levelCount = lua_isnil(L, -1) ? ~0u : luax_checku32(L, -1);
lua_pop(L, 1);
lua_getfield(L, 2, "label");
info.label = lua_tostring(L, -1);
lua_pop(L, 1);
Texture* view = lovrTextureCreateView(texture, &info);
luax_pushtype(L, Texture, view);
lovrRelease(view, lovrTextureDestroy);
return 1;
}
static int l_lovrGraphicsNewSampler(lua_State* L) { static int l_lovrGraphicsNewSampler(lua_State* L) {
SamplerInfo info = { SamplerInfo info = {
.min = FILTER_LINEAR, .min = FILTER_LINEAR,
@ -941,94 +983,88 @@ static int l_lovrGraphicsNewSampler(lua_State* L) {
return 1; return 1;
} }
static ShaderSource luax_checkshadersource(lua_State* L, int index, ShaderStage stage, bool* allocated) { static ShaderSource luax_checkshadersource(lua_State* L, int index, ShaderStage stage, bool* shouldFree) {
ShaderSource source; *shouldFree = false;
if (lua_isstring(L, index)) { if (lua_isstring(L, index)) {
size_t length; size_t length;
const char* string = lua_tolstring(L, index, &length); const char* string = lua_tolstring(L, index, &length);
if (memchr(string, '\n', MIN(256, length))) { if (memchr(string, '\n', MIN(256, length))) {
source.code = string; return (ShaderSource) { stage, string, length };
source.size = length;
*allocated = false;
} else { } else {
for (int i = 0; lovrDefaultShader[i].length; i++) { for (int i = 0; lovrDefaultShader[i].length; i++) {
if (lovrDefaultShader[i].length == length && !memcmp(lovrDefaultShader[i].string, string, length)) { if (lovrDefaultShader[i].length == length && !memcmp(lovrDefaultShader[i].string, string, length)) {
*allocated = false;
return lovrGraphicsGetDefaultShaderSource(i, stage); return lovrGraphicsGetDefaultShaderSource(i, stage);
} }
} }
source.code = luax_readfile(string, &source.size); size_t size;
void* code = luax_readfile(string, &size);
if (source.code) { if (code) {
*allocated = true; *shouldFree = true;
return (ShaderSource) { stage, code, size };
} else { } else {
luaL_argerror(L, index, "single-line string was not filename or DefaultShader"); luaL_argerror(L, index, "single-line string was not filename or DefaultShader");
} }
} }
} else if (lua_isuserdata(L, index)) { } else if (lua_isuserdata(L, index)) {
Blob* blob = luax_checktype(L, index, Blob); Blob* blob = luax_checktype(L, index, Blob);
source.code = blob->data; return (ShaderSource) { stage, blob->data, blob->size };
source.size = blob->size;
*allocated = false;
} else { } else {
*allocated = false; luax_typeerror(L, index, "string, Blob, or DefaultShader");
return lovrGraphicsGetDefaultShaderSource(SHADER_UNLIT, stage);
} }
return (ShaderSource) { 0 };
ShaderSource bytecode = lovrGraphicsCompileShader(stage, &source, luax_readfile);
if (bytecode.code != source.code) {
if (*allocated) free((void*) source.code);
*allocated = true;
return bytecode;
}
return source;
} }
static int l_lovrGraphicsCompileShader(lua_State* L) { static int l_lovrGraphicsCompileShader(lua_State* L) {
ShaderStage stage = luax_checkenum(L, 1, ShaderStage, NULL); ShaderSource inputs[2], outputs[2];
bool allocated; bool shouldFree[2];
ShaderSource spirv = luax_checkshadersource(L, 2, stage, &allocated); uint32_t count;
Blob* blob = lovrBlobCreate((void*) spirv.code, spirv.size, "Compiled Shader Code");
luax_pushtype(L, Blob, blob); if (lua_gettop(L) == 1) {
lovrRelease(blob, lovrBlobDestroy); inputs[0] = luax_checkshadersource(L, 1, STAGE_COMPUTE, &shouldFree[0]);
return 1; 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) { static int l_lovrGraphicsNewShader(lua_State* L) {
ShaderInfo info = { 0 }; ShaderSource source[2], compiled[2];
bool allocated[2]; ShaderInfo info = { .stages = compiled };
bool shouldFree[2] = { 0 };
int index; 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)) {
if (lua_gettop(L) == 1 || (lua_istable(L, 2) && luax_len(L, 2) == 0)) { info.type = SHADER_COMPUTE;
if (lua_type(L, 1) == LUA_TSTRING) { source[0] = luax_checkshadersource(L, 1, STAGE_COMPUTE, &shouldFree[0]);
size_t length; info.stageCount = 1;
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]);
}
index = 2; index = 2;
} else { } else {
info.source[STAGE_VERTEX] = luax_checkshadersource(L, 1, STAGE_VERTEX, &allocated[0]); info.type = SHADER_GRAPHICS;
info.source[STAGE_FRAGMENT] = luax_checkshadersource(L, 2, STAGE_FRAGMENT, &allocated[1]); 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; index = 3;
} }
lovrGraphicsCompileShader(source, compiled, info.stageCount, luax_readfile);
arr_t(ShaderFlag) flags; arr_t(ShaderFlag) flags;
arr_init(&flags, realloc); arr_init(&flags, realloc);
@ -1051,6 +1087,10 @@ static int l_lovrGraphicsNewShader(lua_State* L) {
} }
lua_pop(L, 1); 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"); lua_getfield(L, index, "label");
info.label = lua_tostring(L, -1); info.label = lua_tostring(L, -1);
lua_pop(L, 1); lua_pop(L, 1);
@ -1064,8 +1104,12 @@ static int l_lovrGraphicsNewShader(lua_State* L) {
Shader* shader = lovrShaderCreate(&info); Shader* shader = lovrShaderCreate(&info);
luax_pushtype(L, Shader, shader); luax_pushtype(L, Shader, shader);
lovrRelease(shader, lovrShaderDestroy); 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); arr_free(&flags);
return 1; return 1;
} }
@ -1415,6 +1459,7 @@ static const luaL_Reg lovrGraphics[] = {
{ "getDefaultFont", l_lovrGraphicsGetDefaultFont }, { "getDefaultFont", l_lovrGraphicsGetDefaultFont },
{ "newBuffer", l_lovrGraphicsNewBuffer }, { "newBuffer", l_lovrGraphicsNewBuffer },
{ "newTexture", l_lovrGraphicsNewTexture }, { "newTexture", l_lovrGraphicsNewTexture },
{ "newTextureView", l_lovrGraphicsNewTextureView },
{ "newSampler", l_lovrGraphicsNewSampler }, { "newSampler", l_lovrGraphicsNewSampler },
{ "compileShader", l_lovrGraphicsCompileShader }, { "compileShader", l_lovrGraphicsCompileShader },
{ "newShader", l_lovrGraphicsNewShader }, { "newShader", l_lovrGraphicsNewShader },

View File

@ -643,15 +643,9 @@ static int l_lovrPassSetWireframe(lua_State* L) {
static int l_lovrPassSend(lua_State* L) { static int l_lovrPassSend(lua_State* L) {
Pass* pass = luax_checktype(L, 1, Pass); Pass* pass = luax_checktype(L, 1, Pass);
const char* name = NULL;
size_t length = 0;
uint32_t slot = ~0u;
switch (lua_type(L, 2)) { size_t length;
case LUA_TSTRING: name = lua_tolstring(L, 2, &length); break; const char* name = lua_tolstring(L, 2, &length);
case LUA_TNUMBER: slot = lua_tointeger(L, 2) - 1; break;
default: return luax_typeerror(L, 2, "string or number");
}
if (lua_isnoneornil(L, 3)) { if (lua_isnoneornil(L, 3)) {
return luax_typeerror(L, 3, "Buffer, Texture, Sampler, number, vector, table, or boolean"); 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) { if (buffer) {
uint32_t offset = lua_tointeger(L, 4); uint32_t offset = lua_tointeger(L, 4);
uint32_t extent = lua_tointeger(L, 5); uint32_t extent = lua_tointeger(L, 5);
lovrPassSendBuffer(pass, name, length, slot, buffer, offset, extent); lovrPassSendBuffer(pass, name, length, buffer, offset, extent);
return 0; return 0;
} }
Texture* texture = luax_totype(L, 3, Texture); Texture* texture = luax_totype(L, 3, Texture);
if (texture) { if (texture) {
lovrPassSendTexture(pass, name, length, slot, texture); lovrPassSendTexture(pass, name, length, texture);
return 0; return 0;
} }
Sampler* sampler = luax_totype(L, 3, Sampler); Sampler* sampler = luax_totype(L, 3, Sampler);
if (sampler) { if (sampler) {
lovrPassSendSampler(pass, name, length, slot, sampler); lovrPassSendSampler(pass, name, length, sampler);
return 0; return 0;
} }
void* pointer; void* pointer;
DataField* format; DataField* format;
lovrPassSendData(pass, name, length, slot, &pointer, &format); lovrPassSendData(pass, name, length, &pointer, &format);
char* data = pointer; char* data = pointer;
// Coerce booleans since they aren't supported in buffer formats // 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; 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) { static int l_lovrShaderHasStage(lua_State* L) {
Shader* shader = luax_checktype(L, 1, Shader); Shader* shader = luax_checktype(L, 1, Shader);
ShaderStage stage = luax_checkenum(L, 2, ShaderStage, NULL); ShaderStage stage = luax_checkenum(L, 2, ShaderStage, NULL);
@ -86,18 +93,12 @@ static int l_lovrShaderGetBufferFormat(lua_State* L) {
return 2; 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[] = { const luaL_Reg lovrShader[] = {
{ "clone", l_lovrShaderClone }, { "clone", l_lovrShaderClone },
{ "getType", l_lovrShaderGetType },
{ "hasStage", l_lovrShaderHasStage }, { "hasStage", l_lovrShaderHasStage },
{ "hasAttribute", l_lovrShaderHasAttribute }, { "hasAttribute", l_lovrShaderHasAttribute },
{ "getWorkgroupSize", l_lovrShaderGetWorkgroupSize }, { "getWorkgroupSize", l_lovrShaderGetWorkgroupSize },
{ "getBufferFormat", l_lovrShaderGetBufferFormat }, { "getBufferFormat", l_lovrShaderGetBufferFormat },
{ "getType", l_lovrShaderGetType }, // Deprecated
{ NULL, NULL } { NULL, NULL }
}; };

View File

@ -4,41 +4,6 @@
#include "data/image.h" #include "data/image.h"
#include <string.h> #include <string.h>
static int l_lovrTextureNewView(lua_State* L) {
Texture* texture = luax_checktype(L, 1, Texture);
TextureViewInfo info = { .parent = texture };
if (lua_type(L, 2) == LUA_TNUMBER) {
info.type = TEXTURE_2D;
info.layerIndex = luax_checku32(L, 2) - 1;
info.layerCount = 1;
info.levelIndex = luax_optu32(L, 3, 1) - 1;
info.levelCount = 1;
} else if (lua_isstring(L, 2)) {
info.type = luax_checkenum(L, 2, TextureType, NULL);
info.layerIndex = luaL_optinteger(L, 3, 1) - 1;
info.layerCount = luaL_optinteger(L, 4, 1);
info.levelIndex = luaL_optinteger(L, 5, 1) - 1;
info.levelCount = luaL_optinteger(L, 6, 0);
}
Texture* view = lovrTextureCreateView(&info);
luax_pushtype(L, Texture, view);
lovrRelease(view, lovrTextureDestroy);
return 1;
}
static int l_lovrTextureIsView(lua_State* L) {
Texture* texture = luax_checktype(L, 1, Texture);
lua_pushboolean(L, !!lovrTextureGetInfo(texture)->parent);
return 1;
}
static int l_lovrTextureGetParent(lua_State* L) {
Texture* texture = luax_checktype(L, 1, Texture);
const TextureInfo* info = lovrTextureGetInfo(texture);
luax_pushtype(L, Texture, info->parent);
return 1;
}
static int l_lovrTextureGetType(lua_State* L) { static int l_lovrTextureGetType(lua_State* L) {
Texture* texture = luax_checktype(L, 1, Texture); Texture* texture = luax_checktype(L, 1, Texture);
const TextureInfo* info = lovrTextureGetInfo(texture); const TextureInfo* info = lovrTextureGetInfo(texture);
@ -234,9 +199,6 @@ static int l_lovrTextureGenerateMipmaps(lua_State* L) {
} }
const luaL_Reg lovrTexture[] = { const luaL_Reg lovrTexture[] = {
{ "newView", l_lovrTextureNewView },
{ "isView", l_lovrTextureIsView },
{ "getParent", l_lovrTextureGetParent },
{ "getType", l_lovrTextureGetType }, { "getType", l_lovrTextureGetType },
{ "getFormat", l_lovrTextureGetFormat }, { "getFormat", l_lovrTextureGetFormat },
{ "getWidth", l_lovrTextureGetWidth }, { "getWidth", l_lovrTextureGetWidth },

View File

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

View File

@ -670,6 +670,8 @@ bool gpu_texture_init_view(gpu_texture* texture, gpu_texture_view_info* info) {
return false; return false;
} }
nickname(texture->view, VK_OBJECT_TYPE_IMAGE_VIEW, info->label);
return true; return true;
} }
@ -989,31 +991,28 @@ void gpu_layout_destroy(gpu_layout* layout) {
// Shader // Shader
bool gpu_shader_init(gpu_shader* shader, gpu_shader_info* info) { 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; VkShaderStageFlags stageFlags = 0;
for (uint32_t i = 0; i < COUNTOF(stages); i++) { for (uint32_t i = 0; i < info->stageCount; i++) {
if (!stages[i].source->code) continue; 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 = { VkShaderModuleCreateInfo moduleInfo = {
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
.codeSize = stages[i].source->length, .codeSize = info->stages[i].length,
.pCode = stages[i].source->code .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; return false;
} }
nickname(shader->handles[i], VK_OBJECT_TYPE_SHADER_MODULE, info->label); nickname(shader->handles[i], VK_OBJECT_TYPE_SHADER_MODULE, info->label);
stageFlags |= stages[i].flags;
stageCount++;
} }
VkDescriptorSetLayout layouts[4]; VkDescriptorSetLayout layouts[4];
@ -1592,7 +1591,7 @@ bool gpu_pipeline_init_graphics(gpu_pipeline* pipeline, gpu_pipeline_info* info)
.pData = (const void*) constants .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] = { VkPipelineShaderStageCreateInfo shaders[2] = {
{ {

View File

@ -20,8 +20,7 @@ typedef union {
uint16_t name; uint16_t name;
} attribute; } attribute;
struct { struct {
uint8_t set; uint16_t decoration;
uint8_t binding;
uint16_t name; uint16_t name;
} variable; } variable;
struct { 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 1: spv->cache[id].flag.number = op[3]; break; // SpecID
case 6: spv->cache[id].type.arrayStride = op[3]; break; // ArrayStride (overrides name) 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 30: spv->cache[id].attribute.location = op[3]; break; // Location
case 33: spv->cache[id].variable.binding = op[3]; break; // Binding case 33:
case 34: spv->cache[id].variable.set = op[3]; break; // Set case 34:
if (spv->cache[id].variable.decoration == 0xffff) {
spv->cache[id].variable.decoration = op - spv->words;
}
break;
default: 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); 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 // 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; 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++]; spv_resource* resource = &info->resources[info->resourceCount++];
resource->set = set; // Resolve the set/binding pointers. The cache stores the index of the first set/binding
resource->binding = 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 it's an array, read the array size and unwrap the inner type
if (OP_CODE(type) == 28) { // OpTypeArray if (OP_CODE(type) == 28) { // OpTypeArray

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -202,16 +202,6 @@ enum {
}; };
typedef struct { typedef struct {
Texture* parent;
TextureType type;
uint32_t layerIndex;
uint32_t layerCount;
uint32_t levelIndex;
uint32_t levelCount;
} TextureViewInfo;
typedef struct {
Texture* parent;
TextureType type; TextureType type;
uint32_t format; uint32_t format;
uint32_t width; uint32_t width;
@ -221,12 +211,21 @@ typedef struct {
uint32_t usage; uint32_t usage;
bool srgb; bool srgb;
bool xr; bool xr;
uintptr_t handle;
uint32_t imageCount; uint32_t imageCount;
struct Image** images; struct Image** images;
const char* label; const char* label;
uintptr_t handle;
} TextureInfo; } TextureInfo;
typedef struct {
TextureType type;
uint32_t layerIndex;
uint32_t layerCount;
uint32_t levelIndex;
uint32_t levelCount;
const char* label;
} TextureViewInfo;
typedef enum { typedef enum {
FILTER_NEAREST, FILTER_NEAREST,
FILTER_LINEAR FILTER_LINEAR
@ -234,7 +233,7 @@ typedef enum {
Texture* lovrGraphicsGetWindowTexture(void); Texture* lovrGraphicsGetWindowTexture(void);
Texture* lovrTextureCreate(const TextureInfo* info); Texture* lovrTextureCreate(const TextureInfo* info);
Texture* lovrTextureCreateView(const TextureViewInfo* view); Texture* lovrTextureCreateView(Texture* parent, const TextureViewInfo* info);
void lovrTextureDestroy(void* ref); void lovrTextureDestroy(void* ref);
const TextureInfo* lovrTextureGetInfo(Texture* texture); const TextureInfo* lovrTextureGetInfo(Texture* texture);
struct Image* lovrTextureGetPixels(Texture* texture, uint32_t offset[4], uint32_t extent[3]); struct Image* lovrTextureGetPixels(Texture* texture, uint32_t offset[4], uint32_t extent[3]);
@ -286,21 +285,25 @@ typedef enum {
SHADER_EQUIRECT, SHADER_EQUIRECT,
SHADER_FILL_2D, SHADER_FILL_2D,
SHADER_FILL_ARRAY, SHADER_FILL_ARRAY,
SHADER_LOGO,
SHADER_ANIMATOR, SHADER_ANIMATOR,
SHADER_BLENDER, SHADER_BLENDER,
SHADER_TALLY_MERGE, SHADER_TALLY_MERGE,
DEFAULT_SHADER_COUNT DEFAULT_SHADER_COUNT
} DefaultShader; } DefaultShader;
typedef enum {
SHADER_GRAPHICS,
SHADER_COMPUTE
} ShaderType;
typedef enum { typedef enum {
STAGE_VERTEX, STAGE_VERTEX,
STAGE_FRAGMENT, STAGE_FRAGMENT,
STAGE_COMPUTE, STAGE_COMPUTE
STAGE_COUNT
} ShaderStage; } ShaderStage;
typedef struct { typedef struct {
ShaderStage stage;
const void* code; const void* code;
size_t size; size_t size;
} ShaderSource; } ShaderSource;
@ -312,15 +315,18 @@ typedef struct {
} ShaderFlag; } ShaderFlag;
typedef struct { typedef struct {
ShaderSource source[STAGE_COUNT]; ShaderType type;
uint32_t flagCount; ShaderSource* stages;
uint32_t stageCount;
ShaderFlag* flags; ShaderFlag* flags;
uint32_t flagCount;
const char* label; const char* label;
bool isDefault;
} ShaderInfo; } ShaderInfo;
typedef void* ShaderIncluder(const char* filename, size_t* bytesRead); 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); ShaderSource lovrGraphicsGetDefaultShaderSource(DefaultShader type, ShaderStage stage);
Shader* lovrGraphicsGetDefaultShader(DefaultShader type); Shader* lovrGraphicsGetDefaultShader(DefaultShader type);
Shader* lovrShaderCreate(const ShaderInfo* info); Shader* lovrShaderCreate(const ShaderInfo* info);
@ -607,10 +613,10 @@ void lovrPassSetViewCull(Pass* pass, bool enable);
void lovrPassSetWinding(Pass* pass, Winding winding); void lovrPassSetWinding(Pass* pass, Winding winding);
void lovrPassSetWireframe(Pass* pass, bool wireframe); 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 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, uint32_t slot, Texture* texture); void lovrPassSendTexture(Pass* pass, const char* name, size_t length, Texture* texture);
void lovrPassSendSampler(Pass* pass, const char* name, size_t length, uint32_t slot, Sampler* sampler); void lovrPassSendSampler(Pass* pass, const char* name, size_t length, Sampler* sampler);
void lovrPassSendData(Pass* pass, const char* name, size_t length, uint32_t slot, void** data, DataField** format); void lovrPassSendData(Pass* pass, const char* name, size_t length, void** data, DataField** format);
void lovrPassPoints(Pass* pass, uint32_t count, float** vertices); void lovrPassPoints(Pass* pass, uint32_t count, float** vertices);
void lovrPassLine(Pass* pass, uint32_t count, float** vertices); void lovrPassLine(Pass* pass, uint32_t count, float** vertices);