Add vendor check to avoid glCopyImageSubData on <4x4 compressed mips

This commit is contained in:
baldurk
2014-12-14 12:17:41 +00:00
parent 652f3529a3
commit 7c663de948
3 changed files with 286 additions and 16 deletions
+35
View File
@@ -180,6 +180,41 @@ void DoVendorChecks(const GLHookSet &gl, GLWindowingData context)
}
}
// AMD throws an error if we try to copy the mips that are smaller than 4x4,
if(gl.glGetError && gl.glGenTextures && gl.glBindTexture && gl.glCopyImageSubData &&
gl.glTexStorage2D && gl.glTexParameteri && gl.glDeleteTextures)
{
GLuint texs[2];
gl.glGenTextures(2, texs);
gl.glBindTexture(eGL_TEXTURE_2D, texs[0]);
gl.glTexStorage2D(eGL_TEXTURE_2D, 1, eGL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 1, 1);
gl.glTexParameteri(eGL_TEXTURE_2D, eGL_TEXTURE_MAX_LEVEL, 1);
gl.glBindTexture(eGL_TEXTURE_2D, texs[1]);
gl.glTexStorage2D(eGL_TEXTURE_2D, 1, eGL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 1, 1);
gl.glTexParameteri(eGL_TEXTURE_2D, eGL_TEXTURE_MAX_LEVEL, 1);
// clear all error flags.
GLenum err = gl.glGetError();
while(err != eGL_NONE) err = gl.glGetError();
gl.glCopyImageSubData(texs[0], eGL_TEXTURE_2D, 0, 0, 0, 0, texs[1], eGL_TEXTURE_2D, 0, 0, 0, 0, 1, 1, 1);
err = gl.glGetError();
if(err != eGL_NONE)
{
// if we got an error trying to query that, we should enable this hack
VendorCheck[VendorCheck_AMD_copy_compressed_tinymips] = true;
RDCWARN("Using hack to avoid glCopyImageSubData on lowest mips of compressed texture");
}
gl.glBindTexture(eGL_TEXTURE_2D, 0);
gl.glDeleteTextures(2, texs);
}
// only do this when we have a proper context e.g. on windows where an old
// context is first created. Check to see if FBOs or VAOs are shared between
// contexts.
+1
View File
@@ -111,6 +111,7 @@ enum VendorCheckEnum
VendorCheck_EXT_fbo_shared,
VendorCheck_EXT_vao_shared,
VendorCheck_AMD_polygon_mode_query,
VendorCheck_AMD_copy_compressed_tinymips,
VendorCheck_Count,
};
extern bool VendorCheck[VendorCheck_Count];
+250 -16
View File
@@ -272,6 +272,49 @@ bool GLResourceManager::Prepare_InitialState(GLResource res)
int maxlevel = mips-1;
gl.glTextureParameterivEXT(res.name, details.curType, eGL_TEXTURE_MAX_LEVEL, (GLint *)&maxlevel);
bool iscomp = IsCompressedFormat(details.internalFormat);
GLint packParams[8];
GLint unpackParams[8];
if(iscomp && VendorCheck[VendorCheck_AMD_copy_compressed_tinymips])
{
gl.glGetIntegerv(eGL_PACK_SWAP_BYTES, &packParams[0]);
gl.glGetIntegerv(eGL_PACK_LSB_FIRST, &packParams[1]);
gl.glGetIntegerv(eGL_PACK_ROW_LENGTH, &packParams[2]);
gl.glGetIntegerv(eGL_PACK_IMAGE_HEIGHT, &packParams[3]);
gl.glGetIntegerv(eGL_PACK_SKIP_PIXELS, &packParams[4]);
gl.glGetIntegerv(eGL_PACK_SKIP_ROWS, &packParams[5]);
gl.glGetIntegerv(eGL_PACK_SKIP_IMAGES, &packParams[6]);
gl.glGetIntegerv(eGL_PACK_ALIGNMENT, &packParams[7]);
gl.glPixelStorei(eGL_PACK_SWAP_BYTES, 0);
gl.glPixelStorei(eGL_PACK_LSB_FIRST, 0);
gl.glPixelStorei(eGL_PACK_ROW_LENGTH, 0);
gl.glPixelStorei(eGL_PACK_IMAGE_HEIGHT, 0);
gl.glPixelStorei(eGL_PACK_SKIP_PIXELS, 0);
gl.glPixelStorei(eGL_PACK_SKIP_ROWS, 0);
gl.glPixelStorei(eGL_PACK_SKIP_IMAGES, 0);
gl.glPixelStorei(eGL_PACK_ALIGNMENT, 1);
gl.glGetIntegerv(eGL_UNPACK_SWAP_BYTES, &unpackParams[0]);
gl.glGetIntegerv(eGL_UNPACK_LSB_FIRST, &unpackParams[1]);
gl.glGetIntegerv(eGL_UNPACK_ROW_LENGTH, &unpackParams[2]);
gl.glGetIntegerv(eGL_UNPACK_IMAGE_HEIGHT, &unpackParams[3]);
gl.glGetIntegerv(eGL_UNPACK_SKIP_PIXELS, &unpackParams[4]);
gl.glGetIntegerv(eGL_UNPACK_SKIP_ROWS, &unpackParams[5]);
gl.glGetIntegerv(eGL_UNPACK_SKIP_IMAGES, &unpackParams[6]);
gl.glGetIntegerv(eGL_UNPACK_ALIGNMENT, &unpackParams[7]);
gl.glPixelStorei(eGL_UNPACK_SWAP_BYTES, 0);
gl.glPixelStorei(eGL_UNPACK_LSB_FIRST, 0);
gl.glPixelStorei(eGL_UNPACK_ROW_LENGTH, 0);
gl.glPixelStorei(eGL_UNPACK_IMAGE_HEIGHT, 0);
gl.glPixelStorei(eGL_UNPACK_SKIP_PIXELS, 0);
gl.glPixelStorei(eGL_UNPACK_SKIP_ROWS, 0);
gl.glPixelStorei(eGL_UNPACK_SKIP_IMAGES, 0);
gl.glPixelStorei(eGL_UNPACK_ALIGNMENT, 1);
}
// copy over mips
for(int i=0; i < mips; i++)
{
@@ -286,15 +329,89 @@ bool GLResourceManager::Prepare_InitialState(GLResource res)
details.curType == eGL_TEXTURE_2D_ARRAY)
d = details.depth;
// it seems like everything explodes if I do glCopyImageSubData on a D32F_S8 texture - in-program the overlay
// gets corrupted as one UBO seems to not provide data anymore until it's "refreshed". It seems like a driver bug,
// nvidia specific.
// In most cases a program isn't going to rely on the contents of a depth-stencil buffer (shadow maps that it might
// require would be depth-only formatted).
if(details.internalFormat == eGL_DEPTH32F_STENCIL8 && VendorCheck[VendorCheck_NV_avoid_D32S8_copy])
RDCDEBUG("Not fetching initial contents of D32F_S8 texture");
// AMD throws an error copying mips that are smaller than the block size in one dimension, so do copy via
// CPU instead (will be slow, potentially we could optimise this if there's a different GPU-side image copy
// routine that works on these dimensions. Hopefully there'll only be a couple of such mips).
if(iscomp && VendorCheck[VendorCheck_AMD_copy_compressed_tinymips] && (w < 4 || h < 4))
{
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(details.curType != eGL_TEXTURE_CUBE_MAP)
{
targets[0] = details.curType;
count = 1;
}
for(int trg=0; trg < count; trg++)
{
GLint compSize;
gl.glGetTextureLevelParameterivEXT(res.name, targets[trg], i, eGL_TEXTURE_COMPRESSED_IMAGE_SIZE, &compSize);
size_t size = compSize;
// sometimes cubemaps return the compressed image size for the whole texture, but we read it
// face by face
if(VendorCheck[VendorCheck_EXT_compressed_cube_size] && details.curType == eGL_TEXTURE_CUBE_MAP)
size /= 6;
byte *buf = new byte[size];
// read to CPU
gl.glGetCompressedTextureImageEXT(res.name, targets[trg], i, buf);
// write to GPU
if(details.dimension == 1)
gl.glCompressedTextureSubImage1DEXT(tex, targets[trg], i, 0, w, details.internalFormat, (GLsizei)size, buf);
else if(details.dimension == 2)
gl.glCompressedTextureSubImage2DEXT(tex, targets[trg], i, 0, 0, w, h, details.internalFormat, (GLsizei)size, buf);
else if(details.dimension == 3)
gl.glCompressedTextureSubImage3DEXT(tex, targets[trg], i, 0, 0, 0, w, h, d, details.internalFormat, (GLsizei)size, buf);
delete[] buf;
}
}
else
gl.glCopyImageSubData(res.name, details.curType, i, 0, 0, 0, tex, details.curType, i, 0, 0, 0, w, h, d);
{
// it seems like everything explodes if I do glCopyImageSubData on a D32F_S8 texture - in-program the overlay
// gets corrupted as one UBO seems to not provide data anymore until it's "refreshed". It seems like a driver bug,
// nvidia specific.
// In most cases a program isn't going to rely on the contents of a depth-stencil buffer (shadow maps that it might
// require would be depth-only formatted).
if(details.internalFormat == eGL_DEPTH32F_STENCIL8 && VendorCheck[VendorCheck_NV_avoid_D32S8_copy])
RDCDEBUG("Not fetching initial contents of D32F_S8 texture");
else
gl.glCopyImageSubData(res.name, details.curType, i, 0, 0, 0, tex, details.curType, i, 0, 0, 0, w, h, d);
}
}
if(iscomp && VendorCheck[VendorCheck_AMD_copy_compressed_tinymips])
{
gl.glPixelStorei(eGL_PACK_SWAP_BYTES, packParams[0]);
gl.glPixelStorei(eGL_PACK_LSB_FIRST, packParams[1]);
gl.glPixelStorei(eGL_PACK_ROW_LENGTH, packParams[2]);
gl.glPixelStorei(eGL_PACK_IMAGE_HEIGHT, packParams[3]);
gl.glPixelStorei(eGL_PACK_SKIP_PIXELS, packParams[4]);
gl.glPixelStorei(eGL_PACK_SKIP_ROWS, packParams[5]);
gl.glPixelStorei(eGL_PACK_SKIP_IMAGES, packParams[6]);
gl.glPixelStorei(eGL_PACK_ALIGNMENT, packParams[7]);
gl.glPixelStorei(eGL_UNPACK_SWAP_BYTES, unpackParams[0]);
gl.glPixelStorei(eGL_UNPACK_LSB_FIRST, unpackParams[1]);
gl.glPixelStorei(eGL_UNPACK_ROW_LENGTH, unpackParams[2]);
gl.glPixelStorei(eGL_UNPACK_IMAGE_HEIGHT, unpackParams[3]);
gl.glPixelStorei(eGL_UNPACK_SKIP_PIXELS, unpackParams[4]);
gl.glPixelStorei(eGL_UNPACK_SKIP_ROWS, unpackParams[5]);
gl.glPixelStorei(eGL_UNPACK_SKIP_IMAGES, unpackParams[6]);
gl.glPixelStorei(eGL_UNPACK_ALIGNMENT, unpackParams[7]);
}
gl.glTextureParameterivEXT(res.name, details.curType, eGL_TEXTURE_MAX_LEVEL, (GLint *)&state->maxLevel);
@@ -965,7 +1082,50 @@ void GLResourceManager::Apply_InitialState(GLResource live, InitialContentData i
// correct MAX_LEVEL is set to whatever the program had.
int maxlevel = mips-1;
gl.glTextureParameterivEXT(live.name, details.curType, eGL_TEXTURE_MAX_LEVEL, (GLint *)&maxlevel);
bool iscomp = IsCompressedFormat(details.internalFormat);
GLint packParams[8];
GLint unpackParams[8];
if(iscomp && VendorCheck[VendorCheck_AMD_copy_compressed_tinymips])
{
gl.glGetIntegerv(eGL_PACK_SWAP_BYTES, &packParams[0]);
gl.glGetIntegerv(eGL_PACK_LSB_FIRST, &packParams[1]);
gl.glGetIntegerv(eGL_PACK_ROW_LENGTH, &packParams[2]);
gl.glGetIntegerv(eGL_PACK_IMAGE_HEIGHT, &packParams[3]);
gl.glGetIntegerv(eGL_PACK_SKIP_PIXELS, &packParams[4]);
gl.glGetIntegerv(eGL_PACK_SKIP_ROWS, &packParams[5]);
gl.glGetIntegerv(eGL_PACK_SKIP_IMAGES, &packParams[6]);
gl.glGetIntegerv(eGL_PACK_ALIGNMENT, &packParams[7]);
gl.glPixelStorei(eGL_PACK_SWAP_BYTES, 0);
gl.glPixelStorei(eGL_PACK_LSB_FIRST, 0);
gl.glPixelStorei(eGL_PACK_ROW_LENGTH, 0);
gl.glPixelStorei(eGL_PACK_IMAGE_HEIGHT, 0);
gl.glPixelStorei(eGL_PACK_SKIP_PIXELS, 0);
gl.glPixelStorei(eGL_PACK_SKIP_ROWS, 0);
gl.glPixelStorei(eGL_PACK_SKIP_IMAGES, 0);
gl.glPixelStorei(eGL_PACK_ALIGNMENT, 1);
gl.glGetIntegerv(eGL_UNPACK_SWAP_BYTES, &unpackParams[0]);
gl.glGetIntegerv(eGL_UNPACK_LSB_FIRST, &unpackParams[1]);
gl.glGetIntegerv(eGL_UNPACK_ROW_LENGTH, &unpackParams[2]);
gl.glGetIntegerv(eGL_UNPACK_IMAGE_HEIGHT, &unpackParams[3]);
gl.glGetIntegerv(eGL_UNPACK_SKIP_PIXELS, &unpackParams[4]);
gl.glGetIntegerv(eGL_UNPACK_SKIP_ROWS, &unpackParams[5]);
gl.glGetIntegerv(eGL_UNPACK_SKIP_IMAGES, &unpackParams[6]);
gl.glGetIntegerv(eGL_UNPACK_ALIGNMENT, &unpackParams[7]);
gl.glPixelStorei(eGL_UNPACK_SWAP_BYTES, 0);
gl.glPixelStorei(eGL_UNPACK_LSB_FIRST, 0);
gl.glPixelStorei(eGL_UNPACK_ROW_LENGTH, 0);
gl.glPixelStorei(eGL_UNPACK_IMAGE_HEIGHT, 0);
gl.glPixelStorei(eGL_UNPACK_SKIP_PIXELS, 0);
gl.glPixelStorei(eGL_UNPACK_SKIP_ROWS, 0);
gl.glPixelStorei(eGL_UNPACK_SKIP_IMAGES, 0);
gl.glPixelStorei(eGL_UNPACK_ALIGNMENT, 1);
}
// copy over mips
for(int i=0; i < mips; i++)
{
@@ -980,15 +1140,89 @@ void GLResourceManager::Apply_InitialState(GLResource live, InitialContentData i
details.curType == eGL_TEXTURE_2D_ARRAY)
d = details.depth;
// it seems like everything explodes if I do glCopyImageSubData on a D32F_S8 texture - on replay loads of things
// get heavily corrupted - probably the same as the problems we get in-program, but magnified. It seems like a driver bug,
// nvidia specific.
// In most cases a program isn't going to rely on the contents of a depth-stencil buffer (shadow maps that it might
// require would be depth-only formatted).
if(details.internalFormat == eGL_DEPTH32F_STENCIL8 && VendorCheck[VendorCheck_NV_avoid_D32S8_copy])
RDCDEBUG("Not fetching initial contents of D32F_S8 texture");
// AMD throws an error copying mips that are smaller than the block size in one dimension, so do copy via
// CPU instead (will be slow, potentially we could optimise this if there's a different GPU-side image copy
// routine that works on these dimensions. Hopefully there'll only be a couple of such mips).
if(iscomp && VendorCheck[VendorCheck_AMD_copy_compressed_tinymips] && (w < 4 || h < 4))
{
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(details.curType != eGL_TEXTURE_CUBE_MAP)
{
targets[0] = details.curType;
count = 1;
}
for(int trg=0; trg < count; trg++)
{
GLint compSize;
gl.glGetTextureLevelParameterivEXT(tex, targets[trg], i, eGL_TEXTURE_COMPRESSED_IMAGE_SIZE, &compSize);
size_t size = compSize;
// sometimes cubemaps return the compressed image size for the whole texture, but we read it
// face by face
if(VendorCheck[VendorCheck_EXT_compressed_cube_size] && details.curType == eGL_TEXTURE_CUBE_MAP)
size /= 6;
byte *buf = new byte[size];
// read to CPU
gl.glGetCompressedTextureImageEXT(tex, targets[trg], i, buf);
// write to GPU
if(details.dimension == 1)
gl.glCompressedTextureSubImage1DEXT(live.name, targets[trg], i, 0, w, details.internalFormat, (GLsizei)size, buf);
else if(details.dimension == 2)
gl.glCompressedTextureSubImage2DEXT(live.name, targets[trg], i, 0, 0, w, h, details.internalFormat, (GLsizei)size, buf);
else if(details.dimension == 3)
gl.glCompressedTextureSubImage3DEXT(live.name, targets[trg], i, 0, 0, 0, w, h, d, details.internalFormat, (GLsizei)size, buf);
delete[] buf;
}
}
else
gl.glCopyImageSubData(tex, details.curType, i, 0, 0, 0, live.name, details.curType, i, 0, 0, 0, w, h, d);
{
// it seems like everything explodes if I do glCopyImageSubData on a D32F_S8 texture - on replay loads of things
// get heavily corrupted - probably the same as the problems we get in-program, but magnified. It seems like a driver bug,
// nvidia specific.
// In most cases a program isn't going to rely on the contents of a depth-stencil buffer (shadow maps that it might
// require would be depth-only formatted).
if(details.internalFormat == eGL_DEPTH32F_STENCIL8 && VendorCheck[VendorCheck_NV_avoid_D32S8_copy])
RDCDEBUG("Not fetching initial contents of D32F_S8 texture");
else
gl.glCopyImageSubData(tex, details.curType, i, 0, 0, 0, live.name, details.curType, i, 0, 0, 0, w, h, d);
}
}
if(iscomp && VendorCheck[VendorCheck_AMD_copy_compressed_tinymips])
{
gl.glPixelStorei(eGL_PACK_SWAP_BYTES, packParams[0]);
gl.glPixelStorei(eGL_PACK_LSB_FIRST, packParams[1]);
gl.glPixelStorei(eGL_PACK_ROW_LENGTH, packParams[2]);
gl.glPixelStorei(eGL_PACK_IMAGE_HEIGHT, packParams[3]);
gl.glPixelStorei(eGL_PACK_SKIP_PIXELS, packParams[4]);
gl.glPixelStorei(eGL_PACK_SKIP_ROWS, packParams[5]);
gl.glPixelStorei(eGL_PACK_SKIP_IMAGES, packParams[6]);
gl.glPixelStorei(eGL_PACK_ALIGNMENT, packParams[7]);
gl.glPixelStorei(eGL_UNPACK_SWAP_BYTES, unpackParams[0]);
gl.glPixelStorei(eGL_UNPACK_LSB_FIRST, unpackParams[1]);
gl.glPixelStorei(eGL_UNPACK_ROW_LENGTH, unpackParams[2]);
gl.glPixelStorei(eGL_UNPACK_IMAGE_HEIGHT, unpackParams[3]);
gl.glPixelStorei(eGL_UNPACK_SKIP_PIXELS, unpackParams[4]);
gl.glPixelStorei(eGL_UNPACK_SKIP_ROWS, unpackParams[5]);
gl.glPixelStorei(eGL_UNPACK_SKIP_IMAGES, unpackParams[6]);
gl.glPixelStorei(eGL_UNPACK_ALIGNMENT, unpackParams[7]);
}
TextureStateInitialData *state = (TextureStateInitialData *)initial.blob;