Browse Source

Initial support for integer format textures.

They can't be rendered to and ImageData can't use the formats, for now.
Alex Szpakowski 4 years ago
parent
commit
48b3a4e860

+ 134 - 91
src/common/pixelformat.cpp

@@ -26,81 +26,99 @@ namespace love
 
 static PixelFormatInfo formatInfo[] =
 {
-	// components, blockW, blockH, blockSize, color, depth, stencil, compressed
-    { 0, 1, 1, 0, false, false, false, false }, // PIXELFORMAT_UNKNOWN
-
-	{ 0, 1, 1, 0, true, false, false, false }, // PIXELFORMAT_NORMAL
-	{ 0, 1, 1, 0, true, false, false, false }, // PIXELFORMAT_HDR
-
-	{ 1, 1, 1, 1, true, false, false, false }, // PIXELFORMAT_R8_UNORM
-	{ 1, 1, 1, 2, true, false, false, false }, // PIXELFORMAT_R16_UNORM
-	{ 1, 1, 1, 2, true, false, false, false }, // PIXELFORMAT_R16_FLOAT
-	{ 1, 1, 1, 4, true, false, false, false }, // PIXELFORMAT_R32_FLOAT
-
-	{ 2, 1, 1, 2, true, false, false, false }, // PIXELFORMAT_RG8_UNORM
-	{ 2, 1, 1, 2, true, false, false, false }, // PIXELFORMAT_LA8_UNORM
-	{ 2, 1, 1, 4, true, false, false, false }, // PIXELFORMAT_RG16_UNORM
-	{ 2, 1, 1, 4, true, false, false, false }, // PIXELFORMAT_RG16_FLOAT
-	{ 2, 1, 1, 8, true, false, false, false }, // PIXELFORMAT_RG32_FLOAT
-
-	{ 4, 1, 1, 4,  true, false, false, false }, // PIXELFORMAT_RGBA8_UNORM
-	{ 4, 1, 1, 4,  true, false, false, false }, // PIXELFORMAT_sRGBA8_UNORM
-	{ 4, 1, 1, 8,  true, false, false, false }, // PIXELFORMAT_RGBA16_UNORM
-	{ 4, 1, 1, 8,  true, false, false, false }, // PIXELFORMAT_RGBA16_FLOAT
-	{ 4, 1, 1, 16, true, false, false, false }, // PIXELFORMAT_RGBA32_FLOAT
-
-	{ 4, 1, 1, 2, true, false, false, false }, // PIXELFORMAT_RGBA4_UNORM
-	{ 4, 1, 1, 2, true, false, false, false }, // PIXELFORMAT_RGB5A1_UNORM
-	{ 3, 1, 1, 2, true, false, false, false }, // PIXELFORMAT_RGB565_UNORM
-	{ 4, 1, 1, 4, true, false, false, false }, // PIXELFORMAT_RGB10A2_UNORM
-	{ 3, 1, 1, 4, true, false, false, false }, // PIXELFORMAT_RG11B10_FLOAT
-
-	{ 1, 1, 1, 1, false, false, true , false }, // PIXELFORMAT_STENCIL8
-	{ 1, 1, 1, 2, false, true,  false, false }, // PIXELFORMAT_DEPTH16_UNORM
-	{ 1, 1, 1, 3, false, true,  false, false }, // PIXELFORMAT_DEPTH24_UNORM
-	{ 1, 1, 1, 4, false, true,  false, false }, // PIXELFORMAT_DEPTH32_FLOAT
-	{ 2, 1, 1, 4, false, true,  true , false }, // PIXELFORMAT_DEPTH24_UNORM_STENCIL8
-	{ 2, 1, 1, 5, false, true,  true , false }, // PIXELFORMAT_DEPTH32_FLOAT_STENCIL8
-
-	{ 3, 4, 4, 8,  true, false, false, true }, // PIXELFORMAT_DXT1_UNORM
-	{ 4, 4, 4, 16, true, false, false, true }, // PIXELFORMAT_DXT3_UNORM
-	{ 4, 4, 4, 16, true, false, false, true }, // PIXELFORMAT_DXT5_UNORM
-	{ 1, 4, 4, 8,  true, false, false, true }, // PIXELFORMAT_BC4_UNORM
-	{ 1, 4, 4, 8,  true, false, false, true }, // PIXELFORMAT_BC4_SNORM
-	{ 2, 4, 4, 16, true, false, false, true }, // PIXELFORMAT_BC5_UNORM
-	{ 2, 4, 4, 16, true, false, false, true }, // PIXELFORMAT_BC5_SNORM
-	{ 3, 4, 4, 16, true, false, false, true }, // PIXELFORMAT_BC6H_UFLOAT
-	{ 3, 4, 4, 16, true, false, false, true }, // PIXELFORMAT_BC6H_FLOAT
-	{ 4, 4, 4, 16, true, false, false, true }, // PIXELFORMAT_BC7_UNORM
-
-	{ 3, 16, 8, 32, true, false, false, true }, // PIXELFORMAT_PVR1_RGB2_UNORM
-	{ 3, 8,  8, 32, true, false, false, true }, // PIXELFORMAT_PVR1_RGB4_UNORM
-	{ 4, 16, 8, 32, true, false, false, true }, // PIXELFORMAT_PVR1_RGBA2_UNORM
-	{ 4, 8,  8, 32, true, false, false, true }, // PIXELFORMAT_PVR1_RGBA4_UNORM
-
-	{ 3, 4, 4, 8,  true, false, false, true }, // PIXELFORMAT_ETC1_UNORM
-	{ 3, 4, 4, 8,  true, false, false, true }, // PIXELFORMAT_ETC2_RGB_UNORM
-	{ 4, 4, 4, 16, true, false, false, true }, // PIXELFORMAT_ETC2_RGBA_UNORM
-	{ 4, 4, 4, 8,  true, false, false, true }, // PIXELFORMAT_ETC2_RGBA1_UNORM
-	{ 1, 4, 4, 8,  true, false, false, true }, // PIXELFORMAT_EAC_R_UNORM
-	{ 1, 4, 4, 8,  true, false, false, true }, // PIXELFORMAT_EAC_R_SNORM
-	{ 2, 4, 4, 16, true, false, false, true }, // PIXELFORMAT_EAC_RG_UNORM
-	{ 2, 4, 4, 16, true, false, false, true }, // PIXELFORMAT_EAC_RG_SNORM
-
-	{ 4, 4,  4,  1, true, false, false, true }, // PIXELFORMAT_ASTC_4x4
-	{ 4, 5,  4,  1, true, false, false, true }, // PIXELFORMAT_ASTC_5x4
-	{ 4, 5,  5,  1, true, false, false, true }, // PIXELFORMAT_ASTC_5x5
-	{ 4, 6,  5,  1, true, false, false, true }, // PIXELFORMAT_ASTC_6x5
-	{ 4, 6,  6,  1, true, false, false, true }, // PIXELFORMAT_ASTC_6x6
-	{ 4, 8,  5,  1, true, false, false, true }, // PIXELFORMAT_ASTC_8x5
-	{ 4, 8,  6,  1, true, false, false, true }, // PIXELFORMAT_ASTC_8x6
-	{ 4, 8,  8,  1, true, false, false, true }, // PIXELFORMAT_ASTC_8x8
-	{ 4, 8,  5,  1, true, false, false, true }, // PIXELFORMAT_ASTC_10x5
-	{ 4, 10, 6,  1, true, false, false, true }, // PIXELFORMAT_ASTC_10x6
-	{ 4, 10, 8,  1, true, false, false, true }, // PIXELFORMAT_ASTC_10x8
-	{ 4, 10, 10, 1, true, false, false, true }, // PIXELFORMAT_ASTC_10x10
-	{ 4, 12, 10, 1, true, false, false, true }, // PIXELFORMAT_ASTC_12x10
-	{ 4, 12, 12, 1, true, false, false, true }, // PIXELFORMAT_ASTC_12x12
+	// components, blockW, blockH, blockSize, color, depth, stencil, compressed, dataType
+    { 0, 1, 1, 0, false, false, false, false, PIXELFORMATTYPE_UNORM }, // PIXELFORMAT_UNKNOWN
+
+	{ 0, 1, 1, 0, true, false, false, false, PIXELFORMATTYPE_UNORM  }, // PIXELFORMAT_NORMAL
+	{ 0, 1, 1, 0, true, false, false, false, PIXELFORMATTYPE_SFLOAT }, // PIXELFORMAT_HDR
+
+	{ 1, 1, 1, 1, true, false, false, false, PIXELFORMATTYPE_UNORM  }, // PIXELFORMAT_R8_UNORM
+	{ 1, 1, 1, 1, true, false, false, false, PIXELFORMATTYPE_SINT   }, // PIXELFORMAT_R8_INT
+	{ 1, 1, 1, 1, true, false, false, false, PIXELFORMATTYPE_UINT   }, // PIXELFORMAT_R8_UINT
+	{ 1, 1, 1, 2, true, false, false, false, PIXELFORMATTYPE_UNORM  }, // PIXELFORMAT_R16_UNORM
+	{ 1, 1, 1, 2, true, false, false, false, PIXELFORMATTYPE_SFLOAT }, // PIXELFORMAT_R16_FLOAT
+	{ 1, 1, 1, 2, true, false, false, false, PIXELFORMATTYPE_SINT   }, // PIXELFORMAT_R16_INT
+	{ 1, 1, 1, 2, true, false, false, false, PIXELFORMATTYPE_UINT   }, // PIXELFORMAT_R16_UINT
+	{ 1, 1, 1, 4, true, false, false, false, PIXELFORMATTYPE_SFLOAT }, // PIXELFORMAT_R32_FLOAT
+	{ 1, 1, 1, 4, true, false, false, false, PIXELFORMATTYPE_SINT   }, // PIXELFORMAT_R32_INT
+	{ 1, 1, 1, 4, true, false, false, false, PIXELFORMATTYPE_UINT   }, // PIXELFORMAT_R32_UINT
+
+	{ 2, 1, 1, 2, true, false, false, false, PIXELFORMATTYPE_UNORM  }, // PIXELFORMAT_RG8_UNORM
+	{ 2, 1, 1, 2, true, false, false, false, PIXELFORMATTYPE_SINT   }, // PIXELFORMAT_RG8_INT
+	{ 2, 1, 1, 2, true, false, false, false, PIXELFORMATTYPE_UINT   }, // PIXELFORMAT_RG8_UINT
+	{ 2, 1, 1, 2, true, false, false, false, PIXELFORMATTYPE_UNORM  }, // PIXELFORMAT_LA8_UNORM
+	{ 2, 1, 1, 4, true, false, false, false, PIXELFORMATTYPE_UNORM  }, // PIXELFORMAT_RG16_UNORM
+	{ 2, 1, 1, 4, true, false, false, false, PIXELFORMATTYPE_SFLOAT }, // PIXELFORMAT_RG16_FLOAT
+	{ 2, 1, 1, 4, true, false, false, false, PIXELFORMATTYPE_SINT   }, // PIXELFORMAT_RG16_INT
+	{ 2, 1, 1, 4, true, false, false, false, PIXELFORMATTYPE_UINT   }, // PIXELFORMAT_RG16_UINT
+	{ 2, 1, 1, 8, true, false, false, false, PIXELFORMATTYPE_SFLOAT }, // PIXELFORMAT_RG32_FLOAT
+	{ 2, 1, 1, 8, true, false, false, false, PIXELFORMATTYPE_SINT   }, // PIXELFORMAT_RG32_INT
+	{ 2, 1, 1, 8, true, false, false, false, PIXELFORMATTYPE_UINT   }, // PIXELFORMAT_RG32_UINT
+
+	{ 4, 1, 1, 4,  true, false, false, false, PIXELFORMATTYPE_UNORM  }, // PIXELFORMAT_RGBA8_UNORM
+	{ 4, 1, 1, 4,  true, false, false, false, PIXELFORMATTYPE_UNORM  }, // PIXELFORMAT_sRGBA8_UNORM
+	{ 4, 1, 1, 4,  true, false, false, false, PIXELFORMATTYPE_SINT   }, // PIXELFORMAT_RGBA8_INT
+	{ 4, 1, 1, 4,  true, false, false, false, PIXELFORMATTYPE_UINT   }, // PIXELFORMAT_RGBA8_UINT
+	{ 4, 1, 1, 8,  true, false, false, false, PIXELFORMATTYPE_UNORM  }, // PIXELFORMAT_RGBA16_UNORM
+	{ 4, 1, 1, 8,  true, false, false, false, PIXELFORMATTYPE_SFLOAT }, // PIXELFORMAT_RGBA16_FLOAT
+	{ 4, 1, 1, 8,  true, false, false, false, PIXELFORMATTYPE_SINT   }, // PIXELFORMAT_RGBA16_INT
+	{ 4, 1, 1, 8,  true, false, false, false, PIXELFORMATTYPE_UINT   }, // PIXELFORMAT_RGBA16_UINT
+	{ 4, 1, 1, 16, true, false, false, false, PIXELFORMATTYPE_SFLOAT }, // PIXELFORMAT_RGBA32_FLOAT
+	{ 4, 1, 1, 16, true, false, false, false, PIXELFORMATTYPE_SINT   }, // PIXELFORMAT_RGBA32_INT
+	{ 4, 1, 1, 16, true, false, false, false, PIXELFORMATTYPE_UINT   }, // PIXELFORMAT_RGBA32_UINT
+
+	{ 4, 1, 1, 2, true, false, false, false, PIXELFORMATTYPE_UNORM  }, // PIXELFORMAT_RGBA4_UNORM
+	{ 4, 1, 1, 2, true, false, false, false, PIXELFORMATTYPE_UNORM  }, // PIXELFORMAT_RGB5A1_UNORM
+	{ 3, 1, 1, 2, true, false, false, false, PIXELFORMATTYPE_UNORM  }, // PIXELFORMAT_RGB565_UNORM
+	{ 4, 1, 1, 4, true, false, false, false, PIXELFORMATTYPE_UNORM  }, // PIXELFORMAT_RGB10A2_UNORM
+	{ 3, 1, 1, 4, true, false, false, false, PIXELFORMATTYPE_UFLOAT }, // PIXELFORMAT_RG11B10_FLOAT
+
+	{ 1, 1, 1, 1, false, false, true , false, PIXELFORMATTYPE_UINT   }, // PIXELFORMAT_STENCIL8
+	{ 1, 1, 1, 2, false, true,  false, false, PIXELFORMATTYPE_UNORM  }, // PIXELFORMAT_DEPTH16_UNORM
+	{ 1, 1, 1, 3, false, true,  false, false, PIXELFORMATTYPE_UNORM  }, // PIXELFORMAT_DEPTH24_UNORM
+	{ 1, 1, 1, 4, false, true,  false, false, PIXELFORMATTYPE_SFLOAT }, // PIXELFORMAT_DEPTH32_FLOAT
+	{ 2, 1, 1, 4, false, true,  true , false, PIXELFORMATTYPE_UNORM  }, // PIXELFORMAT_DEPTH24_UNORM_STENCIL8
+	{ 2, 1, 1, 5, false, true,  true , false, PIXELFORMATTYPE_SFLOAT }, // PIXELFORMAT_DEPTH32_FLOAT_STENCIL8
+
+	{ 3, 4, 4, 8,  true, false, false, true, PIXELFORMATTYPE_UNORM  }, // PIXELFORMAT_DXT1_UNORM
+	{ 4, 4, 4, 16, true, false, false, true, PIXELFORMATTYPE_UNORM  }, // PIXELFORMAT_DXT3_UNORM
+	{ 4, 4, 4, 16, true, false, false, true, PIXELFORMATTYPE_UNORM  }, // PIXELFORMAT_DXT5_UNORM
+	{ 1, 4, 4, 8,  true, false, false, true, PIXELFORMATTYPE_UNORM  }, // PIXELFORMAT_BC4_UNORM
+	{ 1, 4, 4, 8,  true, false, false, true, PIXELFORMATTYPE_SNORM  }, // PIXELFORMAT_BC4_SNORM
+	{ 2, 4, 4, 16, true, false, false, true, PIXELFORMATTYPE_UNORM  }, // PIXELFORMAT_BC5_UNORM
+	{ 2, 4, 4, 16, true, false, false, true, PIXELFORMATTYPE_SNORM  }, // PIXELFORMAT_BC5_SNORM
+	{ 3, 4, 4, 16, true, false, false, true, PIXELFORMATTYPE_UFLOAT }, // PIXELFORMAT_BC6H_UFLOAT
+	{ 3, 4, 4, 16, true, false, false, true, PIXELFORMATTYPE_SFLOAT }, // PIXELFORMAT_BC6H_FLOAT
+	{ 4, 4, 4, 16, true, false, false, true, PIXELFORMATTYPE_UNORM  }, // PIXELFORMAT_BC7_UNORM
+
+	{ 3, 16, 8, 32, true, false, false, true, PIXELFORMATTYPE_UNORM }, // PIXELFORMAT_PVR1_RGB2_UNORM
+	{ 3, 8,  8, 32, true, false, false, true, PIXELFORMATTYPE_UNORM }, // PIXELFORMAT_PVR1_RGB4_UNORM
+	{ 4, 16, 8, 32, true, false, false, true, PIXELFORMATTYPE_UNORM }, // PIXELFORMAT_PVR1_RGBA2_UNORM
+	{ 4, 8,  8, 32, true, false, false, true, PIXELFORMATTYPE_UNORM }, // PIXELFORMAT_PVR1_RGBA4_UNORM
+
+	{ 3, 4, 4, 8,  true, false, false, true, PIXELFORMATTYPE_UNORM }, // PIXELFORMAT_ETC1_UNORM
+	{ 3, 4, 4, 8,  true, false, false, true, PIXELFORMATTYPE_UNORM }, // PIXELFORMAT_ETC2_RGB_UNORM
+	{ 4, 4, 4, 16, true, false, false, true, PIXELFORMATTYPE_UNORM }, // PIXELFORMAT_ETC2_RGBA_UNORM
+	{ 4, 4, 4, 8,  true, false, false, true, PIXELFORMATTYPE_UNORM }, // PIXELFORMAT_ETC2_RGBA1_UNORM
+	{ 1, 4, 4, 8,  true, false, false, true, PIXELFORMATTYPE_UNORM }, // PIXELFORMAT_EAC_R_UNORM
+	{ 1, 4, 4, 8,  true, false, false, true, PIXELFORMATTYPE_SNORM }, // PIXELFORMAT_EAC_R_SNORM
+	{ 2, 4, 4, 16, true, false, false, true, PIXELFORMATTYPE_UNORM }, // PIXELFORMAT_EAC_RG_UNORM
+	{ 2, 4, 4, 16, true, false, false, true, PIXELFORMATTYPE_SNORM }, // PIXELFORMAT_EAC_RG_SNORM
+
+	{ 4, 4,  4,  1, true, false, false, true, PIXELFORMATTYPE_UNORM }, // PIXELFORMAT_ASTC_4x4
+	{ 4, 5,  4,  1, true, false, false, true, PIXELFORMATTYPE_UNORM }, // PIXELFORMAT_ASTC_5x4
+	{ 4, 5,  5,  1, true, false, false, true, PIXELFORMATTYPE_UNORM }, // PIXELFORMAT_ASTC_5x5
+	{ 4, 6,  5,  1, true, false, false, true, PIXELFORMATTYPE_UNORM }, // PIXELFORMAT_ASTC_6x5
+	{ 4, 6,  6,  1, true, false, false, true, PIXELFORMATTYPE_UNORM }, // PIXELFORMAT_ASTC_6x6
+	{ 4, 8,  5,  1, true, false, false, true, PIXELFORMATTYPE_UNORM }, // PIXELFORMAT_ASTC_8x5
+	{ 4, 8,  6,  1, true, false, false, true, PIXELFORMATTYPE_UNORM }, // PIXELFORMAT_ASTC_8x6
+	{ 4, 8,  8,  1, true, false, false, true, PIXELFORMATTYPE_UNORM }, // PIXELFORMAT_ASTC_8x8
+	{ 4, 8,  5,  1, true, false, false, true, PIXELFORMATTYPE_UNORM }, // PIXELFORMAT_ASTC_10x5
+	{ 4, 10, 6,  1, true, false, false, true, PIXELFORMATTYPE_UNORM }, // PIXELFORMAT_ASTC_10x6
+	{ 4, 10, 8,  1, true, false, false, true, PIXELFORMATTYPE_UNORM }, // PIXELFORMAT_ASTC_10x8
+	{ 4, 10, 10, 1, true, false, false, true, PIXELFORMATTYPE_UNORM }, // PIXELFORMAT_ASTC_10x10
+	{ 4, 12, 10, 1, true, false, false, true, PIXELFORMATTYPE_UNORM }, // PIXELFORMAT_ASTC_12x10
+	{ 4, 12, 12, 1, true, false, false, true, PIXELFORMATTYPE_UNORM }, // PIXELFORMAT_ASTC_12x12
 };
 
 static_assert(sizeof(formatInfo) / sizeof(PixelFormatInfo) == PIXELFORMAT_MAX_ENUM, "Update the formatInfo array when adding or removing a PixelFormat");
