renderdoc/driver/gl/gl_resources.cpp (1,772 lines of code) (raw):

/****************************************************************************** * The MIT License (MIT) * * Copyright (c) 2019-2024 Baldur Karlsson * Copyright (c) 2014 Crytek * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. ******************************************************************************/ #include "gl_resources.h" #include "gl_dispatch_table.h" #include "gl_manager.h" template <> rdcstr DoStringise(const GLNamespace &el) { BEGIN_ENUM_STRINGISE(GLNamespace) { STRINGISE_ENUM_NAMED(eResUnknown, "Unknown"); STRINGISE_ENUM_NAMED(eResSpecial, "Special Resource"); STRINGISE_ENUM_NAMED(eResTexture, "Texture"); STRINGISE_ENUM_NAMED(eResSampler, "Sampler"); STRINGISE_ENUM_NAMED(eResFramebuffer, "Framebuffer"); STRINGISE_ENUM_NAMED(eResRenderbuffer, "Renderbuffer"); STRINGISE_ENUM_NAMED(eResBuffer, "Buffer"); STRINGISE_ENUM_NAMED(eResVertexArray, "Vertex Array"); STRINGISE_ENUM_NAMED(eResShader, "Shader"); STRINGISE_ENUM_NAMED(eResProgram, "Program"); STRINGISE_ENUM_NAMED(eResProgramPipe, "Program Pipeline"); STRINGISE_ENUM_NAMED(eResFeedback, "Transform Feedback"); STRINGISE_ENUM_NAMED(eResQuery, "Query"); STRINGISE_ENUM_NAMED(eResSync, "Sync"); STRINGISE_ENUM_NAMED(eResExternalMemory, "External Memory"); STRINGISE_ENUM_NAMED(eResExternalSemaphore, "External Semaphore"); } END_ENUM_STRINGISE(); } template <typename SerialiserType> void DoSerialise(SerialiserType &ser, GLResource &el) { GLResourceManager *rm = (GLResourceManager *)ser.GetUserData(); ResourceId id; if(ser.IsWriting() && rm) id = rm->GetResID(el); DoSerialise(ser, id); if(ser.IsReading()) { if(id != ResourceId() && rm && rm->HasLiveResource(id)) el = rm->GetLiveResource(id); else el = GLResource(MakeNullResource); } } INSTANTIATE_SERIALISE_TYPE(GLResource); byte GLResourceRecord::markerValue[32] = { 0xaa, 0xbb, 0xcc, 0xdd, 0x88, 0x77, 0x66, 0x55, 0x01, 0x23, 0x45, 0x67, 0x98, 0x76, 0x54, 0x32, }; size_t GetCompressedByteSize(GLsizei w, GLsizei h, GLsizei d, GLenum internalformat) { if(!IsCompressedFormat(internalformat)) { RDCERR("Not compressed format %s", ToStr(internalformat).c_str()); return GetByteSize(w, h, d, GetBaseFormat(internalformat), GetDataType(internalformat)); } uint32_t astc[3] = {0, 0, 1u}; switch(internalformat) { // BC1 case eGL_COMPRESSED_RGB_S3TC_DXT1_EXT: case eGL_COMPRESSED_RGBA_S3TC_DXT1_EXT: case eGL_COMPRESSED_SRGB_S3TC_DXT1_EXT: case eGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: return (AlignUp4(w) * AlignUp4(h) * d) / 2; // BC2 case eGL_COMPRESSED_RGBA_S3TC_DXT3_EXT: case eGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: return (AlignUp4(w) * AlignUp4(h) * d); // BC3 case eGL_COMPRESSED_RGBA_S3TC_DXT5_EXT: case eGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: return (AlignUp4(w) * AlignUp4(h) * d); // BC4 case eGL_COMPRESSED_RED_RGTC1: case eGL_COMPRESSED_SIGNED_RED_RGTC1: return (AlignUp4(w) * AlignUp4(h) * d) / 2; // BC5 case eGL_COMPRESSED_RG_RGTC2: case eGL_COMPRESSED_SIGNED_RG_RGTC2: return (AlignUp4(w) * AlignUp4(h) * d); // BC6 case eGL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB: case eGL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB: return (AlignUp4(w) * AlignUp4(h) * d); // BC7 case eGL_COMPRESSED_RGBA_BPTC_UNORM_ARB: case eGL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB: return (AlignUp4(w) * AlignUp4(h) * d); // ETC1 case eGL_ETC1_RGB8_OES: // ETC2 case eGL_COMPRESSED_RGB8_ETC2: case eGL_COMPRESSED_SRGB8_ETC2: case eGL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: case eGL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: return (AlignUp4(w) * AlignUp4(h) * d) / 2; // EAC case eGL_COMPRESSED_RGBA8_ETC2_EAC: case eGL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: return (AlignUp4(w) * AlignUp4(h) * d); case eGL_COMPRESSED_R11_EAC: case eGL_COMPRESSED_SIGNED_R11_EAC: return (AlignUp4(w) * AlignUp4(h) * d) / 2; case eGL_COMPRESSED_RG11_EAC: case eGL_COMPRESSED_SIGNED_RG11_EAC: return (AlignUp4(w) * AlignUp4(h) * d); // ASTC case eGL_COMPRESSED_RGBA_ASTC_4x4_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR: astc[0] = 4; astc[1] = 4; break; case eGL_COMPRESSED_RGBA_ASTC_5x4_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR: astc[0] = 5; astc[1] = 4; break; case eGL_COMPRESSED_RGBA_ASTC_5x5_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR: astc[0] = 5; astc[1] = 5; break; case eGL_COMPRESSED_RGBA_ASTC_6x5_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR: astc[0] = 6; astc[1] = 5; break; case eGL_COMPRESSED_RGBA_ASTC_6x6_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR: astc[0] = 6; astc[1] = 6; break; case eGL_COMPRESSED_RGBA_ASTC_8x5_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR: astc[0] = 8; astc[1] = 5; break; case eGL_COMPRESSED_RGBA_ASTC_8x6_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR: astc[0] = 8; astc[1] = 6; break; case eGL_COMPRESSED_RGBA_ASTC_8x8_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR: astc[0] = 8; astc[1] = 8; break; case eGL_COMPRESSED_RGBA_ASTC_10x5_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR: astc[0] = 10; astc[1] = 5; break; case eGL_COMPRESSED_RGBA_ASTC_10x6_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR: astc[0] = 10; astc[1] = 6; break; case eGL_COMPRESSED_RGBA_ASTC_10x8_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR: astc[0] = 10; astc[1] = 8; break; case eGL_COMPRESSED_RGBA_ASTC_10x10_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR: astc[0] = 10; astc[1] = 10; break; case eGL_COMPRESSED_RGBA_ASTC_12x10_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR: astc[0] = 12; astc[1] = 10; break; case eGL_COMPRESSED_RGBA_ASTC_12x12_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR: astc[0] = 12; astc[1] = 12; break; case eGL_COMPRESSED_RGBA_ASTC_3x3x3_OES: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES: astc[0] = 3; astc[1] = 3; astc[2] = 3; break; case eGL_COMPRESSED_RGBA_ASTC_4x3x3_OES: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES: astc[0] = 4; astc[1] = 3; astc[2] = 3; break; case eGL_COMPRESSED_RGBA_ASTC_4x4x3_OES: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES: astc[0] = 4; astc[1] = 4; astc[2] = 3; break; case eGL_COMPRESSED_RGBA_ASTC_4x4x4_OES: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES: astc[0] = 4; astc[1] = 4; astc[2] = 4; break; case eGL_COMPRESSED_RGBA_ASTC_5x4x4_OES: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES: astc[0] = 5; astc[1] = 4; astc[2] = 4; break; case eGL_COMPRESSED_RGBA_ASTC_5x5x4_OES: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES: astc[0] = 5; astc[1] = 5; astc[2] = 4; break; case eGL_COMPRESSED_RGBA_ASTC_5x5x5_OES: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES: astc[0] = 5; astc[1] = 5; astc[2] = 5; break; case eGL_COMPRESSED_RGBA_ASTC_6x5x5_OES: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES: astc[0] = 6; astc[1] = 5; astc[2] = 5; break; case eGL_COMPRESSED_RGBA_ASTC_6x6x5_OES: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES: astc[0] = 6; astc[1] = 6; astc[2] = 5; break; case eGL_COMPRESSED_RGBA_ASTC_6x6x6_OES: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES: astc[0] = 6; astc[1] = 6; astc[2] = 6; break; // PVRTC case eGL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT: case eGL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT: // 4x8 block in 8 bytes = 32 pixels in 8 bytes = 0.25 bytes per pixel return (AlignUp(w, 4) * AlignUp(h, 8) * d) / 4; case eGL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT: case eGL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT: // 4x4 block in 8 bytes = 16 pixels in 8 bytes = 0.5 bytes per pixel return (AlignUp(w, 4) * AlignUp(h, 4) * d) / 2; default: break; } if(astc[0] > 0 && astc[1] > 0 && astc[2] > 0) { uint32_t blocks[3] = {(w / astc[0]), (h / astc[1]), (d / astc[2])}; // how many blocks are needed - including any extra partial blocks blocks[0] += (w % astc[0]) ? 1 : 0; blocks[1] += (h % astc[1]) ? 1 : 0; blocks[2] += (d % astc[2]) ? 1 : 0; // ASTC blocks are all 128 bits each return blocks[0] * blocks[1] * blocks[2] * 16; } RDCERR("Unrecognised compressed format %s", ToStr(internalformat).c_str()); return GetByteSize(w, h, d, GetBaseFormat(internalformat), GetDataType(internalformat)); } rdcfixedarray<uint32_t, 3> GetCompressedBlockSize(GLenum internalformat) { if(!IsCompressedFormat(internalformat)) { RDCERR("Not compressed format %s", ToStr(internalformat).c_str()); return {1u, 1u, 1u}; } uint32_t astc[3] = {0, 0, 1u}; switch(internalformat) { // BC1 case eGL_COMPRESSED_RGB_S3TC_DXT1_EXT: case eGL_COMPRESSED_RGBA_S3TC_DXT1_EXT: case eGL_COMPRESSED_SRGB_S3TC_DXT1_EXT: case eGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: // BC2 case eGL_COMPRESSED_RGBA_S3TC_DXT3_EXT: case eGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: // BC3 case eGL_COMPRESSED_RGBA_S3TC_DXT5_EXT: case eGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: // BC4 case eGL_COMPRESSED_RED_RGTC1: case eGL_COMPRESSED_SIGNED_RED_RGTC1: // BC5 case eGL_COMPRESSED_RG_RGTC2: case eGL_COMPRESSED_SIGNED_RG_RGTC2: // BC6 case eGL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB: case eGL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB: // BC7 case eGL_COMPRESSED_RGBA_BPTC_UNORM_ARB: case eGL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB: // ETC1 case eGL_ETC1_RGB8_OES: // ETC2 case eGL_COMPRESSED_RGB8_ETC2: case eGL_COMPRESSED_SRGB8_ETC2: case eGL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: case eGL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: // EAC case eGL_COMPRESSED_RGBA8_ETC2_EAC: case eGL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: case eGL_COMPRESSED_R11_EAC: case eGL_COMPRESSED_SIGNED_R11_EAC: case eGL_COMPRESSED_RG11_EAC: case eGL_COMPRESSED_SIGNED_RG11_EAC: return {4u, 4u, 1u}; // ASTC case eGL_COMPRESSED_RGBA_ASTC_4x4_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR: astc[0] = 4; astc[1] = 4; break; case eGL_COMPRESSED_RGBA_ASTC_5x4_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR: astc[0] = 5; astc[1] = 4; break; case eGL_COMPRESSED_RGBA_ASTC_5x5_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR: astc[0] = 5; astc[1] = 5; break; case eGL_COMPRESSED_RGBA_ASTC_6x5_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR: astc[0] = 6; astc[1] = 5; break; case eGL_COMPRESSED_RGBA_ASTC_6x6_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR: astc[0] = 6; astc[1] = 6; break; case eGL_COMPRESSED_RGBA_ASTC_8x5_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR: astc[0] = 8; astc[1] = 5; break; case eGL_COMPRESSED_RGBA_ASTC_8x6_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR: astc[0] = 8; astc[1] = 6; break; case eGL_COMPRESSED_RGBA_ASTC_8x8_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR: astc[0] = 8; astc[1] = 8; break; case eGL_COMPRESSED_RGBA_ASTC_10x5_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR: astc[0] = 10; astc[1] = 5; break; case eGL_COMPRESSED_RGBA_ASTC_10x6_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR: astc[0] = 10; astc[1] = 6; break; case eGL_COMPRESSED_RGBA_ASTC_10x8_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR: astc[0] = 10; astc[1] = 8; break; case eGL_COMPRESSED_RGBA_ASTC_10x10_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR: astc[0] = 10; astc[1] = 10; break; case eGL_COMPRESSED_RGBA_ASTC_12x10_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR: astc[0] = 12; astc[1] = 10; break; case eGL_COMPRESSED_RGBA_ASTC_12x12_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR: astc[0] = 12; astc[1] = 12; break; case eGL_COMPRESSED_RGBA_ASTC_3x3x3_OES: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES: astc[0] = 3; astc[1] = 3; astc[2] = 3; break; case eGL_COMPRESSED_RGBA_ASTC_4x3x3_OES: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES: astc[0] = 4; astc[1] = 3; astc[2] = 3; break; case eGL_COMPRESSED_RGBA_ASTC_4x4x3_OES: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES: astc[0] = 4; astc[1] = 4; astc[2] = 3; break; case eGL_COMPRESSED_RGBA_ASTC_4x4x4_OES: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES: astc[0] = 4; astc[1] = 4; astc[2] = 4; break; case eGL_COMPRESSED_RGBA_ASTC_5x4x4_OES: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES: astc[0] = 5; astc[1] = 4; astc[2] = 4; break; case eGL_COMPRESSED_RGBA_ASTC_5x5x4_OES: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES: astc[0] = 5; astc[1] = 5; astc[2] = 4; break; case eGL_COMPRESSED_RGBA_ASTC_5x5x5_OES: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES: astc[0] = 5; astc[1] = 5; astc[2] = 5; break; case eGL_COMPRESSED_RGBA_ASTC_6x5x5_OES: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES: astc[0] = 6; astc[1] = 5; astc[2] = 5; break; case eGL_COMPRESSED_RGBA_ASTC_6x6x5_OES: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES: astc[0] = 6; astc[1] = 6; astc[2] = 5; break; case eGL_COMPRESSED_RGBA_ASTC_6x6x6_OES: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES: astc[0] = 6; astc[1] = 6; astc[2] = 6; break; // PVRTC case eGL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT: case eGL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT: case eGL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT: case eGL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT: return {4u, 4u, 1u}; default: break; } if(astc[0] > 0 && astc[1] > 0 && astc[2] > 0) { return {astc[0], astc[1], astc[2]}; } RDCERR("Unrecognised compressed format %s", ToStr(internalformat).c_str()); return {1u, 1u, 1u}; } size_t GetByteSize(GLsizei w, GLsizei h, GLsizei d, GLenum format, GLenum type) { size_t elemSize = 1; switch(type) { case eGL_UNSIGNED_BYTE: case eGL_BYTE: elemSize = 1; break; case eGL_UNSIGNED_SHORT: case eGL_SHORT: case eGL_HALF_FLOAT_OES: case eGL_HALF_FLOAT: elemSize = 2; break; case eGL_UNSIGNED_INT: case eGL_INT: case eGL_FLOAT: elemSize = 4; break; case eGL_DOUBLE: elemSize = 8; break; case eGL_UNSIGNED_BYTE_3_3_2: case eGL_UNSIGNED_BYTE_2_3_3_REV: return w * h * d; case eGL_UNSIGNED_SHORT_5_6_5: case eGL_UNSIGNED_SHORT_5_6_5_REV: case eGL_UNSIGNED_SHORT_4_4_4_4: case eGL_UNSIGNED_SHORT_4_4_4_4_REV: case eGL_UNSIGNED_SHORT_5_5_5_1: case eGL_UNSIGNED_SHORT_1_5_5_5_REV: return w * h * d * 2; case eGL_UNSIGNED_INT_8_8_8_8: case eGL_UNSIGNED_INT_8_8_8_8_REV: case eGL_UNSIGNED_INT_10_10_10_2: case eGL_UNSIGNED_INT_2_10_10_10_REV: case eGL_INT_2_10_10_10_REV: case eGL_UNSIGNED_INT_10F_11F_11F_REV: case eGL_UNSIGNED_INT_5_9_9_9_REV: return w * h * d * 4; case eGL_DEPTH_COMPONENT16: return w * h * d * 2; case eGL_DEPTH_COMPONENT24: case eGL_DEPTH24_STENCIL8: case eGL_DEPTH_COMPONENT32: case eGL_DEPTH_COMPONENT32F: case eGL_UNSIGNED_INT_24_8: return w * h * d * 4; case eGL_DEPTH32F_STENCIL8: case eGL_FLOAT_32_UNSIGNED_INT_24_8_REV: return w * h * d * 8; default: RDCERR("Unhandled Byte Size type %s!", ToStr(type).c_str()); break; } switch(format) { case eGL_RED: case eGL_RED_INTEGER: case eGL_GREEN: case eGL_GREEN_INTEGER: case eGL_BLUE: case eGL_BLUE_INTEGER: case eGL_LUMINANCE: case eGL_ALPHA: case eGL_ALPHA_INTEGER: case eGL_DEPTH_COMPONENT: case eGL_STENCIL_INDEX: case eGL_STENCIL: return w * h * d * elemSize; case eGL_RG: case eGL_RG_INTEGER: case eGL_LUMINANCE_ALPHA: case eGL_DEPTH_STENCIL: return w * h * d * elemSize * 2; case eGL_RGB: case eGL_RGB_INTEGER: case eGL_BGR: case eGL_BGR_INTEGER: case eGL_SRGB: return w * h * d * elemSize * 3; case eGL_RGBA: case eGL_RGBA_INTEGER: case eGL_BGRA: case eGL_BGRA_INTEGER: case eGL_SRGB_ALPHA: return w * h * d * elemSize * 4; default: RDCERR("Unhandled Byte Size format %s!", ToStr(format).c_str()); break; } RDCERR("Unhandled Byte Size case!"); return 1; } GLenum GetBaseFormat(GLenum internalFormat) { switch((int)internalFormat) { case eGL_R8: case eGL_R8_SNORM: case eGL_SR8_EXT: case eGL_R16: case eGL_R16_SNORM: case eGL_R16F: case eGL_R32F: case eGL_RED: return eGL_RED; case eGL_ALPHA: case eGL_ALPHA8_EXT: return eGL_ALPHA; case eGL_LUMINANCE: return eGL_LUMINANCE; case eGL_LUMINANCE_ALPHA: return eGL_LUMINANCE_ALPHA; case eGL_INTENSITY_EXT: return eGL_INTENSITY_EXT; case eGL_R8I: case eGL_R16I: case eGL_R32I: case eGL_R32UI: case eGL_R16UI: case eGL_R8UI: case eGL_RED_INTEGER: return eGL_RED_INTEGER; case eGL_RG8: case eGL_RG8_SNORM: case eGL_SRG8_EXT: case eGL_RG16: case eGL_RG16_SNORM: case eGL_RG16F: case eGL_RG32F: case eGL_RG: return eGL_RG; case eGL_RG8I: case eGL_RG8UI: case eGL_RG16I: case eGL_RG16UI: case eGL_RG32I: case eGL_RG32UI: case eGL_RG_INTEGER: return eGL_RG_INTEGER; case eGL_R3_G3_B2: case eGL_RGB4: case eGL_RGB5: case eGL_RGB565: case eGL_RGB8: case eGL_RGB8_SNORM: case eGL_RGB10: case eGL_RGB12: case eGL_RGB16: case eGL_RGB16_SNORM: case eGL_SRGB: case eGL_SRGB8: case eGL_RGB16F: case eGL_RGB32F: case eGL_R11F_G11F_B10F: case eGL_RGB9_E5: case eGL_RGB: return eGL_RGB; case eGL_RGB8I: case eGL_RGB8UI: case eGL_RGB16I: case eGL_RGB16UI: case eGL_RGB32I: case eGL_RGB32UI: case eGL_RGB_INTEGER: return eGL_RGB_INTEGER; case eGL_RGBA2: case eGL_RGBA4: case eGL_RGB5_A1: case eGL_RGBA8: case eGL_RGBA8_SNORM: case eGL_RGB10_A2: case eGL_RGBA12: case eGL_RGBA16: case eGL_RGBA16_SNORM: case eGL_SRGB_ALPHA: case eGL_SRGB8_ALPHA8: case eGL_RGBA16F: case eGL_RGBA32F: case eGL_RGBA: return eGL_RGBA; case eGL_RGB10_A2UI: case eGL_RGBA8I: case eGL_RGBA8UI: case eGL_RGBA16I: case eGL_RGBA16UI: case eGL_RGBA32UI: case eGL_RGBA32I: case eGL_RGBA_INTEGER: return eGL_RGBA_INTEGER; case eGL_BGRA8_EXT: case eGL_BGRA: return eGL_BGRA; case eGL_DEPTH_COMPONENT16: case eGL_DEPTH_COMPONENT24: case eGL_DEPTH_COMPONENT32: case eGL_DEPTH_COMPONENT32F: case eGL_DEPTH_COMPONENT: return eGL_DEPTH_COMPONENT; case eGL_DEPTH24_STENCIL8: case eGL_DEPTH32F_STENCIL8: case eGL_DEPTH_STENCIL: return eGL_DEPTH_STENCIL; case eGL_STENCIL_INDEX1: case eGL_STENCIL_INDEX4: case eGL_STENCIL_INDEX8: case eGL_STENCIL_INDEX16: case eGL_STENCIL: return eGL_STENCIL_INDEX; default: break; } RDCERR("Unhandled Base Format case %s!", ToStr(internalFormat).c_str()); return eGL_NONE; } GLenum GetDataType(GLenum internalFormat) { switch((int)internalFormat) { case eGL_RGBA8UI: case eGL_RG8UI: case eGL_R8UI: case eGL_RGBA8: case eGL_RG8: case eGL_R8: case eGL_RGB8: case eGL_RGB8UI: case eGL_BGRA: case eGL_BGRA8_EXT: case eGL_SRGB8_ALPHA8: case eGL_SRGB8: case eGL_SRG8_EXT: case eGL_SR8_EXT: case eGL_SRGB_ALPHA: case eGL_SRGB: case eGL_RED: case eGL_RG: case eGL_RGB: case eGL_RGBA: return eGL_UNSIGNED_BYTE; case eGL_RGBA8I: case eGL_RG8I: case eGL_R8I: case eGL_RGBA8_SNORM: case eGL_RG8_SNORM: case eGL_R8_SNORM: case eGL_RGB8_SNORM: case eGL_RGB8I: return eGL_BYTE; case eGL_RGBA16UI: case eGL_RG16UI: case eGL_R16UI: case eGL_RGBA16: case eGL_RG16: case eGL_R16: case eGL_RGB16: case eGL_RGB16UI: case eGL_DEPTH_COMPONENT16: return eGL_UNSIGNED_SHORT; case eGL_RGBA16I: case eGL_RG16I: case eGL_R16I: case eGL_RGBA16_SNORM: case eGL_RG16_SNORM: case eGL_R16_SNORM: case eGL_RGB16_SNORM: case eGL_RGB16I: return eGL_SHORT; case eGL_RGBA32UI: case eGL_RG32UI: case eGL_R32UI: case eGL_RGB32UI: case eGL_DEPTH_COMPONENT: case eGL_DEPTH_COMPONENT24: case eGL_DEPTH_COMPONENT32: return eGL_UNSIGNED_INT; case eGL_RGBA32I: case eGL_RG32I: case eGL_R32I: case eGL_RGB32I: return eGL_INT; case eGL_RGBA16F: case eGL_RG16F: case eGL_RGB16F: case eGL_R16F: return eGL_HALF_FLOAT; case eGL_RGBA32F: case eGL_RGB32F: case eGL_RG32F: case eGL_R32F: case eGL_DEPTH_COMPONENT32F: return eGL_FLOAT; case eGL_R11F_G11F_B10F: return eGL_UNSIGNED_INT_10F_11F_11F_REV; case eGL_RGB10_A2UI: return eGL_UNSIGNED_INT_2_10_10_10_REV; case eGL_RGB10_A2: return eGL_UNSIGNED_INT_2_10_10_10_REV; case eGL_R3_G3_B2: return eGL_UNSIGNED_BYTE_3_3_2; case eGL_RGB4: case eGL_RGBA4: return eGL_UNSIGNED_SHORT_4_4_4_4; case eGL_RGBA2: return eGL_UNSIGNED_BYTE; case eGL_RGB5_A1: return eGL_UNSIGNED_SHORT_5_5_5_1; case eGL_RGB565: case eGL_RGB5: return eGL_UNSIGNED_SHORT_5_6_5; case eGL_RGB10: return eGL_UNSIGNED_INT_10_10_10_2; case eGL_RGB9_E5: return eGL_UNSIGNED_INT_5_9_9_9_REV; case eGL_DEPTH24_STENCIL8: return eGL_UNSIGNED_INT_24_8; case eGL_DEPTH_STENCIL: case eGL_DEPTH32F_STENCIL8: return eGL_FLOAT_32_UNSIGNED_INT_24_8_REV; case eGL_STENCIL_INDEX: case eGL_STENCIL_INDEX8: return eGL_UNSIGNED_BYTE; case eGL_ALPHA: case eGL_ALPHA8_EXT: case eGL_LUMINANCE_ALPHA: case eGL_LUMINANCE: case eGL_INTENSITY_EXT: return eGL_UNSIGNED_BYTE; default: break; } RDCERR("Unhandled Data Type case %s!", ToStr(internalFormat).c_str()); return eGL_NONE; } int GetNumMips(GLenum target, GLuint tex, GLuint w, GLuint h, GLuint d) { int mips = 1; // renderbuffers don't have mips if(target == eGL_RENDERBUFFER) return 1; GLint immut = 0; GL.glGetTextureParameterivEXT(tex, target, eGL_TEXTURE_IMMUTABLE_FORMAT, &immut); if(immut) GL.glGetTextureParameterivEXT(tex, target, eGL_TEXTURE_IMMUTABLE_LEVELS, (GLint *)&mips); else mips = CalcNumMips(w, h, d); GLint maxLevel = 1000; GL.glGetTextureParameterivEXT(tex, target, eGL_TEXTURE_MAX_LEVEL, &maxLevel); mips = RDCMIN(mips, maxLevel + 1); if(immut == 0) { // check to see if all mips are set, or clip the number of mips to those that are // set. if(target == eGL_TEXTURE_CUBE_MAP) target = eGL_TEXTURE_CUBE_MAP_POSITIVE_X; for(int i = 0; i < mips; i++) { GLint width = 0; GL.glGetTextureLevelParameterivEXT(tex, target, i, eGL_TEXTURE_WIDTH, &width); if(width == 0) { mips = i; break; } } } return RDCMAX(1, mips); } void GetFramebufferMipAndLayer(GLuint framebuffer, GLenum attachment, GLint *mip, GLint *layer) { GL.glGetNamedFramebufferAttachmentParameterivEXT(framebuffer, attachment, eGL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL, mip); GLenum face = eGL_NONE; GL.glGetNamedFramebufferAttachmentParameterivEXT( framebuffer, attachment, eGL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE, (GLint *)&face); if(face == 0) { GL.glGetNamedFramebufferAttachmentParameterivEXT( framebuffer, attachment, eGL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER, layer); } else { *layer = CubeTargetIndex(face); } } // GL_TEXTURE_SWIZZLE_RGBA is not supported on GLES, so for consistency we use r/g/b/a component // swizzles for both GL and GLES. // The same applies to SetTextureSwizzle function. void GetTextureSwizzle(GLuint tex, GLenum target, GLenum *swizzleRGBA) { GL.glGetTextureParameterivEXT(tex, target, eGL_TEXTURE_SWIZZLE_R, (GLint *)&swizzleRGBA[0]); GL.glGetTextureParameterivEXT(tex, target, eGL_TEXTURE_SWIZZLE_G, (GLint *)&swizzleRGBA[1]); GL.glGetTextureParameterivEXT(tex, target, eGL_TEXTURE_SWIZZLE_B, (GLint *)&swizzleRGBA[2]); GL.glGetTextureParameterivEXT(tex, target, eGL_TEXTURE_SWIZZLE_A, (GLint *)&swizzleRGBA[3]); } void SetTextureSwizzle(GLuint tex, GLenum target, const GLenum *swizzleRGBA) { GL.glTextureParameterivEXT(tex, target, eGL_TEXTURE_SWIZZLE_R, (GLint *)&swizzleRGBA[0]); GL.glTextureParameterivEXT(tex, target, eGL_TEXTURE_SWIZZLE_G, (GLint *)&swizzleRGBA[1]); GL.glTextureParameterivEXT(tex, target, eGL_TEXTURE_SWIZZLE_B, (GLint *)&swizzleRGBA[2]); GL.glTextureParameterivEXT(tex, target, eGL_TEXTURE_SWIZZLE_A, (GLint *)&swizzleRGBA[3]); } static rdcstr DimensionString(int dimensions, GLint width, GLint height, GLint depth) { if(dimensions == 1) return StringFormat::Fmt("%d", width); else if(dimensions == 2) return StringFormat::Fmt("%dx%d", width, height); else return StringFormat::Fmt("%dx%dx%d", width, depth); } rdcstr GetTextureCompleteStatus(GLenum target, GLuint tex, GLuint sampler) { // unbound and texture buffers don't need to be checked if(tex == 0 || target == eGL_TEXTURE_BUFFER) return rdcstr(); // the completeness rules are fairly complex. The relevant spec is copied here and each rule is // annotated with a number for easier reference. /* For one-, two-, and three-dimensional and one- and two-dimensional array textures, a texture is mipmap complete if all of the following conditions hold true: * The set of mipmap images levelBase through q (where q is defined in section 8.14.3) were each specified with the same internal format. [RULE_1] * The dimensions of the images follow the sequence described in section 8.14.3. [RULE_2] * level base <= level max [RULE_3] [q is the usual definition - natural mip numbering, clamped by either immutable number of mips or MAX_LEVEL] Image levels k where k < level base or k > q are insignificant to the definition of completeness. A cube map texture is mipmap complete if each of the six texture images, considered individually, is mipmap complete. [RULE_4] Additionally, a cube map texture is cube complete if the following conditions all hold true: * The level base texture images of each of the six cubemap faces have identical, positive, and square dimensions. [RULE_5] * The level base images were each specified with the same internal format. [RULE_6] A cube map array texture is cube array complete if it is complete when treated as a two-dimensional array [RULE_7] and cube complete for every cube map slice within the array texture. [RULE_8] Using the preceding definitions, a texture is complete unless any of the following conditions hold true: * Any dimension of the level base image is not positive. For a rectangle or multisample texture, level base is always zero. [RULE_9] * The texture is a cube map texture, and is not cube complete. [RULE_10] * The texture is a cube map array texture, and is not cube array complete. [RULE_11] * The minification filter requires a mipmap (is neither NEAREST nor LINEAR), and the texture is not mipmap complete. [RULE_12] * Any of - The internal format of the texture is integer (see table 8.12). [RULE_13] - The internal format is STENCIL_INDEX. [RULE_14] - The internal format is DEPTH_STENCIL, and the value of DEPTH_STENCIL_TEXTURE_MODE for the texture is STENCIL_INDEX. [RULE_15] and either the magnification filter is not NEAREST, or the minification filter is neither NEAREST nor NEAREST_MIPMAP_NEAREST */ GLint isImmutable = 0, levelBase = 0, levelMax = 1000; GL.glGetTextureParameterivEXT(tex, target, eGL_TEXTURE_IMMUTABLE_FORMAT, &isImmutable); GL.glGetTextureParameterivEXT(tex, target, eGL_TEXTURE_BASE_LEVEL, &levelBase); GL.glGetTextureParameterivEXT(tex, target, eGL_TEXTURE_MAX_LEVEL, &levelMax); bool noMips = false; // For a rectangle or multisample texture, level base is always zero. if(target == eGL_TEXTURE_RECTANGLE || target == eGL_TEXTURE_2D_MULTISAMPLE || target == eGL_TEXTURE_2D_MULTISAMPLE_ARRAY) { levelBase = 0; noMips = true; } GLenum targets[] = { eGL_TEXTURE_CUBE_MAP_POSITIVE_X, eGL_TEXTURE_CUBE_MAP_NEGATIVE_X, eGL_TEXTURE_CUBE_MAP_POSITIVE_Y, eGL_TEXTURE_CUBE_MAP_NEGATIVE_Y, eGL_TEXTURE_CUBE_MAP_POSITIVE_Z, eGL_TEXTURE_CUBE_MAP_NEGATIVE_Z, }; int faceCount = ARRAY_COUNT(targets); if(target != eGL_TEXTURE_CUBE_MAP) { targets[0] = target; faceCount = 1; } // get the properties of levelBase (on POSITIVE_X for cubes) GLint levelBaseWidth = 1, levelBaseHeight = 1, levelBaseDepth = 1; int dimensions = 2; if(target == eGL_TEXTURE_1D || target == eGL_TEXTURE_1D_ARRAY) { dimensions = 1; } else if(target == eGL_TEXTURE_3D) { dimensions = 3; } GL.glGetTextureLevelParameterivEXT(tex, targets[0], levelBase, eGL_TEXTURE_WIDTH, &levelBaseWidth); if(dimensions >= 2) GL.glGetTextureLevelParameterivEXT(tex, targets[0], levelBase, eGL_TEXTURE_HEIGHT, &levelBaseHeight); if(dimensions >= 3) GL.glGetTextureLevelParameterivEXT(tex, targets[0], levelBase, eGL_TEXTURE_DEPTH, &levelBaseDepth); bool mipmapComplete = true; rdcstr mipmapIncompleteness; bool cube = (target == eGL_TEXTURE_CUBE_MAP || target == eGL_TEXTURE_CUBE_MAP_ARRAY); GLenum levelBaseFormat = eGL_NONE; // immutable textures are always mipmap complete if(isImmutable) { mipmapComplete = true; // get the format so we can check for integer-ness etc GL.glGetTextureLevelParameterivEXT(tex, targets[0], levelBase, eGL_TEXTURE_INTERNAL_FORMAT, (GLint *)&levelBaseFormat); } else { GLint p = 0, q = 0; // Otherwise p = floor(log2(maxsize)) + levelBase, and all arrays from levelBase through // q = min(p, levelMax) must be defined, as discussed in section 8.17. p = CalcNumMips(levelBaseWidth, levelBaseHeight, levelBaseDepth) - 1 + levelBase; q = RDCMIN(p, levelMax); // this isn't part of the spec, but ensure we process at least levelBase, even if levelMax is // less. That's because levelBase <= levelMax is only a mipmap completeness requirement // (otherwise if mips aren't used, levelMax is effectively ignored), but we want to check // [RULE_9] that levelBase has valid dimensions in this loop since we need to check it per-face // for cubemaps q = RDCMAX(levelBase, q); // [RULE_4] - this just requires the loop over faces, completely independently // [RULE_7] [RULE_8] - this mostly is implicit because a single level of a cubemap array can't // vary format or dimension, so as long as we enforce cubemap square rules for arrays it works. for(int face = 0; face < faceCount; face++) { GLint expectedWidth = levelBaseWidth; GLint expectedHeight = levelBaseHeight; GLint expectedDepth = levelBaseDepth; GLenum curFaceLevelBaseFormat = eGL_NONE; for(GLint level = levelBase; level <= q; level++) { GLint levelWidth = 1, levelHeight = 1, levelDepth = 1; GL.glGetTextureLevelParameterivEXT(tex, targets[face], level, eGL_TEXTURE_WIDTH, &levelWidth); if(dimensions >= 2) GL.glGetTextureLevelParameterivEXT(tex, targets[face], level, eGL_TEXTURE_HEIGHT, &levelHeight); if(dimensions >= 3) GL.glGetTextureLevelParameterivEXT(tex, targets[face], level, eGL_TEXTURE_DEPTH, &levelDepth); GLenum fmt = eGL_NONE; GL.glGetTextureLevelParameterivEXT(tex, targets[face], level, eGL_TEXTURE_INTERNAL_FORMAT, (GLint *)&fmt); if(level == levelBase) { curFaceLevelBaseFormat = fmt; if(face == 0) levelBaseFormat = fmt; } rdcstr faceStr; rdcstr face0Str; if(faceCount > 1) { face0Str = StringFormat::Fmt(" of %s", ToStr(targets[0]).c_str()); faceStr = StringFormat::Fmt(" of %s", ToStr(targets[face]).c_str()); } // [RULE_10] [RULE_11] - cubemap completeness issues are fatal, return immediately. // [RULE_9] // [RULE_5] - by the loop, this also checks that all faces have positive dimensions if(level == levelBase && (levelWidth <= 0 || levelHeight <= 0 || levelDepth <= 0)) { return StringFormat::Fmt( "BASE_LEVEL %d%s has invalid dimensions: %s", levelBase, faceStr.c_str(), DimensionString(dimensions, levelWidth, levelHeight, levelDepth).c_str()); } // [RULE_5] - check the square property here // [RULE_8] - checking this applies for cubemap arrays too if(cube && level == levelBase && levelWidth != levelHeight) { return StringFormat::Fmt( "BASE_LEVEL %d%s has non-square dimensions: %s " "(BASE_LEVEL %d)\n", level, faceStr.c_str(), DimensionString(dimensions, levelWidth, levelHeight, levelDepth).c_str()); } // [RULE_5] - check that all faces are identical dimensions here if(cube && level == levelBase && (levelWidth != levelBaseWidth || levelHeight != levelBaseHeight)) { return StringFormat::Fmt( "BASE_LEVEL %d%s has different dimensions: %s to BASE_LEVEL %d%s: %s", levelBase, faceStr.c_str(), DimensionString(dimensions, levelWidth, levelHeight, levelDepth).c_str(), levelBase, face0Str.c_str(), DimensionString(dimensions, levelBaseWidth, levelBaseHeight, levelBaseDepth).c_str()); } // [RULE_6] if(face > 0 && levelBaseFormat != curFaceLevelBaseFormat) { return StringFormat::Fmt( "BASE_LEVEL %d%s has different format: %s to BASE_LEVEL %d%s: %s", levelBase, faceStr.c_str(), ToStr(curFaceLevelBaseFormat).c_str(), levelBase, face0Str.c_str(), ToStr(levelBaseFormat).c_str()); } // below here are only mipmap completeness checks, break out if we're already mipmap // incomplete if(!mipmapComplete) break; // [RULE_1] if(level == levelBase) { curFaceLevelBaseFormat = fmt; // accept any valid format, but if mip 0 isn't defined that's an error. It shouldn't be // possible to have a texture with no format but valid dimensions (see above [RULE_9] // check), but be safe because GL is GL. if(fmt == eGL_NONE) return StringFormat::Fmt("BASE_LEVEL %d%s has no format.\n", levelBase, faceStr.c_str()); } else { if(curFaceLevelBaseFormat != fmt) { mipmapComplete = false; // common case - mip isn't defined at all if(fmt == eGL_NONE) { mipmapIncompleteness += StringFormat::Fmt("Level %d%s is not defined. (BASE_LEVEL %d, MAX_LEVEL %d)\n", level, faceStr.c_str(), levelBase, levelMax); } else { // uncommon case, mip is defined but with the wrong format mipmapIncompleteness += StringFormat::Fmt( "Mip level %d%s has format %s which doesn't match format %s at BASE_LEVEL %d%s " "(MAX_LEVEL is %d)\n", level, faceStr.c_str(), ToStr(fmt).c_str(), ToStr(curFaceLevelBaseFormat).c_str(), levelBase, face0Str.c_str(), levelMax); } // stop processing, other problems may be the same break; } } // [RULE_2] // if the level was completely undefined, it would have failed the format check, so this // must be badly sized mips. // note that for e.g. 2D textures, depth is always 1 so will be trivially as expected. if(levelWidth != expectedWidth || levelHeight != expectedHeight || levelDepth != expectedDepth) { mipmapComplete = false; mipmapIncompleteness += StringFormat::Fmt( "Mip level %d%s has invalid dimensions: %s, expected: %s " "(BASE_LEVEL %d, MAX_LEVEL %d)\n", level, faceStr.c_str(), DimensionString(dimensions, levelWidth, levelHeight, levelDepth).c_str(), DimensionString(dimensions, expectedWidth, expectedHeight, expectedDepth).c_str(), levelBase, levelMax); break; } expectedWidth = RDCMAX(1, expectedWidth >> 1); expectedHeight = RDCMAX(1, expectedHeight >> 1); expectedDepth = RDCMAX(1, expectedDepth >> 1); } } } // [RULE_3] if(mipmapComplete && !(levelBase <= levelMax)) { mipmapComplete = false; mipmapIncompleteness += StringFormat::Fmt("BASE_LEVEL %d must be <= MAX_LEVEL %d\n", levelBase, levelMax); } // MSAA textures don't have sampler state, so they count as if they are NEAREST. This means they // can't fail due to filtering modes, so we can return if(target == eGL_TEXTURE_2D_MULTISAMPLE || target == eGL_TEXTURE_2D_MULTISAMPLE_ARRAY) return rdcstr(); GLenum minf = eGL_NEAREST; GLenum magf = eGL_NEAREST; if(sampler != 0) GL.glGetSamplerParameteriv(sampler, eGL_TEXTURE_MIN_FILTER, (GLint *)&minf); else GL.glGetTextureParameterivEXT(tex, target, eGL_TEXTURE_MIN_FILTER, (GLint *)&minf); if(sampler != 0) GL.glGetSamplerParameteriv(sampler, eGL_TEXTURE_MAG_FILTER, (GLint *)&magf); else GL.glGetTextureParameterivEXT(tex, target, eGL_TEXTURE_MAG_FILTER, (GLint *)&magf); // [RULE_12] if(minf != eGL_NEAREST && minf != eGL_LINEAR && !mipmapComplete) return StringFormat::Fmt( "TEXTURE_MIN_FILTER is %s which requires mipmaps, " "but texture is mipmap incomplete:\n%s", ToStr(minf).c_str(), mipmapIncompleteness.c_str()); rdcstr ret; // [RULE_13] [RULE_14] [RULE_15] - detect linear filters in either direction if(magf != eGL_NEAREST) ret = StringFormat::Fmt("TEXTURE_MAG_FILTER is %s", ToStr(magf).c_str()); else if(minf != eGL_NEAREST && minf != eGL_NEAREST_MIPMAP_NEAREST) ret = StringFormat::Fmt("TEXTURE_MIN_FILTER is %s", ToStr(minf).c_str()); // if we have a linear filter, check for non-filterable formats if(!ret.isEmpty()) { // all compressed formats are filterable if(IsCompressedFormat(levelBaseFormat)) return rdcstr(); // [RULE_13] if(IsUIntFormat(levelBaseFormat) || IsSIntFormat(levelBaseFormat)) { return ret + StringFormat::Fmt(" and texture is integer format (%s)", ToStr(levelBaseFormat).c_str()); } // [RULE_14] else if(GetBaseFormat(levelBaseFormat) == eGL_STENCIL_INDEX) { return ret + StringFormat::Fmt(" and texture is stencil format (%s)", ToStr(levelBaseFormat).c_str()); } // [RULE_15] else if(GetBaseFormat(levelBaseFormat) == eGL_DEPTH_STENCIL) { GLint depthMode = eGL_DEPTH_COMPONENT; if(HasExt[ARB_stencil_texturing]) GL.glGetTextureParameterivEXT(tex, target, eGL_DEPTH_STENCIL_TEXTURE_MODE, &depthMode); if(depthMode == eGL_STENCIL_INDEX) { return ret + StringFormat::Fmt( " and texture is depth/stencil format (%s) " "with DEPTH_STENCIL_TEXTURE_MODE == STENCIL_INDEX", ToStr(levelBaseFormat).c_str()); } } } // no completeness problems! return rdcstr(); } bool EmulateLuminanceFormat(GLuint tex, GLenum target, GLenum &internalFormat, GLenum &dataFormat) { GLenum swizzle[] = {eGL_RED, eGL_GREEN, eGL_BLUE, eGL_ALPHA}; // set swizzles switch(internalFormat) { case eGL_INTENSITY32F_ARB: case eGL_INTENSITY16F_ARB: case eGL_INTENSITY_EXT: case eGL_INTENSITY8_EXT: case eGL_INTENSITY16_EXT: case eGL_INTENSITY32UI_EXT: case eGL_INTENSITY16UI_EXT: case eGL_INTENSITY8UI_EXT: case eGL_INTENSITY32I_EXT: case eGL_INTENSITY16I_EXT: case eGL_INTENSITY8I_EXT: case eGL_INTENSITY_SNORM: case eGL_INTENSITY8_SNORM: case eGL_INTENSITY16_SNORM: // intensity replicates across all 4 of RGBA swizzle[0] = swizzle[1] = swizzle[2] = swizzle[3] = eGL_RED; break; case eGL_ALPHA: case eGL_ALPHA_INTEGER: case eGL_ALPHA32F_ARB: case eGL_ALPHA16F_ARB: case eGL_ALPHA8_EXT: case eGL_ALPHA16_EXT: case eGL_ALPHA32UI_EXT: case eGL_ALPHA16UI_EXT: case eGL_ALPHA8UI_EXT: case eGL_ALPHA32I_EXT: case eGL_ALPHA16I_EXT: case eGL_ALPHA8I_EXT: case eGL_ALPHA_SNORM: case eGL_ALPHA8_SNORM: case eGL_ALPHA16_SNORM: // single component alpha channel swizzle[0] = swizzle[1] = swizzle[2] = eGL_ZERO; swizzle[3] = eGL_RED; break; case eGL_LUMINANCE: case eGL_LUMINANCE32F_ARB: case eGL_LUMINANCE16F_ARB: case eGL_LUMINANCE8_EXT: case eGL_LUMINANCE16_EXT: case eGL_LUMINANCE32UI_EXT: case eGL_LUMINANCE16UI_EXT: case eGL_LUMINANCE8UI_EXT: case eGL_LUMINANCE32I_EXT: case eGL_LUMINANCE16I_EXT: case eGL_LUMINANCE8I_EXT: case eGL_LUMINANCE_INTEGER_EXT: case eGL_LUMINANCE_SNORM: case eGL_LUMINANCE8_SNORM: case eGL_LUMINANCE16_SNORM: case eGL_SLUMINANCE: case eGL_SLUMINANCE8: // luminance replicates over RGB swizzle[0] = swizzle[1] = swizzle[2] = eGL_RED; // alpha explicitly set to 1 in luminance formats swizzle[3] = eGL_ONE; break; case eGL_LUMINANCE_ALPHA: case eGL_LUMINANCE_ALPHA32F_ARB: case eGL_LUMINANCE_ALPHA16F_ARB: case eGL_LUMINANCE8_ALPHA8_EXT: case eGL_LUMINANCE16_ALPHA16_EXT: case eGL_LUMINANCE_ALPHA32UI_EXT: case eGL_LUMINANCE_ALPHA16UI_EXT: case eGL_LUMINANCE_ALPHA8UI_EXT: case eGL_LUMINANCE_ALPHA32I_EXT: case eGL_LUMINANCE_ALPHA16I_EXT: case eGL_LUMINANCE_ALPHA8I_EXT: case eGL_LUMINANCE_ALPHA_INTEGER_EXT: case eGL_LUMINANCE_ALPHA_SNORM: case eGL_LUMINANCE8_ALPHA8_SNORM: case eGL_LUMINANCE16_ALPHA16_SNORM: case eGL_SLUMINANCE_ALPHA: case eGL_SLUMINANCE8_ALPHA8: // luminance over RGB swizzle[0] = swizzle[1] = swizzle[2] = eGL_RED; // alpha in alpha swizzle[3] = eGL_GREEN; break; default: return false; } // patch the data format if(dataFormat == eGL_LUMINANCE || dataFormat == eGL_LUMINANCE_INTEGER_EXT || dataFormat == eGL_LUMINANCE_ALPHA || dataFormat == eGL_LUMINANCE_ALPHA_INTEGER_EXT || dataFormat == eGL_ALPHA || dataFormat == eGL_ALPHA_INTEGER || dataFormat == eGL_INTENSITY_EXT) { switch(internalFormat) { case eGL_INTENSITY_EXT: case eGL_INTENSITY8_EXT: case eGL_INTENSITY16_EXT: case eGL_INTENSITY16F_ARB: case eGL_INTENSITY32F_ARB: case eGL_INTENSITY_SNORM: case eGL_INTENSITY8_SNORM: case eGL_INTENSITY16_SNORM: case eGL_ALPHA: case eGL_ALPHA8_EXT: case eGL_ALPHA16_EXT: case eGL_ALPHA16F_ARB: case eGL_ALPHA32F_ARB: case eGL_ALPHA_SNORM: case eGL_ALPHA8_SNORM: case eGL_ALPHA16_SNORM: case eGL_LUMINANCE: case eGL_LUMINANCE8_EXT: case eGL_LUMINANCE16_EXT: case eGL_LUMINANCE16F_ARB: case eGL_LUMINANCE32F_ARB: case eGL_LUMINANCE_SNORM: case eGL_LUMINANCE8_SNORM: case eGL_LUMINANCE16_SNORM: case eGL_SLUMINANCE: case eGL_SLUMINANCE8: dataFormat = eGL_RED; break; case eGL_INTENSITY8I_EXT: case eGL_INTENSITY16I_EXT: case eGL_INTENSITY32I_EXT: case eGL_INTENSITY8UI_EXT: case eGL_INTENSITY16UI_EXT: case eGL_INTENSITY32UI_EXT: case eGL_ALPHA_INTEGER: case eGL_ALPHA8I_EXT: case eGL_ALPHA16I_EXT: case eGL_ALPHA32I_EXT: case eGL_ALPHA8UI_EXT: case eGL_ALPHA16UI_EXT: case eGL_ALPHA32UI_EXT: case eGL_LUMINANCE_INTEGER_EXT: case eGL_LUMINANCE8I_EXT: case eGL_LUMINANCE16I_EXT: case eGL_LUMINANCE32I_EXT: case eGL_LUMINANCE8UI_EXT: case eGL_LUMINANCE16UI_EXT: case eGL_LUMINANCE32UI_EXT: dataFormat = eGL_RED_INTEGER; break; case eGL_LUMINANCE_ALPHA: case eGL_LUMINANCE8_ALPHA8_EXT: case eGL_LUMINANCE16_ALPHA16_EXT: case eGL_LUMINANCE_ALPHA16F_ARB: case eGL_LUMINANCE_ALPHA32F_ARB: case eGL_LUMINANCE_ALPHA_SNORM: case eGL_LUMINANCE8_ALPHA8_SNORM: case eGL_LUMINANCE16_ALPHA16_SNORM: case eGL_SLUMINANCE_ALPHA: case eGL_SLUMINANCE8_ALPHA8: dataFormat = eGL_RG; break; case eGL_LUMINANCE_ALPHA_INTEGER_EXT: case eGL_LUMINANCE_ALPHA8I_EXT: case eGL_LUMINANCE_ALPHA16I_EXT: case eGL_LUMINANCE_ALPHA32I_EXT: case eGL_LUMINANCE_ALPHA8UI_EXT: case eGL_LUMINANCE_ALPHA16UI_EXT: case eGL_LUMINANCE_ALPHA32UI_EXT: dataFormat = eGL_RG_INTEGER; break; default: RDCERR("Problem in EnumerateLuminanceFormat - all switches should have same cases"); break; } } switch(internalFormat) { case eGL_INTENSITY_EXT: case eGL_ALPHA: case eGL_INTENSITY8_EXT: case eGL_ALPHA8_EXT: case eGL_LUMINANCE: case eGL_LUMINANCE8_EXT: internalFormat = eGL_R8; break; case eGL_INTENSITY16_EXT: case eGL_ALPHA16_EXT: case eGL_LUMINANCE16_EXT: internalFormat = eGL_R16; break; case eGL_INTENSITY16F_ARB: case eGL_ALPHA16F_ARB: case eGL_LUMINANCE16F_ARB: internalFormat = eGL_R16F; break; case eGL_INTENSITY32F_ARB: case eGL_ALPHA32F_ARB: case eGL_LUMINANCE32F_ARB: internalFormat = eGL_R32F; break; case eGL_INTENSITY_SNORM: case eGL_INTENSITY8_SNORM: case eGL_ALPHA_SNORM: case eGL_ALPHA8_SNORM: case eGL_LUMINANCE_SNORM: case eGL_LUMINANCE8_SNORM: internalFormat = eGL_R8_SNORM; break; case eGL_INTENSITY16_SNORM: case eGL_ALPHA16_SNORM: case eGL_LUMINANCE16_SNORM: internalFormat = eGL_R16_SNORM; break; case eGL_INTENSITY8I_EXT: case eGL_ALPHA_INTEGER: case eGL_ALPHA8I_EXT: case eGL_LUMINANCE_INTEGER_EXT: case eGL_LUMINANCE8I_EXT: internalFormat = eGL_R8I; break; case eGL_INTENSITY16I_EXT: case eGL_ALPHA16I_EXT: case eGL_LUMINANCE16I_EXT: internalFormat = eGL_R16I; break; case eGL_INTENSITY32I_EXT: case eGL_ALPHA32I_EXT: case eGL_LUMINANCE32I_EXT: internalFormat = eGL_R32I; break; case eGL_INTENSITY8UI_EXT: case eGL_ALPHA8UI_EXT: case eGL_LUMINANCE8UI_EXT: internalFormat = eGL_R8UI; break; case eGL_INTENSITY16UI_EXT: case eGL_ALPHA16UI_EXT: case eGL_LUMINANCE16UI_EXT: internalFormat = eGL_R16UI; break; case eGL_INTENSITY32UI_EXT: case eGL_ALPHA32UI_EXT: case eGL_LUMINANCE32UI_EXT: internalFormat = eGL_R32UI; break; case eGL_LUMINANCE_ALPHA: case eGL_LUMINANCE8_ALPHA8_EXT: internalFormat = eGL_RG8; break; case eGL_LUMINANCE16_ALPHA16_EXT: internalFormat = eGL_RG16; break; case eGL_LUMINANCE_ALPHA16F_ARB: internalFormat = eGL_RG16F; break; case eGL_LUMINANCE_ALPHA32F_ARB: internalFormat = eGL_RG32F; break; case eGL_LUMINANCE_ALPHA_SNORM: case eGL_LUMINANCE8_ALPHA8_SNORM: internalFormat = eGL_RG8_SNORM; break; case eGL_LUMINANCE16_ALPHA16_SNORM: internalFormat = eGL_RG16_SNORM; break; case eGL_LUMINANCE_ALPHA_INTEGER_EXT: case eGL_LUMINANCE_ALPHA8I_EXT: internalFormat = eGL_RG8I; break; case eGL_LUMINANCE_ALPHA16I_EXT: internalFormat = eGL_RG16I; break; case eGL_LUMINANCE_ALPHA32I_EXT: internalFormat = eGL_RG32I; break; case eGL_LUMINANCE_ALPHA8UI_EXT: internalFormat = eGL_RG8UI; break; case eGL_LUMINANCE_ALPHA16UI_EXT: internalFormat = eGL_RG16UI; break; case eGL_LUMINANCE_ALPHA32UI_EXT: internalFormat = eGL_RG32UI; break; case eGL_SLUMINANCE: case eGL_SLUMINANCE8: internalFormat = eGL_SRGB8; break; case eGL_SLUMINANCE_ALPHA: case eGL_SLUMINANCE8_ALPHA8: internalFormat = eGL_SRGB8_ALPHA8; break; default: RDCERR("Problem in EnumerateLuminanceFormat - all switches should have same cases"); break; } if(tex) { if(HasExt[ARB_texture_swizzle] || HasExt[EXT_texture_swizzle]) { SetTextureSwizzle(tex, target, swizzle); } else { RDCERR("Cannot emulate luminance format without texture swizzle extension"); } } return true; } GLenum GetSizedFormat(GLenum internalformat) { switch(internalformat) { case eGL_DEPTH_COMPONENT: internalformat = eGL_DEPTH_COMPONENT32F; break; case eGL_DEPTH_STENCIL: internalformat = eGL_DEPTH32F_STENCIL8; break; case eGL_STENCIL: case eGL_STENCIL_INDEX: internalformat = eGL_STENCIL_INDEX8; break; case eGL_RGBA: internalformat = eGL_RGBA8; break; case eGL_RGBA_INTEGER: internalformat = eGL_RGBA8I; break; case eGL_RGB: internalformat = eGL_RGB8; break; case eGL_RGB_INTEGER: internalformat = eGL_RGB8I; break; case eGL_RG: internalformat = eGL_RG8; break; case eGL_RG_INTEGER: internalformat = eGL_RG8I; break; case eGL_RED: internalformat = eGL_R8; break; case eGL_RED_INTEGER: internalformat = eGL_R8I; break; default: break; } return internalformat; } bool IsCompressedFormat(GLenum internalFormat) { switch(internalFormat) { // BC1 case eGL_COMPRESSED_RGB_S3TC_DXT1_EXT: case eGL_COMPRESSED_RGBA_S3TC_DXT1_EXT: case eGL_COMPRESSED_SRGB_S3TC_DXT1_EXT: case eGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: // BC2 case eGL_COMPRESSED_RGBA_S3TC_DXT3_EXT: case eGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: // BC3 case eGL_COMPRESSED_RGBA_S3TC_DXT5_EXT: case eGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: // BC4 case eGL_COMPRESSED_RED_RGTC1: case eGL_COMPRESSED_SIGNED_RED_RGTC1: // BC5 case eGL_COMPRESSED_RG_RGTC2: case eGL_COMPRESSED_SIGNED_RG_RGTC2: // BC6 case eGL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB: case eGL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB: // BC7 case eGL_COMPRESSED_RGBA_BPTC_UNORM_ARB: case eGL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB: // ETC1 case eGL_ETC1_RGB8_OES: // ETC2 case eGL_COMPRESSED_RGB8_ETC2: case eGL_COMPRESSED_SRGB8_ETC2: case eGL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: case eGL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: // EAC case eGL_COMPRESSED_RGBA8_ETC2_EAC: case eGL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: case eGL_COMPRESSED_R11_EAC: case eGL_COMPRESSED_SIGNED_R11_EAC: case eGL_COMPRESSED_RG11_EAC: case eGL_COMPRESSED_SIGNED_RG11_EAC: // ASTC case eGL_COMPRESSED_RGBA_ASTC_4x4_KHR: case eGL_COMPRESSED_RGBA_ASTC_5x4_KHR: case eGL_COMPRESSED_RGBA_ASTC_5x5_KHR: case eGL_COMPRESSED_RGBA_ASTC_6x5_KHR: case eGL_COMPRESSED_RGBA_ASTC_6x6_KHR: case eGL_COMPRESSED_RGBA_ASTC_8x5_KHR: case eGL_COMPRESSED_RGBA_ASTC_8x6_KHR: case eGL_COMPRESSED_RGBA_ASTC_8x8_KHR: case eGL_COMPRESSED_RGBA_ASTC_10x5_KHR: case eGL_COMPRESSED_RGBA_ASTC_10x6_KHR: case eGL_COMPRESSED_RGBA_ASTC_10x8_KHR: case eGL_COMPRESSED_RGBA_ASTC_10x10_KHR: case eGL_COMPRESSED_RGBA_ASTC_12x10_KHR: case eGL_COMPRESSED_RGBA_ASTC_12x12_KHR: case eGL_COMPRESSED_RGBA_ASTC_3x3x3_OES: case eGL_COMPRESSED_RGBA_ASTC_4x3x3_OES: case eGL_COMPRESSED_RGBA_ASTC_4x4x3_OES: case eGL_COMPRESSED_RGBA_ASTC_4x4x4_OES: case eGL_COMPRESSED_RGBA_ASTC_5x4x4_OES: case eGL_COMPRESSED_RGBA_ASTC_5x5x4_OES: case eGL_COMPRESSED_RGBA_ASTC_5x5x5_OES: case eGL_COMPRESSED_RGBA_ASTC_6x5x5_OES: case eGL_COMPRESSED_RGBA_ASTC_6x6x5_OES: case eGL_COMPRESSED_RGBA_ASTC_6x6x6_OES: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES: case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES: // PVRTC case eGL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT: case eGL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT: case eGL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT: case eGL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT: return true; default: break; } return false; } bool IsDepthStencilFormat(GLenum internalFormat) { if(IsCompressedFormat(internalFormat)) return false; GLenum fmt = GetBaseFormat(internalFormat); return (fmt == eGL_DEPTH_COMPONENT || fmt == eGL_STENCIL_INDEX || fmt == eGL_DEPTH_STENCIL); } bool IsUIntFormat(GLenum internalFormat) { switch(internalFormat) { case eGL_R8UI: case eGL_RG8UI: case eGL_RGB8UI: case eGL_RGBA8UI: case eGL_R16UI: case eGL_RG16UI: case eGL_RGB16UI: case eGL_RGBA16UI: case eGL_R32UI: case eGL_RG32UI: case eGL_RGB32UI: case eGL_RGBA32UI: case eGL_RGB10_A2UI: return true; default: break; } return false; } bool IsSIntFormat(GLenum internalFormat) { switch(internalFormat) { case eGL_R8I: case eGL_RG8I: case eGL_RGB8I: case eGL_RGBA8I: case eGL_R16I: case eGL_RG16I: case eGL_RGB16I: case eGL_RGBA16I: case eGL_R32I: case eGL_RG32I: case eGL_RGB32I: case eGL_RGBA32I: return true; default: break; } return false; } bool IsSRGBFormat(GLenum internalFormat) { switch(internalFormat) { case eGL_SRGB8: case eGL_SRGB8_ALPHA8: case eGL_SRGB: case eGL_SRGB_ALPHA: case eGL_SLUMINANCE8: case eGL_SLUMINANCE8_ALPHA8: case eGL_COMPRESSED_SRGB_S3TC_DXT1_EXT: case eGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: case eGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: case eGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: case eGL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB: return true; default: break; } return false; } GLenum GetViewCastedFormat(GLenum internalFormat, CompType typeCast) { if(typeCast == CompType::Typeless) return internalFormat; switch(internalFormat) { case eGL_RGBA: case eGL_RGBA8: case eGL_RGBA8_SNORM: case eGL_RGBA8UI: case eGL_RGBA8I: case eGL_SRGB_ALPHA: case eGL_SRGB8_ALPHA8: switch(typeCast) { case CompType::Float: case CompType::UNorm: internalFormat = eGL_RGBA8; break; case CompType::SNorm: internalFormat = eGL_RGBA8_SNORM; break; case CompType::UInt: internalFormat = eGL_RGBA8UI; break; case CompType::SInt: internalFormat = eGL_RGBA8I; break; case CompType::UNormSRGB: internalFormat = eGL_SRGB8_ALPHA8; break; default: break; } break; case eGL_RGB: case eGL_RGB8: case eGL_RGB8_SNORM: case eGL_RGB8UI: case eGL_RGB8I: case eGL_SRGB: case eGL_SRGB8: switch(typeCast) { case CompType::Float: case CompType::UNorm: internalFormat = eGL_RGB8; break; case CompType::SNorm: internalFormat = eGL_RGB8_SNORM; break; case CompType::UInt: internalFormat = eGL_RGB8UI; break; case CompType::SInt: internalFormat = eGL_RGB8I; break; case CompType::UNormSRGB: internalFormat = eGL_SRGB8; break; default: break; } break; case eGL_RG: case eGL_RG8: case eGL_RG8_SNORM: case eGL_RG8UI: case eGL_RG8I: switch(typeCast) { case CompType::Float: case CompType::UNorm: internalFormat = eGL_RG8; break; case CompType::SNorm: internalFormat = eGL_RG8_SNORM; break; case CompType::UInt: internalFormat = eGL_RG8UI; break; case CompType::SInt: internalFormat = eGL_RG8I; break; case CompType::UNormSRGB: internalFormat = eGL_SRG8_EXT; break; default: break; } break; case eGL_RED: case eGL_R8: case eGL_R8_SNORM: case eGL_R8UI: case eGL_R8I: switch(typeCast) { case CompType::Float: case CompType::UNorm: internalFormat = eGL_R8; break; case CompType::SNorm: internalFormat = eGL_R8_SNORM; break; case CompType::UInt: internalFormat = eGL_R8UI; break; case CompType::SInt: internalFormat = eGL_R8I; break; case CompType::UNormSRGB: internalFormat = eGL_SR8_EXT; break; default: break; } break; case eGL_RGBA16F: case eGL_RGBA16: case eGL_RGBA16_SNORM: case eGL_RGBA16UI: case eGL_RGBA16I: switch(typeCast) { case CompType::Float: internalFormat = eGL_RGBA16F; break; case CompType::UNorm: internalFormat = eGL_RGBA16; break; case CompType::SNorm: internalFormat = eGL_RGBA16_SNORM; break; case CompType::UInt: internalFormat = eGL_RGBA16UI; break; case CompType::SInt: internalFormat = eGL_RGBA16I; break; default: break; } break; case eGL_RGB16F: case eGL_RGB16: case eGL_RGB16_SNORM: case eGL_RGB16UI: case eGL_RGB16I: switch(typeCast) { case CompType::Float: internalFormat = eGL_RGB16F; break; case CompType::UNorm: internalFormat = eGL_RGB16; break; case CompType::SNorm: internalFormat = eGL_RGB16_SNORM; break; case CompType::UInt: internalFormat = eGL_RGB16UI; break; case CompType::SInt: internalFormat = eGL_RGB16I; break; default: break; } break; case eGL_RG16F: case eGL_RG16: case eGL_RG16_SNORM: case eGL_RG16UI: case eGL_RG16I: switch(typeCast) { case CompType::Float: internalFormat = eGL_RG16F; break; case CompType::UNorm: internalFormat = eGL_RG16; break; case CompType::SNorm: internalFormat = eGL_RG16_SNORM; break; case CompType::UInt: internalFormat = eGL_RG16UI; break; case CompType::SInt: internalFormat = eGL_RG16I; break; default: break; } break; case eGL_R16F: case eGL_R16: case eGL_R16_SNORM: case eGL_R16UI: case eGL_R16I: switch(typeCast) { case CompType::Float: internalFormat = eGL_R16F; break; case CompType::UNorm: internalFormat = eGL_R16; break; case CompType::SNorm: internalFormat = eGL_R16_SNORM; break; case CompType::UInt: internalFormat = eGL_R16UI; break; case CompType::SInt: internalFormat = eGL_R16I; break; default: break; } break; case eGL_RGBA32F: case eGL_RGBA32UI: case eGL_RGBA32I: switch(typeCast) { case CompType::Float: internalFormat = eGL_RGBA32F; break; case CompType::UInt: internalFormat = eGL_RGBA32UI; break; case CompType::SInt: internalFormat = eGL_RGBA32I; break; default: break; } break; case eGL_RGB32F: case eGL_RGB32UI: case eGL_RGB32I: switch(typeCast) { case CompType::Float: internalFormat = eGL_RGB32F; break; case CompType::UInt: internalFormat = eGL_RGB32UI; break; case CompType::SInt: internalFormat = eGL_RGB32I; break; default: break; } break; case eGL_RG32F: case eGL_RG32UI: case eGL_RG32I: switch(typeCast) { case CompType::Float: internalFormat = eGL_RG32F; break; case CompType::UInt: internalFormat = eGL_RG32UI; break; case CompType::SInt: internalFormat = eGL_RG32I; break; default: break; } break; case eGL_R32F: case eGL_R32UI: case eGL_R32I: switch(typeCast) { case CompType::Float: internalFormat = eGL_R32F; break; case CompType::UInt: internalFormat = eGL_R32UI; break; case CompType::SInt: internalFormat = eGL_R32I; break; default: break; } break; case eGL_RGB10_A2UI: case eGL_RGB10_A2: switch(typeCast) { case CompType::Float: case CompType::UNorm: internalFormat = eGL_RGB10_A2; break; case CompType::UInt: internalFormat = eGL_RGB10_A2UI; break; default: break; } break; case eGL_COMPRESSED_RGB_S3TC_DXT1_EXT: case eGL_COMPRESSED_SRGB_S3TC_DXT1_EXT: internalFormat = (typeCast == CompType::UNormSRGB) ? eGL_COMPRESSED_SRGB_S3TC_DXT1_EXT : eGL_COMPRESSED_RGB_S3TC_DXT1_EXT; break; case eGL_COMPRESSED_RGBA_S3TC_DXT1_EXT: case eGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: internalFormat = (typeCast == CompType::UNormSRGB) ? eGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT : eGL_COMPRESSED_RGBA_S3TC_DXT1_EXT; break; case eGL_COMPRESSED_RGBA_S3TC_DXT3_EXT: case eGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: internalFormat = (typeCast == CompType::UNormSRGB) ? eGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT : eGL_COMPRESSED_RGBA_S3TC_DXT3_EXT; break; case eGL_COMPRESSED_RGBA_S3TC_DXT5_EXT: case eGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: internalFormat = (typeCast == CompType::UNormSRGB) ? eGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT : eGL_COMPRESSED_RGBA_S3TC_DXT5_EXT; break; case eGL_COMPRESSED_RED_RGTC1: case eGL_COMPRESSED_SIGNED_RED_RGTC1: internalFormat = (typeCast == CompType::SNorm) ? eGL_COMPRESSED_SIGNED_RED_RGTC1 : eGL_COMPRESSED_RED_RGTC1; break; case eGL_COMPRESSED_RG_RGTC2: case eGL_COMPRESSED_SIGNED_RG_RGTC2: internalFormat = (typeCast == CompType::SNorm) ? eGL_COMPRESSED_SIGNED_RG_RGTC2 : eGL_COMPRESSED_RG_RGTC2; break; case eGL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB: case eGL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB: internalFormat = (typeCast == CompType::SNorm) ? eGL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB : eGL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB; break; case eGL_COMPRESSED_RGBA_BPTC_UNORM_ARB: case eGL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB: internalFormat = (typeCast == CompType::UNormSRGB) ? eGL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB : eGL_COMPRESSED_RGBA_BPTC_UNORM_ARB; break; case eGL_COMPRESSED_SIGNED_R11_EAC: case eGL_COMPRESSED_R11_EAC: internalFormat = (typeCast == CompType::SNorm) ? eGL_COMPRESSED_SIGNED_R11_EAC : eGL_COMPRESSED_R11_EAC; break; case eGL_COMPRESSED_SIGNED_RG11_EAC: case eGL_COMPRESSED_RG11_EAC: internalFormat = (typeCast == CompType::SNorm) ? eGL_COMPRESSED_SIGNED_RG11_EAC : eGL_COMPRESSED_RG11_EAC; break; default: break; } return internalFormat; } GLenum TextureBinding(GLenum target) { switch(target) { case eGL_TEXTURE_1D: return eGL_TEXTURE_BINDING_1D; case eGL_TEXTURE_1D_ARRAY: return eGL_TEXTURE_BINDING_1D_ARRAY; case eGL_TEXTURE_2D: return eGL_TEXTURE_BINDING_2D; case eGL_TEXTURE_2D_ARRAY: return eGL_TEXTURE_BINDING_2D_ARRAY; case eGL_TEXTURE_2D_MULTISAMPLE: return eGL_TEXTURE_BINDING_2D_MULTISAMPLE; case eGL_TEXTURE_2D_MULTISAMPLE_ARRAY: return eGL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY; case eGL_TEXTURE_RECTANGLE: return eGL_TEXTURE_BINDING_RECTANGLE; case eGL_TEXTURE_3D: return eGL_TEXTURE_BINDING_3D; case eGL_TEXTURE_CUBE_MAP: case eGL_TEXTURE_CUBE_MAP_POSITIVE_X: case eGL_TEXTURE_CUBE_MAP_NEGATIVE_X: case eGL_TEXTURE_CUBE_MAP_POSITIVE_Y: case eGL_TEXTURE_CUBE_MAP_NEGATIVE_Y: case eGL_TEXTURE_CUBE_MAP_POSITIVE_Z: case eGL_TEXTURE_CUBE_MAP_NEGATIVE_Z: return eGL_TEXTURE_BINDING_CUBE_MAP; case eGL_TEXTURE_CUBE_MAP_ARRAY: return eGL_TEXTURE_BINDING_CUBE_MAP_ARRAY; case eGL_TEXTURE_BUFFER: return eGL_TEXTURE_BINDING_BUFFER; default: break; } RDCERR("Unexpected target %s", ToStr(target).c_str()); return eGL_NONE; } GLenum BufferBinding(GLenum target) { switch(target) { case eGL_ARRAY_BUFFER: return eGL_ARRAY_BUFFER_BINDING; case eGL_ATOMIC_COUNTER_BUFFER: return eGL_ATOMIC_COUNTER_BUFFER_BINDING; case eGL_COPY_READ_BUFFER: return eGL_COPY_READ_BUFFER_BINDING; case eGL_COPY_WRITE_BUFFER: return eGL_COPY_WRITE_BUFFER_BINDING; case eGL_DRAW_INDIRECT_BUFFER: return eGL_DRAW_INDIRECT_BUFFER_BINDING; case eGL_DISPATCH_INDIRECT_BUFFER: return eGL_DISPATCH_INDIRECT_BUFFER_BINDING; case eGL_ELEMENT_ARRAY_BUFFER: return eGL_ELEMENT_ARRAY_BUFFER_BINDING; case eGL_PIXEL_PACK_BUFFER: return eGL_PIXEL_PACK_BUFFER_BINDING; case eGL_PIXEL_UNPACK_BUFFER: return eGL_PIXEL_UNPACK_BUFFER_BINDING; case eGL_QUERY_BUFFER: return eGL_QUERY_BUFFER_BINDING; case eGL_SHADER_STORAGE_BUFFER: return eGL_SHADER_STORAGE_BUFFER_BINDING; case eGL_TEXTURE_BUFFER: return eGL_TEXTURE_BUFFER_BINDING; case eGL_TRANSFORM_FEEDBACK_BUFFER: return eGL_TRANSFORM_FEEDBACK_BUFFER_BINDING; case eGL_UNIFORM_BUFFER: return eGL_UNIFORM_BUFFER_BINDING; case eGL_PARAMETER_BUFFER_ARB: return eGL_PARAMETER_BUFFER_BINDING_ARB; default: break; } RDCERR("Unexpected target %s", ToStr(target).c_str()); return eGL_NONE; } GLenum FramebufferBinding(GLenum target) { switch(target) { case eGL_FRAMEBUFFER: return eGL_FRAMEBUFFER_BINDING; case eGL_DRAW_FRAMEBUFFER: return eGL_DRAW_FRAMEBUFFER_BINDING; case eGL_READ_FRAMEBUFFER: return eGL_READ_FRAMEBUFFER_BINDING; default: break; } RDCERR("Unexpected target %s", ToStr(target).c_str()); return eGL_NONE; } bool IsCubeFace(GLenum target) { switch(target) { case eGL_TEXTURE_CUBE_MAP_POSITIVE_X: case eGL_TEXTURE_CUBE_MAP_NEGATIVE_X: case eGL_TEXTURE_CUBE_MAP_POSITIVE_Y: case eGL_TEXTURE_CUBE_MAP_NEGATIVE_Y: case eGL_TEXTURE_CUBE_MAP_POSITIVE_Z: case eGL_TEXTURE_CUBE_MAP_NEGATIVE_Z: return true; default: break; } return false; } GLint CubeTargetIndex(GLenum face) { switch(face) { case eGL_TEXTURE_CUBE_MAP_POSITIVE_X: return 0; case eGL_TEXTURE_CUBE_MAP_NEGATIVE_X: return 1; case eGL_TEXTURE_CUBE_MAP_POSITIVE_Y: return 2; case eGL_TEXTURE_CUBE_MAP_NEGATIVE_Y: return 3; case eGL_TEXTURE_CUBE_MAP_POSITIVE_Z: return 4; case eGL_TEXTURE_CUBE_MAP_NEGATIVE_Z: return 5; default: break; } return 0; } GLenum TextureTarget(GLenum binding) { switch(binding) { case eGL_TEXTURE_BINDING_1D: return eGL_TEXTURE_1D; case eGL_TEXTURE_BINDING_1D_ARRAY: return eGL_TEXTURE_1D_ARRAY; case eGL_TEXTURE_BINDING_2D: return eGL_TEXTURE_2D; case eGL_TEXTURE_BINDING_2D_ARRAY: return eGL_TEXTURE_2D_ARRAY; case eGL_TEXTURE_BINDING_2D_MULTISAMPLE: return eGL_TEXTURE_2D_MULTISAMPLE; case eGL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY: return eGL_TEXTURE_2D_MULTISAMPLE_ARRAY; case eGL_TEXTURE_BINDING_RECTANGLE: return eGL_TEXTURE_RECTANGLE; case eGL_TEXTURE_BINDING_3D: return eGL_TEXTURE_3D; case eGL_TEXTURE_BINDING_CUBE_MAP: case eGL_TEXTURE_CUBE_MAP_POSITIVE_X: case eGL_TEXTURE_CUBE_MAP_NEGATIVE_X: case eGL_TEXTURE_CUBE_MAP_POSITIVE_Y: case eGL_TEXTURE_CUBE_MAP_NEGATIVE_Y: case eGL_TEXTURE_CUBE_MAP_POSITIVE_Z: case eGL_TEXTURE_CUBE_MAP_NEGATIVE_Z: return eGL_TEXTURE_CUBE_MAP; case eGL_TEXTURE_BINDING_CUBE_MAP_ARRAY: return eGL_TEXTURE_CUBE_MAP_ARRAY; case eGL_TEXTURE_BINDING_BUFFER: return eGL_TEXTURE_BUFFER; default: break; } return binding; } bool IsProxyTarget(GLenum target) { switch(target) { case eGL_PROXY_TEXTURE_1D: case eGL_PROXY_TEXTURE_1D_ARRAY: case eGL_PROXY_TEXTURE_2D: case eGL_PROXY_TEXTURE_2D_ARRAY: case eGL_PROXY_TEXTURE_2D_MULTISAMPLE: case eGL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY: case eGL_PROXY_TEXTURE_RECTANGLE: case eGL_PROXY_TEXTURE_3D: case eGL_PROXY_TEXTURE_CUBE_MAP: case eGL_PROXY_TEXTURE_CUBE_MAP_ARRAY: return true; default: break; } return false; }