From 22e15513f9e4a8c17cc8af6d50d782728d97b5c7 Mon Sep 17 00:00:00 2001 From: bjorn Date: Sun, 1 May 2022 15:47:17 -0700 Subject: [PATCH] Sampler; --- CMakeLists.txt | 1 + src/api/l_graphics.c | 92 +++++++++++++++++++++++++++++++++ src/core/gpu.h | 31 +++++++++++ src/core/gpu_vk.c | 61 ++++++++++++++++++++++ src/modules/graphics/graphics.c | 44 ++++++++++++++++ src/modules/graphics/graphics.h | 36 +++++++++++++ 6 files changed, 265 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 217668e0..94fd1a38 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -423,6 +423,7 @@ if(LOVR_ENABLE_GRAPHICS) src/api/l_graphics.c src/api/l_graphics_buffer.c src/api/l_graphics_texture.c + src/api/l_graphics_sampler.c src/api/l_graphics_pass.c ) diff --git a/src/api/l_graphics.c b/src/api/l_graphics.c index 143bd854..a3622b97 100644 --- a/src/api/l_graphics.c +++ b/src/api/l_graphics.c @@ -15,6 +15,17 @@ StringEntry lovrBufferLayout[] = { { 0 } }; +StringEntry lovrCompareMode[] = { + [COMPARE_NONE] = ENTRY("none"), + [COMPARE_EQUAL] = ENTRY("equal"), + [COMPARE_NEQUAL] = ENTRY("notequal"), + [COMPARE_LESS] = ENTRY("less"), + [COMPARE_LEQUAL] = ENTRY("lequal"), + [COMPARE_GREATER] = ENTRY("greater"), + [COMPARE_GEQUAL] = ENTRY("gequal"), + { 0 } +}; + StringEntry lovrFieldType[] = { [FIELD_I8x4] = ENTRY("i8x4"), [FIELD_U8x4] = ENTRY("u8x4"), @@ -51,6 +62,12 @@ StringEntry lovrFieldType[] = { { 0 } }; +StringEntry lovrFilterMode[] = { + [FILTER_NEAREST] = ENTRY("nearest"), + [FILTER_LINEAR] = ENTRY("linear"), + { 0 } +}; + StringEntry lovrPassType[] = { [PASS_RENDER] = ENTRY("render"), [PASS_COMPUTE] = ENTRY("compute"), @@ -86,6 +103,12 @@ StringEntry lovrTextureUsage[] = { { 0 } }; +StringEntry lovrWrapMode[] = { + [WRAP_CLAMP] = ENTRY("clamp"), + [WRAP_REPEAT] = ENTRY("repeat"), + { 0 } +}; + static struct { uint32_t size, scalarAlign, baseAlign, components; } fieldInfo[] = { [FIELD_I8x4] = { 4, 1, 4, 4 }, [FIELD_U8x4] = { 4, 1, 4, 4 }, @@ -705,6 +728,74 @@ static int l_lovrGraphicsNewTexture(lua_State* L) { return 1; } +static int l_lovrGraphicsNewSampler(lua_State* L) { + SamplerInfo info = { + .min = FILTER_LINEAR, + .mag = FILTER_LINEAR, + .mip = FILTER_LINEAR, + .wrap = { WRAP_REPEAT, WRAP_REPEAT, WRAP_REPEAT }, + .range = { 0.f, -1.f } + }; + + luaL_checktype(L, 1, LUA_TTABLE); + + lua_getfield(L, 1, "filter"); + if (lua_isstring(L, -1)) { + info.min = info.mag = info.mip = luax_checkenum(L, -1, FilterMode, NULL); + } else if (lua_istable(L, -1)) { + lua_rawgeti(L, -1, 1); + lua_rawgeti(L, -2, 2); + lua_rawgeti(L, -3, 3); + info.min = luax_checkenum(L, -3, FilterMode, NULL); + info.mag = luax_checkenum(L, -2, FilterMode, NULL); + info.mip = luax_checkenum(L, -1, FilterMode, NULL); + lua_pop(L, 3); + } else if (!lua_isnil(L, -1)) { + lovrThrow("Expected string or table for Sampler filter"); + } + lua_pop(L, 1); + + lua_getfield(L, 1, "wrap"); + if (lua_isstring(L, -1)) { + info.wrap[0] = info.wrap[1] = info.wrap[2] = luax_checkenum(L, -1, WrapMode, NULL); + } else if (lua_istable(L, -1)) { + lua_rawgeti(L, -1, 1); + lua_rawgeti(L, -2, 2); + lua_rawgeti(L, -3, 3); + info.wrap[0] = luax_checkenum(L, -3, WrapMode, NULL); + info.wrap[1] = luax_checkenum(L, -2, WrapMode, NULL); + info.wrap[2] = luax_checkenum(L, -1, WrapMode, NULL); + lua_pop(L, 3); + } else if (!lua_isnil(L, -1)) { + lovrThrow("Expected string or table for Sampler wrap"); + } + lua_pop(L, 1); + + lua_getfield(L, 1, "compare"); + info.compare = luax_checkenum(L, -1, CompareMode, "none"); + lua_pop(L, 1); + + lua_getfield(L, 1, "anisotropy"); + info.anisotropy = luax_optfloat(L, -1, 0.f); + lua_pop(L, 1); + + lua_getfield(L, 1, "mipmaprange"); + if (!lua_isnil(L, -1)) { + lovrAssert(lua_istable(L, -1), "Sampler mipmap range must be nil or a table"); + lua_rawgeti(L, -1, 1); + lua_rawgeti(L, -2, 2); + info.range[0] = luax_checkfloat(L, -2); + info.range[1] = luax_checkfloat(L, -1); + lua_pop(L, 2); + } + lua_pop(L, 1); + + Sampler* sampler = lovrSamplerCreate(&info); + luax_pushtype(L, Sampler, sampler); + lovrRelease(sampler, lovrSamplerDestroy); + return 1; +} + static const luaL_Reg lovrGraphics[] = { { "init", l_lovrGraphicsInit }, { "submit", l_lovrGraphicsSubmit }, @@ -716,6 +807,7 @@ static const luaL_Reg lovrGraphics[] = { { "buffer", l_lovrGraphicsBuffer }, { "newBuffer", l_lovrGraphicsNewBuffer }, { "newTexture", l_lovrGraphicsNewTexture }, + { "newSampler", l_lovrGraphicsNewSampler }, { "pass", l_lovrGraphicsPass }, { NULL, NULL } }; diff --git a/src/core/gpu.h b/src/core/gpu.h index c1ed99cf..b5f3ff46 100644 --- a/src/core/gpu.h +++ b/src/core/gpu.h @@ -4,10 +4,12 @@ typedef struct gpu_buffer gpu_buffer; typedef struct gpu_texture gpu_texture; +typedef struct gpu_sampler gpu_sampler; typedef struct gpu_stream gpu_stream; size_t gpu_sizeof_buffer(void); size_t gpu_sizeof_texture(void); +size_t gpu_sizeof_sampler(void); // Buffer @@ -132,6 +134,35 @@ typedef enum { GPU_FILTER_LINEAR } gpu_filter; +typedef enum { + GPU_WRAP_CLAMP, + GPU_WRAP_REPEAT, + GPU_WRAP_MIRROR +} gpu_wrap; + +typedef enum { + GPU_COMPARE_NONE, + GPU_COMPARE_EQUAL, + GPU_COMPARE_NEQUAL, + GPU_COMPARE_LESS, + GPU_COMPARE_LEQUAL, + GPU_COMPARE_GREATER, + GPU_COMPARE_GEQUAL +} gpu_compare_mode; + +typedef struct { + gpu_filter min; + gpu_filter mag; + gpu_filter mip; + gpu_wrap wrap[3]; + gpu_compare_mode compare; + float anisotropy; + float lodClamp[2]; +} gpu_sampler_info; + +bool gpu_sampler_init(gpu_sampler* sampler, gpu_sampler_info* info); +void gpu_sampler_destroy(gpu_sampler* sampler); + // Stream gpu_stream* gpu_stream_begin(const char* label); diff --git a/src/core/gpu_vk.c b/src/core/gpu_vk.c index 28e2ddb3..c57ca575 100644 --- a/src/core/gpu_vk.c +++ b/src/core/gpu_vk.c @@ -27,12 +27,17 @@ struct gpu_texture { bool layered; }; +struct gpu_sampler { + VkSampler handle; +}; + 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); } // Internals @@ -617,6 +622,62 @@ void gpu_texture_destroy(gpu_texture* texture) { gpu_release(state.memory + texture->memory); } +// Sampler + +bool gpu_sampler_init(gpu_sampler* sampler, gpu_sampler_info* info) { + static const VkFilter filters[] = { + [GPU_FILTER_NEAREST] = VK_FILTER_NEAREST, + [GPU_FILTER_LINEAR] = VK_FILTER_LINEAR + }; + + static const VkSamplerMipmapMode mipFilters[] = { + [GPU_FILTER_NEAREST] = VK_SAMPLER_MIPMAP_MODE_NEAREST, + [GPU_FILTER_LINEAR] = VK_SAMPLER_MIPMAP_MODE_LINEAR + }; + + static const VkSamplerAddressMode wraps[] = { + [GPU_WRAP_CLAMP] = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, + [GPU_WRAP_REPEAT] = VK_SAMPLER_ADDRESS_MODE_REPEAT, + [GPU_WRAP_MIRROR] = VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT + }; + + static const VkCompareOp compareOps[] = { + [GPU_COMPARE_NONE] = VK_COMPARE_OP_ALWAYS, + [GPU_COMPARE_EQUAL] = VK_COMPARE_OP_EQUAL, + [GPU_COMPARE_NEQUAL] = VK_COMPARE_OP_NOT_EQUAL, + [GPU_COMPARE_LESS] = VK_COMPARE_OP_LESS, + [GPU_COMPARE_LEQUAL] = VK_COMPARE_OP_LESS_OR_EQUAL, + [GPU_COMPARE_GREATER] = VK_COMPARE_OP_GREATER, + [GPU_COMPARE_GEQUAL] = VK_COMPARE_OP_GREATER_OR_EQUAL + }; + + VkSamplerCreateInfo samplerInfo = { + .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, + .magFilter = filters[info->mag], + .minFilter = filters[info->min], + .mipmapMode = mipFilters[info->mip], + .addressModeU = wraps[info->wrap[0]], + .addressModeV = wraps[info->wrap[1]], + .addressModeW = wraps[info->wrap[2]], + .anisotropyEnable = info->anisotropy >= 1.f, + .maxAnisotropy = info->anisotropy, + .compareEnable = info->compare != GPU_COMPARE_NONE, + .compareOp = compareOps[info->compare], + .minLod = info->lodClamp[0], + .maxLod = info->lodClamp[1] < 0.f ? VK_LOD_CLAMP_NONE : info->lodClamp[1] + }; + + VK(vkCreateSampler(state.device, &samplerInfo, NULL, &sampler->handle), "Could not create sampler") { + return false; + } + + return true; +} + +void gpu_sampler_destroy(gpu_sampler* sampler) { + condemn(sampler->handle, VK_OBJECT_TYPE_SAMPLER); +} + // Stream gpu_stream* gpu_stream_begin(const char* label) { diff --git a/src/modules/graphics/graphics.c b/src/modules/graphics/graphics.c index 9de8e1f6..4812f720 100644 --- a/src/modules/graphics/graphics.c +++ b/src/modules/graphics/graphics.c @@ -24,6 +24,12 @@ struct Texture { TextureInfo info; }; +struct Sampler { + uint32_t ref; + gpu_sampler* gpu; + SamplerInfo info; +}; + struct Pass { uint32_t ref; PassInfo info; @@ -460,6 +466,44 @@ const TextureInfo* lovrTextureGetInfo(Texture* texture) { return &texture->info; } +// Sampler + +Sampler* lovrSamplerCreate(SamplerInfo* info) { + lovrCheck(info->range[1] < 0.f || info->range[1] >= info->range[0], "Invalid Sampler mipmap range"); + lovrCheck(info->anisotropy <= state.limits.anisotropy, "Sampler anisotropy (%f) exceeds anisotropy limit (%f)", info->anisotropy, state.limits.anisotropy); + + Sampler* sampler = calloc(1, sizeof(Sampler) + gpu_sizeof_sampler()); + lovrAssert(sampler, "Out of memory"); + sampler->gpu = (gpu_sampler*) (sampler + 1); + sampler->info = *info; + sampler->ref = 1; + + gpu_sampler_info gpu = { + .min = (gpu_filter) info->min, + .mag = (gpu_filter) info->mag, + .mip = (gpu_filter) info->mip, + .wrap[0] = (gpu_wrap) info->wrap[0], + .wrap[1] = (gpu_wrap) info->wrap[1], + .wrap[2] = (gpu_wrap) info->wrap[2], + .compare = (gpu_compare_mode) info->compare, + .anisotropy = MIN(info->anisotropy, state.limits.anisotropy), + .lodClamp = { info->range[0], info->range[1] } + }; + + lovrAssert(gpu_sampler_init(sampler->gpu, &gpu), "Failed to initialize sampler"); + return sampler; +} + +void lovrSamplerDestroy(void* ref) { + Sampler* sampler = ref; + gpu_sampler_destroy(sampler->gpu); + free(sampler); +} + +const SamplerInfo* lovrSamplerGetInfo(Sampler* sampler) { + return &sampler->info; +} + // Pass Pass* lovrGraphicsGetPass(PassInfo* info) { diff --git a/src/modules/graphics/graphics.h b/src/modules/graphics/graphics.h index da095b3c..8ab37963 100644 --- a/src/modules/graphics/graphics.h +++ b/src/modules/graphics/graphics.h @@ -7,6 +7,7 @@ struct Image; typedef struct Buffer Buffer; typedef struct Texture Texture; +typedef struct Sampler Sampler; typedef struct Pass Pass; typedef struct { @@ -192,6 +193,41 @@ Texture* lovrTextureCreateView(TextureViewInfo* view); void lovrTextureDestroy(void* ref); const TextureInfo* lovrTextureGetInfo(Texture* texture); +// Sampler + +typedef enum { + FILTER_NEAREST, + FILTER_LINEAR +} FilterMode; + +typedef enum { + WRAP_CLAMP, + WRAP_REPEAT, + WRAP_MIRROR +} WrapMode; + +typedef enum { + COMPARE_NONE, + COMPARE_EQUAL, + COMPARE_NEQUAL, + COMPARE_LESS, + COMPARE_LEQUAL, + COMPARE_GREATER, + COMPARE_GEQUAL +} CompareMode; + +typedef struct { + FilterMode min, mag, mip; + WrapMode wrap[3]; + CompareMode compare; + float anisotropy; + float range[2]; +} SamplerInfo; + +Sampler* lovrSamplerCreate(SamplerInfo* info); +void lovrSamplerDestroy(void* ref); +const SamplerInfo* lovrSamplerGetInfo(Sampler* sampler); + // Pass typedef enum {