@@ -112,22 +130,40 @@ static StringMap<PixelFormat, PIXELFORMAT_MAX_ENUM>::Entry formatEntries[] =
 	{ "normal",  PIXELFORMAT_NORMAL  },
 	{ "hdr",     PIXELFORMAT_HDR     },
 
-	{ "r8",      PIXELFORMAT_R8_UNORM     },
-	{ "r16",     PIXELFORMAT_R16_UNORM    },
-	{ "r16f",    PIXELFORMAT_R16_FLOAT    },
-	{ "r32f",    PIXELFORMAT_R32_FLOAT    },
-
-	{ "rg8",     PIXELFORMAT_RG8_UNORM    },
-	{ "la8",     PIXELFORMAT_LA8_UNORM    },
-	{ "rg16",    PIXELFORMAT_RG16_UNORM   },
-	{ "rg16f",   PIXELFORMAT_RG16_FLOAT   },
-	{ "rg32f",   PIXELFORMAT_RG32_FLOAT   },
-
-	{ "rgba8",   PIXELFORMAT_RGBA8_UNORM  },
-	{ "srgba8",  PIXELFORMAT_sRGBA8_UNORM },
-	{ "rgba16",  PIXELFORMAT_RGBA16_UNORM },
-	{ "rgba16f", PIXELFORMAT_RGBA16_FLOAT },
-	{ "rgba32f", PIXELFORMAT_RGBA32_FLOAT },
+	{ "r8",    PIXELFORMAT_R8_UNORM  },
+	{ "r8i",   PIXELFORMAT_R8_INT    },
+	{ "r8ui",  PIXELFORMAT_R8_UINT   },
+	{ "r16",   PIXELFORMAT_R16_UNORM },
+	{ "r16f",  PIXELFORMAT_R16_FLOAT },
+	{ "r16i",  PIXELFORMAT_R16_INT   },
+	{ "r16ui", PIXELFORMAT_R16_UINT  },
+	{ "r32f",  PIXELFORMAT_R32_FLOAT },
+	{ "r32i",  PIXELFORMAT_R32_INT   },
+	{ "r32ui", PIXELFORMAT_R32_UINT  },
+
+	{ "rg8",    PIXELFORMAT_RG8_UNORM  },
+	{ "rg8i",   PIXELFORMAT_RG8_INT    },
+	{ "rg8ui",  PIXELFORMAT_RG8_UINT   },
+	{ "la8",    PIXELFORMAT_LA8_UNORM  },
+	{ "rg16",   PIXELFORMAT_RG16_UNORM },
+	{ "rg16f",  PIXELFORMAT_RG16_FLOAT },
+	{ "rg16i",  PIXELFORMAT_RG16_INT   },
+	{ "rg16ui", PIXELFORMAT_RG16_UINT  },
+	{ "rg32f",  PIXELFORMAT_RG32_FLOAT },
+	{ "rg32i",  PIXELFORMAT_RG32_INT   },
+	{ "rg32ui", PIXELFORMAT_RG32_UINT  },
+
+	{ "rgba8",    PIXELFORMAT_RGBA8_UNORM  },
+	{ "srgba8",   PIXELFORMAT_sRGBA8_UNORM },
+	{ "rgba8i",   PIXELFORMAT_RGBA8_INT    },
+	{ "rgba8ui",  PIXELFORMAT_RGBA8_UINT   },
+	{ "rgba16",   PIXELFORMAT_RGBA16_UNORM },
+	{ "rgba16f",  PIXELFORMAT_RGBA16_FLOAT },
+	{ "rgba16i",  PIXELFORMAT_RGBA16_INT   },
+	{ "rgba16ui", PIXELFORMAT_RGBA16_UINT  },
+	{ "rgba32f",  PIXELFORMAT_RGBA32_FLOAT },
+	{ "rgba32i",  PIXELFORMAT_RGBA32_INT   },
+	{ "rgba32ui", PIXELFORMAT_RGBA32_UINT  },
 
 	{ "rgba4",    PIXELFORMAT_RGBA4_UNORM    },
 	{ "rgb5a1",   PIXELFORMAT_RGB5A1_UNORM   },
