Skip to content

Commit

Permalink
nv2a: Support compressed 2D texture padding
Browse files Browse the repository at this point in the history
  • Loading branch information
mborgerson committed May 24, 2022
1 parent 14748d9 commit 280a1bb
Showing 1 changed file with 54 additions and 70 deletions.
124 changes: 54 additions & 70 deletions hw/xbox/nv2a/pgraph.c
Original file line number Diff line number Diff line change
Expand Up @@ -6277,49 +6277,30 @@ static void pgraph_bind_textures(NV2AState *d)
depth = 1 << log_depth;
pitch = 0;

/* FIXME: What about 3D mipmaps? */
levels = MIN(levels, max_mipmap_level + 1);
if (f.gl_format != 0) {
/* Discard mipmap levels that would be smaller than 1x1.
* FIXME: Is this actually needed?
*
* >> Level 0: 32 x 4
* Level 1: 16 x 2
* Level 2: 8 x 1
* Level 3: 4 x 1
* Level 4: 2 x 1
* Level 5: 1 x 1
*/
levels = MIN(levels, MAX(log_width, log_height) + 1);
} else {
/* OpenGL requires DXT textures to always have a width and
* height a multiple of 4. The Xbox and DirectX handles DXT
* textures smaller than 4 by padding the reset of the block.
*
* 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!
*/

/* Discard mipmap levels that would be smaller than 1x1.
* FIXME: Is this actually needed?
*
* >> Level 0: 32 x 4
* Level 1: 16 x 2
* Level 2: 8 x 1
* Level 3: 4 x 1
* Level 4: 2 x 1
* Level 5: 1 x 1
*/
levels = MIN(levels, MAX(log_width, log_height) + 1);
assert(levels > 0);

if (dimensionality == 3) {
/* FIXME: What about 3D mipmaps? */
if (log_width < 2 || log_height < 2) {
/* Base level is smaller than 4x4... */
levels = 1;
} else {
levels = MIN(levels, MIN(log_width, log_height) - 1);
}
}
assert(levels > 0);
min_mipmap_level = MIN(levels-1, min_mipmap_level);
max_mipmap_level = MIN(levels-1, max_mipmap_level);
}
Expand All @@ -6335,25 +6316,26 @@ static void pgraph_bind_textures(NV2AState *d)
int level;
if (f.gl_format != 0) {
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;
w /= 2;
h /= 2;
}
} else {
/* Compressed textures are a bit different */
unsigned int block_size;
if (f.gl_internal_format ==
GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) {
block_size = 8;
} else {
block_size = 16;
}

unsigned int block_size =
f.gl_internal_format ==
GL_COMPRESSED_RGBA_S3TC_DXT1_EXT ?
8 : 16;
for (level = 0; level < levels; level++) {
w = MAX(w, 4); h = MAX(h, 4);
length += w/4 * h/4 * block_size;
w /= 2; h /= 2;
w = MAX(w, 1);
h = MAX(h, 1);
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) {
Expand Down Expand Up @@ -6986,44 +6968,42 @@ static void upload_gl_texture(GLenum gl_target,

int level;
for (level = 0; level < s.levels; level++) {
if (f.gl_format == 0) { /* compressed */
assert(width >= 4 && height >= 4 &&
"Compressed texture padding");
width = MAX(width, 4); height = MAX(height, 4);
width = MAX(width, 1);
height = MAX(height, 1);

unsigned int block_size;
if (f.gl_internal_format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) {
block_size = 8;
} else {
block_size = 16;
if (f.gl_format == 0) { /* compressed */
// https://docs.microsoft.com/en-us/windows/win32/direct3d10/d3d10-graphics-programming-guide-resources-block-compression#virtual-size-versus-physical-size
unsigned int block_size =
f.gl_internal_format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT ?
8 : 16;
unsigned int physical_width = (width + 3) & ~3,
physical_height = (height + 3) & ~3;
if (physical_width != width) {
glPixelStorei(GL_UNPACK_ROW_LENGTH, physical_width * 4);
}

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,
GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, converted);
g_free(converted);

texture_data += width/4 * height/4 * block_size;
if (physical_width != width) {
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
}
texture_data +=
physical_width / 4 * physical_height / 4 * block_size;
} else {

width = MAX(width, 1); height = MAX(height, 1);

unsigned int pitch = width * f.bytes_per_pixel;
uint8_t *unswizzled = (uint8_t*)g_malloc(height * pitch);
unswizzle_rect(texture_data, width, height,
unswizzled, pitch, f.bytes_per_pixel);

uint8_t *converted = convert_texture_data(s, unswizzled,
palette_data,
width, height, 1,
pitch, 0);

glTexImage2D(gl_target, level, f.gl_internal_format,
width, height, 0,
f.gl_format, f.gl_type,
glTexImage2D(gl_target, level, f.gl_internal_format, width,
height, 0, f.gl_format, f.gl_type,
converted ? converted : unswizzled);

if (converted) {
g_free(converted);
}
Expand All @@ -7047,7 +7027,8 @@ static void upload_gl_texture(GLenum gl_target,
int level;
for (level = 0; level < s.levels; level++) {
if (f.gl_format == 0) { /* compressed */

assert(width % 4 == 0 && height % 4 == 0 &&
"Compressed 3D texture virtual size");
width = MAX(width, 4);
height = MAX(height, 4);
depth = MAX(depth, 1);
Expand All @@ -7072,6 +7053,9 @@ static void upload_gl_texture(GLenum gl_target,

texture_data += texture_size;
} else {
width = MAX(width, 1);
height = MAX(height, 1);
depth = MAX(depth, 1);

unsigned int row_pitch = width * f.bytes_per_pixel;
unsigned int slice_pitch = row_pitch * height;
Expand Down

0 comments on commit 280a1bb

Please sign in to comment.