From e80d254dc6474176d840724303361f0f4de672b7 Mon Sep 17 00:00:00 2001 From: bjorn Date: Fri, 29 Apr 2022 20:56:23 -0700 Subject: [PATCH] Texture API; Except newTexture because it's hard or something --- CMakeLists.txt | 1 + src/api/api.h | 1 + src/api/l_graphics.c | 18 +++++ src/api/l_graphics_texture.c | 117 ++++++++++++++++++++++++++++++++ src/modules/graphics/graphics.c | 58 ++++++++++++++-- src/modules/graphics/graphics.h | 2 +- 6 files changed, 190 insertions(+), 7 deletions(-) create mode 100644 src/api/l_graphics_texture.c diff --git a/CMakeLists.txt b/CMakeLists.txt index ff492979..52e5b2f3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -422,6 +422,7 @@ if(LOVR_ENABLE_GRAPHICS) src/modules/graphics/graphics.c src/api/l_graphics.c src/api/l_graphics_buffer.c + src/api/l_graphics_texture.c ) if(LOVR_USE_VULKAN) diff --git a/src/api/api.h b/src/api/api.h index a359c8c4..9d04df69 100644 --- a/src/api/api.h +++ b/src/api/api.h @@ -56,6 +56,7 @@ extern StringEntry lovrStencilAction[]; extern StringEntry lovrTextureFeature[]; extern StringEntry lovrTextureFormat[]; extern StringEntry lovrTextureType[]; +extern StringEntry lovrTextureUsage[]; extern StringEntry lovrTimeUnit[]; extern StringEntry lovrUniformAccess[]; extern StringEntry lovrVerticalAlign[]; diff --git a/src/api/l_graphics.c b/src/api/l_graphics.c index ce1b80bb..726edbbd 100644 --- a/src/api/l_graphics.c +++ b/src/api/l_graphics.c @@ -62,6 +62,22 @@ StringEntry lovrTextureFeature[] = { { 0 } }; +StringEntry lovrTextureType[] = { + [TEXTURE_2D] = ENTRY("2d"), + [TEXTURE_3D] = ENTRY("3d"), + [TEXTURE_CUBE] = ENTRY("cube"), + [TEXTURE_ARRAY] = ENTRY("array"), + { 0 } +}; + +StringEntry lovrTextureUsage[] = { + [0] = ENTRY("sample"), + [1] = ENTRY("render"), + [2] = ENTRY("storage"), + [3] = ENTRY("copy"), + { 0 } +}; + static struct { uint32_t size, scalarAlign, baseAlign, components; } fieldInfo[] = { [FIELD_I8x4] = { 4, 1, 4, 4 }, [FIELD_U8x4] = { 4, 1, 4, 4 }, @@ -429,10 +445,12 @@ static const luaL_Reg lovrGraphics[] = { }; extern const luaL_Reg lovrBuffer[]; +extern const luaL_Reg lovrTexture[]; int luaopen_lovr_graphics(lua_State* L) { lua_newtable(L); luax_register(L, lovrGraphics); luax_registertype(L, Buffer); + luax_registertype(L, Texture); return 1; } diff --git a/src/api/l_graphics_texture.c b/src/api/l_graphics_texture.c new file mode 100644 index 00000000..d0061f7a --- /dev/null +++ b/src/api/l_graphics_texture.c @@ -0,0 +1,117 @@ +#include "api.h" +#include "util.h" +#include "graphics/graphics.h" +#include +#include + +static int l_lovrTextureNewView(lua_State* L) { + Texture* texture = luax_checktype(L, 1, Texture); + TextureViewInfo info = { .parent = texture }; + 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); + luax_pushenum(L, TextureType, info->type); + return 1; +} + +static int l_lovrTextureGetFormat(lua_State* L) { + Texture* texture = luax_checktype(L, 1, Texture); + const TextureInfo* info = lovrTextureGetInfo(texture); + luax_pushenum(L, TextureFormat, info->format); + return 1; +} + +static int l_lovrTextureGetWidth(lua_State* L) { + Texture* texture = luax_checktype(L, 1, Texture); + const TextureInfo* info = lovrTextureGetInfo(texture); + lua_pushinteger(L, info->width); + return 1; +} + +static int l_lovrTextureGetHeight(lua_State* L) { + Texture* texture = luax_checktype(L, 1, Texture); + const TextureInfo* info = lovrTextureGetInfo(texture); + lua_pushinteger(L, info->height); + return 1; +} + +static int l_lovrTextureGetDepth(lua_State* L) { + Texture* texture = luax_checktype(L, 1, Texture); + const TextureInfo* info = lovrTextureGetInfo(texture); + lua_pushinteger(L, info->depth); + return 1; +} + +static int l_lovrTextureGetDimensions(lua_State* L) { + Texture* texture = luax_checktype(L, 1, Texture); + const TextureInfo* info = lovrTextureGetInfo(texture); + lua_pushinteger(L, info->width); + lua_pushinteger(L, info->height); + lua_pushinteger(L, info->depth); + return 3; +} + +static int l_lovrTextureGetMipmapCount(lua_State* L) { + Texture* texture = luax_checktype(L, 1, Texture); + const TextureInfo* info = lovrTextureGetInfo(texture); + lua_pushinteger(L, info->mipmaps); + return 1; +} + +static int l_lovrTextureGetSampleCount(lua_State* L) { + Texture* texture = luax_checktype(L, 1, Texture); + const TextureInfo* info = lovrTextureGetInfo(texture); + lua_pushinteger(L, info->samples); + return 1; +} + +static int l_lovrTextureHasUsage(lua_State* L) { + Texture* texture = luax_checktype(L, 1, Texture); + const TextureInfo* info = lovrTextureGetInfo(texture); + luaL_checkany(L, 2); + int top = lua_gettop(L); + for (int i = 2; i <= top; i++) { + int bit = luax_checkenum(L, i, TextureUsage, NULL); + if (~info->usage & (1 << bit)) { + lua_pushboolean(L, false); + } + } + lua_pushboolean(L, true); + return 1; +} + +const luaL_Reg lovrTexture[] = { + { "getType", l_lovrTextureGetType }, + { "getFormat", l_lovrTextureGetFormat }, + { "getWidth", l_lovrTextureGetWidth }, + { "getHeight", l_lovrTextureGetHeight }, + { "getDepth", l_lovrTextureGetDepth }, + { "getDimensions", l_lovrTextureGetDimensions }, + { "getMipmapCount", l_lovrTextureGetMipmapCount }, + { "getSampleCount", l_lovrTextureGetSampleCount }, + { NULL, NULL } +}; diff --git a/src/modules/graphics/graphics.c b/src/modules/graphics/graphics.c index 561c5e85..3419d6cb 100644 --- a/src/modules/graphics/graphics.c +++ b/src/modules/graphics/graphics.c @@ -286,13 +286,13 @@ void lovrBufferClear(Buffer* buffer, uint32_t offset, uint32_t size) { Texture* lovrTextureCreate(TextureInfo* info) { uint32_t limits[] = { [TEXTURE_2D] = state.limits.textureSize2D, + [TEXTURE_3D] = state.limits.textureSize3D, [TEXTURE_CUBE] = state.limits.textureSizeCube, - [TEXTURE_ARRAY] = state.limits.textureSize2D, - [TEXTURE_VOLUME] = state.limits.textureSize3D + [TEXTURE_ARRAY] = state.limits.textureSize2D }; uint32_t limit = limits[info->type]; - uint32_t mips = log2(MAX(MAX(info->width, info->height), (info->type == TEXTURE_VOLUME ? info->depth : 1))) + 1; + uint32_t mips = log2(MAX(MAX(info->width, info->height), (info->type == TEXTURE_3D ? info->depth : 1))) + 1; uint8_t supports = state.features.formats[info->format]; lovrCheck(info->width > 0, "Texture width must be greater than zero"); @@ -300,7 +300,7 @@ Texture* lovrTextureCreate(TextureInfo* info) { lovrCheck(info->depth > 0, "Texture depth must be greater than zero"); lovrCheck(info->width <= limit, "Texture %s exceeds the limit for this texture type (%d)", "width", limit); lovrCheck(info->height <= limit, "Texture %s exceeds the limit for this texture type (%d)", "height", limit); - lovrCheck(info->depth <= limit || info->type != TEXTURE_VOLUME, "Texture %s exceeds the limit for this texture type (%d)", "depth", limit); + lovrCheck(info->depth <= limit || info->type != TEXTURE_3D, "Texture %s exceeds the limit for this texture type (%d)", "depth", limit); lovrCheck(info->depth <= state.limits.textureLayers || info->type != TEXTURE_ARRAY, "Texture %s exceeds the limit for this texture type (%d)", "depth", limit); lovrCheck(info->depth == 1 || info->type != TEXTURE_2D, "2D textures must have a depth of 1"); lovrCheck(info->depth == 6 || info->type != TEXTURE_CUBE, "Cubemaps must have a depth of 6"); @@ -308,7 +308,7 @@ Texture* lovrTextureCreate(TextureInfo* info) { lovrCheck(measureTexture(info->format, info->width, info->height, info->depth) < 1 << 30, "Memory for a Texture can not exceed 1GB"); lovrCheck(info->samples == 1 || info->samples == 4, "Currently, Texture multisample count must be 1 or 4"); lovrCheck(info->samples == 1 || info->type != TEXTURE_CUBE, "Cubemaps can not be multisampled"); - lovrCheck(info->samples == 1 || info->type != TEXTURE_VOLUME, "Volume textures can not be multisampled"); + lovrCheck(info->samples == 1 || info->type != TEXTURE_3D, "Volume textures can not be multisampled"); lovrCheck(info->samples == 1 || ~info->usage & TEXTURE_STORAGE, "Currently, Textures with the 'storage' flag can not be multisampled"); lovrCheck(info->samples == 1 || info->mipmaps == 1, "Multisampled textures can only have 1 mipmap"); lovrCheck(~info->usage & TEXTURE_SAMPLE || (supports & GPU_FEATURE_SAMPLE), "GPU does not support the 'sample' flag for this format"); @@ -344,7 +344,7 @@ Texture* lovrTextureCreate(TextureInfo* info) { }); // Automatically create a renderable view for renderable non-volume textures - if (info->usage & TEXTURE_RENDER && info->type != TEXTURE_VOLUME && info->depth <= state.limits.renderSize[2]) { + if (info->usage & TEXTURE_RENDER && info->type != TEXTURE_3D && info->depth <= state.limits.renderSize[2]) { if (info->mipmaps == 1) { texture->renderView = texture->gpu; } else { @@ -364,6 +364,48 @@ Texture* lovrTextureCreate(TextureInfo* info) { return texture; } +Texture* lovrTextureCreateView(TextureViewInfo* view) { + const TextureInfo* info = &view->parent->info; + uint32_t maxDepth = info->type == TEXTURE_3D ? MAX(info->depth >> view->levelIndex, 1) : info->depth; + lovrCheck(!info->parent, "Can't nest texture views"); + lovrCheck(view->type != TEXTURE_3D, "Texture views may not be volume textures"); + lovrCheck(view->layerCount > 0, "Texture view must have at least one layer"); + lovrCheck(view->levelCount > 0, "Texture view must have at least one mipmap"); + lovrCheck(view->layerIndex + view->layerCount <= maxDepth, "Texture view layer range exceeds depth of parent texture"); + lovrCheck(view->levelIndex + view->levelCount <= info->mipmaps, "Texture view mipmap range exceeds mipmap count of parent texture"); + lovrCheck(view->layerCount == 1 || view->type != TEXTURE_2D, "2D texture can only have a single layer"); + lovrCheck(view->levelCount == 1 || info->type != TEXTURE_3D, "Views of volume textures may only have a single mipmap level"); + lovrCheck(view->layerCount == 6 || view->type != TEXTURE_CUBE, "Cubemaps can only have a six layers"); + + Texture* texture = calloc(1, sizeof(Texture) + gpu_sizeof_texture()); + lovrAssert(texture, "Out of memory"); + texture->ref = 1; + texture->gpu = (gpu_texture*) (texture + 1); + texture->info = *info; + + texture->info.parent = view->parent; + texture->info.mipmaps = view->levelCount; + texture->info.width = MAX(info->width >> view->levelIndex, 1); + texture->info.height = MAX(info->height >> view->levelIndex, 1); + texture->info.depth = view->layerCount; + + gpu_texture_init_view(texture->gpu, &(gpu_texture_view_info) { + .source = view->parent->gpu, + .type = (gpu_texture_type) view->type, + .layerIndex = view->layerIndex, + .layerCount = view->layerCount, + .levelIndex = view->levelIndex, + .levelCount = view->levelCount + }); + + if (view->levelCount == 1 && view->type != TEXTURE_3D && view->layerCount <= 6) { + texture->renderView = texture->gpu; + } + + lovrRetain(view->parent); + return texture; +} + void lovrTextureDestroy(void* ref) { Texture* texture = ref; lovrRelease(texture->info.parent, lovrTextureDestroy); @@ -372,6 +414,10 @@ void lovrTextureDestroy(void* ref) { free(texture); } +const TextureInfo* lovrTextureGetInfo(Texture* texture) { + return &texture->info; +} + // Pass Pass* lovrGraphicsGetPass(PassInfo* info) { diff --git a/src/modules/graphics/graphics.h b/src/modules/graphics/graphics.h index 96c1f662..4db0094c 100644 --- a/src/modules/graphics/graphics.h +++ b/src/modules/graphics/graphics.h @@ -149,7 +149,7 @@ void lovrBufferClear(Buffer* buffer, uint32_t offset, uint32_t size); typedef enum { TEXTURE_2D, - TEXTURE_VOLUME, + TEXTURE_3D, TEXTURE_CUBE, TEXTURE_ARRAY } TextureType;