mirror of https://github.com/bjornbytes/lovr.git
Slightly broken compileShader/newShader;
This commit is contained in:
parent
9ae285afa7
commit
d9e5ba8b9f
|
@ -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
|
||||
)
|
||||
|
||||
|
|
|
@ -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[];
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 }
|
||||
};
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue