Skip to content

Commit

Permalink
rdpq_sprite: support SHQ in rdpq_sprite_upload
Browse files Browse the repository at this point in the history
  • Loading branch information
rasky committed Oct 4, 2024
1 parent cb92851 commit a2e24ae
Show file tree
Hide file tree
Showing 10 changed files with 140 additions and 14 deletions.
11 changes: 7 additions & 4 deletions include/rdpq_macros.h
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,7 @@ typedef uint32_t rdpq_blender_t;
///@{
#define SOMX_NUMLODS_MASK ((cast64(7))<<59) ///< Rdpq extension: number of LODs
#define SOMX_NUMLODS_SHIFT 59 ///< Rdpq extension: number of LODs shift
#define SOMX_FOG ((cast64(1))<<58) ///< RDPQ special state: fogging is enabled

#define SOM_ATOMIC_PRIM ((cast64(1))<<55) ///< Atomic: serialize command execution

Expand Down Expand Up @@ -563,10 +564,12 @@ typedef uint32_t rdpq_blender_t;
#define SOM_ALPHADITHER_MASK ((cast64(3))<<36) ///< Alpha Dithering mask
#define SOM_ALPHADITHER_SHIFT 36 ///< Alpha Dithering mask shift

#define SOMX_FOG ((cast64(1))<<32) ///< RDPQ special state: fogging is enabled
#define SOMX_UPDATE_FREEZE ((cast64(1))<<33) ///< RDPQ special state: render mode update is frozen (see #rdpq_mode_begin)
#define SOMX_AA_REDUCED ((cast64(1))<<34) ///< RDPQ special state: reduced antialiasing is enabled
#define SOMX_LOD_INTERPOLATE ((cast64(1))<<35) ///< RDPQ special state: mimap interpolation (aka trilinear) requested
#define SOMX_LOD_INTERPOLATE ((cast64(1))<<32) ///< RDPQ special state: mimap interpolation (aka trilinear) requested
#define SOMX_LOD_INTERPOLATE_SHQ ((cast64(1))<<33) ///< RDPQ special state: mimap interpolation for SHC texture format
#define SOMX_LOD_INTERP_MASK ((cast64(3))<<32) ///< RDPQ special state: mask for LOD interpolation formulas
#define SOMX_LOD_INTERP_SHIFT 32 ///< RDPQ special state: shift for LOD interpolation formulas
#define SOMX_AA_REDUCED ((cast64(1))<<34) ///< RDPQ special state: reduced antialiasing is enabled
#define SOMX_UPDATE_FREEZE ((cast64(1))<<35) ///< RDPQ special state: render mode update is frozen (see #rdpq_mode_begin)

#define SOM_BLEND0_MASK (cast64(0xCCCC0000) | SOM_BLENDING | SOM_READ_ENABLE | SOMX_BLEND_2PASS) ///< Blender: mask of settings related to pass 0
#define SOM_BLEND1_MASK (cast64(0x33330000) | SOM_BLENDING | SOM_READ_ENABLE | SOMX_BLEND_2PASS) ///< Blender: mask of settings related to pass 1
Expand Down
1 change: 1 addition & 0 deletions include/rdpq_mode.h
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ typedef enum rdpq_mipmap_s {
MIPMAP_INTERPOLATE = (SOM_TEXTURE_LOD | SOMX_LOD_INTERPOLATE) >> 32, ///< Interpolate between the two nearest mipmap levels (also known as "trilinear")
MIPMAP_INTERPOLATE_SHARPEN = (SOM_TEXTURE_LOD | SOMX_LOD_INTERPOLATE | SOM_TEXTURE_SHARPEN) >> 32, ///< Interpolate between the two nearest mipmap levels (also known as "trilinear") with sharpening enabled
MIPMAP_INTERPOLATE_DETAIL = (SOM_TEXTURE_LOD | SOMX_LOD_INTERPOLATE | SOM_TEXTURE_DETAIL) >> 32, ///< Interpolate between the two nearest mipmap levels (also known as "trilinear") with detail texture enabled
MIPMAP_INTERPOLATE_SHQ = (SOM_TEXTURE_LOD | SOMX_LOD_INTERPOLATE_SHQ) >> 32, ///< Special mipmap mode that must be used for SHC textures
} rdpq_mipmap_t;

