Browse Source

Change the 'viewformats' texture setting field to be a list of pixel formats.

Replace Texture:hasViewFormats with Texture:getViewFormats.
Sasha Szpakowski 1 year ago
parent
commit
e20f018272

+ 25 - 21
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;

+ 6 - 3
src/modules/graphics/Texture.h

@@ -39,6 +39,7 @@
 
 // C
 #include <stddef.h>
+#include <vector>
 
 namespace love
 {
@@ -195,7 +196,7 @@ public:
 		int msaa = 1;
 		bool renderTarget = false;
 		bool computeWrite = false;
-		bool viewFormats = false;
+		std::vector<PixelFormat> 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<PixelFormat> &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<PixelFormat> viewFormats;
+
 	MipmapsMode mipmapsMode;
 
 	int width;

+ 9 - 2
src/modules/graphics/metal/Texture.mm

@@ -76,8 +76,15 @@ Texture::Texture(love::graphics::Graphics *gfxbase, id<MTLDevice> 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];
 

+ 2 - 0
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<love::graphics::ShaderStage> stages[SHADERSTAGE_MAX_ENUM], const Shader::CompileOptions &options) override;

+ 21 - 2
src/modules/graphics/vulkan/Texture.cpp

@@ -100,9 +100,17 @@ bool Texture::loadVolatile()
 	if (root)
 	{
 		VkImageCreateFlags createFlags = 0;
+		std::vector<VkFormat> 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)

+ 19 - 1
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))

+ 14 - 3
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<PixelFormat> &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 },