@@ -164,6 +200,7 @@ static StringMap<PixelFormat, PIXELFORMAT_MAX_ENUM>::Entry formatEntries[] =
 	{ "EACrs",     PIXELFORMAT_EAC_R_SNORM      },
 	{ "EACrg",     PIXELFORMAT_EAC_RG_UNORM     },
 	{ "EACrgs",    PIXELFORMAT_EAC_RG_SNORM     },
+
 	{ "ASTC4x4",   PIXELFORMAT_ASTC_4x4   },
 	{ "ASTC5x4",   PIXELFORMAT_ASTC_5x4   },
 	{ "ASTC5x5",   PIXELFORMAT_ASTC_5x5   },
@@ -220,6 +257,12 @@ bool isPixelFormatStencil(PixelFormat format)
 	return formatInfo[format].stencil;
 }
 
+bool isPixelFormatInteger(PixelFormat format)
+{
+	auto type = formatInfo[format].dataType;
+	return type == PIXELFORMATTYPE_SINT || type == PIXELFORMATTYPE_UINT;
+}
+
 PixelFormat getSRGBPixelFormat(PixelFormat format)
 {
 	if (format == PIXELFORMAT_RGBA8_UNORM)

+ 34 - 0
src/common/pixelformat.h

@@ -35,23 +35,41 @@ enum PixelFormat
 
 	// 1-channel normal formats
 	PIXELFORMAT_R8_UNORM,
+	PIXELFORMAT_R8_INT,
+	PIXELFORMAT_R8_UINT,
 	PIXELFORMAT_R16_UNORM,
 	PIXELFORMAT_R16_FLOAT,
+	PIXELFORMAT_R16_INT,
+	PIXELFORMAT_R16_UINT,
 	PIXELFORMAT_R32_FLOAT,
+	PIXELFORMAT_R32_INT,
+	PIXELFORMAT_R32_UINT,
 
 	// 2-channel normal formats
 	PIXELFORMAT_RG8_UNORM,
+	PIXELFORMAT_RG8_INT,
+	PIXELFORMAT_RG8_UINT,
 	PIXELFORMAT_LA8_UNORM, // Same as RG8, but accessed as (L, L, L, A)
 	PIXELFORMAT_RG16_UNORM,
 	PIXELFORMAT_RG16_FLOAT,
+	PIXELFORMAT_RG16_INT,
+	PIXELFORMAT_RG16_UINT,
 	PIXELFORMAT_RG32_FLOAT,
+	PIXELFORMAT_RG32_INT,
+	PIXELFORMAT_RG32_UINT,
 
 	// 4-channel normal formats
 	PIXELFORMAT_RGBA8_UNORM,
 	PIXELFORMAT_sRGBA8_UNORM,
+	PIXELFORMAT_RGBA8_INT,
+	PIXELFORMAT_RGBA8_UINT,
 	PIXELFORMAT_RGBA16_UNORM,
 	PIXELFORMAT_RGBA16_FLOAT,
+	PIXELFORMAT_RGBA16_INT,
+	PIXELFORMAT_RGBA16_UINT,
 	PIXELFORMAT_RGBA32_FLOAT,
+	PIXELFORMAT_RGBA32_INT,
+	PIXELFORMAT_RGBA32_UINT,
 
 	// packed formats
 	PIXELFORMAT_RGBA4_UNORM,    // LSB->MSB: [a, b, g, r]
@@ -109,6 +127,16 @@ enum PixelFormat
 	PIXELFORMAT_MAX_ENUM
 };
 
