diff --git a/CMakeLists.txt b/CMakeLists.txt index 1413f57ac..726e778db 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -580,14 +580,10 @@ set(LOVE_SRC_MODULE_GRAPHICS_ROOT set(LOVE_SRC_MODULE_GRAPHICS_OPENGL src/modules/graphics/opengl/Buffer.cpp src/modules/graphics/opengl/Buffer.h - src/modules/graphics/opengl/Canvas.cpp - src/modules/graphics/opengl/Canvas.h src/modules/graphics/opengl/FenceSync.cpp src/modules/graphics/opengl/FenceSync.h src/modules/graphics/opengl/Graphics.cpp src/modules/graphics/opengl/Graphics.h - src/modules/graphics/opengl/Image.cpp - src/modules/graphics/opengl/Image.h src/modules/graphics/opengl/OpenGL.cpp src/modules/graphics/opengl/OpenGL.h src/modules/graphics/opengl/Shader.cpp @@ -596,6 +592,8 @@ set(LOVE_SRC_MODULE_GRAPHICS_OPENGL src/modules/graphics/opengl/ShaderStage.h src/modules/graphics/opengl/StreamBuffer.cpp src/modules/graphics/opengl/StreamBuffer.h + src/modules/graphics/opengl/Texture.cpp + src/modules/graphics/opengl/Texture.h ) set(LOVE_SRC_MODULE_GRAPHICS diff --git a/platform/xcode/liblove.xcodeproj/project.pbxproj b/platform/xcode/liblove.xcodeproj/project.pbxproj index 73496ae8a..0a692bf79 100644 --- a/platform/xcode/liblove.xcodeproj/project.pbxproj +++ b/platform/xcode/liblove.xcodeproj/project.pbxproj @@ -403,15 +403,12 @@ FA0B7D301A95902C000E1D17 /* Graphics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7B8A1A95902C000E1D17 /* Graphics.cpp */; }; FA0B7D311A95902C000E1D17 /* Graphics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7B8A1A95902C000E1D17 /* Graphics.cpp */; }; FA0B7D321A95902C000E1D17 /* Graphics.h in Headers */ = {isa = PBXBuildFile; fileRef = FA0B7B8B1A95902C000E1D17 /* Graphics.h */; }; - FA0B7D331A95902C000E1D17 /* Canvas.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7B8D1A95902C000E1D17 /* Canvas.cpp */; }; - FA0B7D341A95902C000E1D17 /* Canvas.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7B8D1A95902C000E1D17 /* Canvas.cpp */; }; - FA0B7D351A95902C000E1D17 /* Canvas.h in Headers */ = {isa = PBXBuildFile; fileRef = FA0B7B8E1A95902C000E1D17 /* Canvas.h */; }; FA0B7D391A95902C000E1D17 /* Graphics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7B911A95902C000E1D17 /* Graphics.cpp */; }; FA0B7D3A1A95902C000E1D17 /* Graphics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7B911A95902C000E1D17 /* Graphics.cpp */; }; FA0B7D3B1A95902C000E1D17 /* Graphics.h in Headers */ = {isa = PBXBuildFile; fileRef = FA0B7B921A95902C000E1D17 /* Graphics.h */; }; - FA0B7D3C1A95902C000E1D17 /* Image.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7B931A95902C000E1D17 /* Image.cpp */; }; - FA0B7D3D1A95902C000E1D17 /* Image.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7B931A95902C000E1D17 /* Image.cpp */; }; - FA0B7D3E1A95902C000E1D17 /* Image.h in Headers */ = {isa = PBXBuildFile; fileRef = FA0B7B941A95902C000E1D17 /* Image.h */; }; + FA0B7D3C1A95902C000E1D17 /* Texture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7B931A95902C000E1D17 /* Texture.cpp */; }; + FA0B7D3D1A95902C000E1D17 /* Texture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7B931A95902C000E1D17 /* Texture.cpp */; }; + FA0B7D3E1A95902C000E1D17 /* Texture.h in Headers */ = {isa = PBXBuildFile; fileRef = FA0B7B941A95902C000E1D17 /* Texture.h */; }; FA0B7D421A95902C000E1D17 /* OpenGL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7B971A95902C000E1D17 /* OpenGL.cpp */; }; FA0B7D431A95902C000E1D17 /* OpenGL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7B971A95902C000E1D17 /* OpenGL.cpp */; }; FA0B7D441A95902C000E1D17 /* OpenGL.h in Headers */ = {isa = PBXBuildFile; fileRef = FA0B7B981A95902C000E1D17 /* OpenGL.h */; }; @@ -1536,12 +1533,10 @@ FA0B7B891A95902C000E1D17 /* Drawable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Drawable.h; sourceTree = ""; }; FA0B7B8A1A95902C000E1D17 /* Graphics.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = Graphics.cpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; FA0B7B8B1A95902C000E1D17 /* Graphics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Graphics.h; sourceTree = ""; }; - FA0B7B8D1A95902C000E1D17 /* Canvas.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = Canvas.cpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; - FA0B7B8E1A95902C000E1D17 /* Canvas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Canvas.h; sourceTree = ""; }; FA0B7B911A95902C000E1D17 /* Graphics.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = Graphics.cpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; FA0B7B921A95902C000E1D17 /* Graphics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Graphics.h; sourceTree = ""; }; - FA0B7B931A95902C000E1D17 /* Image.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Image.cpp; sourceTree = ""; }; - FA0B7B941A95902C000E1D17 /* Image.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Image.h; sourceTree = ""; }; + FA0B7B931A95902C000E1D17 /* Texture.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Texture.cpp; sourceTree = ""; }; + FA0B7B941A95902C000E1D17 /* Texture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Texture.h; sourceTree = ""; }; FA0B7B971A95902C000E1D17 /* OpenGL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OpenGL.cpp; sourceTree = ""; }; FA0B7B981A95902C000E1D17 /* OpenGL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OpenGL.h; sourceTree = ""; }; FA0B7B9B1A95902C000E1D17 /* Polyline.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Polyline.cpp; sourceTree = ""; }; @@ -2887,14 +2882,10 @@ children = ( FA0B7BA41A95902C000E1D17 /* Buffer.cpp */, FA0B7BA51A95902C000E1D17 /* Buffer.h */, - FA0B7B8D1A95902C000E1D17 /* Canvas.cpp */, - FA0B7B8E1A95902C000E1D17 /* Canvas.h */, FA28EBD31E352DB5003446F4 /* FenceSync.cpp */, FA28EBD41E352DB5003446F4 /* FenceSync.h */, FA0B7B911A95902C000E1D17 /* Graphics.cpp */, FA0B7B921A95902C000E1D17 /* Graphics.h */, - FA0B7B931A95902C000E1D17 /* Image.cpp */, - FA0B7B941A95902C000E1D17 /* Image.h */, FA0B7B971A95902C000E1D17 /* OpenGL.cpp */, FA0B7B981A95902C000E1D17 /* OpenGL.h */, FA0B7B9D1A95902C000E1D17 /* Shader.cpp */, @@ -2903,6 +2894,8 @@ FA3C5E461F8D80CA0003C579 /* ShaderStage.h */, FA7634481E28722A0066EF9E /* StreamBuffer.cpp */, FA7634491E28722A0066EF9E /* StreamBuffer.h */, + FA0B7B931A95902C000E1D17 /* Texture.cpp */, + FA0B7B941A95902C000E1D17 /* Texture.h */, ); path = opengl; sourceTree = ""; @@ -3820,9 +3813,8 @@ FA0B7CFC1A95902C000E1D17 /* Filesystem.h in Headers */, FA0B7AD81A958EA3000E1D17 /* lua-enet.h in Headers */, FA0B7A3A1A958EA3000E1D17 /* b2DynamicTree.h in Headers */, - FA0B7D351A95902C000E1D17 /* Canvas.h in Headers */, FA0B7EBA1A95902C000E1D17 /* Channel.h in Headers */, - FA0B7D3E1A95902C000E1D17 /* Image.h in Headers */, + FA0B7D3E1A95902C000E1D17 /* Texture.h in Headers */, FA0B7ECA1A95902C000E1D17 /* threads.h in Headers */, FADF54361E3DAE6E00012CC0 /* wrap_SpriteBatch.h in Headers */, FA0B7DB01A95902C000E1D17 /* wrap_CompressedImageData.h in Headers */, @@ -4381,7 +4373,7 @@ FA0B7D191A95902C000E1D17 /* TrueTypeRasterizer.cpp in Sources */, FAC271E723B5B5B400C200D3 /* renderstate.cpp in Sources */, FA0B7CFB1A95902C000E1D17 /* Filesystem.cpp in Sources */, - FA0B7D3D1A95902C000E1D17 /* Image.cpp in Sources */, + FA0B7D3D1A95902C000E1D17 /* Texture.cpp in Sources */, FA0B7B351A958EA3000E1D17 /* wuff_convert.c in Sources */, FAF140941E20934C00F898D2 /* PpScanner.cpp in Sources */, FA9D53AD1F5307E900125C6B /* Deprecations.cpp in Sources */, @@ -4545,7 +4537,6 @@ FA0B79411A958E3B000E1D17 /* utf8.cpp in Sources */, FAE64A862071363100BC7981 /* physfs_archiver_qpak.c in Sources */, FA0B7ADF1A958EA3000E1D17 /* lodepng.cpp in Sources */, - FA0B7D341A95902C000E1D17 /* Canvas.cpp in Sources */, FAF140761E20934C00F898D2 /* IntermTraverse.cpp in Sources */, FA0B7E8C1A95902C000E1D17 /* FLACDecoder.cpp in Sources */, FA0B7A421A958EA3000E1D17 /* b2CircleShape.cpp in Sources */, @@ -4778,7 +4769,7 @@ FA0B7CFA1A95902C000E1D17 /* Filesystem.cpp in Sources */, FA1BA0A21E16D97500AA2803 /* wrap_Font.cpp in Sources */, FAC7CD781FE35E95006A60C7 /* physfs_platform_qnx.c in Sources */, - FA0B7D3C1A95902C000E1D17 /* Image.cpp in Sources */, + FA0B7D3C1A95902C000E1D17 /* Texture.cpp in Sources */, FA0B7A8C1A958EA3000E1D17 /* b2DistanceJoint.cpp in Sources */, FADF53FD1E3D74F200012CC0 /* Text.cpp in Sources */, FA6A2B741F60B6710074C308 /* ByteData.cpp in Sources */, @@ -4939,7 +4930,6 @@ FA0B7AD41A958EA3000E1D17 /* unix.c in Sources */, FA0B7A771A958EA3000E1D17 /* b2CircleContact.cpp in Sources */, FADF543B1E3DAFF700012CC0 /* wrap_Graphics.cpp in Sources */, - FA0B7D331A95902C000E1D17 /* Canvas.cpp in Sources */, FA0B7E941A95902C000E1D17 /* Mpg123Decoder.cpp in Sources */, FA0B7E8B1A95902C000E1D17 /* FLACDecoder.cpp in Sources */, FA0B7B3A1A958EA3000E1D17 /* wuff_memory.c in Sources */, diff --git a/src/common/pixelformat.cpp b/src/common/pixelformat.cpp index 0048236b4..6ded27517 100644 --- a/src/common/pixelformat.cpp +++ b/src/common/pixelformat.cpp @@ -24,6 +24,87 @@ 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 +}; + +static_assert(sizeof(formatInfo) / sizeof(PixelFormatInfo) == PIXELFORMAT_MAX_ENUM, "Update the formatInfo array when adding or removing a PixelFormat"); + static StringMap::Entry formatEntries[] = { { "unknown", PIXELFORMAT_UNKNOWN }, @@ -113,28 +194,30 @@ bool getConstant(PixelFormat in, const char *&out) return formats.find(in, out); } +const PixelFormatInfo &getPixelFormatInfo(PixelFormat format) +{ + return formatInfo[format]; +} + bool isPixelFormatCompressed(PixelFormat format) { - // I'm lazy - int iformat = (int) format; - return iformat >= (int) PIXELFORMAT_DXT1_UNORM && iformat < (int) PIXELFORMAT_MAX_ENUM; + return formatInfo[format].compressed; } bool isPixelFormatDepthStencil(PixelFormat format) { - int iformat = (int) format; - return iformat >= (int) PIXELFORMAT_STENCIL8 && iformat <= (int) PIXELFORMAT_DEPTH32_FLOAT_STENCIL8; + const PixelFormatInfo &info = formatInfo[format]; + return info.depth || info.stencil; } bool isPixelFormatDepth(PixelFormat format) { - int iformat = (int) format; - return iformat >= (int) PIXELFORMAT_DEPTH16_UNORM && iformat <= (int) PIXELFORMAT_DEPTH32_FLOAT_STENCIL8; + return formatInfo[format].depth; } bool isPixelFormatStencil(PixelFormat format) { - return format == PIXELFORMAT_STENCIL8 || format == PIXELFORMAT_DEPTH24_UNORM_STENCIL8 || format == PIXELFORMAT_DEPTH32_FLOAT_STENCIL8; + return formatInfo[format].stencil; } PixelFormat getSRGBPixelFormat(PixelFormat format) @@ -151,76 +234,43 @@ PixelFormat getLinearPixelFormat(PixelFormat format) return format; } -size_t getPixelFormatSize(PixelFormat format) +size_t getPixelFormatBlockSize(PixelFormat format) { - switch (format) - { - case PIXELFORMAT_R8_UNORM: - case PIXELFORMAT_STENCIL8: - return 1; - case PIXELFORMAT_RG8_UNORM: - case PIXELFORMAT_R16_UNORM: - case PIXELFORMAT_R16_FLOAT: - case PIXELFORMAT_LA8_UNORM: - case PIXELFORMAT_RGBA4_UNORM: - case PIXELFORMAT_RGB5A1_UNORM: - case PIXELFORMAT_RGB565_UNORM: - case PIXELFORMAT_DEPTH16_UNORM: - return 2; - case PIXELFORMAT_RGBA8_UNORM: - case PIXELFORMAT_sRGBA8_UNORM: - case PIXELFORMAT_RG16_UNORM: - case PIXELFORMAT_RG16_FLOAT: - case PIXELFORMAT_R32_FLOAT: - case PIXELFORMAT_RGB10A2_UNORM: - case PIXELFORMAT_RG11B10_FLOAT: - case PIXELFORMAT_DEPTH24_UNORM: - case PIXELFORMAT_DEPTH32_FLOAT: - case PIXELFORMAT_DEPTH24_UNORM_STENCIL8: - return 4; - case PIXELFORMAT_RGBA16_UNORM: - case PIXELFORMAT_RGBA16_FLOAT: - case PIXELFORMAT_RG32_FLOAT: - case PIXELFORMAT_DEPTH32_FLOAT_STENCIL8: - return 8; - case PIXELFORMAT_RGBA32_FLOAT: - return 16; - default: - // TODO: compressed formats - return 0; - } + return formatInfo[format].blockSize; +} + +size_t getPixelFormatUncompressedRowSize(PixelFormat format, int width) +{ + const PixelFormatInfo &info = formatInfo[format]; + if (info.compressed) return 0; + return info.blockSize * width / info.blockWidth; +} + +size_t getPixelFormatCompressedBlockRowSize(PixelFormat format, int width) +{ + const PixelFormatInfo &info = formatInfo[format]; + if (!info.compressed) return 0; + return info.blockSize * ((width + info.blockWidth - 1) / info.blockWidth); +} + +size_t getPixelFormatCompressedBlockRowCount(PixelFormat format, int height) +{ + const PixelFormatInfo &info = formatInfo[format]; + if (!info.compressed) return 0; + return (height + info.blockHeight - 1) / info.blockHeight; +} + +size_t getPixelFormatSliceSize(PixelFormat format, int width, int height) +{ + const PixelFormatInfo &info = formatInfo[format]; + size_t blockW = (width + info.blockWidth - 1) / info.blockWidth; + size_t blockH = (height + info.blockHeight - 1) / info.blockHeight; + return info.blockSize * blockW * blockH; } int getPixelFormatColorComponents(PixelFormat format) { - switch (format) - { - case PIXELFORMAT_R8_UNORM: - case PIXELFORMAT_R16_UNORM: - case PIXELFORMAT_R16_FLOAT: - case PIXELFORMAT_R32_FLOAT: - return 1; - case PIXELFORMAT_RG8_UNORM: - case PIXELFORMAT_RG16_UNORM: - case PIXELFORMAT_RG16_FLOAT: - case PIXELFORMAT_RG32_FLOAT: - case PIXELFORMAT_LA8_UNORM: - return 2; - case PIXELFORMAT_RGB565_UNORM: - case PIXELFORMAT_RG11B10_FLOAT: - return 3; - case PIXELFORMAT_RGBA8_UNORM: - case PIXELFORMAT_sRGBA8_UNORM: - case PIXELFORMAT_RGBA16_UNORM: - case PIXELFORMAT_RGBA16_FLOAT: - case PIXELFORMAT_RGBA32_FLOAT: - case PIXELFORMAT_RGBA4_UNORM: - case PIXELFORMAT_RGB5A1_UNORM: - case PIXELFORMAT_RGB10A2_UNORM: - return 4; - default: - return 0; - } + return formatInfo[format].components; } } // love diff --git a/src/common/pixelformat.h b/src/common/pixelformat.h index 40f1be664..a1a4b1ddb 100644 --- a/src/common/pixelformat.h +++ b/src/common/pixelformat.h @@ -109,9 +109,23 @@ enum PixelFormat PIXELFORMAT_MAX_ENUM }; +struct PixelFormatInfo +{ + int components; + size_t blockWidth; + size_t blockHeight; + size_t blockSize; + bool color; + bool depth; + bool stencil; + bool compressed; +}; + bool getConstant(PixelFormat in, const char *&out); bool getConstant(const char *in, PixelFormat &out); +const PixelFormatInfo &getPixelFormatInfo(PixelFormat format); + /** * Gets whether the specified pixel format is a compressed type. **/ @@ -143,10 +157,36 @@ PixelFormat getSRGBPixelFormat(PixelFormat format); PixelFormat getLinearPixelFormat(PixelFormat format); /** - * Gets the size in bytes of the specified pixel format. - * NOTE: Currently returns 0 for compressed formats. + * Gets the block size in bytes of the specified pixel format. + * This is the size in bytes of a pixel for uncompressed formats, but *not* + * for compressed formats! **/ -size_t getPixelFormatSize(PixelFormat format); +size_t getPixelFormatBlockSize(PixelFormat format); + +/** + * Gets the size in bytes of a row of an uncompressed pixel format. + **/ +size_t getPixelFormatUncompressedRowSize(PixelFormat format, int width); + +/** + * Gets the size in bytes of a row of a compressed pixel format. This is the + * number of blocks used by the given width, multiplied by the block size. The + * number of rows of blocks for a given height can be computed by + * getPixelFormatCompressedBlockRowCount. + **/ +size_t getPixelFormatCompressedBlockRowSize(PixelFormat format, int width); + +/** + * Gets the number of rows of blocks the given compressed pixel format will use, + * for the given height in pixels. + **/ +size_t getPixelFormatCompressedBlockRowCount(PixelFormat format, int height); + +/** + * Gets the size in bytes of a slice (width x height 2D plane) which uses the + * given pixel format. + **/ +size_t getPixelFormatSliceSize(PixelFormat format, int width, int height); /** * Gets the number of color components in the given pixel format. diff --git a/src/modules/font/GlyphData.cpp b/src/modules/font/GlyphData.cpp index 640888322..69ec25ca8 100644 --- a/src/modules/font/GlyphData.cpp +++ b/src/modules/font/GlyphData.cpp @@ -78,7 +78,7 @@ void *GlyphData::getData() const size_t GlyphData::getPixelSize() const { - return getPixelFormatSize(format); + return getPixelFormatBlockSize(format); } void *GlyphData::getData(int x, int y) const diff --git a/src/modules/graphics/Font.cpp b/src/modules/graphics/Font.cpp index 3d71b84fd..928d96f7d 100644 --- a/src/modules/graphics/Font.cpp +++ b/src/modules/graphics/Font.cpp @@ -158,7 +158,7 @@ void Font::createTexture() texture->setSamplerState(samplerState); { - size_t bpp = getPixelFormatSize(pixelFormat); + size_t bpp = getPixelFormatBlockSize(pixelFormat); size_t pixelcount = size.width * size.height; // Initialize the texture with transparent white for Luminance-Alpha diff --git a/src/modules/graphics/Texture.cpp b/src/modules/graphics/Texture.cpp index 8d8f21121..7ebd427c5 100644 --- a/src/modules/graphics/Texture.cpp +++ b/src/modules/graphics/Texture.cpp @@ -184,6 +184,9 @@ Texture::Texture(const Settings &settings, const Slices *slices) { texType = slices->getTextureType(); + if (requestedMSAA > 1) + throw love::Exception("MSAA textures cannot be created from image data."); + int dataMipmaps = 1; if (slices->validate() && slices->getMipmapCount() > 1) dataMipmaps = slices->getMipmapCount(); @@ -218,7 +221,7 @@ Texture::Texture(const Settings &settings, const Slices *slices) if (settings.readable.hasValue) readable = settings.readable.value; else - readable = !isPixelFormatDepthStencil(format); + readable = !renderTarget || !isPixelFormatDepthStencil(format); format = gfx->getSizedFormat(format, renderTarget, readable, sRGB); @@ -234,6 +237,9 @@ Texture::Texture(const Settings &settings, const Slices *slices) if (texType != TEXTURE_2D && requestedMSAA > 1) throw love::Exception("MSAA is only supported for textures with the 2D texture type."); + if (!renderTarget && requestedMSAA > 1) + throw love::Exception("MSAA is only supported with render target textures."); + if (readable && isPixelFormatDepthStencil(format) && settings.msaa > 1) throw love::Exception("Readable depth/stencil textures with MSAA are not currently supported."); @@ -269,7 +275,7 @@ Texture::Texture(const Settings &settings, const Slices *slices) throw love::Exception("%s textures are not supported on this system.", textypestr); } - validateDimensions(!renderTarget); + validateDimensions(renderTarget || !readable); samplerState = gfx->getDefaultSamplerState(); @@ -619,8 +625,11 @@ int Texture::getRequestedMSAA() const void Texture::setSamplerState(const SamplerState &s) { - if (s.depthSampleMode.hasValue && (!readable || !isPixelFormatDepthStencil(format))) - throw love::Exception("Only readable depth textures can have a depth sample compare mode."); + if (!readable) + return; + + if (s.depthSampleMode.hasValue && !isPixelFormatDepth(format)) + throw love::Exception("Only depth textures can have a depth sample compare mode."); Graphics::flushStreamDrawsGlobal(); diff --git a/src/modules/graphics/Texture.h b/src/modules/graphics/Texture.h index ec2d7cc68..8b82e583e 100644 --- a/src/modules/graphics/Texture.h +++ b/src/modules/graphics/Texture.h @@ -164,7 +164,7 @@ public: PixelFormat format = PIXELFORMAT_NORMAL; bool linear = false; float dpiScale = 1.0f; - int msaa = 0; + int msaa = 1; bool renderTarget = false; OptionalBool readable; }; diff --git a/src/modules/graphics/Video.cpp b/src/modules/graphics/Video.cpp index 8854e64c7..9f2df9c5b 100644 --- a/src/modules/graphics/Video.cpp +++ b/src/modules/graphics/Video.cpp @@ -90,7 +90,7 @@ Video::Video(Graphics *gfx, love::video::VideoStream *stream, float dpiscale) tex->setSamplerState(samplerState); - size_t bpp = getPixelFormatSize(PIXELFORMAT_R8_UNORM); + size_t bpp = getPixelFormatBlockSize(PIXELFORMAT_R8_UNORM); size_t size = bpp * widths[i] * heights[i]; Rect rect = {0, 0, widths[i], heights[i]}; @@ -167,7 +167,7 @@ void Video::update() for (int i = 0; i < 3; i++) { - size_t bpp = getPixelFormatSize(PIXELFORMAT_R8_UNORM); + size_t bpp = getPixelFormatBlockSize(PIXELFORMAT_R8_UNORM); size_t size = bpp * widths[i] * heights[i]; Rect rect = {0, 0, widths[i], heights[i]}; diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index ef7c46622..9f2bf702c 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -132,10 +132,7 @@ love::graphics::StreamBuffer *Graphics::newStreamBuffer(BufferType type, size_t love::graphics::Texture *Graphics::newTexture(const Texture::Settings &settings, const Texture::Slices *data) { - if (settings.renderTarget) - return new Canvas(settings); - else - return new Image(settings, data); + return new Texture(settings, data); } love::graphics::ShaderStage *Graphics::newShaderStageInternal(ShaderStage::StageType stage, const std::string &cachekey, const std::string &source, bool gles) @@ -569,8 +566,7 @@ void Graphics::endPass() for (int i = 0; i < (int) rts.colors.size(); i++) { - // FIXME - Canvas *c = (Canvas *) rts.colors[i].texture.get(); + Texture *c = (Texture *) rts.colors[i].texture.get(); if (!c->isReadable()) continue; @@ -588,7 +584,7 @@ void Graphics::endPass() if (depthstencil != nullptr && depthstencil->getMSAA() > 1 && depthstencil->isReadable()) { - gl.bindFramebuffer(OpenGL::FRAMEBUFFER_DRAW, ((Canvas *) depthstencil)->getFBO()); + gl.bindFramebuffer(OpenGL::FRAMEBUFFER_DRAW, ((Texture *) depthstencil)->getFBO()); if (GLAD_APPLE_framebuffer_multisample) glResolveMultisampleFramebufferAPPLE(); @@ -613,14 +609,13 @@ void Graphics::endPass() for (const auto &rt : rts.colors) { - // TODO -// if (rt.texture->getMipmapMode() == Canvas::MIPMAPS_AUTO && rt.mipmap == 0) -// rt.texture->generateMipmaps(); + if (rt.texture->getMipmapsMode() == Texture::MIPMAPS_AUTO && rt.mipmap == 0) + rt.texture->generateMipmaps(); } -// int dsmipmap = rts.depthStencil.mipmap; -// if (depthstencil != nullptr && depthstencil->getMipmapMode() == Canvas::MIPMAPS_AUTO && dsmipmap == 0) -// depthstencil->generateMipmaps(); + int dsmipmap = rts.depthStencil.mipmap; + if (depthstencil != nullptr && depthstencil->getMipmapsMode() == Texture::MIPMAPS_AUTO && dsmipmap == 0) + depthstencil->generateMipmaps(); } void Graphics::clear(OptionalColorf c, OptionalInt stencil, OptionalDouble depth) @@ -813,7 +808,7 @@ void Graphics::discard(OpenGL::FramebufferTarget target, const std::vector glDiscardFramebufferEXT(gltarget, (GLint) attachments.size(), &attachments[0]); } -void Graphics::cleanupRenderTexture(Texture *texture) +void Graphics::cleanupRenderTexture(love::graphics::Texture *texture) { if (!texture->isRenderTarget()) return; diff --git a/src/modules/graphics/opengl/Graphics.h b/src/modules/graphics/opengl/Graphics.h index b2ba4d186..b6df9b629 100644 --- a/src/modules/graphics/opengl/Graphics.h +++ b/src/modules/graphics/opengl/Graphics.h @@ -36,8 +36,7 @@ #include "image/Image.h" #include "image/ImageData.h" -#include "Image.h" -#include "Canvas.h" +#include "Texture.h" #include "Shader.h" #include "libraries/xxHash/xxhash.h" @@ -71,7 +70,7 @@ public: void draw(const DrawCommand &cmd) override; void draw(const DrawIndexedCommand &cmd) override; - void drawQuads(int start, int count, const vertex::Attributes &attributes, const vertex::BufferBindings &buffers, Texture *texture) override; + void drawQuads(int start, int count, const vertex::Attributes &attributes, const vertex::BufferBindings &buffers, love::graphics::Texture *texture) override; void clear(OptionalColorf color, OptionalInt stencil, OptionalDouble depth) override; void clear(const std::vector &colors, OptionalInt stencil, OptionalDouble depth) override; @@ -110,7 +109,7 @@ public: Shader::Language getShaderLanguageTarget() const override; // Internal use. - void cleanupRenderTexture(Texture *texture); + void cleanupRenderTexture(love::graphics::Texture *texture); private: diff --git a/src/modules/graphics/opengl/Image.cpp b/src/modules/graphics/opengl/Image.cpp deleted file mode 100644 index 233e7a291..000000000 --- a/src/modules/graphics/opengl/Image.cpp +++ /dev/null @@ -1,296 +0,0 @@ -/** - * Copyright (c) 2006-2020 LOVE Development Team - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - **/ - -#include "Image.h" - -#include "graphics/Graphics.h" -#include "common/int.h" - -// STD -#include // for min/max - -namespace love -{ -namespace graphics -{ -namespace opengl -{ - -Image::Image(const Settings &settings, const Slices *data) - : love::graphics::Texture(settings, data) - , slices(settings.type) - , texture(0) -{ - if (data != nullptr) - slices = *data; - loadVolatile(); -} - -Image::~Image() -{ - unloadVolatile(); -} - -void Image::generateMipmaps() -{ - if (getMipmapCount() > 1 && !isCompressed() && - (GLAD_ES_VERSION_2_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object || GLAD_EXT_framebuffer_object)) - { - gl.bindTextureToUnit(this, 0, false); - - GLenum gltextype = OpenGL::getGLTextureType(texType); - - if (gl.bugs.generateMipmapsRequiresTexture2DEnable) - glEnable(gltextype); - - glGenerateMipmap(gltextype); - } -} - -void Image::loadDefaultTexture() -{ - usingDefaultTexture = true; - - gl.bindTextureToUnit(this, 0, false); - setSamplerState(samplerState); - - bool isSRGB = false; - gl.rawTexStorage(texType, 1, PIXELFORMAT_RGBA8_UNORM, isSRGB, 2, 2, 1); - - // A nice friendly checkerboard to signify invalid textures... - GLubyte px[] = {0xFF,0xFF,0xFF,0xFF, 0xFF,0xA0,0xA0,0xFF, - 0xFF,0xA0,0xA0,0xFF, 0xFF,0xFF,0xFF,0xFF}; - - int slices = texType == TEXTURE_CUBE ? 6 : 1; - Rect rect = {0, 0, 2, 2}; - for (int slice = 0; slice < slices; slice++) - uploadByteData(PIXELFORMAT_RGBA8_UNORM, px, sizeof(px), 0, slice, rect, nullptr); -} - -void Image::loadData() -{ - int mipcount = getMipmapCount(); - int slicecount = 1; - - if (texType == TEXTURE_VOLUME) - slicecount = getDepth(); - else if (texType == TEXTURE_2D_ARRAY) - slicecount = getLayerCount(); - else if (texType == TEXTURE_CUBE) - slicecount = 6; - - if (!isCompressed()) - gl.rawTexStorage(texType, mipcount, format, sRGB, pixelWidth, pixelHeight, texType == TEXTURE_VOLUME ? depth : layers); - - int w = pixelWidth; - int h = pixelHeight; - int d = depth; - - OpenGL::TextureFormat fmt = gl.convertPixelFormat(format, false, sRGB); - - for (int mip = 0; mip < mipcount; mip++) - { - if (isCompressed() && (texType == TEXTURE_2D_ARRAY || texType == TEXTURE_VOLUME)) - { - size_t mipsize = 0; - - if (texType == TEXTURE_2D_ARRAY || texType == TEXTURE_VOLUME) - { - for (int slice = 0; slice < slices.getSliceCount(mip); slice++) - { - auto id = slices.get(slice, mip); - if (id != nullptr) - mipsize += id->getSize(); - } - } - - if (mipsize > 0) - { - GLenum gltarget = OpenGL::getGLTextureType(texType); - glCompressedTexImage3D(gltarget, mip, fmt.internalformat, w, h, d, 0, mipsize, nullptr); - } - } - - for (int slice = 0; slice < slicecount; slice++) - { - love::image::ImageDataBase *id = slices.get(slice, mip); - if (id != nullptr) - uploadImageData(id, mip, slice, 0, 0); - } - - w = std::max(w / 2, 1); - h = std::max(h / 2, 1); - - if (texType == TEXTURE_VOLUME) - d = std::max(d / 2, 1); - } - - if (getMipmapCount() > 1 && slices.getMipmapCount() <= 1) - generateMipmaps(); -} - -void Image::uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r, love::image::ImageDataBase *imgd) -{ - love::image::ImageDataBase *oldd = slices.get(slice, level); - - // We can only replace the internal Data (used when reloading due to setMode) - // if the dimensions match. - if (imgd != nullptr && oldd != nullptr && oldd->getWidth() == imgd->getWidth() - && oldd->getHeight() == imgd->getHeight()) - { - slices.set(slice, level, imgd); - } - - OpenGL::TempDebugGroup debuggroup("Image data upload"); - - gl.bindTextureToUnit(this, 0, false); - - OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(pixelformat, false, sRGB); - GLenum gltarget = OpenGL::getGLTextureType(texType); - - if (texType == TEXTURE_CUBE) - gltarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + slice; - - if (isPixelFormatCompressed(pixelformat)) - { - if (r.x != 0 || r.y != 0) - throw love::Exception("x and y parameters must be 0 for compressed images."); - - if (texType == TEXTURE_2D || texType == TEXTURE_CUBE) - glCompressedTexImage2D(gltarget, level, fmt.internalformat, r.w, r.h, 0, size, data); - else if (texType == TEXTURE_2D_ARRAY || texType == TEXTURE_VOLUME) - glCompressedTexSubImage3D(gltarget, level, 0, 0, slice, r.w, r.h, 1, fmt.internalformat, size, data); - } - else - { - if (texType == TEXTURE_2D || texType == TEXTURE_CUBE) - glTexSubImage2D(gltarget, level, r.x, r.y, r.w, r.h, fmt.externalformat, fmt.type, data); - else if (texType == TEXTURE_2D_ARRAY || texType == TEXTURE_VOLUME) - glTexSubImage3D(gltarget, level, r.x, r.y, slice, r.w, r.h, 1, fmt.externalformat, fmt.type, data); - } -} - -bool Image::loadVolatile() -{ - if (texture != 0) - return true; - - OpenGL::TempDebugGroup debuggroup("Image load"); - - // NPOT textures don't support mipmapping without full NPOT support. - if ((GLAD_ES_VERSION_2_0 && !(GLAD_ES_VERSION_3_0 || GLAD_OES_texture_npot)) - && (pixelWidth != nextP2(pixelWidth) || pixelHeight != nextP2(pixelHeight))) - { - mipmapCount = 1; - samplerState.mipmapFilter = SamplerState::MIPMAP_FILTER_NONE; - } - - glGenTextures(1, &texture); - gl.bindTextureToUnit(this, 0, false); - - // Use a default texture if the size is too big for the system. - if (!validateDimensions(false)) - { - loadDefaultTexture(); - return true; - } - - setSamplerState(samplerState); - - while (glGetError() != GL_NO_ERROR); // Clear errors. - - try - { - loadData(); - - GLenum glerr = glGetError(); - if (glerr != GL_NO_ERROR) - throw love::Exception("Cannot create image (OpenGL error: %s)", OpenGL::errorString(glerr)); - } - catch (love::Exception &) - { - gl.deleteTexture(texture); - texture = 0; - throw; - } - - int64 memsize = 0; - - for (int slice = 0; slice < slices.getSliceCount(0); slice++) - memsize += slices.get(slice, 0)->getSize(); - - if (getMipmapCount() > 1) - memsize *= 1.33334; - - setGraphicsMemorySize(memsize); - - usingDefaultTexture = false; - return true; -} - -void Image::unloadVolatile() -{ - if (texture == 0) - return; - - gl.deleteTexture(texture); - texture = 0; - - setGraphicsMemorySize(0); -} - -ptrdiff_t Image::getHandle() const -{ - return texture; -} - -void Image::setSamplerState(const SamplerState &s) -{ - Texture::setSamplerState(s); - - if (!OpenGL::hasTextureFilteringSupport(getPixelFormat())) - { - samplerState.magFilter = samplerState.minFilter = SamplerState::FILTER_NEAREST; - - if (samplerState.mipmapFilter == SamplerState::MIPMAP_FILTER_LINEAR) - samplerState.mipmapFilter = SamplerState::MIPMAP_FILTER_NEAREST; - } - - // We don't want filtering or (attempted) mipmaps on the default texture. - if (usingDefaultTexture) - { - samplerState.mipmapFilter = SamplerState::MIPMAP_FILTER_NONE; - samplerState.minFilter = samplerState.magFilter = SamplerState::FILTER_NEAREST; - } - - // If we only have limited NPOT support then the wrap mode must be CLAMP. - if ((GLAD_ES_VERSION_2_0 && !(GLAD_ES_VERSION_3_0 || GLAD_OES_texture_npot)) - && (pixelWidth != nextP2(pixelWidth) || pixelHeight != nextP2(pixelHeight) || depth != nextP2(depth))) - { - samplerState.wrapU = samplerState.wrapV = samplerState.wrapW = SamplerState::WRAP_CLAMP; - } - - gl.bindTextureToUnit(this, 0, false); - gl.setSamplerState(texType, samplerState); -} - -} // opengl -} // graphics -} // love diff --git a/src/modules/graphics/opengl/Image.h b/src/modules/graphics/opengl/Image.h deleted file mode 100644 index f2811e827..000000000 --- a/src/modules/graphics/opengl/Image.h +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright (c) 2006-2020 LOVE Development Team - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - **/ - -#pragma once - -// LOVE -#include "graphics/Texture.h" -#include "graphics/Volatile.h" - -// OpenGL -#include "OpenGL.h" - -namespace love -{ -namespace graphics -{ -namespace opengl -{ - -class Image final : public love::graphics::Texture, public Volatile -{ -public: - - Image(const Settings &settings, const Slices *data); - - virtual ~Image(); - - // Implements Volatile. - bool loadVolatile() override; - void unloadVolatile() override; - - ptrdiff_t getHandle() const override; - ptrdiff_t getRenderTargetHandle() const override { return 0; } - - int getMSAA() const override { return 1; } - void setSamplerState(const SamplerState &s) override; - void generateMipmaps() override; - -private: - - void uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r, love::image::ImageDataBase *imgd = nullptr) override; - - void loadDefaultTexture(); - void loadData(); - - Slices slices; - - // OpenGL texture identifier. - GLuint texture; - -}; // Image - -} // opengl -} // graphics -} // love diff --git a/src/modules/graphics/opengl/OpenGL.cpp b/src/modules/graphics/opengl/OpenGL.cpp index 984140633..2b4c15454 100644 --- a/src/modules/graphics/opengl/OpenGL.cpp +++ b/src/modules/graphics/opengl/OpenGL.cpp @@ -23,7 +23,6 @@ #include "OpenGL.h" #include "Shader.h" -#include "Canvas.h" #include "common/Exception.h" #include "graphics/Graphics.h" diff --git a/src/modules/graphics/opengl/Shader.cpp b/src/modules/graphics/opengl/Shader.cpp index cb175e082..519cba618 100644 --- a/src/modules/graphics/opengl/Shader.cpp +++ b/src/modules/graphics/opengl/Shader.cpp @@ -195,7 +195,7 @@ void Shader::mapActiveUniforms() glUniform1iv(u.location, u.count, u.ints); - u.textures = new Texture*[u.count]; + u.textures = new love::graphics::Texture*[u.count]; memset(u.textures, 0, sizeof(Texture *) * u.count); } } @@ -564,12 +564,12 @@ void Shader::updateUniform(const UniformInfo *info, int count, bool internalupda } } -void Shader::sendTextures(const UniformInfo *info, Texture **textures, int count) +void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **textures, int count) { Shader::sendTextures(info, textures, count, false); } -void Shader::sendTextures(const UniformInfo *info, Texture **textures, int count, bool internalUpdate) +void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **textures, int count, bool internalUpdate) { if (info->baseType != UNIFORM_SAMPLER) return; @@ -584,7 +584,7 @@ void Shader::sendTextures(const UniformInfo *info, Texture **textures, int count // Bind the textures to the texture units. for (int i = 0; i < count; i++) { - Texture *tex = textures[i]; + love::graphics::Texture *tex = textures[i]; if (tex != nullptr) { @@ -671,7 +671,7 @@ int Shader::getVertexAttributeIndex(const std::string &name) return location; } -void Shader::setVideoTextures(Texture *ytexture, Texture *cbtexture, Texture *crtexture) +void Shader::setVideoTextures(love::graphics::Texture *ytexture, love::graphics::Texture *cbtexture, love::graphics::Texture *crtexture) { const BuiltinUniform builtins[3] = { BUILTIN_TEXTURE_VIDEO_Y, @@ -679,7 +679,7 @@ void Shader::setVideoTextures(Texture *ytexture, Texture *cbtexture, Texture *cr BUILTIN_TEXTURE_VIDEO_CR, }; - Texture *textures[3] = {ytexture, cbtexture, crtexture}; + love::graphics::Texture *textures[3] = {ytexture, cbtexture, crtexture}; for (int i = 0; i < 3; i++) { diff --git a/src/modules/graphics/opengl/Shader.h b/src/modules/graphics/opengl/Shader.h index 8522716bd..23397ac57 100644 --- a/src/modules/graphics/opengl/Shader.h +++ b/src/modules/graphics/opengl/Shader.h @@ -61,10 +61,10 @@ public: const UniformInfo *getUniformInfo(const std::string &name) const override; const UniformInfo *getUniformInfo(BuiltinUniform builtin) const override; void updateUniform(const UniformInfo *info, int count) override; - void sendTextures(const UniformInfo *info, Texture **textures, int count) override; + void sendTextures(const UniformInfo *info, love::graphics::Texture **textures, int count) override; bool hasUniform(const std::string &name) const override; ptrdiff_t getHandle() const override; - void setVideoTextures(Texture *ytexture, Texture *cbtexture, Texture *crtexture) override; + void setVideoTextures(love::graphics::Texture *ytexture, love::graphics::Texture *cbtexture, love::graphics::Texture *crtexture) override; void updatePointSize(float size); void updateBuiltinUniforms(love::graphics::Graphics *gfx, int viewportW, int viewportH); @@ -82,7 +82,7 @@ private: void mapActiveUniforms(); void updateUniform(const UniformInfo *info, int count, bool internalupdate); - void sendTextures(const UniformInfo *info, Texture **textures, int count, bool internalupdate); + void sendTextures(const UniformInfo *info, love::graphics::Texture **textures, int count, bool internalupdate); int getUniformTypeComponents(GLenum type) const; MatrixSize getMatrixSize(GLenum type) const; diff --git a/src/modules/graphics/opengl/Canvas.cpp b/src/modules/graphics/opengl/Texture.cpp similarity index 57% rename from src/modules/graphics/opengl/Canvas.cpp rename to src/modules/graphics/opengl/Texture.cpp index 4b77c06f4..a9675ff67 100644 --- a/src/modules/graphics/opengl/Canvas.cpp +++ b/src/modules/graphics/opengl/Texture.cpp @@ -18,11 +18,14 @@ * 3. This notice may not be removed or altered from any source distribution. **/ -#include "Canvas.h" +#include "Texture.h" + #include "graphics/Graphics.h" #include "Graphics.h" +#include "common/int.h" -#include // For min/max +// STD +#include // for min/max namespace love { @@ -31,7 +34,7 @@ namespace graphics namespace opengl { -static GLenum createFBO(GLuint &framebuffer, TextureType texType, PixelFormat format, GLuint texture, int layers) +static GLenum createFBO(GLuint &framebuffer, TextureType texType, PixelFormat format, GLuint texture, int layers, bool clear) { // get currently bound fbo to reset to it later GLuint current_fbo = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL); @@ -100,7 +103,7 @@ static GLenum createFBO(GLuint &framebuffer, TextureType texType, PixelFormat fo return status; } -static bool createRenderbuffer(int width, int height, int &samples, PixelFormat pixelformat, GLuint &buffer) +static bool newRenderbuffer(int width, int height, int &samples, PixelFormat pixelformat, GLuint &buffer) { int reqsamples = samples; bool unusedSRGB = false; @@ -140,8 +143,6 @@ static bool createRenderbuffer(int width, int height, int &samples, PixelFormat if (samples > 1) glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples); - else - samples = 0; glBindRenderbuffer(GL_RENDERBUFFER, 0); @@ -173,7 +174,7 @@ static bool createRenderbuffer(int width, int height, int &samples, PixelFormat { glDeleteRenderbuffers(1, &buffer); buffer = 0; - samples = 0; + samples = 1; } gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo); @@ -182,110 +183,221 @@ static bool createRenderbuffer(int width, int height, int &samples, PixelFormat return status == GL_FRAMEBUFFER_COMPLETE; } -Canvas::Canvas(const Settings &settings) - : love::graphics::Texture(settings, nullptr) +Texture::Texture(const Settings &settings, const Slices *data) + : love::graphics::Texture(settings, data) + , slices(settings.type) , fbo(0) , texture(0) - , renderbuffer(0) - , actualSamples(0) + , renderbuffer(0) + , framebufferStatus(GL_FRAMEBUFFER_COMPLETE) + , actualSamples(1) { - auto gfx = Module::getInstance(Module::M_GRAPHICS); - if (gfx != nullptr) - format = gfx->getSizedFormat(format, renderTarget, readable, sRGB); - + if (data != nullptr) + slices = *data; loadVolatile(); - - if (status != GL_FRAMEBUFFER_COMPLETE) - throw love::Exception("Cannot create Canvas: %s", OpenGL::framebufferStatusString(status)); } -Canvas::~Canvas() +Texture::~Texture() { unloadVolatile(); } -bool Canvas::loadVolatile() +bool Texture::createTexture() { - if (texture != 0) - return true; + // The base class handles some validation. For example, if ImageData is + // given then it must exist for all mip levels, a render target can't use + // a compressed format, etc. - OpenGL::TempDebugGroup debuggroup("Canvas load"); + glGenTextures(1, &texture); + gl.bindTextureToUnit(this, 0, false); - fbo = texture = 0; - renderbuffer = 0; - status = GL_FRAMEBUFFER_COMPLETE; - - // getMaxRenderbufferSamples will be 0 on systems that don't support - // multisampled renderbuffers / don't export FBO multisample extensions. - actualSamples = std::min(getRequestedMSAA(), gl.getMaxRenderbufferSamples()); - actualSamples = std::max(actualSamples, 0); - actualSamples = actualSamples == 1 ? 0 : actualSamples; - - if (isReadable()) + // Use a default texture if the size is too big for the system. + // validateDimensions is also called in the base class for RTs and + // non-readable textures. + if (!renderTarget && !validateDimensions(false)) { - glGenTextures(1, &texture); - gl.bindTextureToUnit(this, 0, false); - - GLenum gltype = OpenGL::getGLTextureType(texType); - - if (GLAD_ANGLE_texture_usage) - glTexParameteri(gltype, GL_TEXTURE_USAGE_ANGLE, GL_FRAMEBUFFER_ATTACHMENT_ANGLE); + usingDefaultTexture = true; setSamplerState(samplerState); - while (glGetError() != GL_NO_ERROR) - /* Clear the error buffer. */; + bool isSRGB = false; + gl.rawTexStorage(texType, 1, PIXELFORMAT_RGBA8_UNORM, isSRGB, 2, 2, 1); - bool isSRGB = format == PIXELFORMAT_sRGBA8_UNORM; - if (!gl.rawTexStorage(texType, mipmapCount, format, isSRGB, pixelWidth, pixelHeight, texType == TEXTURE_VOLUME ? depth : layers)) - { - status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; - return false; - } + // A nice friendly checkerboard to signify invalid textures... + GLubyte px[] = {0xFF,0xFF,0xFF,0xFF, 0xFF,0xA0,0xA0,0xFF, + 0xFF,0xA0,0xA0,0xFF, 0xFF,0xFF,0xFF,0xFF}; - if (glGetError() != GL_NO_ERROR) - { - gl.deleteTexture(texture); - texture = 0; - status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; - return false; - } + int slices = texType == TEXTURE_CUBE ? 6 : 1; + Rect rect = {0, 0, 2, 2}; + for (int slice = 0; slice < slices; slice++) + uploadByteData(PIXELFORMAT_RGBA8_UNORM, px, sizeof(px), 0, slice, rect, nullptr); - // Create a local FBO used for glReadPixels as well as MSAA blitting. - status = createFBO(fbo, texType, format, texture, texType == TEXTURE_VOLUME ? depth : layers); - - if (status != GL_FRAMEBUFFER_COMPLETE) - { - if (fbo != 0) - { - gl.deleteFramebuffer(fbo); - fbo = 0; - } - return false; - } + return true; } - if (!isReadable() || actualSamples > 0) - createRenderbuffer(pixelWidth, pixelHeight, actualSamples, format, renderbuffer); + GLenum gltype = OpenGL::getGLTextureType(texType); + if (renderTarget && GLAD_ANGLE_texture_usage) + glTexParameteri(gltype, GL_TEXTURE_USAGE_ANGLE, GL_FRAMEBUFFER_ATTACHMENT_ANGLE); - int64 memsize = getPixelFormatSize(format) * pixelWidth * pixelHeight; - if (getMipmapCount() > 1) - memsize *= 1.33334; + setSamplerState(samplerState); - if (actualSamples > 1 && isReadable()) - memsize += getPixelFormatSize(format) * pixelWidth * pixelHeight * actualSamples; - else if (actualSamples > 1) - memsize *= actualSamples; + int mipcount = getMipmapCount(); + int slicecount = 1; - setGraphicsMemorySize(memsize); + if (texType == TEXTURE_VOLUME) + slicecount = getDepth(); + else if (texType == TEXTURE_2D_ARRAY) + slicecount = getLayerCount(); + else if (texType == TEXTURE_CUBE) + slicecount = 6; - if (getMipmapCount() > 1) + if (!isCompressed()) + gl.rawTexStorage(texType, mipcount, format, sRGB, pixelWidth, pixelHeight, texType == TEXTURE_VOLUME ? depth : layers); + + int w = pixelWidth; + int h = pixelHeight; + int d = depth; + + OpenGL::TextureFormat fmt = gl.convertPixelFormat(format, false, sRGB); + + for (int mip = 0; mip < mipcount; mip++) + { + if (isCompressed() && (texType == TEXTURE_2D_ARRAY || texType == TEXTURE_VOLUME)) + { + size_t mipsize = 0; + + if (texType == TEXTURE_2D_ARRAY || texType == TEXTURE_VOLUME) + { + for (int slice = 0; slice < slices.getSliceCount(mip); slice++) + { + auto id = slices.get(slice, mip); + if (id != nullptr) + mipsize += id->getSize(); + } + } + + if (mipsize > 0) + { + GLenum gltarget = OpenGL::getGLTextureType(texType); + glCompressedTexImage3D(gltarget, mip, fmt.internalformat, w, h, d, 0, mipsize, nullptr); + } + } + + for (int slice = 0; slice < slicecount; slice++) + { + love::image::ImageDataBase *id = slices.get(slice, mip); + if (id != nullptr) + uploadImageData(id, mip, slice, 0, 0); + } + + w = std::max(w / 2, 1); + h = std::max(h / 2, 1); + + if (texType == TEXTURE_VOLUME) + d = std::max(d / 2, 1); + } + + bool hasdata = slices.get(0, 0) != nullptr; + + // Create a local FBO used for glReadPixels as well as MSAA blitting. + if (isRenderTarget()) + { + bool clear = !hasdata; + int slices = texType == TEXTURE_VOLUME ? depth : layers; + framebufferStatus = createFBO(fbo, texType, format, texture, slices, clear); + } + else if (!hasdata) + { + // Initialize all slices to transparent black. + std::vector emptydata(getPixelFormatSliceSize(format, w, h)); + + Rect r = {0, 0, w, h}; + int slices = texType == TEXTURE_VOLUME ? depth : layers; + slices = texType == TEXTURE_CUBE ? 6 : slices; + for (int i = 0; i < slices; i++) + uploadByteData(format, emptydata.data(), emptydata.size(), 0, i, r); + } + + // Non-readable textures can't have mipmaps (enforced in the base class), + // so generateMipmaps here is fine - when they aren't already initialized. + if (getMipmapCount() > 1 && slices.getMipmapCount() <= 1) generateMipmaps(); return true; } -void Canvas::unloadVolatile() +bool Texture::createRenderbuffer() +{ + if (isReadable() && actualSamples <= 1) + return true; + + return newRenderbuffer(pixelWidth, pixelHeight, actualSamples, format, renderbuffer); +} + +bool Texture::loadVolatile() +{ + if (texture != 0 || renderbuffer != 0) + return true; + + OpenGL::TempDebugGroup debuggroup("Texture load"); + + // NPOT textures don't support mipmapping without full NPOT support. + if ((GLAD_ES_VERSION_2_0 && !(GLAD_ES_VERSION_3_0 || GLAD_OES_texture_npot)) + && (pixelWidth != nextP2(pixelWidth) || pixelHeight != nextP2(pixelHeight))) + { + mipmapCount = 1; + samplerState.mipmapFilter = SamplerState::MIPMAP_FILTER_NONE; + } + + // getMaxRenderbufferSamples will be 0 on systems that don't support + // multisampled renderbuffers / don't export FBO multisample extensions. + actualSamples = std::min(getRequestedMSAA(), gl.getMaxRenderbufferSamples()); + actualSamples = std::max(actualSamples, 1); + + while (glGetError() != GL_NO_ERROR); // Clear errors. + + try + { + if (isReadable()) + createTexture(); + if (!isReadable() && actualSamples > 1) + createRenderbuffer(); + + GLenum glerr = glGetError(); + if (glerr != GL_NO_ERROR) + throw love::Exception("Cannot create texture (OpenGL error: %s)", OpenGL::errorString(glerr)); + } + catch (love::Exception &) + { + unloadVolatile(); + throw; + } + + int64 memsize = 0; + + for (int mip = 0; mip < getMipmapCount(); mip++) + { + int w = getPixelWidth(mip); + int h = getPixelHeight(mip); + int slices = getDepth(mip) * layers * (texType == TEXTURE_CUBE ? 6 : 1); + memsize += getPixelFormatSliceSize(format, w, h) * slices; + } + + if (actualSamples > 1 && isReadable()) + { + int slices = depth * layers * (texType == TEXTURE_CUBE ? 6 : 1); + memsize += getPixelFormatSliceSize(format, pixelWidth, pixelHeight) * slices * actualSamples; + } + else if (actualSamples > 1) + memsize *= actualSamples; + + setGraphicsMemorySize(memsize); + + usingDefaultTexture = false; + return true; +} + +void Texture::unloadVolatile() { if (isRenderTarget() && (fbo != 0 || renderbuffer != 0 || texture != 0)) { @@ -312,41 +424,73 @@ void Canvas::unloadVolatile() setGraphicsMemorySize(0); } -void Canvas::setSamplerState(const SamplerState &s) +void Texture::uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r, love::image::ImageDataBase *imgd) { - Texture::setSamplerState(s); + love::image::ImageDataBase *oldd = slices.get(slice, level); - if (samplerState.depthSampleMode.hasValue && !gl.isDepthCompareSampleSupported()) - throw love::Exception("Depth comparison sampling in shaders is not supported on this system."); - - if (!OpenGL::hasTextureFilteringSupport(getPixelFormat())) + // We can only replace the internal Data (used when reloading due to setMode) + // if the dimensions match. + if (imgd != nullptr && oldd != nullptr && oldd->getWidth() == imgd->getWidth() + && oldd->getHeight() == imgd->getHeight()) { - samplerState.magFilter = samplerState.minFilter = SamplerState::FILTER_NEAREST; - - if (samplerState.mipmapFilter == SamplerState::MIPMAP_FILTER_LINEAR) - samplerState.mipmapFilter = SamplerState::MIPMAP_FILTER_NEAREST; + slices.set(slice, level, imgd); } - // If we only have limited NPOT support then the wrap mode must be CLAMP. - if ((GLAD_ES_VERSION_2_0 && !(GLAD_ES_VERSION_3_0 || GLAD_OES_texture_npot)) - && (pixelWidth != nextP2(pixelWidth) || pixelHeight != nextP2(pixelHeight) || depth != nextP2(depth))) - { - samplerState.wrapU = samplerState.wrapV = samplerState.wrapW = SamplerState::WRAP_CLAMP; - } + OpenGL::TempDebugGroup debuggroup("Texture data upload"); gl.bindTextureToUnit(this, 0, false); - gl.setSamplerState(texType, samplerState); + + OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(pixelformat, false, sRGB); + GLenum gltarget = OpenGL::getGLTextureType(texType); + + if (texType == TEXTURE_CUBE) + gltarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + slice; + + if (isPixelFormatCompressed(pixelformat)) + { + if (r.x != 0 || r.y != 0) + throw love::Exception("x and y parameters must be 0 for compressed textures."); + + if (texType == TEXTURE_2D || texType == TEXTURE_CUBE) + glCompressedTexImage2D(gltarget, level, fmt.internalformat, r.w, r.h, 0, size, data); + else if (texType == TEXTURE_2D_ARRAY || texType == TEXTURE_VOLUME) + glCompressedTexSubImage3D(gltarget, level, 0, 0, slice, r.w, r.h, 1, fmt.internalformat, size, data); + } + else + { + if (texType == TEXTURE_2D || texType == TEXTURE_CUBE) + glTexSubImage2D(gltarget, level, r.x, r.y, r.w, r.h, fmt.externalformat, fmt.type, data); + else if (texType == TEXTURE_2D_ARRAY || texType == TEXTURE_VOLUME) + glTexSubImage3D(gltarget, level, r.x, r.y, slice, r.w, r.h, 1, fmt.externalformat, fmt.type, data); + } } -ptrdiff_t Canvas::getHandle() const +void Texture::generateMipmaps() { - return texture; + if (getMipmapCount() == 1 || getMipmapsMode() == MIPMAPS_NONE) + throw love::Exception("generateMipmaps can only be called on a Texture which was created with mipmaps enabled."); + + if (isPixelFormatCompressed(format)) + throw love::Exception("generateMipmaps cannot be called on a compressed Texture."); + + gl.bindTextureToUnit(this, 0, false); + + GLenum gltextype = OpenGL::getGLTextureType(texType); + + if (gl.bugs.generateMipmapsRequiresTexture2DEnable) + glEnable(gltextype); + + glGenerateMipmap(gltextype); } -love::image::ImageData *Canvas::newImageData(love::image::Image *module, int slice, int mipmap, const Rect &r) +love::image::ImageData *Texture::newImageData(love::image::Image *module, int slice, int mipmap, const Rect &r) { + // Base class does validation (only RTs allowed, etc) and creates ImageData. love::image::ImageData *data = love::graphics::Texture::newImageData(module, slice, mipmap, r); + if (fbo == 0) // Should never be reached. + return data; + bool isSRGB = false; OpenGL::TextureFormat fmt = gl.convertPixelFormat(data->getFormat(), false, isSRGB); @@ -370,53 +514,48 @@ love::image::ImageData *Canvas::newImageData(love::image::Image *module, int sli return data; } -void Canvas::generateMipmaps() +void Texture::setSamplerState(const SamplerState &s) { - if (getMipmapCount() == 1 || getMipmapsMode() == MIPMAPS_NONE) - throw love::Exception("generateMipmaps can only be called on a Texture which was created with mipmaps enabled."); + if (samplerState.depthSampleMode.hasValue && !gl.isDepthCompareSampleSupported()) + throw love::Exception("Depth comparison sampling in shaders is not supported on this system."); - if (isPixelFormatCompressed(format)) - throw love::Exception("generateMipmaps cannot be called on a compressed Texture."); + // Base class does common validation and assigns samplerState. + love::graphics::Texture::setSamplerState(s); + + if (!OpenGL::hasTextureFilteringSupport(getPixelFormat())) + { + samplerState.magFilter = samplerState.minFilter = SamplerState::FILTER_NEAREST; + + if (samplerState.mipmapFilter == SamplerState::MIPMAP_FILTER_LINEAR) + samplerState.mipmapFilter = SamplerState::MIPMAP_FILTER_NEAREST; + } + + // We don't want filtering or (attempted) mipmaps on the default texture. + if (usingDefaultTexture) + { + samplerState.mipmapFilter = SamplerState::MIPMAP_FILTER_NONE; + samplerState.minFilter = samplerState.magFilter = SamplerState::FILTER_NEAREST; + } + + // If we only have limited NPOT support then the wrap mode must be CLAMP. + if ((GLAD_ES_VERSION_2_0 && !(GLAD_ES_VERSION_3_0 || GLAD_OES_texture_npot)) + && (pixelWidth != nextP2(pixelWidth) || pixelHeight != nextP2(pixelHeight) || depth != nextP2(depth))) + { + samplerState.wrapU = samplerState.wrapV = samplerState.wrapW = SamplerState::WRAP_CLAMP; + } gl.bindTextureToUnit(this, 0, false); - - GLenum gltextype = OpenGL::getGLTextureType(texType); - - if (gl.bugs.generateMipmapsRequiresTexture2DEnable) - glEnable(gltextype); - - glGenerateMipmap(gltextype); + gl.setSamplerState(texType, samplerState); } -void Canvas::uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r, love::image::ImageDataBase */*imgd*/) +ptrdiff_t Texture::getHandle() const { - OpenGL::TempDebugGroup debuggroup("Texture data upload"); + return texture; +} - gl.bindTextureToUnit(this, 0, false); - - OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(pixelformat, false, sRGB); - GLenum gltarget = OpenGL::getGLTextureType(texType); - - if (texType == TEXTURE_CUBE) - gltarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + slice; - - if (isPixelFormatCompressed(pixelformat)) - { - if (r.x != 0 || r.y != 0) - throw love::Exception("x and y parameters must be 0 for compressed images."); - - if (texType == TEXTURE_2D || texType == TEXTURE_CUBE) - glCompressedTexImage2D(gltarget, level, fmt.internalformat, r.w, r.h, 0, size, data); - else if (texType == TEXTURE_2D_ARRAY || texType == TEXTURE_VOLUME) - glCompressedTexSubImage3D(gltarget, level, 0, 0, slice, r.w, r.h, 1, fmt.internalformat, size, data); - } - else - { - if (texType == TEXTURE_2D || texType == TEXTURE_CUBE) - glTexSubImage2D(gltarget, level, r.x, r.y, r.w, r.h, fmt.externalformat, fmt.type, data); - else if (texType == TEXTURE_2D_ARRAY || texType == TEXTURE_VOLUME) - glTexSubImage3D(gltarget, level, r.x, r.y, slice, r.w, r.h, 1, fmt.externalformat, fmt.type, data); - } +ptrdiff_t Texture::getRenderTargetHandle() const +{ + return renderTarget ? (renderbuffer != 0 ? renderbuffer : texture) : 0; } } // opengl diff --git a/src/modules/graphics/opengl/Canvas.h b/src/modules/graphics/opengl/Texture.h similarity index 74% rename from src/modules/graphics/opengl/Canvas.h rename to src/modules/graphics/opengl/Texture.h index 158f134b0..6699a586d 100644 --- a/src/modules/graphics/opengl/Canvas.h +++ b/src/modules/graphics/opengl/Texture.h @@ -18,14 +18,13 @@ * 3. This notice may not be removed or altered from any source distribution. **/ -#ifndef LOVE_GRAPHICS_OPENGL_CANVAS_H -#define LOVE_GRAPHICS_OPENGL_CANVAS_H +#pragma once -#include "common/config.h" -#include "common/Color.h" -#include "common/int.h" +// LOVE #include "graphics/Texture.h" #include "graphics/Volatile.h" + +// OpenGL #include "OpenGL.h" namespace love @@ -35,56 +34,48 @@ namespace graphics namespace opengl { -class Canvas final : public love::graphics::Texture, public Volatile +class Texture final : public love::graphics::Texture, public Volatile { public: - Canvas(const Settings &settings); - virtual ~Canvas(); + Texture(const Settings &settings, const Slices *data); + + virtual ~Texture(); // Implements Volatile. bool loadVolatile() override; void unloadVolatile() override; - // Implements Texture. - void setSamplerState(const SamplerState &s) override; - ptrdiff_t getHandle() const override; - - love::image::ImageData *newImageData(love::image::Image *module, int slice, int mipmap, const Rect &rect) override; void generateMipmaps() override; + love::image::ImageData *newImageData(love::image::Image *module, int slice, int mipmap, const Rect &rect) override; + void setSamplerState(const SamplerState &s) override; - int getMSAA() const override - { - return actualSamples; - } + ptrdiff_t getHandle() const override; + ptrdiff_t getRenderTargetHandle() const override; + int getMSAA() const override { return actualSamples; } - ptrdiff_t getRenderTargetHandle() const override - { - return renderbuffer != 0 ? renderbuffer : texture; - } - - inline GLuint getFBO() const - { - return fbo; - } + inline GLuint getFBO() const { return fbo; } private: + bool createTexture(); + bool createRenderbuffer(); + void uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r, love::image::ImageDataBase *imgd = nullptr) override; + Slices slices; + GLuint fbo; GLuint texture; GLuint renderbuffer; - GLenum status; + GLenum framebufferStatus; int actualSamples; -}; // Canvas +}; // Texture } // opengl } // graphics } // love - -#endif // LOVE_GRAPHICS_OPENGL_CANVAS_H diff --git a/src/modules/image/ImageData.cpp b/src/modules/image/ImageData.cpp index d9238ba7c..41c89299c 100644 --- a/src/modules/image/ImageData.cpp +++ b/src/modules/image/ImageData.cpp @@ -84,7 +84,7 @@ love::image::ImageData *ImageData::clone() const void ImageData::create(int width, int height, PixelFormat format, void *data) { - size_t datasize = width * height * getPixelFormatSize(format); + size_t datasize = getPixelFormatSliceSize(format, width, height); try { @@ -140,7 +140,7 @@ void ImageData::decode(Data *data) throw love::Exception("Could not decode data to ImageData: unsupported encoded format"); } - if (decodedimage.size != decodedimage.width * decodedimage.height * getPixelFormatSize(decodedimage.format)) + if (decodedimage.size != getPixelFormatSliceSize(decodedimage.format, decodedimage.width, decodedimage.height)) { decoder->freeRawPixels(decodedimage.data); throw love::Exception("Could not convert image!"); @@ -782,7 +782,7 @@ love::thread::Mutex *ImageData::getMutex() const size_t ImageData::getPixelSize() const { - return getPixelFormatSize(format); + return getPixelFormatBlockSize(format); } bool ImageData::validPixelFormat(PixelFormat format) diff --git a/src/modules/image/magpie/EXRHandler.cpp b/src/modules/image/magpie/EXRHandler.cpp index 85cfde0fa..b37f692b1 100644 --- a/src/modules/image/magpie/EXRHandler.cpp +++ b/src/modules/image/magpie/EXRHandler.cpp @@ -193,7 +193,7 @@ FormatHandler::DecodedImage EXRHandler::decode(Data *data) throw love::Exception("Could not decode EXR image: unknown pixel format."); } - img.size = img.width * img.height * getPixelFormatSize(img.format); + img.size = getPixelFormatSliceSize(img.format, img.width, img.height); FreeEXRImage(&exrImage); diff --git a/src/modules/window/sdl/Window.cpp b/src/modules/window/sdl/Window.cpp index 5e9d2d505..43d8ec238 100644 --- a/src/modules/window/sdl/Window.cpp +++ b/src/modules/window/sdl/Window.cpp @@ -933,7 +933,7 @@ bool Window::setIcon(love::image::ImageData *imgd) int w = imgd->getWidth(); int h = imgd->getHeight(); - int bytesperpixel = (int) getPixelFormatSize(imgd->getFormat()); + int bytesperpixel = (int) getPixelFormatBlockSize(imgd->getFormat()); int pitch = w * bytesperpixel; SDL_Surface *sdlicon = nullptr;