Skip to content

Commit

Permalink
Add placeholder surfaces to streamline block recording with lookup ad…
Browse files Browse the repository at this point in the history
…dresses
  • Loading branch information
rasky committed Aug 13, 2023
1 parent e3b756e commit 4d6377c
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 18 deletions.
19 changes: 12 additions & 7 deletions include/rdpq.h
Original file line number Diff line number Diff line change
Expand Up @@ -1279,31 +1279,36 @@ inline void rdpq_set_texture_image_raw(uint8_t index, uint32_t offset, tex_forma
* an index into the lookup table. The caller of the block will then store
* the actual buffer pointers in the table, before playing back the block.
*
* The rdpq functions that can optionally load an address from the table are
* #rdpq_set_color_image_raw, #rdpq_set_z_image_raw and #rdpq_set_texture_image_raw.
* While recording, you can create a placeholder surface via #surface_make_placeholder or
* #surface_make_placeholder_linear that is just an "index" into the lookup
* table.
*
* @code{.c}
* // Create placeholder surfaces with indices 3 and 4
* surface_t tex1 = surface_make_placeholder_linear(3, FMT_RGBA16, 32, 32);
* surface_t tex2 = surface_make_placeholder_linear(4, FMT_RGBA16, 32, 32);
*
* // Start recording a block.
* rspq_block_begin();
* rdpq_set_mode_standard();
*
* // Load texture from lookup table (slot 3) and draw it to the screen
* rdpq_set_texture_image_raw(3, 0, FMT_RGBA16, 32, 32, 32*2);
* rdpq_set_texture_image(&tex1);
* rdpq_load_tile(0, 0, 32, 32);
* rdpq_texture_rectangle(0, 0, 32, 32);
*
* // Load texture from lookup table (slot 4) and draw it to the screen
* rdpq_set_texture_image_raw(3, 0, FMT_RGBA16, 32, 32, 32*2);
* rdpq_set_texture_image(&tex2);
* rdpq_load_tile(0, 0, 32, 32);
* rdpq_texture_rectangle(32, 0, 64, 32);
*
* rspq_block_t *bl = rspq_block_end();
*
* [...]
*
* // Set two textures into the the lookup table and call the block
* rdpq_set_lookup_address(3, tex1.buffer);
* rdpq_set_lookup_address(4, tex2.buffer);
* // Set two real textures into the the lookup table and call the block
* rdpq_set_lookup_address(3, robot->buffer);
* rdpq_set_lookup_address(4, dragon->buffer);
* rspq_block_run(bl);
* @endcode
*
Expand Down
72 changes: 70 additions & 2 deletions include/surface.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,9 @@ typedef enum {
/** @brief Return the name of the texture format as a string (for debugging purposes) */
const char* tex_format_name(tex_format_t fmt);

#define SURFACE_FLAGS_TEXFORMAT 0x1F ///< Pixel format of the surface
#define SURFACE_FLAGS_OWNEDBUFFER 0x20 ///< Set if the buffer must be freed
#define SURFACE_FLAGS_TEXFORMAT 0x001F ///< Pixel format of the surface
#define SURFACE_FLAGS_OWNEDBUFFER 0x0020 ///< Set if the buffer must be freed
#define SURFACE_FLAGS_TEXINDEX 0x0F00 ///< Placeholder for rdpq lookup table

/**
* @brief A surface buffer for graphics
Expand Down Expand Up @@ -265,6 +266,73 @@ inline bool surface_has_owned_buffer(const surface_t *surface)
return surface->buffer != NULL && surface->flags & SURFACE_FLAGS_OWNEDBUFFER;
}

/**
* @brief Create a placeholder surface, that can be used during rdpq block recording.
*
* When recording a rspq block (via #rspq_block_begin / #rspq_block_end) it might
* be useful sometimes to issue draw commands that refer to a surface, but
* allowing the actual surface to change later at any time.
*
* See #rdpq_set_lookup_address for more information.
*
* @note A placeholder surface holds a NULL pointer to the actual bytes. Make sure
* not to use it anywhere else but with rdpq.
*
* @param index Index that will be used to lookup the surface at playback time
* @param format Pixel format
* @param width Width of the surface in pixels
* @param height Height of the surface in pixels
* @param stride Stride of the surface in bytes
* @return surface_t The initialized placeholder surface
*
* @see #surface_make_placeholder_linear
* @see #rdpq_set_lookup_address
*/
inline surface_t surface_make_placeholder(int index, tex_format_t format, uint32_t width, uint32_t height, uint32_t stride) {
return (surface_t){
.flags = format | (index << 8),
.width = width,
.height = height,
.stride = stride,
.buffer = NULL,
};
}

/**
* @brief Create a linear placeholder surface, that can be used during rdpq block recording.
*
* This function is similar to #surface_make_placeholder, but it creates
* a surface that is linearly mapped with no per-line padding or extraneous data.
* (so the stride is automatically deduced from the width).
*
* @param index Index that will be used to lookup the surface at playback time
* @param format Pixel format
* @param width Width of the surface in pixels
* @param height Height of the surface in pixels
* @return surface_t The initialized placeholder surface
*
* @see #surface_make_placeholder
*/
inline surface_t surface_make_placeholder_linear(int index, tex_format_t format, uint32_t width, uint32_t height) {
return surface_make_placeholder(index, format, width, height, TEX_FORMAT_PIX2BYTES(format, width));
}

/**
* @brief Returns the lookup index of a placeholder surface
*
* If ths surface is a placeholder, this function returns the associated lookup
* index that will be used to retrieve the actual surface at playback time.
* Otherwise, if it is a normal surface, this function will return 0.
*
* @param surface Placeholder surface
* @return int The lookup index of the placeholder surface, or 0 if it is a normal surface
*/
inline int surface_get_placeholder_index(const surface_t *surface)
{
return (surface->flags >> 8) & 0xF;
}


#ifdef __cplusplus
}
#endif
Expand Down
6 changes: 3 additions & 3 deletions include/yuv.h
Original file line number Diff line number Diff line change
Expand Up @@ -367,9 +367,9 @@ void yuv_blitter_free(yuv_blitter_t *blitter);
*
* For more information on how to use this function, see #rdpq_tex_blit.
*
* @param yp Pointer to the Y plane
* @param up Pointer to the U plane
* @param vp Pointer to the V plane
* @param yp Pointer to the Y plane (must be #FMT_I8)
* @param up Pointer to the U plane (must be #FMT_I8)
* @param vp Pointer to the V plane (must be #FMT_I8)
* @param x0 X coordinate where to blit the frame
* @param y0 Y coordinate where to blit the frame
* @param parms Optional blitting parameters (see #rdpq_blitparms_t)
Expand Down
8 changes: 4 additions & 4 deletions src/rdpq/rdpq_tex.c
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ static void texload_block_4bpp(tex_loader_t *tload, int s0, int t0, int s1, int
// * SET_TILE must be configured with tmem_pitch=0, as that is weirdly used as the number of
// texels to skip per line, which we don't need.
assertf(ROUND_UP(tload->tex->width, 2) % 4 == 0, "Internal Error: invalid width for LOAD_BLOCK (%d)", tload->tex->width);
rdpq_set_texture_image_raw(0, PhysicalAddr(tload->tex->buffer), FMT_RGBA16, tload->tex->width/4, tload->tex->height);
rdpq_set_texture_image_raw(surface_get_placeholder_index(tload->tex), PhysicalAddr(tload->tex->buffer), FMT_RGBA16, tload->tex->width/4, tload->tex->height);
rdpq_set_tile(tile_internal, FMT_RGBA16, tload->tmem_addr, 0, NULL);
rdpq_set_tile(tload->tile, surface_get_format(tload->tex), tload->tmem_addr, tload->rect.tmem_pitch, &(tload->tileparms));
tload->load_mode = TEX_LOAD_BLOCK;
Expand All @@ -225,7 +225,7 @@ static void texload_block_8bpp(tex_loader_t *tload, int s0, int t0, int s1, int
// Use LOAD_BLOCK if we are uploading a full texture. Notice the weirdness of LOAD_BLOCK:
// * SET_TILE must be configured with tmem_pitch=0, as that is weirdly used as the number of
// texels to skip per line, which we don't need.
rdpq_set_texture_image_raw(0, PhysicalAddr(tload->tex->buffer), FMT_RGBA16, tload->tex->width/2, tload->tex->height);
rdpq_set_texture_image_raw(surface_get_placeholder_index(tload->tex), PhysicalAddr(tload->tex->buffer), FMT_RGBA16, tload->tex->width/2, tload->tex->height);
rdpq_set_tile(tile_internal, FMT_RGBA16, tload->tmem_addr, 0, NULL);
rdpq_set_tile(tload->tile, fmt, tload->tmem_addr, tload->rect.tmem_pitch, &(tload->tileparms));
tload->load_mode = TEX_LOAD_BLOCK;
Expand All @@ -248,7 +248,7 @@ static void texload_block(tex_loader_t *tload, int s0, int t0, int s1, int t1)
// Use LOAD_BLOCK if we are uploading a full texture. Notice the weirdness of LOAD_BLOCK:
// * SET_TILE must be configured with tmem_pitch=0, as that is weirdly used as the number of
// texels to skip per line, which we don't need.
rdpq_set_texture_image_raw(0, PhysicalAddr(tload->tex->buffer), fmt, tload->tex->width, tload->tex->height);
rdpq_set_texture_image_raw(surface_get_placeholder_index(tload->tex), PhysicalAddr(tload->tex->buffer), fmt, tload->tex->width, tload->tex->height);
rdpq_set_tile(tile_internal, fmt, tload->tmem_addr, 0, NULL);
rdpq_set_tile(tload->tile, fmt, tload->tmem_addr, tload->rect.tmem_pitch, &(tload->tileparms));
tload->load_mode = TEX_LOAD_BLOCK;
Expand All @@ -267,7 +267,7 @@ static void texload_tile_4bpp(tex_loader_t *tload, int s0, int t0, int s1, int t
{
rdpq_tile_t tile_internal = (tload->tile + 1) & 7;
if (tload->load_mode != TEX_LOAD_TILE) {
rdpq_set_texture_image_raw(0, PhysicalAddr(tload->tex->buffer), FMT_I8, tload->tex->stride, tload->tex->height);
rdpq_set_texture_image_raw(surface_get_placeholder_index(tload->tex), PhysicalAddr(tload->tex->buffer), FMT_I8, tload->tex->stride, tload->tex->height);
rdpq_set_tile(tile_internal, FMT_I8, tload->tmem_addr, tload->rect.tmem_pitch, NULL);
rdpq_set_tile(tload->tile, surface_get_format(tload->tex), tload->tmem_addr, tload->rect.tmem_pitch, &(tload->tileparms));
tload->load_mode = TEX_LOAD_TILE;
Expand Down
7 changes: 5 additions & 2 deletions src/video/yuv.c
Original file line number Diff line number Diff line change
Expand Up @@ -318,18 +318,21 @@ static void yuv_tex_blit_run(int width, int height, float x0, float y0,
// 2048.
rdpq_set_tile(TILE6, FMT_I8, 2048, 0, NULL);

surface_t yp = surface_make_placeholder_linear(1, FMT_I8, width, height);
surface_t uvp = surface_make_placeholder_linear(2, FMT_IA16, width/2, height/2);

void ltd_yuv2(rdpq_tile_t tile, const surface_t *_, int s0, int t0, int s1, int t1,
void (*draw_cb)(rdpq_tile_t tile, int s0, int t0, int s1, int t1), bool filtering)
{
for (int y=t0; y<t1; y+=2) {
// Load two Y lines with a single LOAD_BLOCK, from the surface configured
// in lookup block 1. Notice that we will not byteswap the second line.
rdpq_set_texture_image_raw(1, 0, FMT_I8, width, height);
rdpq_set_texture_image(&yp);
rdpq_load_block_fx(TILE6, 0, y, width*2, 0);

// Load one UV line two times, with two LOAD_BLOCK commands, from the
// surface configured in lookup block 2. subsequent offsets in TMEM.
rdpq_set_texture_image_raw(2, 0, FMT_IA16, width/2, height/2);
rdpq_set_texture_image(&uvp);
rdpq_load_block_fx(TILE4, 0, y/2, width, 0);
rdpq_load_block_fx(TILE5, 0, y/2, width, 0);

Expand Down

0 comments on commit 4d6377c

Please sign in to comment.