/**
Expand Down
21 changes: 13 additions & 8 deletions include/rsp_rdpq.inc
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,8 @@ AA_BLEND_DEFAULT_FORMULA:
.word RDPQ_BLENDER((IN_RGB, IN_ALPHA, MEMORY_RGB, MEMORY_CVG)) # Standard AA
.word RDPQ_BLENDER((IN_RGB, IN_ALPHA, MEMORY_RGB, MEMORY_CVG)) & ~SOM_READ_ENABLE # Reduced AA

#define RDPQ_COMB_MIPMAP2 RDPQ_COMBINER2((TEX1, TEX0, LOD_FRAC, TEX0), (TEX1, TEX0, LOD_FRAC, TEX0), (0,0,0,0), (0,0,0,0))
#define RDPQ_COMB_LOD_INTERP RDPQ_COMBINER2((TEX1, TEX0, LOD_FRAC, TEX0), (TEX1, TEX0, LOD_FRAC, TEX0), (0,0,0,0), (0,0,0,0))
#define RDPQ_COMB_LOD_SHQ RDPQ_COMBINER2((TEX1, TEX0, K5, 0 ), (0, 0, 0, TEX1), (0,0,0,0), (0,0,0,0))
#define RDPQ_COMB_SHADE_FOG RDPQ_COMBINER1((0,0,0,SHADE), (0,0,0,1))
#define RDPQ_COMB_TEX_SHADE_FOG RDPQ_COMBINER1((TEX0,0,SHADE,0), (0,0,0,TEX0))

Expand All @@ -228,7 +229,9 @@ COMBINER_SHADE_FOG: .quad RDPQ_COMB_SHADE_FOG
COMBINER_TEX_SHADE: .quad RDPQ_COMBINER_TEX_SHADE
COMBINER_TEX_SHADE_FOG: .quad RDPQ_COMB_TEX_SHADE_FOG

COMBINER_MIPMAP2: .quad (RDPQ_COMB_MIPMAP2 & RDPQ_COMB0_MASK) | RDPQ_COMBINER_2PASS
COMBINER_MIPMAPS:
.quad (RDPQ_COMB_LOD_INTERP & RDPQ_COMB0_MASK) | RDPQ_COMBINER_2PASS
.quad (RDPQ_COMB_LOD_SHQ & RDPQ_COMB0_MASK) | RDPQ_COMBINER_2PASS

.section .bss.rdpq_mode_api

Expand Down Expand Up @@ -351,7 +354,8 @@ RDPQ_UpdateRenderMode:

calc_comb_1cyc:
# Check if fogging is active
andi t0, som_hi, SOMX_FOG >> 32
li t1, SOMX_FOG >> 32
and t0, som_hi, t1
beqz t0, check_mipmap_interp

# Create a copy of comb_hi without the cmd ID in the top MSB.
Expand Down Expand Up @@ -383,10 +387,11 @@ fog_change:
lw comb_lo, 4(s0)

check_mipmap_interp:
and t0, som_hi, SOMX_LOD_INTERPOLATE >> 32
beqz t0, store_comb_1cyc
andi t2, som_hi, SOMX_LOD_INTERP_MASK >> 32
beqz t2, store_comb_1cyc
sll t2, 3 - (SOMX_LOD_INTERP_SHIFT - 32) # Prepare offset for the current mipmap interpolation mode

# Interpolated mipmapping is active. We want to add RDPQ_COMB_MIPMAP as step0
# Interpolated mipmapping is active. We want to add a special combiner step as step0
# and use only step 1 of the incoming formula. Unfortunately, this
# also means that all TEX0 slots must be converted into COMBINED slots.
# We do this by using the mask already loaded in a2/a3
Expand All @@ -397,8 +402,8 @@ check_mipmap_interp:
# Since this combiner now requires two-cycle mode, we can simply store in the
# 2-cycle mode slot. No need to touch the 1-cycle mode slot as it will not
# be used anyway.
lw t0, %lo(COMBINER_MIPMAP2) + 0
lw t1, %lo(COMBINER_MIPMAP2) + 4
lw t0, %lo(COMBINER_MIPMAPS) - 8(t2)
lw t1, %lo(COMBINER_MIPMAPS) - 4(t2)
or comb_hi, t0
j store_comb_2cyc
or comb_lo, t1
Expand Down
10 changes: 10 additions & 0 deletions include/sprite.h
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,16 @@ int sprite_get_lod_count(sprite_t *sprite);
*/
bool sprite_fits_tmem(sprite_t *sprite);

