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 += '-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"),
@ -873,6 +878,43 @@ static int l_lovrGraphicsNewTexture(lua_State* L) {
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) {
SamplerInfo info = {
.min = FILTER_LINEAR,
@ -941,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);
@ -1051,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);
@ -1064,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;
}
@ -1415,6 +1459,7 @@ static const luaL_Reg lovrGraphics[] = {
{ "getDefaultFont", l_lovrGraphicsGetDefaultFont },
{ "newBuffer", l_lovrGraphicsNewBuffer },
{ "newTexture", l_lovrGraphicsNewTexture },
{ "newTextureView", l_lovrGraphicsNewTextureView },
{ "newSampler", l_lovrGraphicsNewSampler },
{ "compileShader", l_lovrGraphicsCompileShader },
{ "newShader", l_lovrGraphicsNewShader },

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

@ -4,41 +4,6 @@
#include "data/image.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) {
Texture* texture = luax_checktype(L, 1, Texture);
const TextureInfo* info = lovrTextureGetInfo(texture);
@ -234,9 +199,6 @@ static int l_lovrTextureGenerateMipmaps(lua_State* L) {
}
const luaL_Reg lovrTexture[] = {
{ "newView", l_lovrTextureNewView },
{ "isView", l_lovrTextureIsView },
{ "getParent", l_lovrTextureGetParent },
{ "getType", l_lovrTextureGetType },
{ "getFormat", l_lovrTextureGetFormat },
{ "getWidth", l_lovrTextureGetWidth },

View File

@ -122,6 +122,7 @@ typedef struct {
uint32_t layerCount;
uint32_t levelIndex;
uint32_t levelCount;
const char* label;
} gpu_texture_view_info;
typedef struct {
@ -245,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

@ -670,6 +670,8 @@ bool gpu_texture_init_view(gpu_texture* texture, gpu_texture_view_info* info) {
return false;
}
nickname(texture->view, VK_OBJECT_TYPE_IMAGE_VIEW, info->label);
return true;
}
@ -989,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];
@ -1592,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

@ -202,16 +202,6 @@ enum {
};
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;
uint32_t format;
uint32_t width;
@ -221,12 +211,21 @@ typedef struct {
uint32_t usage;
bool srgb;
bool xr;
uintptr_t handle;
uint32_t imageCount;
struct Image** images;
const char* label;
uintptr_t handle;
} TextureInfo;
typedef struct {
TextureType type;
uint32_t layerIndex;
uint32_t layerCount;
uint32_t levelIndex;
uint32_t levelCount;
const char* label;
} TextureViewInfo;
typedef enum {
FILTER_NEAREST,
FILTER_LINEAR
@ -234,7 +233,7 @@ typedef enum {
Texture* lovrGraphicsGetWindowTexture(void);
Texture* lovrTextureCreate(const TextureInfo* info);
Texture* lovrTextureCreateView(const TextureViewInfo* view);
Texture* lovrTextureCreateView(Texture* parent, const TextureViewInfo* info);
void lovrTextureDestroy(void* ref);
const TextureInfo* lovrTextureGetInfo(Texture* texture);
struct Image* lovrTextureGetPixels(Texture* texture, uint32_t offset[4], uint32_t extent[3]);
@ -286,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;
@ -312,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);
@ -607,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);