From e20f0182723aafcb403a9dd96313d21bdde3d3bc Mon Sep 17 00:00:00 2001 From: Sasha Szpakowski Date: Sat, 24 Feb 2024 23:42:28 -0400 Subject: [PATCH] Change the 'viewformats' texture setting field to be a list of pixel formats. Replace Texture:hasViewFormats with Texture:getViewFormats. --- src/modules/graphics/Texture.cpp | 46 ++++++++++++++----------- src/modules/graphics/Texture.h | 9 +++-- src/modules/graphics/metal/Texture.mm | 11 ++++-- src/modules/graphics/vulkan/Graphics.h | 2 ++ src/modules/graphics/vulkan/Texture.cpp | 23 +++++++++++-- src/modules/graphics/wrap_Graphics.cpp | 20 ++++++++++- src/modules/graphics/wrap_Texture.cpp | 17 +++++++-- 7 files changed, 96 insertions(+), 32 deletions(-) diff --git a/src/modules/graphics/Texture.cpp b/src/modules/graphics/Texture.cpp index fb989328d..a880a51e8 100644 --- a/src/modules/graphics/Texture.cpp +++ b/src/modules/graphics/Texture.cpp @@ -287,6 +287,28 @@ Texture::Texture(Graphics *gfx, const Settings &settings, const Slices *slices) if (isCompressed() && renderTarget) throw love::Exception("Compressed textures cannot be render targets."); + for (PixelFormat viewformat : viewFormats) + { + if (getLinearPixelFormat(viewformat) == getLinearPixelFormat(format)) + continue; + + if (isPixelFormatCompressed(format) || isPixelFormatCompressed(viewformat)) + throw love::Exception("Compressed textures cannot use different pixel formats for texture views, aside from sRGB versus linear variants of the same pixel format."); + + if (isPixelFormatColor(viewformat) != isPixelFormatColor(format)) + throw love::Exception("Color-format textures cannot use depth/stencil pixel formats and vice versa, in texture views."); + + // TODO: depth[24|32f]_stencil8 -> stencil8 can work. + if (isPixelFormatDepthStencil(viewformat)) + throw love::Exception("Using different pixel formats for texture views is not currently supported for depth or stencil formats."); + + size_t viewbytes = getPixelFormatBlockSize(viewformat); + size_t basebytes = getPixelFormatBlockSize(format); + + if (viewbytes != basebytes) + throw love::Exception("Texture view pixel formats must have the same bits per pixel as the base texture's pixel format."); + } + validatePixelFormat(gfx); if (!caps.textureTypes[texType]) @@ -409,28 +431,10 @@ Texture::Texture(Graphics *gfx, Texture *base, const ViewSettings &viewsettings) throw love::Exception("Unknown texture type."); } - auto baseformat = base->getPixelFormat(); - - if (format != baseformat) + if (format != base->getPixelFormat()) { - if (isPixelFormatCompressed(baseformat) || isPixelFormatCompressed(format)) - throw love::Exception("Compressed textures cannot use a different pixel format in a texture view."); - - if (isPixelFormatColor(baseformat) != isPixelFormatColor(format)) - throw love::Exception("Color-format textures cannot use a depth/stencil pixel format and vice versa, in a texture view."); - - // TODO: depth[24|32f]_stencil8 -> stencil8 can work. - if (isPixelFormatDepthStencil(baseformat)) - throw love::Exception("Using a different pixel format in a texture view is not currently supported for depth or stencil formats."); - - if (!viewFormats) - throw love::Exception("Using a different pixel format in a texture view requires the original texture to be created with the 'viewformats' setting set to true."); - - size_t bytes = getPixelFormatBlockSize(format); - size_t basebytes = getPixelFormatBlockSize(baseformat); - - if (bytes != basebytes) - throw love::Exception("Texture views must have the same bits per pixel as the original texture."); + if (std::find(viewFormats.begin(), viewFormats.end(), format) == viewFormats.end()) + throw love::Exception("Using a different pixel format in a texture view requires the original texture to be created with a 'viewformats' setting that includes the given format in its list."); } const char *miperr = nullptr; diff --git a/src/modules/graphics/Texture.h b/src/modules/graphics/Texture.h index 099442d50..0f228848f 100644 --- a/src/modules/graphics/Texture.h +++ b/src/modules/graphics/Texture.h @@ -39,6 +39,7 @@ // C #include +#include namespace love { @@ -195,7 +196,7 @@ public: int msaa = 1; bool renderTarget = false; bool computeWrite = false; - bool viewFormats = false; + std::vector viewFormats; OptionalBool readable; std::string debugName; }; @@ -279,7 +280,8 @@ public: bool isRenderTarget() const { return renderTarget; } bool isComputeWritable() const { return computeWrite; } bool isReadable() const { return readable; } - bool hasViewFormats() const { return viewFormats; } + + const std::vector &getViewFormats() const { return viewFormats; } bool isCompressed() const; bool isFormatLinear() const; @@ -353,9 +355,10 @@ protected: PixelFormat format; bool renderTarget; bool computeWrite; - bool viewFormats; bool readable; + std::vector viewFormats; + MipmapsMode mipmapsMode; int width; diff --git a/src/modules/graphics/metal/Texture.mm b/src/modules/graphics/metal/Texture.mm index b5c23c8f3..e880495ae 100644 --- a/src/modules/graphics/metal/Texture.mm +++ b/src/modules/graphics/metal/Texture.mm @@ -76,8 +76,15 @@ Texture::Texture(love::graphics::Graphics *gfxbase, id device, const desc.usage |= MTLTextureUsageRenderTarget; if (computeWrite) desc.usage |= MTLTextureUsageShaderWrite; - if (viewFormats) - desc.usage |= MTLTextureUsagePixelFormatView; + + for (PixelFormat viewformat : viewFormats) + { + if (getLinearPixelFormat(viewformat) != getLinearPixelFormat(format)) + { + desc.usage |= MTLTextureUsagePixelFormatView; + break; + } + } texture = [device newTextureWithDescriptor:desc]; diff --git a/src/modules/graphics/vulkan/Graphics.h b/src/modules/graphics/vulkan/Graphics.h index 1726b200c..084c2b836 100644 --- a/src/modules/graphics/vulkan/Graphics.h +++ b/src/modules/graphics/vulkan/Graphics.h @@ -326,6 +326,8 @@ public: void setVsync(int vsync); int getVsync() const; + uint32 getDeviceApiVersion() const { return deviceApiVersion; } + protected: graphics::ShaderStage *newShaderStageInternal(ShaderStageType stage, const std::string &cachekey, const std::string &source, bool gles) override; graphics::Shader *newShaderInternal(StrongRef stages[SHADERSTAGE_MAX_ENUM], const Shader::CompileOptions &options) override; diff --git a/src/modules/graphics/vulkan/Texture.cpp b/src/modules/graphics/vulkan/Texture.cpp index 46a6a01cc..229e6bc50 100644 --- a/src/modules/graphics/vulkan/Texture.cpp +++ b/src/modules/graphics/vulkan/Texture.cpp @@ -100,9 +100,17 @@ bool Texture::loadVolatile() if (root) { VkImageCreateFlags createFlags = 0; + std::vector vkviewformats; - if (viewFormats) - createFlags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; + for (PixelFormat viewformat : viewFormats) + { + if (viewformat != format) + { + createFlags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; + TextureFormat f = Vulkan::getTextureFormat(viewformat); + vkviewformats.push_back(f.internalFormat); + } + } if (texType == TEXTURE_CUBE || texType == TEXTURE_2D_ARRAY) createFlags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; @@ -125,6 +133,17 @@ bool Texture::loadVolatile() imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; imageInfo.samples = msaaSamples; + VkImageFormatListCreateInfo viewFormatsInfo{}; + viewFormatsInfo.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO; + + if (!vkviewformats.empty() && vgfx->getDeviceApiVersion() >= VK_API_VERSION_1_2) + { + viewFormatsInfo.viewFormatCount = (uint32)vkviewformats.size(); + viewFormatsInfo.pViewFormats = vkviewformats.data(); + + imageInfo.pNext = &viewFormatsInfo; + } + VmaAllocationCreateInfo imageAllocationCreateInfo{}; if (vmaCreateImage(allocator, &imageInfo, &imageAllocationCreateInfo, &textureImage, &textureImageAllocation, nullptr) != VK_SUCCESS) diff --git a/src/modules/graphics/wrap_Graphics.cpp b/src/modules/graphics/wrap_Graphics.cpp index e2221c9b6..15af25563 100644 --- a/src/modules/graphics/wrap_Graphics.cpp +++ b/src/modules/graphics/wrap_Graphics.cpp @@ -830,7 +830,25 @@ static void luax_checktexturesettings(lua_State *L, int idx, bool opt, bool chec s.msaa = luax_intflag(L, idx, Texture::getConstant(Texture::SETTING_MSAA), s.msaa); s.computeWrite = luax_boolflag(L, idx, Texture::getConstant(Texture::SETTING_COMPUTE_WRITE), s.computeWrite); - s.viewFormats = luax_boolflag(L, idx, Texture::getConstant(Texture::SETTING_VIEW_FORMATS), s.viewFormats); + + lua_getfield(L, idx, Texture::getConstant(Texture::SETTING_VIEW_FORMATS)); + if (!lua_isnoneornil(L, -1)) + { + if (lua_type(L, -1) != LUA_TTABLE) + luaL_argerror(L, idx, "expected field 'viewformats' to be a table type"); + + for (int i = 1; i <= luax_objlen(L, -1); i++) + { + lua_rawgeti(L, -1, i); + const char *str = luaL_checkstring(L, -1); + PixelFormat viewformat = PIXELFORMAT_UNKNOWN; + if (!getConstant(str, viewformat)) + luax_enumerror(L, "pixel format", str); + s.viewFormats.push_back(viewformat); + lua_pop(L, 1); + } + } + lua_pop(L, 1); lua_getfield(L, idx, Texture::getConstant(Texture::SETTING_READABLE)); if (!lua_isnoneornil(L, -1)) diff --git a/src/modules/graphics/wrap_Texture.cpp b/src/modules/graphics/wrap_Texture.cpp index 533cb1997..b667c48b3 100644 --- a/src/modules/graphics/wrap_Texture.cpp +++ b/src/modules/graphics/wrap_Texture.cpp @@ -300,10 +300,21 @@ int w_Texture_isReadable(lua_State *L) return 1; } -int w_Texture_hasViewFormats(lua_State *L) +int w_Texture_getViewFormats(lua_State *L) { Texture *t = luax_checktexture(L, 1); - luax_pushboolean(L, t->hasViewFormats()); + const std::vector &viewformats = t->getViewFormats(); + + lua_createtable(L, (int) viewformats.size(), 0); + for (int i = 0; i < (int) viewformats.size(); i++) + { + const char *str = nullptr; + if (!getConstant(viewformats[i], str)) + return luaL_error(L, "Unknown pixel format."); + lua_pushstring(L, str); + lua_rawseti(L, -2, i + 1); + } + return 1; } @@ -518,7 +529,7 @@ const luaL_Reg w_Texture_functions[] = { "isCanvas", w_Texture_isCanvas }, { "isComputeWritable", w_Texture_isComputeWritable }, { "isReadable", w_Texture_isReadable }, - { "hasViewFormats", w_Texture_hasViewFormats }, + { "getViewFormats", w_Texture_getViewFormats }, { "getMipmapMode", w_Texture_getMipmapMode }, { "getDepthSampleMode", w_Texture_getDepthSampleMode }, { "setDepthSampleMode", w_Texture_setDepthSampleMode },