/**
* @brief Return true if the sprite is in SHQ format
*
* This is a special sprite made of two mipmaps (one I4 and one RGBA16)
* that must be displayed using subtractive blending.
*
* @param sprite The sprite to access
* @return True if the sprite is in SHQ format, false otherwise
*/
bool sprite_is_shq(sprite_t *sprite);

#ifdef __cplusplus
}
Expand Down
7 changes: 6 additions & 1 deletion src/rdpq/rdpq_sprite.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ int __rdpq_sprite_upload(rdpq_tile_t tile, sprite_t *sprite, const rdpq_texparms
sprite_detail_t detail; rdpq_texparms_t detailtexparms = {0};
surface_t detailsurf = sprite_get_detail_pixels(sprite, &detail, &detailtexparms);
bool use_detail = detailsurf.buffer != NULL;
bool is_shq = sprite_is_shq(sprite);

rdpq_tex_multi_begin();

Expand Down Expand Up @@ -112,7 +113,11 @@ int __rdpq_sprite_upload(rdpq_tile_t tile, sprite_t *sprite, const rdpq_texparms

if (__builtin_expect(set_mode, 1)) {
// Enable/disable mipmapping
if(use_detail) rdpq_mode_mipmap(MIPMAP_INTERPOLATE_DETAIL, num_mipmaps+1);
if(is_shq) {
rdpq_mode_mipmap(MIPMAP_INTERPOLATE_SHQ, num_mipmaps+1);
rdpq_set_yuv_parms(0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF);
}
else if(use_detail) rdpq_mode_mipmap(MIPMAP_INTERPOLATE_DETAIL, num_mipmaps+1);
else if (num_mipmaps) rdpq_mode_mipmap(MIPMAP_INTERPOLATE, num_mipmaps);
else rdpq_mode_mipmap(MIPMAP_NONE, 0);
}
Expand Down
8 changes: 8 additions & 0 deletions src/sprite.c
Original file line number Diff line number Diff line change
Expand Up @@ -224,4 +224,12 @@ bool sprite_fits_tmem(sprite_t *sprite)
return (sx->flags & SPRITE_FLAG_FITS_TMEM) != 0;
}

bool sprite_is_shq(sprite_t *sprite)
{
sprite_ext_t *sx = __sprite_ext(sprite);
if (!sx)
return false;
return (sx->flags & SPRITE_FLAG_SHQ) != 0;
}

extern inline tex_format_t sprite_get_format(sprite_t *sprite);
1 change: 1 addition & 0 deletions src/sprite_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#define SPRITE_FLAG_HAS_TEXPARMS 0x0008 ///< Sprite contains texture parameters
#define SPRITE_FLAG_HAS_DETAIL 0x0010 ///< Sprite contains detail texture
#define SPRITE_FLAG_FITS_TMEM 0x0020 ///< Set if the sprite does fit TMEM without splitting
#define SPRITE_FLAG_SHQ 0x0040 ///< Sprite is in special SHQ format (2 mipmap levels with subtractive blending)

/**
* @brief Internal structure used as additional sprite header
Expand Down
90 changes: 90 additions & 0 deletions tests/test_rdpq.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ uint64_t debug_rdp_stream_last_cc(void) {
return rdp_stream[rdp_stream_ctx.last_cc];
}

const char* debug_rdp_stream_last_cc_disasm(void) {
uint64_t cmds[1] = { debug_rdp_stream_last_cc() };
static char buf[256];
FILE *out = fmemopen(buf, sizeof(buf), "w");
rdpq_debug_disasm(cmds, out);
fclose(out);
return buf;
}

uint32_t debug_rdp_stream_count_cmd(uint32_t cmd_id) {
uint32_t count = 0;
for (int i=0;i<rdp_stream_ctx.idx;i++) {
Expand Down Expand Up @@ -1996,6 +2005,87 @@ void test_rdpq_mipmap(TestContext *ctx) {
}
}

void test_rdpq_mipmap_interpolate(TestContext *ctx) {
RDPQ_INIT();
debug_rdp_stream_init();

const int FBWIDTH = 16;
surface_t fb = surface_alloc(FMT_RGBA16, FBWIDTH, FBWIDTH);
DEFER(surface_free(&fb));
surface_clear(&fb, 0);

rdpq_attach(&fb, NULL);
rdpq_set_mode_standard();
rdpq_mode_combiner(RDPQ_COMBINER_TEX_SHADE);
rspq_wait();

ASSERT_EQUAL_HEX(debug_rdp_stream_last_som() &
(SOM_CYCLE_MASK | SOM_TEXTURE_LOD | SOMX_LOD_INTERP_MASK),
SOM_CYCLE_1 ,
"invalid SOM configuration: %08llx", debug_rdp_stream_last_som());
ASSERT_EQUAL_HEX(debug_rdp_stream_last_cc() & RDPQ_COMB1_MASK,
RDPQ_COMBINER1((TEX0, 0, SHADE, 0), (TEX0, 0, SHADE, 0)) & RDPQ_COMB1_MASK,
"invalid combiner configuration:\n%s", debug_rdp_stream_last_cc_disasm());

rdpq_mode_mipmap(MIPMAP_NEAREST, 4);
rspq_wait();

#define CC_PASSTHROUGH1 (COMBINED,COMBINED,COMBINED,COMBINED)
#define CC_PASSTHROUGH2 (COMBINED,COMBINED,LOD_FRAC,COMBINED)

ASSERT_EQUAL_HEX(debug_rdp_stream_last_som() &
(SOM_CYCLE_MASK | SOM_TEXTURE_LOD | SOMX_LOD_INTERP_MASK),
SOM_CYCLE_2 | SOM_TEXTURE_LOD ,
"invalid SOM configuration: %08llx", debug_rdp_stream_last_som());
ASSERT_EQUAL_HEX(debug_rdp_stream_last_cc() & (RDPQ_COMB0_MASK|RDPQ_COMB1_MASK),
RDPQ_COMBINER2((TEX0,0,SHADE,0), (TEX0,0,SHADE,0), CC_PASSTHROUGH1, CC_PASSTHROUGH2) & (RDPQ_COMB0_MASK|RDPQ_COMB1_MASK),
"invalid combiner configuration:\n%s", debug_rdp_stream_last_cc_disasm());

rdpq_mode_mipmap(MIPMAP_INTERPOLATE, 4);
rspq_wait();

ASSERT_EQUAL_HEX(debug_rdp_stream_last_som() &
(SOM_CYCLE_MASK | SOM_TEXTURE_LOD | SOMX_LOD_INTERP_MASK),
SOM_CYCLE_2 | SOM_TEXTURE_LOD | SOMX_LOD_INTERPOLATE ,
"invalid SOM configuration: %08llx", debug_rdp_stream_last_som());
ASSERT_EQUAL_HEX(debug_rdp_stream_last_cc() & (RDPQ_COMB0_MASK|RDPQ_COMB1_MASK),
RDPQ_COMBINER2((TEX1,TEX0,LOD_FRAC,TEX0), (TEX1,TEX0,LOD_FRAC,TEX0), (COMBINED,0,SHADE,0),(COMBINED,0,SHADE,0)) & (RDPQ_COMB0_MASK|RDPQ_COMB1_MASK),
"invalid combiner configuration:\n%s", debug_rdp_stream_last_cc_disasm());

rdpq_mode_combiner(RDPQ_COMBINER_TEX_FLAT);
rspq_wait();

ASSERT_EQUAL_HEX(debug_rdp_stream_last_som() &
(SOM_CYCLE_MASK | SOM_TEXTURE_LOD | SOMX_LOD_INTERP_MASK),
SOM_CYCLE_2 | SOM_TEXTURE_LOD | SOMX_LOD_INTERPOLATE ,
"invalid SOM configuration: %08llx", debug_rdp_stream_last_som());
ASSERT_EQUAL_HEX(debug_rdp_stream_last_cc() & (RDPQ_COMB0_MASK|RDPQ_COMB1_MASK),
RDPQ_COMBINER2((TEX1,TEX0,LOD_FRAC,TEX0), (TEX1,TEX0,LOD_FRAC,TEX0), (COMBINED,0,PRIM,0),(COMBINED,0,PRIM,0)) & (RDPQ_COMB0_MASK|RDPQ_COMB1_MASK),
"invalid combiner configuration:\n%s", debug_rdp_stream_last_cc_disasm());

rdpq_mode_mipmap(MIPMAP_INTERPOLATE_SHQ, 4);
rspq_wait();

ASSERT_EQUAL_HEX(debug_rdp_stream_last_som() &
(SOM_CYCLE_MASK | SOM_TEXTURE_LOD | SOMX_LOD_INTERP_MASK),
SOM_CYCLE_2 | SOM_TEXTURE_LOD | SOMX_LOD_INTERPOLATE_SHQ,
"invalid SOM configuration: %08llx", debug_rdp_stream_last_som());
ASSERT_EQUAL_HEX(debug_rdp_stream_last_cc() & (RDPQ_COMB0_MASK|RDPQ_COMB1_MASK),
RDPQ_COMBINER2((TEX1,TEX0,K5,0), (0,0,0,TEX1), (COMBINED,0,PRIM,0),(COMBINED,0,PRIM,0)) & (RDPQ_COMB0_MASK|RDPQ_COMB1_MASK),
"invalid combiner configuration:\n%s", debug_rdp_stream_last_cc_disasm());

rdpq_mode_mipmap(MIPMAP_NONE, 4);
rspq_wait();

ASSERT_EQUAL_HEX(debug_rdp_stream_last_som() &
(SOM_CYCLE_MASK | SOM_TEXTURE_LOD | SOMX_LOD_INTERP_MASK),
SOM_CYCLE_1 ,
"invalid SOM configuration: %08llx", debug_rdp_stream_last_som());
ASSERT_EQUAL_HEX(debug_rdp_stream_last_cc() & (RDPQ_COMB0_MASK),
RDPQ_COMBINER1((TEX0,0,PRIM,0),(TEX0,0,PRIM,0)) & (RDPQ_COMB0_MASK),
"invalid combiner configuration:\n%s", debug_rdp_stream_last_cc_disasm());
}

void test_rdpq_autotmem(TestContext *ctx) {
RDPQ_INIT();
debug_rdp_stream_init();
Expand Down
1 change: 1 addition & 0 deletions tests/testrom.c
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,7 @@ static const struct Testsuite
TEST_FUNC(test_rdpq_mode_freeze, 0, TEST_FLAGS_NO_BENCHMARK),
TEST_FUNC(test_rdpq_mode_freeze_stack, 0, TEST_FLAGS_NO_BENCHMARK),
TEST_FUNC(test_rdpq_mipmap, 0, TEST_FLAGS_NO_BENCHMARK),
TEST_FUNC(test_rdpq_mipmap_interpolate, 0, TEST_FLAGS_NO_BENCHMARK),
TEST_FUNC(test_rdpq_autotmem, 0, TEST_FLAGS_NO_BENCHMARK),
TEST_FUNC(test_rdpq_autotmem_reuse, 0, TEST_FLAGS_NO_BENCHMARK),
TEST_FUNC(test_rdpq_texrect_passthrough, 0, TEST_FLAGS_NO_BENCHMARK),
Expand Down
4 changes: 3 additions & 1 deletion tools/mksprite/mksprite.c
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ typedef struct {
int vslices; // Number of vertical slices (deprecated API for old rdp.c)
int hslices; // Number of horizontal slices (deprecated API for old rdp.c)
texparms_t texparms; // Texture parameters
int out_flags; // Flags to store into output
struct{
const char *infn; // Input file for detail texture
texparms_t texparms; // Texture parameters for the detail
Expand Down Expand Up @@ -1366,7 +1367,7 @@ bool spritemaker_write(spritemaker_t *spr) {
w16(out, spr->images[i].height);
w_lodpos[i-1] = w32_placeholder(out); // placeholder for position of LOD
}
uint16_t flags = 0;
uint16_t flags = spr->out_flags;
assert(numlods <= 7); // 3 bits
flags |= numlods;
if (spr->texparms.defined) flags |= 0x8;
Expand Down Expand Up @@ -1530,6 +1531,7 @@ int convert(const char *infn, const char *outfn, const parms_t *pm) {
// Compute mipmaps for IHQ
mipmap_algo = MIPMAP_ALGO_BOX;
} else if (spr.images[0].fmt == FMT_SHQ) {
spr.out_flags |= 0x40;
if (!spritemaker_convert_shq(&spr))
goto error;
// Compute mipmaps for IHQ
Expand Down

0 comments on commit a2e24ae

Please sign in to comment.