Slightly broken compileShader/newShader;

This commit is contained in:
bjorn 2022-05-09 11:47:06 -07:00
parent 9ae285afa7
commit d9e5ba8b9f
8 changed files with 336 additions and 6 deletions

View File

@ -439,6 +439,7 @@ if(LOVR_ENABLE_GRAPHICS)
src/api/l_graphics_buffer.c
src/api/l_graphics_texture.c
src/api/l_graphics_sampler.c
src/api/l_graphics_shader.c
src/api/l_graphics_pass.c
)

View File

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

View File

@ -75,6 +75,19 @@ StringEntry lovrPassType[] = {
{ 0 }
};
StringEntry lovrShaderStage[] = {
[STAGE_VERTEX] = ENTRY("vertex"),
[STAGE_FRAGMENT] = ENTRY("fragment"),
[STAGE_COMPUTE] = ENTRY("compute"),
{ 0 }
};
StringEntry lovrShaderType[] = {
[SHADER_GRAPHICS] = ENTRY("graphics"),
[SHADER_COMPUTE] = ENTRY("compute"),
{ 0 }
};
StringEntry lovrStackType[] = {
[STACK_TRANSFORM] = ENTRY("transform"),
[STACK_PIPELINE] = ENTRY("pipeline"),
@ -802,6 +815,88 @@ static int l_lovrGraphicsNewSampler(lua_State* L) {
return 1;
}
static Blob* luax_checkshadercode(lua_State* L, int index, ShaderStage stage) {
Blob* source;
size_t length;
const char* string = lua_tolstring(L, index, &length);
if (string && memchr(string, '\n', MIN(256, length))) {
void* data = malloc(length);
lovrAssert(data, "Out of memory");
memcpy(data, string, length);
source = lovrBlobCreate(data, length, "Shader code");
} else {
source = luax_readblob(L, index, "Shader");
}
Blob* code = lovrGraphicsCompileShader(stage, source);
lovrRelease(source, lovrBlobDestroy);
return code;
}
static int l_lovrGraphicsCompileShader(lua_State* L) {
ShaderStage stage = luax_checkenum(L, 1, ShaderStage, NULL);
Blob* source = luax_checkshadercode(L, 2, stage);
Blob* output = lovrGraphicsCompileShader(stage, source);
luax_pushtype(L, Blob, output);
lovrRelease(output, lovrBlobDestroy);
lovrRelease(source, lovrBlobDestroy);
return 1;
}
static int l_lovrGraphicsNewShader(lua_State* L) {
ShaderInfo info = { 0 };
int index;
if (lua_gettop(L) == 1 || lua_istable(L, 2)) {
info.type = SHADER_COMPUTE;
info.stages[0] = luax_checkshadercode(L, 1, STAGE_COMPUTE);
index = 2;
} else {
info.type = SHADER_GRAPHICS;
info.stages[0] = luax_checkshadercode(L, 1, STAGE_VERTEX);
info.stages[1] = luax_checkshadercode(L, 2, STAGE_FRAGMENT);
index = 3;
}
arr_t(ShaderFlag) flags;
arr_init(&flags, realloc);
if (lua_istable(L, index)) {
lua_getfield(L, index, "flags");
if (!lua_isnil(L, -1)) {
luaL_checktype(L, -1, LUA_TTABLE);
lua_pushnil(L);
while (lua_next(L, -2) != 0) {
ShaderFlag flag = { 0 };
flag.value = lua_isboolean(L, -1) ? (double) lua_toboolean(L, -1) : lua_tonumber(L, -1);
switch (lua_type(L, -2)) {
case LUA_TSTRING: flag.name = lua_tostring(L, -2); break;
case LUA_TNUMBER: flag.id = lua_tointeger(L, -2); break;
default: lovrThrow("Unexpected ShaderFlag key type (%s)", lua_typename(L, lua_type(L, -2)));
}
arr_push(&flags, flag);
lua_pop(L, 1);
}
}
lua_pop(L, 1);
lua_getfield(L, index, "label");
info.label = lua_tostring(L, -1);
lua_pop(L, 1);
}
info.flags = flags.data;
info.flagCount = flags.length;
Shader* shader = lovrShaderCreate(&info);
lovrRelease(info.stages[0], lovrBlobDestroy);
lovrRelease(info.stages[1], lovrBlobDestroy);
arr_free(&flags);
luax_pushtype(L, Shader, shader);
lovrRelease(shader, lovrShaderDestroy);
return 1;
}
static const luaL_Reg lovrGraphics[] = {
{ "init", l_lovrGraphicsInit },
{ "submit", l_lovrGraphicsSubmit },
@ -814,6 +909,8 @@ static const luaL_Reg lovrGraphics[] = {
{ "newBuffer", l_lovrGraphicsNewBuffer },
{ "newTexture", l_lovrGraphicsNewTexture },
{ "newSampler", l_lovrGraphicsNewSampler },
{ "compileShader", l_lovrGraphicsCompileShader },
{ "newShader", l_lovrGraphicsNewShader },
{ "pass", l_lovrGraphicsPass },
{ NULL, NULL }
};
@ -821,6 +918,7 @@ static const luaL_Reg lovrGraphics[] = {
extern const luaL_Reg lovrBuffer[];
extern const luaL_Reg lovrTexture[];
extern const luaL_Reg lovrSampler[];
extern const luaL_Reg lovrShader[];
extern const luaL_Reg lovrPass[];
int luaopen_lovr_graphics(lua_State* L) {
@ -829,6 +927,7 @@ int luaopen_lovr_graphics(lua_State* L) {
luax_registertype(L, Buffer);
luax_registertype(L, Texture);
luax_registertype(L, Sampler);
luax_registertype(L, Shader);
luax_registertype(L, Pass);
return 1;
}

View File

@ -0,0 +1,17 @@
#include "api.h"
#include "graphics/graphics.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
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;
}
const luaL_Reg lovrShader[] = {
{ "getType", l_lovrShaderGetType },
{ NULL, NULL }
};

View File

@ -6,12 +6,14 @@ typedef struct gpu_buffer gpu_buffer;
typedef struct gpu_texture gpu_texture;
typedef struct gpu_sampler gpu_sampler;
typedef struct gpu_layout gpu_layout;
typedef struct gpu_shader gpu_shader;
typedef struct gpu_stream gpu_stream;
size_t gpu_sizeof_buffer(void);
size_t gpu_sizeof_texture(void);
size_t gpu_sizeof_sampler(void);
size_t gpu_sizeof_layout(void);
size_t gpu_sizeof_shader(void);
// Buffer
@ -200,6 +202,23 @@ typedef struct {
bool gpu_layout_init(gpu_layout* layout, gpu_layout_info* info);
void gpu_layout_destroy(gpu_layout* layout);
// Shader
typedef struct {
const void* code;
uint32_t length;
} gpu_shader_stage;
typedef struct {
gpu_shader_stage stages[2];
gpu_layout* layouts[4];
uint32_t pushConstantSize;
const char* label;
} gpu_shader_info;
bool gpu_shader_init(gpu_shader* shader, gpu_shader_info* info);
void gpu_shader_destroy(gpu_shader* shader);
// Stream
gpu_stream* gpu_stream_begin(const char* label);

View File

@ -31,19 +31,25 @@ struct gpu_sampler {
VkSampler handle;
};
struct gpu_stream {
VkCommandBuffer commands;
};
struct gpu_layout {
VkDescriptorSetLayout handle;
uint32_t descriptorCounts[7];
};
struct gpu_shader {
VkShaderModule handles[2];
VkPipelineLayout pipelineLayout;
};
struct gpu_stream {
VkCommandBuffer commands;
};
size_t gpu_sizeof_buffer() { return sizeof(gpu_buffer); }
size_t gpu_sizeof_texture() { return sizeof(gpu_texture); }
size_t gpu_sizeof_sampler() { return sizeof(gpu_sampler); }
size_t gpu_sizeof_layout() { return sizeof(gpu_layout); }
size_t gpu_sizeof_shader() { return sizeof(gpu_shader); }
// Internals
@ -733,6 +739,55 @@ void gpu_layout_destroy(gpu_layout* layout) {
condemn(layout->handle, VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT);
}
// Shader
bool gpu_shader_init(gpu_shader* shader, gpu_shader_info* info) {
for (uint32_t i = 0; i < COUNTOF(info->stages) && info->stages[i].code; i++) {
VkShaderModuleCreateInfo moduleInfo = {
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
.codeSize = info->stages[i].length,
.pCode = info->stages[i].code
};
VK(vkCreateShaderModule(state.device, &moduleInfo, NULL, &shader->handles[i]), "Failed to load shader") {
return false;
}
}
VkDescriptorSetLayout layouts[4];
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.pSetLayouts = layouts,
.pushConstantRangeCount = info->pushConstantSize > 0,
.pPushConstantRanges = &(VkPushConstantRange) {
.stageFlags = info->stages[1].code ?
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT :
VK_SHADER_STAGE_COMPUTE_BIT,
.offset = 0,
.size = info->pushConstantSize
}
};
for (uint32_t i = 0; i < COUNTOF(info->layouts) && info->layouts[i]; i++) {
layouts[i] = info->layouts[i]->handle;
pipelineLayoutInfo.setLayoutCount++;
}
VK(vkCreatePipelineLayout(state.device, &pipelineLayoutInfo, NULL, &shader->pipelineLayout), "Failed to create pipeline layout") {
gpu_shader_destroy(shader);
return false;
}
return true;
}
void gpu_shader_destroy(gpu_shader* shader) {
// The spec says it's safe to destroy shaders while still in use
if (shader->handles[0]) vkDestroyShaderModule(state.device, shader->handles[0], NULL);
if (shader->handles[1]) vkDestroyShaderModule(state.device, shader->handles[1], NULL);
condemn(shader->pipelineLayout, VK_OBJECT_TYPE_PIPELINE_LAYOUT);
}
// Stream
gpu_stream* gpu_stream_begin(const char* label) {
@ -1395,6 +1450,7 @@ static void expunge() {
case VK_OBJECT_TYPE_IMAGE_VIEW: vkDestroyImageView(state.device, victim->handle, NULL); break;
case VK_OBJECT_TYPE_SAMPLER: vkDestroySampler(state.device, victim->handle, NULL); break;
case VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT: vkDestroyDescriptorSetLayout(state.device, victim->handle, NULL); break;
case VK_OBJECT_TYPE_PIPELINE_LAYOUT: vkDestroyPipelineLayout(state.device, victim->handle, NULL); break;
case VK_OBJECT_TYPE_DEVICE_MEMORY: vkFreeMemory(state.device, victim->handle, NULL); break;
default: break;
}

View File

@ -1,4 +1,5 @@
#include "graphics/graphics.h"
#include "data/blob.h"
#include "data/image.h"
#include "core/gpu.h"
#include "core/maf.h"
@ -7,6 +8,10 @@
#include <math.h>
#include <stdlib.h>
#include <string.h>
#ifdef LOVR_USE_GLSLANG
#include "glslang_c_interface.h"
#include "resource_limits_c.h"
#endif
#define MAX_FRAME_MEMORY (1 << 30)
@ -31,6 +36,12 @@ struct Sampler {
SamplerInfo info;
};
struct Shader {
uint32_t ref;
gpu_shader* gpu;
ShaderInfo info;
};
struct Pass {
uint32_t ref;
PassInfo info;
@ -70,6 +81,7 @@ static void onMessage(void* context, const char* message, bool severe);
bool lovrGraphicsInit(bool debug) {
if (state.initialized) return false;
glslang_initialize_process();
float16Init();
gpu_config config = {
@ -98,6 +110,7 @@ bool lovrGraphicsInit(bool debug) {
void lovrGraphicsDestroy() {
if (!state.initialized) return;
gpu_destroy();
glslang_finalize_process();
os_vm_free(state.allocator.memory, MAX_FRAME_MEMORY);
memset(&state, 0, sizeof(state));
}
@ -478,9 +491,9 @@ Sampler* lovrSamplerCreate(SamplerInfo* info) {
Sampler* sampler = calloc(1, sizeof(Sampler) + gpu_sizeof_sampler());
lovrAssert(sampler, "Out of memory");
sampler->ref = 1;
sampler->gpu = (gpu_sampler*) (sampler + 1);
sampler->info = *info;
sampler->ref = 1;
gpu_sampler_info gpu = {
.min = (gpu_filter) info->min,
@ -494,7 +507,8 @@ Sampler* lovrSamplerCreate(SamplerInfo* info) {
.lodClamp = { info->range[0], info->range[1] }
};
lovrAssert(gpu_sampler_init(sampler->gpu, &gpu), "Failed to initialize sampler");
gpu_sampler_init(sampler->gpu, &gpu);
return sampler;
}
@ -508,6 +522,95 @@ const SamplerInfo* lovrSamplerGetInfo(Sampler* sampler) {
return &sampler->info;
}
// Shader
Blob* lovrGraphicsCompileShader(ShaderStage stage, Blob* source) {
#ifdef LOVR_USE_GLSLANG
const glslang_stage_t stages[] = {
[STAGE_VERTEX] = GLSLANG_STAGE_VERTEX,
[STAGE_FRAGMENT] = GLSLANG_STAGE_FRAGMENT,
[STAGE_COMPUTE] = GLSLANG_STAGE_COMPUTE
};
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,
.code = source->data,
.default_version = 460,
.default_profile = GLSLANG_NO_PROFILE,
.resource = resource
};
glslang_shader_t* shader = glslang_shader_create(&input);
if (!glslang_shader_preprocess(shader, &input)) {
lovrLog(LOG_INFO, "Could not preprocess shader: %s", glslang_shader_get_info_log(shader));
return NULL;
}
if (!glslang_shader_parse(shader, &input)) {
lovrLog(LOG_INFO, "Could not parse shader: %s", glslang_shader_get_info_log(shader));
return NULL;
}
glslang_program_t* program = glslang_program_create();
glslang_program_add_shader(program, shader);
if (!glslang_program_link(program, 0)) {
lovrLog(LOG_INFO, "Could not link shader: %s", glslang_program_get_info_log(program));
return NULL;
}
glslang_program_SPIRV_generate(program, stages[stage]);
void* words = glslang_program_SPIRV_get_ptr(program);
size_t size = glslang_program_SPIRV_get_size(program) * 4;
void* data = malloc(size);
lovrAssert(data, "Out of memory");
memcpy(data, words, size);
Blob* blob = lovrBlobCreate(data, size, "SPIRV");
glslang_program_delete(program);
glslang_shader_delete(shader);
return blob;
#endif
return NULL;
}
Shader* lovrShaderCreate(ShaderInfo* info) {
Shader* shader = calloc(1, sizeof(Shader) + gpu_sizeof_shader());
lovrAssert(shader, "Out of memory");
shader->ref = 1;
shader->gpu = (gpu_shader*) (shader + 1);
shader->info = *info;
gpu_shader_info gpu = {
.stages[0] = { info->stages[0]->data, info->stages[0]->size },
.stages[1] = { info->stages[1]->data, info->stages[1]->size }
};
gpu_shader_init(shader->gpu, &gpu);
return shader;
}
void lovrShaderDestroy(void* ref) {
Shader* shader = ref;
gpu_shader_destroy(shader->gpu);
free(shader);
}
const ShaderInfo* lovrShaderGetInfo(Shader* shader) {
return &shader->info;
}
// Pass
Pass* lovrGraphicsGetPass(PassInfo* info) {

View File

@ -3,11 +3,13 @@
#pragma once
struct Blob;
struct Image;
typedef struct Buffer Buffer;
typedef struct Texture Texture;
typedef struct Sampler Sampler;
typedef struct Shader Shader;
typedef struct Pass Pass;
typedef struct {
@ -228,6 +230,38 @@ Sampler* lovrSamplerCreate(SamplerInfo* info);
void lovrSamplerDestroy(void* ref);
const SamplerInfo* lovrSamplerGetInfo(Sampler* sampler);
// Shader
typedef enum {
SHADER_GRAPHICS,
SHADER_COMPUTE
} ShaderType;
typedef enum {
STAGE_VERTEX,
STAGE_FRAGMENT,
STAGE_COMPUTE
} ShaderStage;
typedef struct {
const char* name;
uint32_t id;
double value;
} ShaderFlag;
typedef struct {
ShaderType type;
struct Blob* stages[2];
uint32_t flagCount;
ShaderFlag* flags;
const char* label;
} ShaderInfo;
struct Blob* lovrGraphicsCompileShader(ShaderStage stage, struct Blob* source);
Shader* lovrShaderCreate(ShaderInfo* info);
void lovrShaderDestroy(void* ref);
const ShaderInfo* lovrShaderGetInfo(Shader* shader);
// Pass
typedef enum {