+enum PixelFormatType
+{
+	PIXELFORMATTYPE_UNORM,
+	PIXELFORMATTYPE_SNORM,
+	PIXELFORMATTYPE_UFLOAT,
+	PIXELFORMATTYPE_SFLOAT,
+	PIXELFORMATTYPE_UINT,
+	PIXELFORMATTYPE_SINT,
+};
+
 struct PixelFormatInfo
 {
 	int components;
@@ -119,6 +147,7 @@ struct PixelFormatInfo
 	bool depth;
 	bool stencil;
 	bool compressed;
+	PixelFormatType dataType;
 };
 
 bool getConstant(PixelFormat in, const char *&out);
@@ -146,6 +175,11 @@ bool isPixelFormatDepth(PixelFormat format);
  **/
 bool isPixelFormatStencil(PixelFormat format);
 
+/**
+ * Gets whether the specified pixel format is a signed or unsigned integer type.
+ **/
+bool isPixelFormatInteger(PixelFormat format);
+
 /**
  * Gets the sRGB version of a linear pixel format, if applicable.
  **/

+ 6 - 0
src/modules/graphics/Graphics.cpp

@@ -693,6 +693,9 @@ void Graphics::setRenderTargets(const RenderTargets &rts)
 	if (!firsttex->isValidSlice(firsttarget.slice))
 		throw love::Exception("Invalid slice index: %d.", firsttarget.slice + 1);
 
+	if (isPixelFormatInteger(firstcolorformat))
+		throw love::Exception("Textures with integer pixel formats cannot be rendered to, currently.");
+
 	bool hasSRGBtexture = firstcolorformat == PIXELFORMAT_sRGBA8_UNORM;
 	int pixelw = firsttex->getPixelWidth(firsttarget.mipmap);
 	int pixelh = firsttex->getPixelHeight(firsttarget.mipmap);
@@ -726,6 +729,9 @@ void Graphics::setRenderTargets(const RenderTargets &rts)
 		if (isPixelFormatDepthStencil(format))
 			throw love::Exception("Depth/stencil format textures must be used with the 'depthstencil' field of the table passed into setRenderTargets.");
 
+		if (isPixelFormatInteger(format))
+			throw love::Exception("Textures with integer pixel formats cannot be rendered to, currently.");
+
 		if (format == PIXELFORMAT_sRGBA8_UNORM)
 			hasSRGBtexture = true;
 	}

+ 2 - 3
src/modules/graphics/Shader.cpp

@@ -635,10 +635,9 @@ bool Shader::isDefaultActive()
 	return false;
 }
 
-TextureType Shader::getMainTextureType() const
+const Shader::UniformInfo *Shader::getMainTextureInfo() const
 {
-	const UniformInfo *info = getUniformInfo(BUILTIN_TEXTURE_MAIN);
-	return info != nullptr ? info->textureType : TEXTURE_MAX_ENUM;
+	return getUniformInfo(BUILTIN_TEXTURE_MAIN);
 }
 
 void Shader::validateDrawState(PrimitiveType primtype, Texture *maintex) const

+ 3 - 3
src/modules/graphics/Shader.h

@@ -131,9 +131,9 @@ public:
 		};
 
 		UniformType baseType;
-		Access access;
+		DataBaseType dataBaseType;
 		TextureType textureType;
-		DataBaseType texelBufferType;
+		Access access;
 		bool isDepthSampler;
 		size_t bufferStride;
 		size_t bufferMemberCount;
@@ -221,7 +221,7 @@ public:
 	 **/
 	virtual void setVideoTextures(Texture *ytexture, Texture *cbtexture, Texture *crtexture) = 0;
 
-	TextureType getMainTextureType() const;
+	const UniformInfo *getMainTextureInfo() const;
 	void validateDrawState(PrimitiveType primtype, Texture *maintexture) const;
 
 	void getLocalThreadgroupSize(int *x, int *y, int *z);

