Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions game/graphics/opengl_renderer/TextureAnimator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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();
Expand Down
208 changes: 55 additions & 153 deletions game/graphics/texture/TextureConverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,28 @@

#include "fmt/core.h"

//TODO: IS THIS CLASS ANYWHERE USED?

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));
}
}
Expand All @@ -45,167 +43,71 @@ 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;
}
// 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;

// 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, "Wrong Clut_PSM given!");
return 0;
};

} 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;
// Main Code: loop over pixels in output texture image
for (u32 y = 0; y < h; y++) {
for (u32 x = 0; x < w; x++) {
u32 rgba_value = 0;

// 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);
if (psm == int(PSM::PSMT8)) {
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 = 0, cly = 0;
if (clut_chunk & 1) {
clx = 8;
}
cly = (clut_chunk >> 1) * 2;
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;

// 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;
}
}

} 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;
}
rgba_value = lookup_CLUT(clx, cly, clut_psm);
} else if (psm == int(PSM::PSMT4)) {
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;

// 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;
rgba_value = lookup_CLUT(clx, cly, clut_psm);
} else if (psm == int(PSM::PSMCT16) && clut_psm == 0) {
// 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);
rgba_value = rgba16_to_rgba32(value);
} else {
ASSERT(false);
}
}
} 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
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);
out_offset += 4;
}
}
} 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;
}
memcpy(result + out_offset, &rgba_value, 4);
out_offset += 4;
}
}

else {
ASSERT(false);
}

ASSERT(out_offset == expected_size_bytes);
}
37 changes: 35 additions & 2 deletions game/graphics/texture/TextureConverter.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer to use default arguments only when the defaults are valid, and prefer multiple implementations over tricky use of defaults.

For example, converter.upload(data, dest); will fail the validation, but looks like a valid call.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the test-case with converter.upload(data,dest) is validated by:
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.");
}


/**
* @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,
Expand All @@ -19,6 +51,7 @@ class TextureConverter {
u32 clut_vram_addr,
u32 expected_size_bytes);


private:
std::vector<u8> m_vram;
};