From bbcf853477ae6091c589bdcfccb90d9e713310c3 Mon Sep 17 00:00:00 2001 From: Navaljit Ghotra Date: Sun, 19 Jan 2025 22:52:09 +0100 Subject: [PATCH 1/2] Refactor Code of TextureConverter --- .../opengl_renderer/TextureAnimator.cpp | 6 +- game/graphics/texture/TextureConverter.cpp | 230 +++++------------- game/graphics/texture/TextureConverter.h | 37 ++- 3 files changed, 101 insertions(+), 172 deletions(-) diff --git a/game/graphics/opengl_renderer/TextureAnimator.cpp b/game/graphics/opengl_renderer/TextureAnimator.cpp index 1146ae9b38f..bdb5d60509c 100644 --- a/game/graphics/opengl_renderer/TextureAnimator.cpp +++ b/game/graphics/opengl_renderer/TextureAnimator.cpp @@ -2005,7 +2005,7 @@ void TextureAnimator::load_clut_to_converter() { switch (clut_lookup->second.kind) { case VramEntry::Kind::CLUT16_16_IN_PSM32: - m_converter.upload_width(clut_lookup->second.data.data(), m_current_shader.tex0.cbp(), 16, + m_converter.upload(clut_lookup->second.data.data(), m_current_shader.tex0.cbp(), 16, 16); break; default: @@ -2104,8 +2104,8 @@ GLuint TextureAnimator::make_or_get_gpu_texture_for_current_shader(TexturePool& } } else { Timer timer; - m_converter.upload_width(vram_entry->data.data(), m_current_shader.tex0.tbp0(), - vram_entry->tex_width, vram_entry->tex_height); + m_converter.upload(vram_entry->data.data(), m_current_shader.tex0.tbp0(), + vram_entry->tex_width, vram_entry->tex_height); // also needs clut lookup load_clut_to_converter(); diff --git a/game/graphics/texture/TextureConverter.cpp b/game/graphics/texture/TextureConverter.cpp index f1fa144fb18..50a767c9f13 100644 --- a/game/graphics/texture/TextureConverter.cpp +++ b/game/graphics/texture/TextureConverter.cpp @@ -10,26 +10,22 @@ TextureConverter::TextureConverter() { m_vram.resize(4 * 1024 * 1024); } -void TextureConverter::upload(const u8* data, u32 dest, u32 size_vram_words) { - // all textures are copied to vram 128 pixels wide, regardless of actual width - int copy_width = 128; - // scale the copy height to be whatever it needs to be to transfer the right amount of data. - int copy_height = size_vram_words / copy_width; +void TextureConverter::upload(const u8* data, u32 dest, u32 width, u32 height, u32 size_vram_words) { + // Validation (optional) + if ((size_vram_words == 0 && (width == 0 || height == 0)) || data == nullptr) { + throw std::invalid_argument( + "Invalid parameters: Provide either size_vram_words or width and height, and ensure data " + "is not null."); + } + + // Calculate Width and Height from Parameters + int copy_width = (size_vram_words > 0) ? 128 : width; + int copy_height = (size_vram_words > 0) ? (size_vram_words / copy_width) : height; for (int y = 0; y < copy_height; y++) { for (int x = 0; x < copy_width; x++) { // VRAM address (bytes) - auto addr32 = psmct32_addr(x, y, copy_width) + dest * 4; - *(u32*)(m_vram.data() + addr32) = *((const u32*)(data) + (x + y * copy_width)); - } - } -} - -void TextureConverter::upload_width(const u8* data, u32 dest, u32 width, u32 height) { - for (u32 y = 0; y < height; y++) { - for (u32 x = 0; x < width; x++) { - // VRAM address (bytes) - auto addr32 = psmct32_addr(x, y, width) + dest * 256; + auto addr32 = psmct32_addr(x, y, copy_width) + dest * ((size_vram_words > 0) ? 4 : 256); *(u32*)(m_vram.data() + addr32) = *((const u32*)(data) + (x + y * width)); } } @@ -45,167 +41,67 @@ void TextureConverter::download_rgba8888(u8* result, u32 clut_vram_addr, u32 expected_size_bytes) { u32 out_offset = 0; - if (psm == int(PSM::PSMT8) && clut_psm == int(CPSM::PSMCT32)) { - // width is like the TEX0 register, in 64 texel units. - // not sure what the other widths are yet. - int read_width = 64 * goal_tex_width; - // loop over pixels in output texture image - for (u32 y = 0; y < h; y++) { - for (u32 x = 0; x < w; x++) { - // read as the PSMT8 type. The dest field tells us a block offset. - auto addr8 = psmt8_addr(x, y, read_width) + vram_addr * 256; - u8 value = *(u8*)(m_vram.data() + addr8); - - // there's yet another scramble from the CLUT. The palette index turns into an X, Y value - // See GS manual 2.7.3 CLUT Storage Mode, IDTEX8 in CSM1 mode. - u32 clut_chunk = value / 16; - u32 off_in_chunk = value % 16; - u8 clx = 0, cly = 0; - if (clut_chunk & 1) { - clx = 8; - } - cly = (clut_chunk >> 1) * 2; - if (off_in_chunk >= 8) { - off_in_chunk -= 8; - cly++; - } - clx += off_in_chunk; - - // the x, y CLUT value is looked up in PSMCT32 mode - u32 clut_addr = psmct32_addr(clx, cly, 64) + clut_vram_addr * 256; - u32 clut_value = *(u32*)(m_vram.data() + clut_addr); - memcpy(result + out_offset, &clut_value, 4); - out_offset += 4; - } + int read_width = 64 * goal_tex_width; + + // Helper for CLUT addressing + auto calculate_clut_address = [&](u8 value, int clut_psm) -> u32 { + u32 clut_chunk = value / 16; + u32 off_in_chunk = value % 16; + u8 clx = (clut_chunk & 1) ? 8 : 0; + u8 cly = (clut_chunk >> 1) * 2; + if (off_in_chunk >= 8) { + off_in_chunk -= 8; + cly++; } - - } else if (psm == int(PSM::PSMT8) && clut_psm == int(CPSM::PSMCT16)) { - // width is like the TEX0 register, in 64 texel units. - // not sure what the other widths are yet. - int read_width = 64 * goal_tex_width; - - // loop over pixels in output texture image - for (u32 y = 0; y < h; y++) { - for (u32 x = 0; x < w; x++) { - // read as the PSMT8 type. The dest field tells us a block offset. - auto addr8 = psmt8_addr(x, y, read_width) + vram_addr * 256; - u8 value = *(u8*)(m_vram.data() + addr8); - - // there's yet another scramble from the CLUT. The palette index turns into an X, Y value - // See GS manual 2.7.3 CLUT Storage Mode, IDTEX8 in CSM1 mode. - u32 clut_chunk = value / 16; - u32 off_in_chunk = value % 16; - u8 clx = 0, cly = 0; - if (clut_chunk & 1) { - clx = 8; - } - cly = (clut_chunk >> 1) * 2; - if (off_in_chunk >= 8) { - off_in_chunk -= 8; - cly++; - } - clx += off_in_chunk; - - // the x, y CLUT value is looked up in PSMCT32 mode - u32 clut_addr = psmct16_addr(clx, cly, 64) + clut_vram_addr * 256; - u32 clut_value = *(u16*)(m_vram.data() + clut_addr); - u32 rgba32 = rgba16_to_rgba32(clut_value); - memcpy(result + out_offset, &rgba32, 4); - out_offset += 4; - } + clx += off_in_chunk; + if (clut_psm == int(CPSM::PSMCT32)) { + return psmct32_addr(clx, cly, 64) + clut_vram_addr * 256; + } else if (clut_psm == int(CPSM::PSMCT16)) { + return psmct16_addr(clx, cly, 64) + clut_vram_addr * 256; } - - } else if (psm == int(PSM::PSMT4) && clut_psm == int(CPSM::PSMCT16)) { - // width is like the TEX0 register, in 64 texel units. - // not sure what the other widths are yet. - int read_width = 64 * goal_tex_width; - - // loop over pixels in output texture image - for (u32 y = 0; y < h; y++) { - for (u32 x = 0; x < w; x++) { - // read as the PSMT4 type, use half byte addressing - auto addr4 = psmt4_addr_half_byte(x, y, read_width) + vram_addr * 512; - - // read (half bytes) - u8 value = *(u8*)(m_vram.data() + addr4 / 2); - if (addr4 & 1) { - value >>= 4; - } else { - value = value & 0x0f; - } - - // there's yet another scramble from the CLUT. The palette index turns into an X, Y value - // See GS manual 2.7.3 CLUT Storage Mode, IDTEX4 in CSM1 mode. - - u8 clx = value & 0x7; - u8 cly = value >> 3; - - // the x, y CLUT value is looked up in PSMCT16 mode - u32 clut_addr = psmct16_addr(clx, cly, 64) + clut_vram_addr * 256; - u32 clut_value = *(u16*)(m_vram.data() + clut_addr); - u32 rgba32 = rgba16_to_rgba32(clut_value); - memcpy(result + out_offset, &rgba32, 4); - out_offset += 4; - } + ASSERT(false); // Invalid CLUT format + return 0; + }; + + // Pixel processing function + auto process_pixel = [&](u32 x, u32 y, u32 addr, int clut_psm) -> u32 { + u8 value = *(u8*)(m_vram.data() + addr); + u32 clut_addr = calculate_clut_address(value, clut_psm); + if (clut_psm == int(CPSM::PSMCT32)) { + return *(u32*)(m_vram.data() + clut_addr); + } else if (clut_psm == int(CPSM::PSMCT16)) { + return rgba16_to_rgba32(*(u16*)(m_vram.data() + clut_addr)); } - } else if (psm == int(PSM::PSMT4) && clut_psm == int(CPSM::PSMCT32)) { - // width is like the TEX0 register, in 64 texel units. - // not sure what the other widths are yet. - int read_width = 64 * goal_tex_width; - - // loop over pixels in output texture image - for (u32 y = 0; y < h; y++) { - for (u32 x = 0; x < w; x++) { - // read as the PSMT4 type, use half byte addressing + ASSERT(false); + return 0; + }; + + // Main loop + for (u32 y = 0; y < h; y++) { + for (u32 x = 0; x < w; x++) { + u32 addr = 0; + if (psm == int(PSM::PSMT8)) { + addr = psmt8_addr(x, y, read_width) + vram_addr * 256; + } else if (psm == int(PSM::PSMT4)) { auto addr4 = psmt4_addr_half_byte(x, y, read_width) + vram_addr * 512; - - // read (half bytes) u8 value = *(u8*)(m_vram.data() + addr4 / 2); - if (addr4 & 1) { - value >>= 4; - } else { - value = value & 0x0f; - } - - // there's yet another scramble from the CLUT. The palette index turns into an X, Y value - // See GS manual 2.7.3 CLUT Storage Mode, IDTEX4 in CSM1 mode. - - u8 clx = value & 0x7; - u8 cly = value >> 3; - - // the x, y CLUT value is looked up in PSMCT16 mode - u32 clut_addr = psmct32_addr(clx, cly, 64) + clut_vram_addr * 256; - u32 clut_value = *(u32*)(m_vram.data() + clut_addr); - // fmt::print("{} {}\n", value, clut_value); - memcpy(result + out_offset, &clut_value, 4); + value = (addr4 & 1) ? (value >> 4) : (value & 0x0F); + addr = value; + } else if (psm == int(PSM::PSMCT16) && clut_psm == 0) { + addr = psmct16_addr(x, y, read_width) + vram_addr * 256; + u16 value = *(u16*)(m_vram.data() + addr); + u32 rgba32 = rgba16_to_rgba32(value); + memcpy(result + out_offset, &rgba32, 4); out_offset += 4; + continue; } - } - } else if (psm == int(PSM::PSMCT16) && clut_psm == 0) { - // plain 16-bit texture - // not a clut. - // will store output pixels, rgba (8888) - - // width is like the TEX0 register, in 64 texel units. - // not sure what the other widths are yet. - int read_width = 64 * goal_tex_width; - // loop over pixels in output texture image - for (u32 y = 0; y < h; y++) { - for (u32 x = 0; x < w; x++) { - // read as the PSMT8 type. The dest field tells us a block offset. - auto addr8 = psmct16_addr(x, y, read_width) + vram_addr * 256; - u16 value = *(u16*)(m_vram.data() + addr8); - u32 val32 = rgba16_to_rgba32(value); - memcpy(result + out_offset, &val32, 4); - out_offset += 4; - } + // Process pixel through CLUT + u32 rgba_value = process_pixel(x, y, addr, clut_psm); + memcpy(result + out_offset, &rgba_value, 4); + out_offset += 4; } } - else { - ASSERT(false); - } - ASSERT(out_offset == expected_size_bytes); } diff --git a/game/graphics/texture/TextureConverter.h b/game/graphics/texture/TextureConverter.h index d81ceaa4882..43fabaf1f26 100644 --- a/game/graphics/texture/TextureConverter.h +++ b/game/graphics/texture/TextureConverter.h @@ -7,8 +7,40 @@ class TextureConverter { public: TextureConverter(); - void upload(const u8* data, u32 dest, u32 size_vram_words); - void upload_width(const u8* data, u32 dest, u32 width, u32 height); + + /** + * @brief Copies PS2 texture data into simulated VRAM. + * + * Copies texture data from the provided source pointer (`data`) into the simulated + * PlayStation 2 VRAM (`m_vram`). The texture size can be defined using either + * `width` and `height` or `size_vram_words`. + * + * @param data Pointer to the source texture data. + * @param dest Destination address in the simulated VRAM. + * @param width (Optional) Texture width in texels. Used if `size_vram_words` is not provided. + * @param height (Optional) Texture height in texels. Used if `size_vram_words` is not provided. + * @param size_vram_words (Optional) Texture size in VRAM words. Assumes a width of 128 texels. + */ + void upload(const u8* data, u32 dest, u32 width = 0, u32 height = 0, u32 size_vram_words = 0); + + /** + * @brief Converts PS2 texture data from simulated VRAM into RGBA8888 format. + * + * This function processes PS2 texture data stored in simulated VRAM and converts it + * into the RGBA8888 format, storing the result in the provided buffer (`result`). + * It supports multiple PS2 formats, including PSMT8, PSMT4, and PSMCT16, and handles + * CLUT-based textures by accessing the appropriate color lookup table (CLUT). + * + * @param result Pointer to the output buffer where RGBA8888 data will be stored. + * @param vram_addr Starting address in the simulated VRAM for the texture data. + * @param goal_tex_width Width of the texture in 64-texel units, as defined by PS2 format. + * @param w Width of the texture in texels. + * @param h Height of the texture in texels. + * @param psm PS2 pixel storage format (e.g., PSMT8, PSMT4, PSMCT16). + * @param clut_psm PS2 CLUT format (e.g., PSMCT32, PSMCT16). + * @param clut_vram_addr Address of the CLUT in simulated VRAM, if applicable. + * @param expected_size_bytes Expected size of the output buffer in bytes for validation. + */ void download_rgba8888(u8* result, u32 vram_addr, u32 goal_tex_width, @@ -19,6 +51,7 @@ class TextureConverter { u32 clut_vram_addr, u32 expected_size_bytes); + private: std::vector m_vram; }; From 59bf13660adb9a906e3fc7a49963d328b8dff564 Mon Sep 17 00:00:00 2001 From: Navaljit Ghotra Date: Mon, 20 Jan 2025 01:59:28 +0100 Subject: [PATCH 2/2] Fix for Code --- game/graphics/texture/TextureConverter.cpp | 86 ++++++++++++---------- 1 file changed, 46 insertions(+), 40 deletions(-) diff --git a/game/graphics/texture/TextureConverter.cpp b/game/graphics/texture/TextureConverter.cpp index 50a767c9f13..4152691b0a8 100644 --- a/game/graphics/texture/TextureConverter.cpp +++ b/game/graphics/texture/TextureConverter.cpp @@ -6,6 +6,8 @@ #include "fmt/core.h" +//TODO: IS THIS CLASS ANYWHERE USED? + TextureConverter::TextureConverter() { m_vram.resize(4 * 1024 * 1024); } @@ -41,63 +43,67 @@ void TextureConverter::download_rgba8888(u8* result, u32 clut_vram_addr, u32 expected_size_bytes) { u32 out_offset = 0; - int read_width = 64 * goal_tex_width; - - // Helper for CLUT addressing - auto calculate_clut_address = [&](u8 value, int clut_psm) -> u32 { - u32 clut_chunk = value / 16; - u32 off_in_chunk = value % 16; - u8 clx = (clut_chunk & 1) ? 8 : 0; - u8 cly = (clut_chunk >> 1) * 2; - if (off_in_chunk >= 8) { - off_in_chunk -= 8; - cly++; - } - clx += off_in_chunk; - if (clut_psm == int(CPSM::PSMCT32)) { - return psmct32_addr(clx, cly, 64) + clut_vram_addr * 256; - } else if (clut_psm == int(CPSM::PSMCT16)) { - return psmct16_addr(clx, cly, 64) + clut_vram_addr * 256; - } - ASSERT(false); // Invalid CLUT format - return 0; - }; + // width is like the TEX0 register, in 64 texel units. + // not sure what the other widths are yet. + u32 read_width = 64 * goal_tex_width; - // Pixel processing function - auto process_pixel = [&](u32 x, u32 y, u32 addr, int clut_psm) -> u32 { - u8 value = *(u8*)(m_vram.data() + addr); - u32 clut_addr = calculate_clut_address(value, clut_psm); + // Looks for CLUT Value for given PSMCT Mode + auto lookup_CLUT = [&](u8 clx, u8 cly, u32 clut_psm) -> u32 { if (clut_psm == int(CPSM::PSMCT32)) { + u32 clut_addr = psmct32_addr(clx, cly, 64) + clut_vram_addr * 256; return *(u32*)(m_vram.data() + clut_addr); } else if (clut_psm == int(CPSM::PSMCT16)) { + u32 clut_addr = psmct16_addr(clx, cly, 64) + clut_vram_addr * 256; return rgba16_to_rgba32(*(u16*)(m_vram.data() + clut_addr)); } - ASSERT(false); + ASSERT(false, "Wrong Clut_PSM given!"); return 0; }; - // Main loop + // Main Code: loop over pixels in output texture image for (u32 y = 0; y < h; y++) { for (u32 x = 0; x < w; x++) { - u32 addr = 0; + u32 rgba_value = 0; + if (psm == int(PSM::PSMT8)) { - addr = psmt8_addr(x, y, read_width) + vram_addr * 256; + u32 addr = psmt8_addr(x, y, read_width) + vram_addr * 256; + u8 value = *(u8*)(m_vram.data() + addr); + + // there's yet another scramble from the CLUT. The palette index turns into an X, Y value + // See GS manual 2.7.3 CLUT Storage Mode, IDTEX8 in CSM1 mode. + u32 clut_chunk = value / 16; + u32 off_in_chunk = value % 16; + u8 clx = (clut_chunk & 1) ? 8 : 0; + u8 cly = (clut_chunk >> 1) * 2; + if (off_in_chunk >= 8) { + off_in_chunk -= 8; + cly++; + } + clx += off_in_chunk; + + rgba_value = lookup_CLUT(clx, cly, clut_psm); } else if (psm == int(PSM::PSMT4)) { - auto addr4 = psmt4_addr_half_byte(x, y, read_width) + vram_addr * 512; - u8 value = *(u8*)(m_vram.data() + addr4 / 2); - value = (addr4 & 1) ? (value >> 4) : (value & 0x0F); - addr = value; + u32 addr = psmt4_addr_half_byte(x, y, read_width) + vram_addr * 512; + u8 value = *(u8*)(m_vram.data() + addr / 2); + value = (addr & 1) ? (value >> 4) : (value & 0x0F); + + // there's yet another scramble from the CLUT. The palette index turns into an X, Y value + // See GS manual 2.7.3 CLUT Storage Mode, IDTEX4 in CSM1 mode. + u8 clx = value & 0x7; + u8 cly = value >> 3; + + rgba_value = lookup_CLUT(clx, cly, clut_psm); } else if (psm == int(PSM::PSMCT16) && clut_psm == 0) { - addr = psmct16_addr(x, y, read_width) + vram_addr * 256; + // plain 16-bit texture + // not a clut. + // will store output pixels, rgba (8888) + u32 addr = psmct16_addr(x, y, read_width) + vram_addr * 256; u16 value = *(u16*)(m_vram.data() + addr); - u32 rgba32 = rgba16_to_rgba32(value); - memcpy(result + out_offset, &rgba32, 4); - out_offset += 4; - continue; + rgba_value = rgba16_to_rgba32(value); + } else { + ASSERT(false); } - // Process pixel through CLUT - u32 rgba_value = process_pixel(x, y, addr, clut_psm); memcpy(result + out_offset, &rgba_value, 4); out_offset += 4; }