+ 3 - 0
src/modules/graphics/Texture.cpp

@@ -504,6 +504,9 @@ void Texture::generateMipmaps()
 	if (isPixelFormatDepthStencil(format))
 		throw love::Exception("generateMipmaps cannot be called on a depth/stencil Texture.");
 
+	if (isPixelFormatInteger(format))
+		throw love::Exception("generateMipmaps cannot be called on an integer Texture.");
+
 	generateMipmapsInternal();
 }
 

+ 2 - 1
src/modules/graphics/opengl/Graphics.cpp

@@ -341,6 +341,7 @@ bool Graphics::setMode(int width, int height, int pixelwidth, int pixelheight, b
 		batchedDrawState.indexBuffer = CreateStreamBuffer(BUFFERUSAGE_INDEX, sizeof(uint16) * LOVE_UINT16_MAX);
 	}
 
+	// TODO: one buffer each for float, int, uint
 	if (capabilities.features[FEATURE_TEXEL_BUFFER] && defaultBuffers[BUFFERUSAGE_TEXEL].get() == nullptr)
 	{
 		Buffer::Settings settings(BUFFERUSAGEFLAG_TEXEL, BUFFERDATAUSAGE_STATIC);
@@ -1723,7 +1724,7 @@ bool Graphics::isPixelFormatSupported(PixelFormat format, bool rendertarget, boo
 	// Make sure at least something is bound to a color attachment. I believe
 	// this is required on ES2 but I'm not positive.
 	if (isPixelFormatDepthStencil(format))
-		gl.framebufferTexture(GL_COLOR_ATTACHMENT0, TEXTURE_2D, gl.getDefaultTexture(TEXTURE_2D), 0, 0, 0);
+		gl.framebufferTexture(GL_COLOR_ATTACHMENT0, TEXTURE_2D, gl.getDefaultTexture(TEXTURE_2D, DATA_BASETYPE_FLOAT), 0, 0, 0);
 
 	if (readable)
 	{

+ 169 - 32
src/modules/graphics/opengl/OpenGL.cpp

@@ -304,10 +304,13 @@ void OpenGL::deInitContext()
 
 	for (int i = 0; i < TEXTURE_MAX_ENUM; i++)
 	{
-		if (state.defaultTexture[i] != 0)
+		for (int datatype = DATA_BASETYPE_FLOAT; datatype <= DATA_BASETYPE_UINT; datatype++)
 		{
-			gl.deleteTexture(state.defaultTexture[i]);
-			state.defaultTexture[i] = 0;
+			if (state.defaultTexture[i][datatype] != 0)
+			{
+				gl.deleteTexture(state.defaultTexture[i][datatype]);
+				state.defaultTexture[i][datatype] = 0;
+			}
 		}
 	}
 
@@ -569,6 +572,7 @@ void OpenGL::createDefaultTexture()
 	// which would create the need to use different "passthrough" shaders for
 	// untextured primitives vs images.
 	const GLubyte pix[] = {255, 255, 255, 255};
+	const GLubyte intpix[] = {1, 1, 1, 1};
 
 	SamplerState s;
 	s.minFilter = s.magFilter = SamplerState::FILTER_NEAREST;
@@ -576,41 +580,55 @@ void OpenGL::createDefaultTexture()
 
 	for (int i = 0; i < TEXTURE_MAX_ENUM; i++)
 	{
-		state.defaultTexture[i] = 0;
+		for (int datatype = (int)DATA_BASETYPE_FLOAT; datatype <= (int)DATA_BASETYPE_UINT; datatype++)
+		{
+			state.defaultTexture[i][datatype] = 0;
 
-		TextureType type = (TextureType) i;
+			TextureType type = (TextureType) i;
 
-		if (!isTextureTypeSupported(type))
-			continue;
+			if (!isTextureTypeSupported(type))
+				continue;
 
-		GLuint curtexture = state.boundTextures[type][0];
+			if (datatype != DATA_BASETYPE_FLOAT && !(GLAD_VERSION_3_0 || GLAD_ES_VERSION_3_0))
+				continue;
 
-		glGenTextures(1, &state.defaultTexture[type]);
-		bindTextureToUnit(type, state.defaultTexture[type], 0, false);
+			GLuint curtexture = state.boundTextures[type][0];
 
-		setSamplerState(type, s);
+			glGenTextures(1, &state.defaultTexture[type][datatype]);
+			bindTextureToUnit(type, state.defaultTexture[type][datatype], 0, false);
 
-		bool isSRGB = false;
-		rawTexStorage(type, 1, PIXELFORMAT_RGBA8_UNORM, isSRGB, 1, 1);
+			setSamplerState(type, s);
 
-		TextureFormat fmt = convertPixelFormat(PIXELFORMAT_RGBA8_UNORM, false, isSRGB);
+			PixelFormat format = PIXELFORMAT_RGBA8_UNORM;
+			if (datatype == DATA_BASETYPE_INT)
+				format = PIXELFORMAT_RGBA8_INT;
+			else if (datatype == DATA_BASETYPE_UINT)
+				format = PIXELFORMAT_RGBA8_UINT;
 
-		int slices = type == TEXTURE_CUBE ? 6 : 1;
+			const GLubyte *p = datatype == DATA_BASETYPE_FLOAT ? pix : intpix;
 
-		for (int slice = 0; slice < slices; slice++)
-		{
-			GLenum gltarget = getGLTextureType(type);
+			bool isSRGB = false;
+			rawTexStorage(type, 1, format, isSRGB, 1, 1);
 
-			if (type == TEXTURE_CUBE)
-				gltarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + slice;
+			TextureFormat fmt = convertPixelFormat(format, false, isSRGB);
 
-			if (type == TEXTURE_2D || type == TEXTURE_CUBE)
-				glTexSubImage2D(gltarget, 0, 0, 0, 1, 1, fmt.externalformat, fmt.type, pix);
-			else if (type == TEXTURE_2D_ARRAY || type == TEXTURE_VOLUME)
-				glTexSubImage3D(gltarget, 0, 0, 0, slice, 1, 1, 1, fmt.externalformat, fmt.type, pix);
-		}
+			int slices = type == TEXTURE_CUBE ? 6 : 1;
 
-		bindTextureToUnit(type, curtexture, 0, false);
+			for (int slice = 0; slice < slices; slice++)
+			{
+				GLenum gltarget = getGLTextureType(type);
+
+				if (type == TEXTURE_CUBE)
+					gltarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + slice;
+
+				if (type == TEXTURE_2D || type == TEXTURE_CUBE)
+					glTexSubImage2D(gltarget, 0, 0, 0, 1, 1, fmt.externalformat, fmt.type, p);
+				else if (type == TEXTURE_2D_ARRAY || type == TEXTURE_VOLUME)
+					glTexSubImage3D(gltarget, 0, 0, 0, slice, 1, 1, 1, fmt.externalformat, fmt.type, p);
+			}
+
+			bindTextureToUnit(type, curtexture, 0, false);
+		}
 	}
 }
 
@@ -1113,9 +1131,9 @@ GLuint OpenGL::getDefaultFBO() const
 #endif
 }
 
-GLuint OpenGL::getDefaultTexture(TextureType type) const
+GLuint OpenGL::getDefaultTexture(TextureType type, DataBaseType datatype) const
 {
-	return state.defaultTexture[type];
+	return state.defaultTexture[type][datatype];
 }
 
 void OpenGL::setTextureUnit(int textureunit)
@@ -1166,14 +1184,19 @@ void OpenGL::bindTextureToUnit(Texture *texture, int textureunit, bool restorepr
 	}
 	else
 	{
+		DataBaseType datatype = DATA_BASETYPE_FLOAT;
+
 		if (textureunit == 0 && Shader::current != nullptr)
 		{
-			TextureType shadertex = Shader::current->getMainTextureType();
-			if (shadertex != TEXTURE_MAX_ENUM)
-				textype = shadertex;
+			const Shader::UniformInfo *info = Shader::current->getMainTextureInfo();
+			if (info != nullptr)
+			{
+				textype = info->textureType;
+				datatype = info->dataBaseType;
+			}
 		}
 
-		handle = getDefaultTexture(textype);
+		handle = getDefaultTexture(textype, datatype);
 	}
 
 	bindTextureToUnit(textype, handle, textureunit, restoreprev, bindforedit);
@@ -1662,6 +1685,7 @@ OpenGL::TextureFormat OpenGL::convertPixelFormat(PixelFormat pixelformat, bool r
 		f.externalformat = GL_RGBA;
 		f.type = GL_UNSIGNED_SHORT;
 		break;
+
 	case PIXELFORMAT_R16_FLOAT:
 		f.internalformat = GL_R16F;
 		f.externalformat = GL_RED;
@@ -1702,6 +1726,97 @@ OpenGL::TextureFormat OpenGL::convertPixelFormat(PixelFormat pixelformat, bool r
 		f.type = GL_FLOAT;
 		break;
 
+	case PIXELFORMAT_R8_INT:
+		f.internalformat = GL_R8I;
+		f.externalformat = GL_RED_INTEGER;
+		f.type = GL_BYTE;
+		break;
+	case PIXELFORMAT_R8_UINT:
+		f.internalformat = GL_R8UI;
+		f.externalformat = GL_RED_INTEGER;
+		f.type = GL_UNSIGNED_BYTE;
+		break;
+	case PIXELFORMAT_RG8_INT:
+		f.internalformat = GL_RG8I;
+		f.externalformat = GL_RG_INTEGER;
+		f.type = GL_BYTE;
+		break;
+	case PIXELFORMAT_RG8_UINT:
+		f.internalformat = GL_RG8UI;
+		f.externalformat = GL_RG_INTEGER;
+		f.type = GL_UNSIGNED_BYTE;
+		break;
+	case PIXELFORMAT_RGBA8_INT:
+		f.internalformat = GL_RGBA8I;
+		f.externalformat = GL_RGBA_INTEGER;
+		f.type = GL_BYTE;
+		break;
+	case PIXELFORMAT_RGBA8_UINT:
+		f.internalformat = GL_RGBA8UI;
+		f.externalformat = GL_RGBA_INTEGER;
+		f.type = GL_UNSIGNED_BYTE;
+		break;
+	case PIXELFORMAT_R16_INT:
+		f.internalformat = GL_R16I;
+		f.externalformat = GL_RED_INTEGER;
+		f.type = GL_SHORT;
+		break;
+	case PIXELFORMAT_R16_UINT:
+		f.internalformat = GL_R16UI;
+		f.externalformat = GL_RED_INTEGER;
+		f.type = GL_UNSIGNED_SHORT;
+		break;
+	case PIXELFORMAT_RG16_INT:
+		f.internalformat = GL_RG16I;
+		f.externalformat = GL_RG_INTEGER;
+		f.type = GL_SHORT;
+		break;
+	case PIXELFORMAT_RG16_UINT:
+		f.internalformat = GL_RG16UI;
+		f.externalformat = GL_RG_INTEGER;
+		f.type = GL_UNSIGNED_SHORT;
+		break;
+	case PIXELFORMAT_RGBA16_INT:
+		f.internalformat = GL_RGBA16I;
+		f.externalformat = GL_RGBA_INTEGER;
+		f.type = GL_SHORT;
+		break;
+	case PIXELFORMAT_RGBA16_UINT:
+		f.internalformat = GL_RGBA16UI;
+		f.externalformat = GL_RGBA_INTEGER;
+		f.type = GL_UNSIGNED_SHORT;
+		break;
+	case PIXELFORMAT_R32_INT:
+		f.internalformat = GL_R32I;
+		f.externalformat = GL_RED_INTEGER;
+		f.type = GL_INT;
+		break;
+	case PIXELFORMAT_R32_UINT:
+		f.internalformat = GL_R32UI;
+		f.externalformat = GL_RED_INTEGER;
+		f.type = GL_UNSIGNED_INT;
+		break;
+	case PIXELFORMAT_RG32_INT:
+		f.internalformat = GL_RG32I;
+		f.externalformat = GL_RG_INTEGER;
+		f.type = GL_INT;
+		break;
+	case PIXELFORMAT_RG32_UINT:
+		f.internalformat = GL_RG32UI;
+		f.externalformat = GL_RG_INTEGER;
+		f.type = GL_UNSIGNED_INT;
+		break;
+	case PIXELFORMAT_RGBA32_INT:
+		f.internalformat = GL_RGBA32I;
+		f.externalformat = GL_RGBA_INTEGER;
+		f.type = GL_INT;
+		break;
+	case PIXELFORMAT_RGBA32_UINT:
+		f.internalformat = GL_RGBA32UI;
+		f.externalformat = GL_RGBA_INTEGER;
+		f.type = GL_UNSIGNED_INT;
+		break;
+
 	case PIXELFORMAT_LA8_UNORM:
 		if (gl.isCoreProfile() || GLAD_ES_VERSION_3_0)
 		{
@@ -2046,6 +2161,28 @@ uint32 OpenGL::getPixelFormatUsageFlags(PixelFormat pixelformat)
 			flags &= ~PIXELFORMATUSAGEFLAGS_LINEAR;
 		break;
 
+		case PIXELFORMAT_R8_INT:
+		case PIXELFORMAT_R8_UINT:
+		case PIXELFORMAT_RG8_INT:
+		case PIXELFORMAT_RG8_UINT:
+		case PIXELFORMAT_RGBA8_INT:
+		case PIXELFORMAT_RGBA8_UINT:
+		case PIXELFORMAT_R16_INT:
+		case PIXELFORMAT_R16_UINT:
+		case PIXELFORMAT_RG16_INT:
+		case PIXELFORMAT_RG16_UINT:
+		case PIXELFORMAT_RGBA16_INT:
+		case PIXELFORMAT_RGBA16_UINT:
+		case PIXELFORMAT_R32_INT:
+		case PIXELFORMAT_R32_UINT:
+		case PIXELFORMAT_RG32_INT:
+		case PIXELFORMAT_RG32_UINT:
+		case PIXELFORMAT_RGBA32_INT:
+		case PIXELFORMAT_RGBA32_UINT:
+			if (GLAD_VERSION_3_0 || GLAD_ES_VERSION_3_0)
+				flags |= PIXELFORMATUSAGEFLAGS_SAMPLE | PIXELFORMATUSAGEFLAGS_RENDERTARGET;
+			break;
+
 	case PIXELFORMAT_LA8_UNORM:
 		flags |= commonsample;
 		break;

+ 2 - 2
src/modules/graphics/opengl/OpenGL.h

@@ -312,7 +312,7 @@ public:
 	/**
 	 * Gets the ID for love's default texture (used for "untextured" primitives.)
 	 **/
-	GLuint getDefaultTexture(TextureType type) const;
+	GLuint getDefaultTexture(TextureType type, DataBaseType datatype) const;
 
 	/**
 	 * Gets the texture ID for love's default texel buffer.
@@ -523,7 +523,7 @@ private:
 
 		GLuint boundFramebuffers[2];
 
-		GLuint defaultTexture[TEXTURE_MAX_ENUM];
+		GLuint defaultTexture[TEXTURE_MAX_ENUM][DATA_BASETYPE_MAX_ENUM];
 		GLuint defaultTexelBuffer;
 		GLuint defaultStorageBuffer;
 

+ 148 - 118
src/modules/graphics/opengl/Shader.cpp

@@ -119,16 +119,8 @@ void Shader::mapActiveUniforms()
 
 		u.name = std::string(cname, (size_t) namelen);
 		u.location = glGetUniformLocation(program, u.name.c_str());
-		u.baseType = getUniformBaseType(gltype);
 		u.access = ACCESS_READ;
-		u.textureType = getUniformTextureType(gltype);
-		u.texelBufferType = getUniformTexelBufferType(gltype);
-		u.isDepthSampler = isDepthTextureType(gltype);
-
-		if (u.baseType == UNIFORM_MATRIX)
-			u.matrix = getMatrixSize(gltype);
-		else
-			u.components = getUniformTypeComponents(gltype);
+		computeUniformTypeInfo(gltype, u);
 
 		// glGetActiveUniform appends "[0]" to the end of array uniform names...
 		if (u.name.length() > 3)
@@ -160,7 +152,7 @@ void Shader::mapActiveUniforms()
 			else
 			{
 				unit.isTexelBuffer = false;
-				unit.texture = gl.getDefaultTexture(u.textureType);
+				unit.texture = gl.getDefaultTexture(u.textureType, u.dataBaseType);
 			}
 
 			for (int i = 0; i < u.count; i++)
@@ -718,6 +710,40 @@ void Shader::updateUniform(const UniformInfo *info, int count, bool internalupda
 	}
 }
 
+static bool isResourceBaseTypeCompatible(DataBaseType a, DataBaseType b)
+{
+	if (a == DATA_BASETYPE_FLOAT || a == DATA_BASETYPE_UNORM || a == DATA_BASETYPE_SNORM)
+		return b == DATA_BASETYPE_FLOAT || b == DATA_BASETYPE_UNORM || b == DATA_BASETYPE_SNORM;
+
+	if (a == DATA_BASETYPE_INT && b == DATA_BASETYPE_INT)
+		return true;
+
+	if (a == DATA_BASETYPE_UINT && b == DATA_BASETYPE_UINT)
+		return true;
+
+	return false;
+}
+
+static DataBaseType getDataBaseType(PixelFormat format)
+{
+	switch (getPixelFormatInfo(format).dataType)
+	{
+		case PIXELFORMATTYPE_UNORM:
+			return DATA_BASETYPE_UNORM;
+		case PIXELFORMATTYPE_SNORM:
+			return DATA_BASETYPE_SNORM;
+		case PIXELFORMATTYPE_UFLOAT:
+		case PIXELFORMATTYPE_SFLOAT:
+			return DATA_BASETYPE_FLOAT;
+		case PIXELFORMATTYPE_SINT:
+			return DATA_BASETYPE_INT;
+		case PIXELFORMATTYPE_UINT:
+			return DATA_BASETYPE_UINT;
+		default:
+			return DATA_BASETYPE_FLOAT;
+	}
+}
+
 void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **textures, int count)
 {
 	Shader::sendTextures(info, textures, count, false);
@@ -742,7 +768,6 @@ void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **tex
 
 		if (tex != nullptr)
 		{
-			const SamplerState &sampler = tex->getSamplerState();
 			if (!tex->isReadable())
 			{
 				if (internalUpdate)
@@ -750,7 +775,7 @@ void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **tex
 				else
 					throw love::Exception("Textures with non-readable formats cannot be sampled from in a shader.");
 			}
-			else if (info->isDepthSampler != sampler.depthSampleMode.hasValue)
+			else if (info->isDepthSampler != tex->getSamplerState().depthSampleMode.hasValue)
 			{
 				if (internalUpdate)
 					continue;
@@ -772,6 +797,13 @@ void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **tex
 					throw love::Exception("Texture's type (%s) must match the type of %s (%s).", textypestr, info->name.c_str(), shadertextypestr);
 				}
 			}
+			else if (!isResourceBaseTypeCompatible(info->dataBaseType, getDataBaseType(tex->getPixelFormat())))
+			{
+				if (internalUpdate)
+					continue;
+				else
+					throw love::Exception("Texture's data format base type must match the uniform variable declared in the shader (float, int, or uint).");
+			}
 
 			tex->retain();
 		}
@@ -785,7 +817,7 @@ void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **tex
 		if (textures[i] != nullptr)
 			gltex = (GLuint) tex->getHandle();
 		else
-			gltex = gl.getDefaultTexture(info->textureType);
+			gltex = gl.getDefaultTexture(info->textureType, info->dataBaseType);
 
 		int texunit = info->ints[i];
 
@@ -802,20 +834,6 @@ void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffe
 	Shader::sendBuffers(info, buffers, count, false);
 }
 
-static bool isTexelBufferTypeCompatible(DataBaseType a, DataBaseType b)
-{
-	if (a == DATA_BASETYPE_FLOAT || a == DATA_BASETYPE_UNORM || a == DATA_BASETYPE_SNORM)
-		return b == DATA_BASETYPE_FLOAT || b == DATA_BASETYPE_UNORM || b == DATA_BASETYPE_SNORM;
-
-	if (a == DATA_BASETYPE_INT && b == DATA_BASETYPE_INT)
-		return true;
-
-	if (a == DATA_BASETYPE_UINT && b == DATA_BASETYPE_UINT)
-		return true;
-
-	return false;
-}
-
 void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count, bool internalUpdate)
 {
 	uint32 requiredtypeflags = 0;
@@ -860,7 +878,7 @@ void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffe
 			if (texelbinding)
 			{
 				DataBaseType basetype = buffer->getDataMember(0).info.baseType;
-				if (!isTexelBufferTypeCompatible(basetype, info->texelBufferType))
+				if (!isResourceBaseTypeCompatible(basetype, info->dataBaseType))
 				{
 					if (internalUpdate)
 						continue;
@@ -1043,11 +1061,6 @@ void Shader::updateBuiltinUniforms(love::graphics::Graphics *gfx, int viewportW,
 
 int Shader::getUniformTypeComponents(GLenum type) const
 {
-	UniformType basetype = getUniformBaseType(type);
-
-	if (basetype == UNIFORM_SAMPLER || basetype == UNIFORM_TEXELBUFFER)
-		return 1;
-
 	switch (type)
 	{
 	case GL_INT:
@@ -1122,25 +1135,35 @@ Shader::MatrixSize Shader::getMatrixSize(GLenum type) const
 	return m;
 }
 
-Shader::UniformType Shader::getUniformBaseType(GLenum type) const
+void Shader::computeUniformTypeInfo(GLenum type, UniformInfo &u)
 {
+	u.isDepthSampler = false;
+	u.components = getUniformTypeComponents(type);
+	u.baseType = UNIFORM_UNKNOWN;
+
 	switch (type)
 	{
 	case GL_INT:
 	case GL_INT_VEC2:
 	case GL_INT_VEC3:
 	case GL_INT_VEC4:
-		return UNIFORM_INT;
+		u.baseType = UNIFORM_INT;
+		u.dataBaseType = DATA_BASETYPE_INT;
+		break;
 	case GL_UNSIGNED_INT:
 	case GL_UNSIGNED_INT_VEC2:
 	case GL_UNSIGNED_INT_VEC3:
 	case GL_UNSIGNED_INT_VEC4:
-		return UNIFORM_UINT;
+		u.baseType = UNIFORM_UINT;
+		u.dataBaseType = DATA_BASETYPE_UINT;
+		break;
 	case GL_FLOAT:
 	case GL_FLOAT_VEC2:
 	case GL_FLOAT_VEC3:
 	case GL_FLOAT_VEC4:
-		return UNIFORM_FLOAT;
+		u.baseType = UNIFORM_FLOAT;
+		u.dataBaseType = DATA_BASETYPE_FLOAT;
+		break;
 	case GL_FLOAT_MAT2:
 	case GL_FLOAT_MAT3:
 	case GL_FLOAT_MAT4:
@@ -1150,105 +1173,112 @@ Shader::UniformType Shader::getUniformBaseType(GLenum type) const
 	case GL_FLOAT_MAT3x4:
 	case GL_FLOAT_MAT4x2:
 	case GL_FLOAT_MAT4x3:
-		return UNIFORM_MATRIX;
+		u.baseType = UNIFORM_MATRIX;
+		u.dataBaseType = DATA_BASETYPE_FLOAT;
+		u.matrix = getMatrixSize(type);
+		break;
 	case GL_BOOL:
 	case GL_BOOL_VEC2:
 	case GL_BOOL_VEC3:
 	case GL_BOOL_VEC4:
-		return UNIFORM_BOOL;
-	case GL_SAMPLER_1D:
-	case GL_SAMPLER_1D_SHADOW:
-	case GL_SAMPLER_1D_ARRAY:
-	case GL_SAMPLER_1D_ARRAY_SHADOW:
-	case GL_SAMPLER_2D:
-	case GL_SAMPLER_2D_MULTISAMPLE:
-	case GL_SAMPLER_2D_MULTISAMPLE_ARRAY:
-	case GL_SAMPLER_2D_RECT:
-	case GL_SAMPLER_2D_RECT_SHADOW:
-	case GL_SAMPLER_2D_SHADOW:
-	case GL_SAMPLER_2D_ARRAY:
-	case GL_SAMPLER_2D_ARRAY_SHADOW:
-	case GL_SAMPLER_3D:
-	case GL_SAMPLER_CUBE:
-	case GL_SAMPLER_CUBE_SHADOW:
-	case GL_SAMPLER_CUBE_MAP_ARRAY:
-	case GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW:
-		return UNIFORM_SAMPLER;
-	case GL_SAMPLER_BUFFER:
-	case GL_INT_SAMPLER_BUFFER:
-	case GL_UNSIGNED_INT_SAMPLER_BUFFER:
-		return UNIFORM_TEXELBUFFER;
-	default:
-		return UNIFORM_UNKNOWN;
-	}
-}
+		u.baseType = UNIFORM_BOOL;
+		u.dataBaseType = DATA_BASETYPE_BOOL;
+		break;
 
-TextureType Shader::getUniformTextureType(GLenum type) const
-{
-	switch (type)
-	{
-	case GL_SAMPLER_1D:
-	case GL_SAMPLER_1D_SHADOW:
-	case GL_SAMPLER_1D_ARRAY:
-	case GL_SAMPLER_1D_ARRAY_SHADOW:
-		// 1D-typed textures are not supported.
-		return TEXTURE_MAX_ENUM;
 	case GL_SAMPLER_2D:
+		u.baseType = UNIFORM_SAMPLER;
+		u.dataBaseType = DATA_BASETYPE_FLOAT;
+		u.textureType = TEXTURE_2D;
+		break;
 	case GL_SAMPLER_2D_SHADOW:
-		return TEXTURE_2D;
-	case GL_SAMPLER_2D_MULTISAMPLE:
-	case GL_SAMPLER_2D_MULTISAMPLE_ARRAY:
-		// Multisample textures are not supported.
-		return TEXTURE_MAX_ENUM;
-	case GL_SAMPLER_2D_RECT:
-	case GL_SAMPLER_2D_RECT_SHADOW:
-		// Rectangle textures are not supported.
-		return TEXTURE_MAX_ENUM;
+		u.baseType = UNIFORM_SAMPLER;
+		u.dataBaseType = DATA_BASETYPE_FLOAT;
+		u.textureType = TEXTURE_2D;
+		u.isDepthSampler = true;
+		break;
+	case GL_INT_SAMPLER_2D:
+		u.baseType = UNIFORM_SAMPLER;
+		u.dataBaseType = DATA_BASETYPE_INT;
+		u.textureType = TEXTURE_2D;
+		break;
+	case GL_UNSIGNED_INT_SAMPLER_2D:
+		u.baseType = UNIFORM_SAMPLER;
+		u.dataBaseType = DATA_BASETYPE_UINT;
+		u.textureType = TEXTURE_2D;
+		break;
 	case GL_SAMPLER_2D_ARRAY:
+		u.baseType = UNIFORM_SAMPLER;
+		u.dataBaseType = DATA_BASETYPE_FLOAT;
+		u.textureType = TEXTURE_2D_ARRAY;
+		break;
 	case GL_SAMPLER_2D_ARRAY_SHADOW:
-		return TEXTURE_2D_ARRAY;
+		u.baseType = UNIFORM_SAMPLER;
+		u.dataBaseType = DATA_BASETYPE_FLOAT;
+		u.textureType = TEXTURE_2D_ARRAY;
+		u.isDepthSampler = true;
+		break;
+	case GL_INT_SAMPLER_2D_ARRAY:
+		u.baseType = UNIFORM_SAMPLER;
+		u.dataBaseType = DATA_BASETYPE_INT;
+		u.textureType = TEXTURE_2D_ARRAY;
+		break;
+	case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
+		u.baseType = UNIFORM_SAMPLER;
+		u.dataBaseType = DATA_BASETYPE_UINT;
+		u.textureType = TEXTURE_2D_ARRAY;
+		break;
 	case GL_SAMPLER_3D:
-		return TEXTURE_VOLUME;
+		u.baseType = UNIFORM_SAMPLER;
+		u.dataBaseType = DATA_BASETYPE_FLOAT;
+		u.textureType = TEXTURE_VOLUME;
+		break;
+	case GL_INT_SAMPLER_3D:
+		u.baseType = UNIFORM_SAMPLER;
+		u.dataBaseType = DATA_BASETYPE_INT;
+		u.textureType = TEXTURE_VOLUME;
+		break;
+	case GL_UNSIGNED_INT_SAMPLER_3D:
+		u.baseType = UNIFORM_SAMPLER;
+		u.dataBaseType = DATA_BASETYPE_UINT;
+		u.textureType = TEXTURE_VOLUME;
+		break;
 	case GL_SAMPLER_CUBE:
+		u.baseType = UNIFORM_SAMPLER;
+		u.dataBaseType = DATA_BASETYPE_FLOAT;
+		u.textureType = TEXTURE_CUBE;
+		break;
 	case GL_SAMPLER_CUBE_SHADOW:
-		return TEXTURE_CUBE;
-	case GL_SAMPLER_CUBE_MAP_ARRAY:
-	case GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW:
-		// Cubemap array textures are not supported.
-		return TEXTURE_MAX_ENUM;
-	default:
-		return TEXTURE_MAX_ENUM;
-	}
-}
+		u.baseType = UNIFORM_SAMPLER;
+		u.dataBaseType = DATA_BASETYPE_FLOAT;
+		u.textureType = TEXTURE_CUBE;
+		u.isDepthSampler = true;
+		break;
+	case GL_INT_SAMPLER_CUBE:
+		u.baseType = UNIFORM_SAMPLER;
+		u.dataBaseType = DATA_BASETYPE_INT;
+		u.textureType = TEXTURE_CUBE;
+		break;
+	case GL_UNSIGNED_INT_SAMPLER_CUBE:
+		u.baseType = UNIFORM_SAMPLER;
+		u.dataBaseType = DATA_BASETYPE_UINT;
+		u.textureType = TEXTURE_CUBE;
+		break;
 
-DataBaseType Shader::getUniformTexelBufferType(GLenum type) const
-{
-	switch (type)
-	{
 	case GL_SAMPLER_BUFFER:
-		return DATA_BASETYPE_FLOAT;
+		u.baseType = UNIFORM_TEXELBUFFER;
+		u.dataBaseType = DATA_BASETYPE_FLOAT;
+		break;
 	case GL_INT_SAMPLER_BUFFER:
-		return DATA_BASETYPE_INT;
+		u.baseType = UNIFORM_TEXELBUFFER;
+		u.dataBaseType = DATA_BASETYPE_INT;
+		break;
 	case GL_UNSIGNED_INT_SAMPLER_BUFFER:
-		return DATA_BASETYPE_UINT;
-	default:
-		return DATA_BASETYPE_MAX_ENUM;
-	}
-}
+		u.baseType = UNIFORM_TEXELBUFFER;
+		u.dataBaseType = DATA_BASETYPE_UINT;
+		break;
 
-bool Shader::isDepthTextureType(GLenum type) const
-{
-	switch (type)
-	{
-	case GL_SAMPLER_1D_SHADOW:
-	case GL_SAMPLER_1D_ARRAY_SHADOW:
-	case GL_SAMPLER_2D_SHADOW:
-	case GL_SAMPLER_2D_ARRAY_SHADOW:
-	case GL_SAMPLER_CUBE_SHADOW:
-	case GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW:
-		return true;
 	default:
-		return false;
+		break;
 	}
 }
 

+ 1 - 4
src/modules/graphics/opengl/Shader.h

@@ -92,11 +92,8 @@ private:
 	void sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count, bool internalupdate);
 
 	int getUniformTypeComponents(GLenum type) const;
+	void computeUniformTypeInfo(GLenum type, UniformInfo &u);
 	MatrixSize getMatrixSize(GLenum type) const;
-	UniformType getUniformBaseType(GLenum type) const;
-	TextureType getUniformTextureType(GLenum type) const;
-	DataBaseType getUniformTexelBufferType(GLenum type) const;
-	bool isDepthTextureType(GLenum type) const;
 
 	void flushBatchedDraws() const;