diff --git a/renderdoc/driver/gl/gl_driver.h b/renderdoc/driver/gl/gl_driver.h index fcaf9644d..f99e1c19e 100644 --- a/renderdoc/driver/gl/gl_driver.h +++ b/renderdoc/driver/gl/gl_driver.h @@ -279,6 +279,11 @@ private: // bound to the second. GLuint renderbufferReadTex; GLuint renderbufferFBOs[2]; + + // since compressed textures cannot be read back on GLES we have to store them during the + // uploading + // level -> data + map > compressedData; }; map m_Textures; @@ -517,6 +522,10 @@ private: vector m_GLExtensions; vector m_GLESExtensions; + void StoreCompressedTexData(ResourceId texId, GLenum target, GLint level, GLint xoffset, + GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, + GLsizei depth, GLenum format, GLsizei imageSize, const void *pixels); + // no copy semantics WrappedOpenGL(const WrappedOpenGL &); WrappedOpenGL &operator=(const WrappedOpenGL &); diff --git a/renderdoc/driver/gl/gl_manager.cpp b/renderdoc/driver/gl/gl_manager.cpp index c21567312..1c00422a0 100644 --- a/renderdoc/driver/gl/gl_manager.cpp +++ b/renderdoc/driver/gl/gl_manager.cpp @@ -562,6 +562,102 @@ bool GLResourceManager::Prepare_InitialState(GLResource res) return true; } +void GLResourceManager::CreateTextureImage(GLuint tex, GLenum internalFormat, GLenum textype, + GLint dim, GLint width, GLint height, GLint depth, + GLint samples, int mips) +{ + const GLHookSet &gl = m_GL->GetHookset(); + + if(textype == eGL_TEXTURE_BUFFER) + { + return; + } + else if(textype == eGL_TEXTURE_2D_MULTISAMPLE) + { + gl.glTextureStorage2DMultisampleEXT(tex, textype, samples, internalFormat, width, height, + GL_TRUE); + } + else if(textype == eGL_TEXTURE_2D_MULTISAMPLE_ARRAY) + { + gl.glTextureStorage3DMultisampleEXT(tex, textype, samples, internalFormat, width, height, depth, + GL_TRUE); + } + else + { + gl.glTextureParameteriEXT(tex, textype, eGL_TEXTURE_MAX_LEVEL, mips - 1); + + bool isCompressed = IsCompressedFormat(internalFormat); + + GLenum baseFormat = eGL_RGBA; + GLenum dataType = eGL_UNSIGNED_BYTE; + if(!isCompressed) + { + baseFormat = GetBaseFormat(internalFormat); + dataType = GetDataType(internalFormat); + } + + 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 count = ARRAY_COUNT(targets); + + if(textype != eGL_TEXTURE_CUBE_MAP) + { + targets[0] = textype; + count = 1; + } + + GLsizei w = (GLsizei)width; + GLsizei h = (GLsizei)height; + GLsizei d = (GLsizei)depth; + + for(int m = 0; m < mips; m++) + { + for(int t = 0; t < count; t++) + { + if(isCompressed) + { + GLsizei compSize = (GLsizei)GetCompressedByteSize(w, h, d, internalFormat); + + vector dummy; + dummy.resize(compSize); + + if(dim == 1) + gl.glCompressedTextureImage1DEXT(tex, targets[t], m, internalFormat, w, 0, compSize, + &dummy[0]); + else if(dim == 2) + gl.glCompressedTextureImage2DEXT(tex, targets[t], m, internalFormat, w, h, 0, compSize, + &dummy[0]); + else if(dim == 3) + gl.glCompressedTextureImage3DEXT(tex, targets[t], m, internalFormat, w, h, d, 0, + compSize, &dummy[0]); + } + else + { + if(dim == 1) + gl.glTextureImage1DEXT(tex, targets[t], m, internalFormat, w, 0, baseFormat, dataType, + NULL); + else if(dim == 2) + gl.glTextureImage2DEXT(tex, targets[t], m, internalFormat, w, h, 0, baseFormat, + dataType, NULL); + else if(dim == 3) + gl.glTextureImage3DEXT(tex, targets[t], m, internalFormat, w, h, d, 0, baseFormat, + dataType, NULL); + } + } + + w = RDCMAX(1, w >> 1); + if(textype != eGL_TEXTURE_1D_ARRAY) + h = RDCMAX(1, h >> 1); + if(textype != eGL_TEXTURE_2D_ARRAY && textype != eGL_TEXTURE_CUBE_MAP_ARRAY) + d = RDCMAX(1, d >> 1); + } + } +} + void GLResourceManager::PrepareTextureInitialContents(ResourceId liveid, ResourceId origid, GLResource res) { @@ -671,95 +767,16 @@ void GLResourceManager::PrepareTextureInitialContents(ResourceId liveid, Resourc gl.glBindTexture(details.curType, oldtex); } - int depth = details.depth; - if(details.curType != eGL_TEXTURE_3D) - depth = 1; - int mips = GetNumMips(gl, details.curType, res.name, details.width, details.height, details.depth); - GLenum baseFormat = eGL_RGBA; - GLenum dataType = eGL_UNSIGNED_BYTE; - if(!IsCompressedFormat(details.internalFormat)) - { - baseFormat = GetBaseFormat(details.internalFormat); - dataType = GetDataType(details.internalFormat); - } + if(details.curType == eGL_TEXTURE_2D_MULTISAMPLE || + details.curType == eGL_TEXTURE_2D_MULTISAMPLE_ARRAY) + mips = 1; // create texture of identical format/size to store initial contents - if(details.curType == eGL_TEXTURE_2D_MULTISAMPLE) - { - gl.glTextureStorage2DMultisampleEXT(tex, details.curType, details.samples, - details.internalFormat, details.width, details.height, - GL_TRUE); - mips = 1; - } - else if(details.curType == eGL_TEXTURE_2D_MULTISAMPLE_ARRAY) - { - gl.glTextureStorage3DMultisampleEXT(tex, details.curType, details.samples, - details.internalFormat, details.width, details.height, - details.depth, GL_TRUE); - mips = 1; - } - else if(details.dimension == 1) - { - gl.glTextureParameteriEXT(tex, details.curType, eGL_TEXTURE_MAX_LEVEL, mips - 1); - - int w = details.width; - for(int i = 0; i < mips; i++) - { - gl.glTextureImage1DEXT(tex, details.curType, i, details.internalFormat, w, 0, baseFormat, - dataType, NULL); - w = RDCMAX(1, w >> 1); - } - } - else if(details.dimension == 2) - { - 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, - }; - - gl.glTextureParameteriEXT(tex, details.curType, eGL_TEXTURE_MAX_LEVEL, mips - 1); - - int w = details.width; - int h = details.height; - for(int i = 0; i < mips; i++) - { - if(details.curType != eGL_TEXTURE_CUBE_MAP) - { - gl.glTextureImage2DEXT(tex, details.curType, i, details.internalFormat, w, h, 0, - baseFormat, dataType, NULL); - } - else - { - for(size_t t = 0; t < ARRAY_COUNT(targets); t++) - gl.glTextureImage2DEXT(tex, targets[t], i, details.internalFormat, w, h, 0, - baseFormat, dataType, NULL); - } - w = RDCMAX(1, w >> 1); - if(details.curType != eGL_TEXTURE_1D_ARRAY) - h = RDCMAX(1, h >> 1); - } - } - else if(details.dimension == 3) - { - gl.glTextureParameteriEXT(tex, details.curType, eGL_TEXTURE_MAX_LEVEL, mips - 1); - - int w = details.width; - int h = details.height; - int d = details.depth; - for(int i = 0; i < mips; i++) - { - gl.glTextureImage3DEXT(tex, details.curType, i, details.internalFormat, w, h, d, 0, - baseFormat, dataType, NULL); - w = RDCMAX(1, w >> 1); - h = RDCMAX(1, h >> 1); - if(details.curType == eGL_TEXTURE_3D) - d = RDCMAX(1, d >> 1); - } - } + CreateTextureImage(tex, details.internalFormat, details.curType, details.dimension, + details.width, details.height, details.depth, details.samples, mips); // we need to set maxlevel appropriately for number of mips to force the texture to be // complete. @@ -1164,7 +1181,21 @@ bool GLResourceManager::Serialise_InitialState(ResourceId resid, GLResource res) byte *buf = new byte[size]; - gl.glGetCompressedTextureImageEXT(tex, targets[trg], i, buf); + if(IsGLES) + { + const vector &data = details.compressedData[i]; + const byte *src = + (count == 1) ? data.data() : data.data() + CubeTargetIndex(targets[trg]) * size; + size_t storedSize = data.size() / count; + if(storedSize == size) + memcpy(buf, src, size); + else + RDCERR("Different expected and stored compressed texture sizes!"); + } + else + { + gl.glGetCompressedTextureImageEXT(tex, targets[trg], i, buf); + } m_pSerialiser->SerialiseBuffer("image", buf, size); @@ -1378,90 +1409,18 @@ bool GLResourceManager::Serialise_InitialState(ResourceId resid, GLResource res) GLenum dummy; EmulateLuminanceFormat(gl, tex, textype, internalformat, dummy); - GLenum baseFormat = eGL_RGBA; - GLenum dataType = eGL_UNSIGNED_BYTE; - if(!IsCompressedFormat(internalformat)) - { - baseFormat = GetBaseFormat(internalformat); - dataType = GetDataType(internalformat); - } - // create texture of identical format/size to store initial contents if(textype == eGL_TEXTURE_BUFFER || details.view) { // no 'contents' texture to create } - else if(textype == eGL_TEXTURE_2D_MULTISAMPLE) + else { - gl.glTextureStorage2DMultisampleEXT(tex, textype, samples, internalformat, width, height, - GL_TRUE); + CreateTextureImage(tex, internalformat, textype, dim, width, height, depth, samples, mips); + } + + if(textype == eGL_TEXTURE_2D_MULTISAMPLE || textype == eGL_TEXTURE_2D_MULTISAMPLE_ARRAY) mips = 1; - } - else if(textype == eGL_TEXTURE_2D_MULTISAMPLE_ARRAY) - { - gl.glTextureStorage3DMultisampleEXT(tex, textype, samples, internalformat, width, height, - depth, GL_TRUE); - mips = 1; - } - else if(dim == 1) - { - gl.glTextureParameteriEXT(tex, textype, eGL_TEXTURE_MAX_LEVEL, mips - 1); - - int w = width; - for(int i = 0; i < mips; i++) - { - gl.glTextureImage1DEXT(tex, textype, i, internalformat, w, 0, baseFormat, dataType, NULL); - w = RDCMAX(1, w >> 1); - } - } - else if(dim == 2) - { - 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, - }; - - gl.glTextureParameteriEXT(tex, textype, eGL_TEXTURE_MAX_LEVEL, mips - 1); - - int w = width; - int h = height; - for(int i = 0; i < mips; i++) - { - if(textype != eGL_TEXTURE_CUBE_MAP) - { - gl.glTextureImage2DEXT(tex, textype, i, internalformat, w, h, 0, baseFormat, dataType, - NULL); - } - else - { - for(size_t t = 0; t < ARRAY_COUNT(targets); t++) - gl.glTextureImage2DEXT(tex, targets[t], i, internalformat, w, h, 0, baseFormat, - dataType, NULL); - } - - w = RDCMAX(1, w >> 1); - if(textype != eGL_TEXTURE_1D_ARRAY) - h = RDCMAX(1, h >> 1); - } - } - else if(dim == 3) - { - gl.glTextureParameteriEXT(tex, textype, eGL_TEXTURE_MAX_LEVEL, mips - 1); - - int w = width; - int h = height; - int d = depth; - for(int i = 0; i < mips; i++) - { - gl.glTextureImage3DEXT(tex, textype, i, internalformat, w, h, d, 0, baseFormat, - dataType, NULL); - w = RDCMAX(1, w >> 1); - h = RDCMAX(1, h >> 1); - if(textype == eGL_TEXTURE_3D) - d = RDCMAX(1, d >> 1); - } - } if(textype == eGL_TEXTURE_BUFFER || details.view) { diff --git a/renderdoc/driver/gl/gl_manager.h b/renderdoc/driver/gl/gl_manager.h index 2937c9a24..68ad7729b 100644 --- a/renderdoc/driver/gl/gl_manager.h +++ b/renderdoc/driver/gl/gl_manager.h @@ -220,6 +220,8 @@ private: bool Need_InitialStateChunk(GLResource res); bool Prepare_InitialState(GLResource res); + void CreateTextureImage(GLuint tex, GLenum internalFormat, GLenum textype, GLint dim, GLint width, + GLint height, GLint depth, GLint samples, int mips); void PrepareTextureInitialContents(ResourceId liveid, ResourceId origid, GLResource res); void Create_InitialState(ResourceId id, GLResource live, bool hasData); diff --git a/renderdoc/driver/gl/gl_replay.cpp b/renderdoc/driver/gl/gl_replay.cpp index 5543eff53..0689d5597 100644 --- a/renderdoc/driver/gl/gl_replay.cpp +++ b/renderdoc/driver/gl/gl_replay.cpp @@ -2525,7 +2525,18 @@ byte *GLReplay::GetTextureData(ResourceId tex, uint32_t arrayIdx, uint32_t mip, if(m_GetTexturePrevData[mip] == NULL) { m_GetTexturePrevData[mip] = new byte[dataSize * arraysize]; - gl.glGetCompressedTexImage(target, mip, m_GetTexturePrevData[mip]); + if(IsGLES) + { + const vector &data = texDetails.compressedData[mip]; + if(data.size() == dataSize * arraysize) + memcpy(m_GetTexturePrevData[mip], data.data(), data.size()); + else + RDCERR("Different expected and stored compressed texture sizes for array texture!"); + } + else + { + gl.glGetCompressedTexImage(target, mip, m_GetTexturePrevData[mip]); + } } // now copy the slice from the cache into ret @@ -2537,7 +2548,18 @@ byte *GLReplay::GetTextureData(ResourceId tex, uint32_t arrayIdx, uint32_t mip, else { // for non-arrays we can just readback without caching - gl.glGetCompressedTexImage(target, mip, ret); + if(IsGLES) + { + const vector &data = texDetails.compressedData[mip]; + if(data.size() == dataSize) + memcpy(m_GetTexturePrevData[mip], data.data(), data.size()); + else + RDCERR("Different expected and stored compressed texture sizes!"); + } + else + { + gl.glGetCompressedTexImage(target, mip, m_GetTexturePrevData[mip]); + } } } else diff --git a/renderdoc/driver/gl/gl_resources.cpp b/renderdoc/driver/gl/gl_resources.cpp index f8d8678b6..53569ac96 100644 --- a/renderdoc/driver/gl/gl_resources.cpp +++ b/renderdoc/driver/gl/gl_resources.cpp @@ -996,6 +996,22 @@ GLenum FramebufferBinding(GLenum target) 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) diff --git a/renderdoc/driver/gl/gl_resources.h b/renderdoc/driver/gl/gl_resources.h index 19bc92a73..808136717 100644 --- a/renderdoc/driver/gl/gl_resources.h +++ b/renderdoc/driver/gl/gl_resources.h @@ -58,6 +58,7 @@ bool IsUIntFormat(GLenum internalFormat); bool IsSIntFormat(GLenum internalFormat); bool IsSRGBFormat(GLenum internalFormat); +bool IsCubeFace(GLenum target); GLint CubeTargetIndex(GLenum face); GLenum TextureBinding(GLenum target); GLenum TextureTarget(GLenum target); diff --git a/renderdoc/driver/gl/wrappers/gl_emulated.cpp b/renderdoc/driver/gl/wrappers/gl_emulated.cpp index 9bb43fc2d..60be38183 100644 --- a/renderdoc/driver/gl/wrappers/gl_emulated.cpp +++ b/renderdoc/driver/gl/wrappers/gl_emulated.cpp @@ -1138,7 +1138,15 @@ void APIENTRY _glCopyImageSubData(GLuint srcName, GLenum srcTarget, GLint srcLev for(int trg = 0; trg < count; trg++) { // read to CPU - hookset->glGetCompressedTextureImageEXT(srcName, targets[trg], srcLevel, buf); + if(IsGLES) + { + RDCERR("Can't emulate glCopyImageSubData without glGetCompressedTexImage on GLES"); + memset(buf, 0, size); + } + else + { + hookset->glGetCompressedTextureImageEXT(srcName, targets[trg], srcLevel, buf); + } // write to GPU if(srcTarget == eGL_TEXTURE_1D || srcTarget == eGL_TEXTURE_1D_ARRAY) diff --git a/renderdoc/driver/gl/wrappers/gl_texture_funcs.cpp b/renderdoc/driver/gl/wrappers/gl_texture_funcs.cpp index 4cb4778ef..1e082183d 100644 --- a/renderdoc/driver/gl/wrappers/gl_texture_funcs.cpp +++ b/renderdoc/driver/gl/wrappers/gl_texture_funcs.cpp @@ -2816,6 +2816,104 @@ void WrappedOpenGL::glCompressedMultiTexImage1DEXT(GLenum texunit, GLenum target } } +void WrappedOpenGL::StoreCompressedTexData(ResourceId texId, GLenum target, GLint level, + GLint xoffset, GLint yoffset, GLint zoffset, + GLsizei width, GLsizei height, GLsizei depth, + GLenum format, GLsizei imageSize, const void *pixels) +{ + byte *unpackedPixels = NULL; + byte *srcPixels = NULL; + GLint unpackbuf = 0; + + m_Real.glGetIntegerv(eGL_PIXEL_UNPACK_BUFFER_BINDING, &unpackbuf); + + if(unpackbuf == 0 && pixels != NULL) + { + PixelUnpackState unpack; + unpack.Fetch(&m_Real, false); + + if(unpack.FastPathCompressed(width, height, depth)) + srcPixels = (byte *)pixels; + else + srcPixels = unpackedPixels = + unpack.UnpackCompressed((byte *)pixels, width, height, depth, imageSize); + } + + if(unpackbuf != 0) + srcPixels = (byte *)m_Real.glMapBufferRange(eGL_PIXEL_UNPACK_BUFFER, (GLintptr)pixels, + imageSize, eGL_MAP_READ_BIT); + + if(srcPixels) + { + string error; + + // Only the trivial case is handled yet. + if(xoffset == 0 && yoffset == 0) + { + if(target == GL_TEXTURE_2D || target == GL_TEXTURE_CUBE_MAP_POSITIVE_X || + target == GL_TEXTURE_CUBE_MAP_NEGATIVE_X || target == GL_TEXTURE_CUBE_MAP_POSITIVE_Y || + target == GL_TEXTURE_CUBE_MAP_NEGATIVE_Y || target == GL_TEXTURE_CUBE_MAP_POSITIVE_Z || + target == GL_TEXTURE_CUBE_MAP_NEGATIVE_Z || target == GL_TEXTURE_2D_ARRAY || + target == GL_TEXTURE_CUBE_MAP_ARRAY) + { + if(depth <= 1) + { + size_t compressedImageSize = GetCompressedByteSize(width, height, 1, format); + RDCASSERT(compressedImageSize == (size_t)imageSize); + auto &cd = m_Textures[texId].compressedData; + auto &cdData = cd[level]; + GLint zoff = IsCubeFace(target) ? CubeTargetIndex(target) : zoffset; + size_t startOffset = imageSize * zoff; + if(cdData.size() < startOffset + imageSize) + cdData.resize(startOffset + imageSize); + memcpy(cdData.data() + startOffset, srcPixels, imageSize); + } + else + { + error = StringFormat::Fmt("depth (%d)", depth); + } + } + else if(target == GL_TEXTURE_3D) + { + if(zoffset == 0) + { + RDCASSERT(GetCompressedByteSize(width, height, depth, format) == (size_t)imageSize); + auto &cd = m_Textures[texId].compressedData; + auto &cdData = cd[level]; + cdData.resize(imageSize); + memcpy(cdData.data(), srcPixels, imageSize); + } + else + { + error = StringFormat::Fmt("zoffset (%d)", zoffset); + } + } + else + { + error = "target"; + } + } + else + { + error = StringFormat::Fmt("xoffset (%d) and/or yoffset (%d)", xoffset, yoffset); + } + + if(unpackbuf != 0) + m_Real.glUnmapBuffer(eGL_PIXEL_UNPACK_BUFFER); + + if(!error.empty()) + RDCWARN("StoreCompressedTexData: Unexpected %s (tex:%llu, target:%s)", error.c_str(), texId, + ToStr::Get(target).c_str()); + } + else + { + RDCWARN("StoreCompressedTexData: No source pixels to copy from (tex:%llu, target:%s)", texId, + ToStr::Get(target).c_str()); + } + + SAFE_DELETE_ARRAY(unpackedPixels); +} + bool WrappedOpenGL::Serialise_glCompressedTextureImage2DEXT(GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, @@ -2951,6 +3049,10 @@ void WrappedOpenGL::Common_glCompressedTextureImage2DEXT(ResourceId texId, GLenu GLResourceRecord *record = GetResourceManager()->GetResourceRecord(texId); RDCASSERT(record); + if(IsGLES) + StoreCompressedTexData(record->GetResourceID(), target, level, 0, 0, 0, width, height, 0, + internalformat, imageSize, pixels); + // This is kind of an arbitary heuristic, but in the past a game has re-specified a texture with // glTexImage over and over // so we need to attempt to catch the case where glTexImage is called to re-upload data, not @@ -3176,6 +3278,10 @@ void WrappedOpenGL::Common_glCompressedTextureImage3DEXT(ResourceId texId, GLenu GLResourceRecord *record = GetResourceManager()->GetResourceRecord(texId); RDCASSERT(record); + if(IsGLES) + StoreCompressedTexData(record->GetResourceID(), target, level, 0, 0, 0, width, height, depth, + internalformat, imageSize, pixels); + // This is kind of an arbitary heuristic, but in the past a game has re-specified a texture with // glTexImage over and over // so we need to attempt to catch the case where glTexImage is called to re-upload data, not @@ -5166,6 +5272,10 @@ void WrappedOpenGL::Common_glCompressedTextureSubImage2DEXT(GLResourceRecord *re GLint unpackbuf = 0; m_Real.glGetIntegerv(eGL_PIXEL_UNPACK_BUFFER_BINDING, &unpackbuf); + if(IsGLES) + StoreCompressedTexData(record->GetResourceID(), target, level, xoffset, yoffset, 0, width, + height, 0, format, imageSize, pixels); + if(m_State == WRITING_IDLE && unpackbuf != 0) { GetResourceManager()->MarkDirtyResource(record->GetResourceID()); @@ -5360,6 +5470,10 @@ void WrappedOpenGL::Common_glCompressedTextureSubImage3DEXT(GLResourceRecord *re GLint unpackbuf = 0; m_Real.glGetIntegerv(eGL_PIXEL_UNPACK_BUFFER_BINDING, &unpackbuf); + if(IsGLES) + StoreCompressedTexData(record->GetResourceID(), target, level, xoffset, yoffset, zoffset, width, + height, depth, format, imageSize, pixels); + if(m_State == WRITING_IDLE && unpackbuf != 0) { GetResourceManager()->MarkDirtyResource(record->GetResourceID());