nv2a: Support compressed 2D texture padding

This commit is contained in:
Matt Borgerson 2022-05-24 00:38:56 -07:00 committed by mborgerson
parent 14748d9bbb
commit 280a1bb6a1
1 changed files with 54 additions and 70 deletions

View File

@ -6277,41 +6277,23 @@ static void pgraph_bind_textures(NV2AState *d)
depth = 1 << log_depth; depth = 1 << log_depth;
pitch = 0; pitch = 0;
/* FIXME: What about 3D mipmaps? */
levels = MIN(levels, max_mipmap_level + 1); levels = MIN(levels, max_mipmap_level + 1);
if (f.gl_format != 0) {
/* Discard mipmap levels that would be smaller than 1x1. /* Discard mipmap levels that would be smaller than 1x1.
* FIXME: Is this actually needed? * FIXME: Is this actually needed?
* *
* >> Level 0: 32 x 4 * >> Level 0: 32 x 4
* Level 1: 16 x 2 * Level 1: 16 x 2
* Level 2: 8 x 1 * Level 2: 8 x 1
* Level 3: 4 x 1 * Level 3: 4 x 1
* Level 4: 2 x 1 * Level 4: 2 x 1
* Level 5: 1 x 1 * Level 5: 1 x 1
*/ */
levels = MIN(levels, MAX(log_width, log_height) + 1); levels = MIN(levels, MAX(log_width, log_height) + 1);
} else { assert(levels > 0);
/* OpenGL requires DXT textures to always have a width and
* height a multiple of 4. The Xbox and DirectX handles DXT if (dimensionality == 3) {
* textures smaller than 4 by padding the reset of the block. /* FIXME: What about 3D mipmaps? */
*
* See:
* https://msdn.microsoft.com/en-us/library/windows/desktop/bb204843(v=vs.85).aspx
* https://msdn.microsoft.com/en-us/library/windows/desktop/bb694531%28v=vs.85%29.aspx#Virtual_Size
*
* Work around this for now by discarding mipmap levels that
* would result in too-small textures. A correct solution
* will be to decompress these levels manually, or add texture
* sampling logic.
*
* >> Level 0: 64 x 8
* Level 1: 32 x 4
* Level 2: 16 x 2 << Ignored
* >> Level 0: 16 x 16
* Level 1: 8 x 8
* Level 2: 4 x 4 << OK!
*/
if (log_width < 2 || log_height < 2) { if (log_width < 2 || log_height < 2) {
/* Base level is smaller than 4x4... */ /* Base level is smaller than 4x4... */
levels = 1; levels = 1;
@ -6319,7 +6301,6 @@ static void pgraph_bind_textures(NV2AState *d)
levels = MIN(levels, MIN(log_width, log_height) - 1); levels = MIN(levels, MIN(log_width, log_height) - 1);
} }
} }
assert(levels > 0);
min_mipmap_level = MIN(levels-1, min_mipmap_level); min_mipmap_level = MIN(levels-1, min_mipmap_level);
max_mipmap_level = MIN(levels-1, max_mipmap_level); max_mipmap_level = MIN(levels-1, max_mipmap_level);
} }
@ -6335,25 +6316,26 @@ static void pgraph_bind_textures(NV2AState *d)
int level; int level;
if (f.gl_format != 0) { if (f.gl_format != 0) {
for (level = 0; level < levels; level++) { for (level = 0; level < levels; level++) {
w = MAX(w, 1); h = MAX(h, 1); w = MAX(w, 1);
h = MAX(h, 1);
length += w * h * f.bytes_per_pixel; length += w * h * f.bytes_per_pixel;
w /= 2; w /= 2;
h /= 2; h /= 2;
} }
} else { } else {
/* Compressed textures are a bit different */ /* Compressed textures are a bit different */
unsigned int block_size; unsigned int block_size =
if (f.gl_internal_format == f.gl_internal_format ==
GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) { GL_COMPRESSED_RGBA_S3TC_DXT1_EXT ?
block_size = 8; 8 : 16;
} else {
block_size = 16;
}
for (level = 0; level < levels; level++) { for (level = 0; level < levels; level++) {
w = MAX(w, 4); h = MAX(h, 4); w = MAX(w, 1);
length += w/4 * h/4 * block_size; h = MAX(h, 1);
w /= 2; h /= 2; unsigned int phys_w = (w + 3) & ~3,
phys_h = (h + 3) & ~3;
length += phys_w/4 * phys_h/4 * block_size;
w /= 2;
h /= 2;
} }
} }
if (cubemap) { if (cubemap) {
@ -6986,44 +6968,42 @@ static void upload_gl_texture(GLenum gl_target,
int level; int level;
for (level = 0; level < s.levels; level++) { for (level = 0; level < s.levels; level++) {
width = MAX(width, 1);
height = MAX(height, 1);
if (f.gl_format == 0) { /* compressed */ if (f.gl_format == 0) { /* compressed */
assert(width >= 4 && height >= 4 && // https://docs.microsoft.com/en-us/windows/win32/direct3d10/d3d10-graphics-programming-guide-resources-block-compression#virtual-size-versus-physical-size
"Compressed texture padding"); unsigned int block_size =
width = MAX(width, 4); height = MAX(height, 4); f.gl_internal_format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT ?
8 : 16;
unsigned int block_size; unsigned int physical_width = (width + 3) & ~3,
if (f.gl_internal_format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) { physical_height = (height + 3) & ~3;
block_size = 8; if (physical_width != width) {
} else { glPixelStorei(GL_UNPACK_ROW_LENGTH, physical_width * 4);
block_size = 16;
} }
uint8_t *converted = decompress_2d_texture_data( uint8_t *converted = decompress_2d_texture_data(
f.gl_internal_format, texture_data, width, height); f.gl_internal_format, texture_data, physical_width,
physical_height);
glTexImage2D(gl_target, level, GL_RGBA, width, height, 0, glTexImage2D(gl_target, level, GL_RGBA, width, height, 0,
GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, converted); GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, converted);
g_free(converted); g_free(converted);
if (physical_width != width) {
texture_data += width/4 * height/4 * block_size; glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
}
texture_data +=
physical_width / 4 * physical_height / 4 * block_size;
} else { } else {
width = MAX(width, 1); height = MAX(height, 1);
unsigned int pitch = width * f.bytes_per_pixel; unsigned int pitch = width * f.bytes_per_pixel;
uint8_t *unswizzled = (uint8_t*)g_malloc(height * pitch); uint8_t *unswizzled = (uint8_t*)g_malloc(height * pitch);
unswizzle_rect(texture_data, width, height, unswizzle_rect(texture_data, width, height,
unswizzled, pitch, f.bytes_per_pixel); unswizzled, pitch, f.bytes_per_pixel);
uint8_t *converted = convert_texture_data(s, unswizzled, uint8_t *converted = convert_texture_data(s, unswizzled,
palette_data, palette_data,
width, height, 1, width, height, 1,
pitch, 0); pitch, 0);
glTexImage2D(gl_target, level, f.gl_internal_format, width,
glTexImage2D(gl_target, level, f.gl_internal_format, height, 0, f.gl_format, f.gl_type,
width, height, 0,
f.gl_format, f.gl_type,
converted ? converted : unswizzled); converted ? converted : unswizzled);
if (converted) { if (converted) {
g_free(converted); g_free(converted);
} }
@ -7047,7 +7027,8 @@ static void upload_gl_texture(GLenum gl_target,
int level; int level;
for (level = 0; level < s.levels; level++) { for (level = 0; level < s.levels; level++) {
if (f.gl_format == 0) { /* compressed */ if (f.gl_format == 0) { /* compressed */
assert(width % 4 == 0 && height % 4 == 0 &&
"Compressed 3D texture virtual size");
width = MAX(width, 4); width = MAX(width, 4);
height = MAX(height, 4); height = MAX(height, 4);
depth = MAX(depth, 1); depth = MAX(depth, 1);
@ -7072,6 +7053,9 @@ static void upload_gl_texture(GLenum gl_target,
texture_data += texture_size; texture_data += texture_size;
} else { } else {
width = MAX(width, 1);
height = MAX(height, 1);
depth = MAX(depth, 1);
unsigned int row_pitch = width * f.bytes_per_pixel; unsigned int row_pitch = width * f.bytes_per_pixel;
unsigned int slice_pitch = row_pitch * height; unsigned int slice_pitch = row_pitch * height;