![]() |
libdragon
|
RDP Command queue. More...
Data Structures | |
struct | rdpq_state_t |
State of the rdpq ucode overlay (partial). More... | |
Functions | |
DEFINE_RSP_UCODE (rsp_rdpq,.assert_handler=rdpq_assert_handler) | |
The rdpq ucode overlay. | |
void | rdpq_init () |
Initialize the RDPQ library. More... | |
void | rdpq_close () |
Shutdown the RDPQ library. More... | |
uint32_t | rdpq_config_set (uint32_t cfg) |
Set the configuration of the RDPQ module. More... | |
uint32_t | rdpq_config_enable (uint32_t cfg) |
Enable a specific set of configuration flags. More... | |
uint32_t | rdpq_config_disable (uint32_t cfg) |
Disable a specific set of configuration flags. More... | |
void | rdpq_fence (void) |
Public rdpq_fence API, redefined it. More... | |
void | rdpq_exec (void *buffer, int size) |
Send to the RDP a buffer of RDP commands from RDRAM. More... | |
void | __rdpq_autosync_use (uint32_t res) |
Autosync engine: mark certain resources as in use. | |
void | __rdpq_autosync_change (uint32_t res) |
Autosync engine: mark certain resources as being changed. More... | |
void | rdpq_set_fill_color (color_t color) |
Enqueue a SET_FILL_COLOR RDP command. More... | |
void | rdpq_set_fill_color_stripes (color_t color1, color_t color2) |
Enqueue a SET_FILL_COLOR RDP command to draw a striped pattern. More... | |
void | rdpq_set_fog_color (color_t color) |
Set the RDP FOG blender register. More... | |
void | rdpq_set_blend_color (color_t color) |
Set the RDP BLEND blender register. More... | |
void | rdpq_set_prim_color (color_t color) |
Set the RDP PRIM combiner register (color only) (RDP command: SET_PRIM_COLOR) More... | |
void | rdpq_set_detail_factor (float value) |
Set the detail/sharpen blending factor (RDP command: SET_PRIM_COLOR (partial)) More... | |
void | rdpq_set_prim_lod_frac (uint8_t value) |
Set the RDP PRIM LOD FRAC combiner register (RDP command: SET_PRIM_COLOR (partial)) More... | |
void | rdpq_set_prim_register_raw (color_t color, uint8_t minlod, uint8_t primlod) |
Set the RDP PRIM combiner register (raw version) (RDP command: SET_PRIM_COLOR) More... | |
void | rdpq_set_env_color (color_t color) |
Set the RDP ENV combiner register (RDP command: SET_ENV_COLOR) More... | |
void | rdpq_set_prim_depth_raw (uint16_t primitive_z, int16_t primitive_delta_z) |
Set a fixed Z value to be used instead of a per-pixel value (RDP command; SET_PRIM_DEPTH) More... | |
void | rdpq_load_tlut_raw (rdpq_tile_t tile, uint8_t lowidx, uint8_t highidx) |
Load a palette of colors into TMEM (RDP command: LOAD_TLUT) More... | |
void | rdpq_set_tile_size_fx (rdpq_tile_t tile, uint16_t s0, uint16_t t0, uint16_t s1, uint16_t t1) |
Configure the extents of a tile descriptor – fixed point version (RDP command: SET_TILE_SIZE) More... | |
void | rdpq_load_block (rdpq_tile_t tile, uint16_t s0, uint16_t t0, uint16_t num_texels, uint16_t tmem_pitch) |
Load a texture image into TMEM with a single contiguous memory transfer (RDP command: LOAD_BLOCK) More... | |
void | rdpq_load_block_fx (rdpq_tile_t tile, uint16_t s0, uint16_t t0, uint16_t num_texels, uint16_t dxt) |
Low level function to load a texture image into TMEM in a single memory transfer. | |
void | rdpq_load_tile_fx (rdpq_tile_t tile, uint16_t s0, uint16_t t0, uint16_t s1, uint16_t t1) |
Load a portion of a texture into TMEM – fixed point version (RDP command: LOAD_TILE) More... | |
void | rdpq_set_combiner_raw (uint64_t cc) |
Low-level function to change the RDP combiner. More... | |
void | rdpq_set_other_modes_raw (uint64_t mode) |
Low-level function to set the rendering mode register. More... | |
void | rdpq_change_other_modes_raw (uint64_t mask, uint64_t val) |
Low-level function to partly change the rendering mode register. More... | |
void | rdpq_set_color_image_raw (uint8_t index, uint32_t offset, tex_format_t format, uint32_t width, uint32_t height, uint32_t stride) |
Low-level version of rdpq_set_color_image, with address lookup capability. More... | |
void | rdpq_set_z_image_raw (uint8_t index, uint32_t offset) |
Low-level version of rdpq_set_z_image, with address lookup capability. More... | |
void | rdpq_set_texture_image_raw (uint8_t index, uint32_t offset, tex_format_t format, uint16_t width, uint16_t height) |
Low-level version of rdpq_set_texture_image, with address lookup capability. More... | |
void | rdpq_set_lookup_address (uint8_t index, void *rdram_addr) |
Store an address into the rdpq lookup table. More... | |
void | rdpq_set_tile (rdpq_tile_t tile, tex_format_t format, int32_t tmem_addr, uint16_t tmem_pitch, const rdpq_tileparms_t *parms) |
Enqueue a RDP SET_TILE command (full version) More... | |
RDP block management functions. | |
All the functions in this group are called in the context of creation of a RDP block (part of a rspq block). See the top-level documentation for a general overview of how RDP blocks work. | |
void | __rdpq_block_begin () |
Initialize RDP block mangament. More... | |
void | __rdpq_block_next_buffer (void) |
Allocate a new RDP block buffer, chaining it to the current one (if any) More... | |
rdpq_block_t * | __rdpq_block_end () |
Finish creation of a RDP block. More... | |
void | __rdpq_block_run (rdpq_block_t *block) |
Notify that a rspq block was run (called by rspq_block_run). | |
void | __rdpq_block_free (rdpq_block_t *block) |
Free a block. More... | |
void | __rdpq_block_reserve (int num_rdp_commands) |
Reserve space in the RDP static buffer for a number of RDP commands. More... | |
void | __rdpq_block_update (volatile uint32_t *wptr) |
Set a new RDP write pointer, and enqueue a RSP command to run the buffer until there. More... | |
Helpers to write generic RDP commands | |
All the functions in this group are wrappers around rdpq_passthrough_write to help generating RDP commands. They are called by inlined functions in rdpq.h. See the top-level documentation about inline functions to understand the reason of this split. | |
void | __rdpq_write8 (uint32_t cmd_id, uint32_t arg0, uint32_t arg1) |
Write a standard 8-byte RDP command. | |
void | __rdpq_write8_syncchange (uint32_t cmd_id, uint32_t arg0, uint32_t arg1, uint32_t autosync) |
Write a standard 8-byte RDP command, which changes some autosync resources | |
void | __rdpq_write8_syncuse (uint32_t cmd_id, uint32_t arg0, uint32_t arg1, uint32_t autosync) |
Write a standard 8-byte RDP command, which uses some autosync resources | |
void | __rdpq_write8_syncchangeuse (uint32_t cmd_id, uint32_t arg0, uint32_t arg1, uint32_t autosync_c, uint32_t autosync_u) |
Write a standard 8-byte RDP command, which changes some autosync resources and uses others. | |
void | __rdpq_write16 (uint32_t cmd_id, uint32_t arg0, uint32_t arg1, uint32_t arg2, uint32_t arg3) |
Write a standard 16-byte RDP command | |
void | __rdpq_write16_syncuse (uint32_t cmd_id, uint32_t arg0, uint32_t arg1, uint32_t arg2, uint32_t arg3, uint32_t autosync) |
Write a standard 16-byte RDP command, which uses some autosync resources | |
void | __rdpq_fixup_write8_syncchange (uint32_t cmd_id, uint32_t w0, uint32_t w1, uint32_t autosync) |
Write a 8-byte RDP command fixup. | |
RDP fixups out-of-line implementations | |
These are the out-of line implementations of RDP commands which needs specific logic, mostly because they are fixups. | |
void | __rdpq_set_scissor (uint32_t w0, uint32_t w1) |
Out-of-line implementation of rdpq_set_scissor. | |
void | __rdpq_set_fill_color (uint32_t w1) |
Out-of-line implementation of rdpq_set_fill_color. | |
void | __rdpq_set_color_image (uint32_t w0, uint32_t w1, uint32_t sw0, uint32_t sw1) |
Out-of-line implementation of rdpq_set_color_image. | |
void | rdpq_set_color_image (const surface_t *surface) |
Configure the framebuffer to render to (RDP command: SET_COLOR_IMAGE) More... | |
void | rdpq_set_z_image (const surface_t *surface) |
Configure the Z-buffer to use (RDP command: SET_Z_IMAGE) More... | |
void | rdpq_set_texture_image (const surface_t *surface) |
Configure the texture to use (RDP command: SET_TEX_IMAGE) More... | |
void | __rdpq_set_other_modes (uint32_t w0, uint32_t w1) |
Out-of-line implementation of rdpq_set_other_modes_raw. | |
void | __rdpq_change_other_modes (uint32_t w0, uint32_t w1, uint32_t w2) |
Out-of-line implementation of rdpq_change_other_modes_raw. | |
uint64_t | rdpq_get_other_modes_raw (void) |
Read the current render mode register. More... | |
void | rdpq_set_tile_autotmem (int16_t tmem_bytes) |
Configure the auto-TMEM feature of rdpq_set_tile. More... | |
void | rdpq_sync_full (void(*callback)(void *), void *arg) |
Schedule a RDP SYNC_FULL command and register a callback when it is done. More... | |
void | rdpq_sync_pipe (void) |
Schedule a RDP SYNC_PIPE command. More... | |
void | rdpq_sync_tile (void) |
Schedule a RDP SYNC_TILE command. More... | |
void | rdpq_sync_load (void) |
Schedule a RDP SYNC_LOAD command. More... | |
Variables | |
bool | __rdpq_inited = false |
True if rdpq_init was called. More... | |
rdpq_block_state_t | rdpq_block_state |
RDP block management state. | |
rdpq_tracking_t | rdpq_tracking |
Tracking state of RDP. | |
RDP Command queue.
This documentation block describes the internal workings of the RDP Queue. This is useful to understand the implementation, but it is not required to read or understand this to use rdpq.
For description of the API of the RDP queue, see rdpq.h
RDPQ provides a very low-level API over the RDP graphics chips, exposing all its settings and most of its limits. Still, rdpq tries to hide a few low-level hardware details to make programming the RDP less surprising and more orthogonal. To do so, it "patches" some RDP commands, typically via RSP code and depending on the current RDP state. We called these improvements "fixups".
The documentation of the public rdpq API does not explicitly mention which behavior has been adjusted via fixups. Instead, this section explains in details all the fixups performed by rdpq. Reading this section is not necessary to understand and use rdpq, but it might be useful for people that are familiar with RDP outside of libdragon (eg: libultra programmers), to avoid getting confused in places where rdpq deviates from RDP (even if for the better).
The RDP SET_SCISSOR and TEXTURE_RECTANGLE commands accept a rectangle whose major bounds (bottom and right) are either inclusive or exclusive, depending on the current RDP cycle type (fill/copy: exclusive, 1cyc/2cyc: inclusive). rdpq_set_scissor and rdpq_texture_rectangle, instead, always use exclusive major bounds, and automatically adjust them depending on the current RDP cycle type.
Moreover, any time the RDP cycle type changes, the current scissoring is adjusted to guarantee consistent results. This is especially important where the scissoring covers the whole framebuffer, because otherwise the RDP might overflow the buffer while drawing.
The RDP SET_COLOR_IMAGE command only contains a memory pointer and a pitch: the RDP is not aware of the actual size of the buffer in terms of width/height, and expects commands to be correctly clipped, or scissoring to be configured. To avoid mistakes leading to memory corruption, rdpq_set_color_image always reconfigures scissoring to respect the actual buffer size.
Note also that when the RDP is cold-booted, the internal scissoring register contains random data. This means that this auto-scissoring fixup also provides a workaround to this, by making sure scissoring is always configured at least once. In fact, by forgetting to configure scissoring, the RDP can happily draw outside the framebuffer, or draw nothing, or even freeze.
The RDP has different internal parallel units and exposes three different syncing primitives to stall and avoid write-during-use bugs: SYNC_PIPE, SYNC_LOAD and SYNC_TILE. Correct usage of these commands is not complicated but it can be complex to get right, and require extensive hardware testing because emulators do not implement the bugs caused by the absence of RDP stalls.
rdpq implements a smart auto-syncing engine that tracks the commands sent to RDP (on the CPU) and automatically inserts syncing whenever necessary. Insertion of syncing primitives is optimal for SYNC_PIPE and SYNC_TILE, and conservative for SYNC_LOAD (it does not currently handle partial TMEM updates).
Autosync also works within blocks, but since it is not possible to know the context in which a block will be run, it has to be conservative and might issue more stalls than necessary.
More details on the autosync engine are below.
The RDP command SET_OTHER_MODES contains most the RDP mode settings. Unfortunately the command does not allow to change only some settings, but all of them must be reconfigured. This is in contrast with most graphics APIs that allow to configure each render mode setting by itself (eg: it is possible to just change the dithering algorithm).
rdpq instead tracks the current render mode on the RSP, and allows to do partial updates via either the low-level rdpq_change_other_modes_raw function (where it is possible to change only a subset of the 56 bits), or via the high-level rdpq_mode_* APIs (eg: rdpq_mode_dithering), which mostly build upon rdpq_change_other_modes_raw in their implementation.
The RDP has two main operating modes: 1 cycle per pixel and 2 cycles per pixel. The latter is twice as slow, as the name implies, but it allows more complex color combiners and/or blenders. Moreover, 2-cycles mode also allows for multi-texturing.
At the hardware level, it is up to the programmer to explicitly activate either 1-cycle or 2-cycle mode. The problem with this is that there are specific rules to follow for either mode, which does not compose cleanly with partial mode changes. For instance, fogging is typically implemented using the 2-cycle mode as it requires two passes in the blender. If the user disables fogging for some meshes, it might be more performant to switch back to 1-cycle mode, but that requires also reconfiguring the combiner.
To solve this problem, the higher level rdpq mode APIs (rdpq_mode_*) automatically select the best cycle type depending on the current settings. More specifically, 1-cycle mode is preferred as it is faster, but 2-cycle mode is activated whenever one of the following conditions is true:
The correct cycle-type is automatically reconfigured any time that either the blender or the combiner settings are changed. Notice that this means that rdpq also transparently handles a few more details for the user, to make it for an easier API:
((ZERO, ZERO, ZERO, COMBINED), (ZERO, ZERO, ZERO, COMBINED))
).((PIXEL_RGB, ZERO, PIXEL_RGB, ONE))
). Notice that this is required because there is no pure passthrough in second step of the blender.The RDP command SET_FILL_COLOR (used to configure the color register to be used in fill cycle type) has a very low-level interface: its argument is basically a 32-bit value which is copied to the framebuffer as-is, irrespective of the framebuffer color depth. For a 16-bit buffer, then, it must be programmed with two copies of the same 16-bit color.
rdpq_set_fill_color, instead, accepts a color_t argument and does the conversion to the "packed" format internally, depending on the current framebuffer's color depth.
Most of the rdpq APIs are defined as inline functions in the header rdpq.h, but they then internally call some non-public function to do emit the command. So basically the actual function is split in tow parts: an inlined part and a non-inlined part.
The reason for this split is to help the compiler generate better code. In fact, it is extremely common to call rdpq functions using many constant parameters, and we want those constants to be propagated into the various bit shifts and masks to be assembled into single words. Once the (often constant) arguments have been handled, the rest of the operation can normally be performed in a separate out-of-line function.
This section describes in general how the commands flow from CPU to RDP via RSP. There are several different code-paths here depending on whether the command has a fixup or not, and it is part of a block.
In general, the rdpq library sends the commands to RDP using a buffer in RDRAM. The hardware feature called XBUS (which allows to send commands from RSP DMEM to RDP directly) is not used or supported. There are a few reasons for this architectural choice:
The buffer in RDRAM where RDP commands are enqueued by RSP is called "RDP dynamic buffer". It is used as a ring buffer, so once full, it is recycled, making sure not to overwrite commands that the RDP has not executed yet.
Let's check the workflow for a standard RDP command, that is one for which rdpq provides no fixups:
0xF3
.0xF3
, and dispatch it to the rdpq overlay.0xF3
is the same for all non-fixup commands: it writes the 8 bytes of the command into a temporary buffer in DMEM, and then sends it via DMA to the RDP dynamic buffer in RDRAM. This act of forwarding a command through CPU -> RSP -> RDP is called "passthrough", and is implemented by RDPQCmd_Passthrough8
and RDPQCmd_Passthrough16
in the ucode (rsp_rdpq.S), and RSPQ_RdpSend
in rsp_queue.inc.DP_END
register. When the buffer is finished, recycling it requires instead to write both DP_START
and DP_END
. See RSPQCmd_RdpAppendBuffer
and RSPQCmd_RdpSetBuffer
respectively.Now let's see the workflow for a RDP fixup: these are the RDP commands which are modified/tweaked by RSP to provide a more sane programming interface to the programmer.
0xD2
, and dispatch it to the rdpq overlay.0xD2
is a RSP function called RDPQCmd_SetScissorEx
. It inspects the RDP state to check the current cycle type and adapts the scissoring bounds if required. Then, it assembles a real SET_SCISSOR (with ID 0xD2) and calls RSPQ_RdpSend
to send it to the RDP dynamic buffer.The overall workflow is similar to the passthrough, but the command is tweaked by RSP in the process.
In block mode, rdpq completely changes its way of operating.
A rspq block (as described in rspq.c) is a buffer containing a sequence of rspq commands that can be played back by RSP itself, with the CPU just triggering it via rspq_block_run. When using rdpq, the rspq block contains one additional buffer: a "RDP static buffer", which contains RDP commands.
At block creation time, in fact, RDP commands are not enqueued as rspq commands, but are rather written into this separate buffer. The goal is to avoid the passthrough overhead: since RDP commands don't change during the block execution, they can be sent directly to RDP by RSP, referencing the RDP static buffer, without ever transferring them into RSP DMEM and back.
Let's check the sequence at block compilation time:
And now at block run time:
enqueues a rspq command for the rdpq overlay. This command does not need to have the same encoding of a real RDP command, but it is usually similar (to simplify work on the RSP). For instance, in our example the rdpq command is 0xD2, which is meaningless if sent to RDP, but has otherwise the same encoding of a real SET_SCISSOR (whose ID would be 0xED).
0xD2
, and dispatch it to the rdpq overlay.0xD2
is a RSP function called RDPQCmd_SetScissorEx
. It inspects the RDP state to check the current cycle type and adapts the scissoring bounds if required. Then, it assembles a real SET_SCISSOR (with ID 0xD2) and calls RSPQ_RdpSend
to send it to the RDP dynamic buffer.As explained above, the autosync engine is able to emit sync commands (SYNC_PIPE, SYNC_TILE, SYNC_LOAD) automatically when necessary, liberating the developer from this additional task. This section describes how it works.
The autosync engine works around one simple abstraction and logic. There are "hardware resources" that can be either "used" or "changed" (aka configured) by RDP commands. If a resource is in use, a command changing it requires a sync before. Each resource is tracked by one bit in a single 32-bit word called the "autosync state".
The following resources are tracked:
AUTOSYNC_PIPE
). All render mode commands "change" this bit (eg: rdpq_set_other_modes_raw or rdpq_set_yuv_parms). All draw commands "use" this bit (eg: rdpq_triangle). So for instance, if you draw a triangle, next rdpq_set_mode_standard call will automatically issue a SYNC_PIPE
.AUTOSYNC_TILE(n)
) so that tracking is actually done at the single tile granularity. Commands modifying the tile descriptor (such as rdpq_set_tile or rdpq_load_tile) will "change" the resource corresponding for the affect tile. Commands drawing textured primitives (eg: rdpq_texture_rectangle) will "use" the resource. For instance, calling rdpq_texture_rectangle using TILE4, and later calling rdpq_load_tile on TILE4 will cause a SYNC_TILE
to be issued just before the LOAD_TILE
command. Notice that if rdpq_load_tile used TILE5 instead, no SYNC_TILE
would have been issued, assuming TILE5 was never used before. This means that having a logic to cycle through tile descriptors (instead of always using the same) will reduce the number of SYNC_TILE
commands.AUTOSYNC_TMEM(0)
. Any command that writes to TMEM (eg: rdpq_load_block) will "change" the resource. Any command that reads from TMEM (eg: rdpq_triangle with a texture) will "use" the resource. Writing to TMEM while something is reading requires a SYNC_LOAD
command to be issued.Note that there is a limit with the current implementation: the RDP can use multiple tiles with a single command (eg: when using multi-texturing or LODs), but we are not able to track that correctly: all drawing commands for now assume that a single tile will be used. If this proves to be a problem, it is always possible to call rdpq_sync_tile to manually issue a sync.
Autosync also works with blocks, albeit conservatively. When recording a block, it is not possible to know what the autosync state will be at the point of call (and obviously, it could be called in different situations with different states). The engine thus handles the worst case: at the beginning of a block, it assumes that all resources are "in use". This might cause some sync commands to be run in situations where it would not be strictly required, but the performance impact is unlikely to be noticeable.
Autosync engine can be enabled or disabled via rdpq_config_enable / rdpq_config_disable. Remember that manually issuing sync commands require careful debugging on real hardware, as no emulator today is able to reproduce the effects of a missing sync command.
struct rdpq_state_t |
State of the rdpq ucode overlay (partial).
This must be kept in sync with rsp_rdpq.S.
We don't map the whole state here as we don't need to access it from C in whole. We just map the initial part of the state, which is what we need.
void rdpq_init | ( | void | ) |
Initialize the RDPQ library.
This should be called by the initialization functions of the higher-level libraries using RDPQ to emit RDP commands, and/or by the application main if the application itself calls rdpq functions.
It is safe to call this function multiple times (it does nothing), so that multiple independent libraries using rdpq can call rdpq_init with no side effects.
void rdpq_close | ( | void | ) |
Shutdown the RDPQ library.
This is mainly used for testing.
uint32_t rdpq_config_set | ( | uint32_t | cfg | ) |
Set the configuration of the RDPQ module.
This function allows you to change the configuration of rdpq to enable/disable features. This is useful mainly for advanced users that want to manually tune RDP programming, disabling some automatisms performed by rdpq.
The configuration is a bitmask that can be composed using the RDPQ_CFG_*
macros.
To enable or disable specific configuration options use rdpq_config_enable or rdpq_config_disable.
cfg | The new configuration to set |
uint32_t rdpq_config_enable | ( | uint32_t | cfg_enable_bits | ) |
Enable a specific set of configuration flags.
This function allows you to modify the configuration of rdpq activating a specific set of features. It can be useful to temporarily modify the configuration and then restore it.
cfg_enable_bits | Configuration flags to enable |
uint32_t rdpq_config_disable | ( | uint32_t | cfg_disable_bits | ) |
Disable a specific set of configuration flags.
This function allows you to modify the configuration of rdpq disabling a specific set of features. It can be useful to temporarily modify the configuration and then restore it.
cfg_disable_bits | Configuration flags to disable |
void rdpq_fence | ( | void | ) |
Public rdpq_fence API, redefined it.
Add a fence to synchronize RSP with RDP commands.
void rdpq_exec | ( | void * | buffer, |
int | size | ||
) |
Send to the RDP a buffer of RDP commands from RDRAM.
This command can be used to execute raw RDP commands from RDRAM. It is normally not necessary to call this function as normal rdpq functions will simply enqueue the commands in the RSP queue, but there can be cases where commands have been prepared in RAM somehow (especially, for compatibility with existing code that assembled RDP commands in RDRAM, or to playback RDP command lists prepared with offline tools).
This function fully interoperates with the rest of RDPQ, so you can freely intermix it with standard rdpq calls.
buffer | Pointer to the buffer containing RDP commands |
size | Size of the buffer, in bytes (must be a multiple of 8) |
void __rdpq_autosync_change | ( | uint32_t | res | ) |
Autosync engine: mark certain resources as being changed.
This is the core of the autosync engine. Whenever a resource is "changed" while "in use", a SYNC command must be issued. This is a slightly conservative approach, as the RDP might already have finished using that resource, but we have no way to know it. The SYNC command will then reset the "use" status of each respective resource.
void __rdpq_block_begin | ( | ) |
Initialize RDP block mangament.
This is called by rspq_block_begin. It resets all the block management state to default.
Notice that no allocation is performed. This is because we do block allocation lazily as soon as a rdpq command is issued. In fact, if the block does not contain rdpq commands, it would be a waste of time and memory to allocate a RDP buffer. The allocations will be performed by __rdpq_block_next_buffer as soon as a rdpq command is written.
void __rdpq_block_next_buffer | ( | void | ) |
Allocate a new RDP block buffer, chaining it to the current one (if any)
This function is called by rdpq_passthrough_write and rdpq_write when we are about to write a rdpq command in a block, and the current RDP buffer is full (wptr + cmdsize >= wend
). By extension, it is also called when the current RDP buffer has not been allocated yet (wptr == wend == NULL
).
rdpq_block_t * __rdpq_block_end | ( | ) |
Finish creation of a RDP block.
This is called by rspq_block_end. It finalizes block creation and return a pointer to the first node of the block, which will be put within the rspq_block_t structure, so to be able to reference it in __rdpq_block_run and __rdpq_block_free.
void __rdpq_block_free | ( | rdpq_block_t * | block | ) |
Free a block.
This function is called when a block is freed. It is called by rspq_block_free.
void __rdpq_block_reserve | ( | int | num_rdp_commands | ) |
Reserve space in the RDP static buffer for a number of RDP commands.
This is called by rdpq_write when run within a block. It makes sure that the static buffer has enough space for the specified number of RDP commands, and also switch back to the dynamic buffer if the command is going to generate a large or unbounded number of commands.
void __rdpq_block_update | ( | volatile uint32_t * | wptr | ) |
Set a new RDP write pointer, and enqueue a RSP command to run the buffer until there.
This function is called by rdpq_passthrough_write after some RDP commands have been written into the block's RDP buffer. A rspq command RSPQ_CMD_RDP_APPEND_BUFFER will be issued so that the RSP will tell the RDP to fetch and run the new commands, appended at the end of the current buffer.
If possible, though, this function will coalesce the command with an immediately preceding RSPQ_CMD_RDP_APPEND_BUFFER (or even RSPQ_CMD_RDP_SET_BUFFER, if we are at the start of the buffer), so that only a single RSP command is issued, which covers multiple RDP commands.
wptr | New block's RDP write pointer |
void rdpq_set_color_image | ( | const surface_t * | surface | ) |
Configure the framebuffer to render to (RDP command: SET_COLOR_IMAGE)
This command is used to specify the render target that the RDP will draw to.
Calling this function also automatically configures scissoring (via rdpq_set_scissor), so that all draw commands are clipped within the buffer, to avoid overwriting memory around it. Use rdpq_config_disable(RDPQ_CFG_AUTOSCISSOR)
if you need to disable this behavior.
If you have a raw pointer instead of a surface_t, you can use surface_make to create a temporary surface structure to pass the information to rdpq_set_color_image.
If the passed surface is NULL, rdpq will be detached from the render target. If a drawing command is issued without a render target, it will be silently ignored (but the validator will flag it as an error).
The only valid formats for a surface to be used as a render target are: FMT_RGBA16, FMT_RGBA32, and FMT_I8.
[in] | surface | Surface to set as render target |
void rdpq_set_z_image | ( | const surface_t * | surface | ) |
Configure the Z-buffer to use (RDP command: SET_Z_IMAGE)
This commands is used to specify the Z-buffer that will be used by RDP for the next rendering commands.
The surface must have the same width and height of the surface set as render target (via rdpq_set_color_image or rdpq_set_color_image_raw). The color format should be FMT_RGBA16, even though Z values will be written to it.
If the passed surface is NULL, rdpq will be detached from the Z buffer. If a drawing command using Z is issued without a Z buffer, the behaviour will be undefined (but the validator will flag it as an error).
surface | Surface to set as Z buffer |
void rdpq_set_texture_image | ( | const surface_t * | surface | ) |
Configure the texture to use (RDP command: SET_TEX_IMAGE)
This commands is used to specify the texture image that will be used by RDP for the next load commands (rdpq_load_tile and rdpq_load_block).
The surface must have the same width and height of the surface set as render target (via rdpq_set_color_image or rdpq_set_color_image_raw). The color format should be FMT_RGBA16, even though Z values will be written to it.
surface | Surface to set as texture |
uint64_t rdpq_get_other_modes_raw | ( | void | ) |
Read the current render mode register.
This function executes a full sync (rspq_wait) and then extracts the current raw render mode from the RSP state. This should be used only for debugging purposes.
void rdpq_set_tile_autotmem | ( | int16_t | tmem_bytes | ) |
Configure the auto-TMEM feature of rdpq_set_tile.
This function is used to manage the auto-TMEM allocation feature for rdpq_set_tile. It allows to keep track of the allocated space in TMEM, which can be a simplification. It is used by the rdpq_tex module (eg: rdpq_tex_upload).
The feature works like this:
While this API might seem as a small simplification over manually tracking TMEM allocation, it might help modularizing the code, and also allows to record rspq blocks that handle texture loading without hardcoding the TMEM position.
tmem_bytes | 0: begin, -1: end, >0: number of additional bytes that were used in TMEM. |
void rdpq_sync_full | ( | void(*)(void *) | callback, |
void * | arg | ||
) |
Schedule a RDP SYNC_FULL command and register a callback when it is done.
This function schedules a RDP SYNC_FULL command into the RSP queue. This command basically forces the RDP to finish drawing everything that has been sent to it before it, and then generate an interrupt when it is done.
This is normally useful at the end of the frame. For instance, it is used internally by rdpq_detach to make sure RDP is finished drawing on the target display before detaching it.
The function can be passed an optional callback that will be called when the RDP interrupt triggers. This can be useful to perform some operations asynchronously.
callback | A callback to invoke under interrupt when the RDP is finished drawing, or NULL if no callback is necessary. |
arg | Opaque argument that will be passed to the callback. |
void rdpq_sync_pipe | ( | void | ) |
Schedule a RDP SYNC_PIPE command.
This command must be sent before changing the RDP pipeline configuration (eg: color combiner, blender, colors, etc.) if the RDP is currently drawing.
Normally, you do not need to call this function because rdpq automatically emits sync commands whenever necessary. You must call this function only if you have disabled autosync for SYNC_PIPE (see RDPQ_CFG_AUTOSYNCPIPE).
void rdpq_sync_tile | ( | void | ) |
Schedule a RDP SYNC_TILE command.
This command must be sent before changing a RDP tile configuration if the RDP is currently drawing using that same tile.
Normally, you do not need to call this function because rdpq automatically emits sync commands whenever necessary. You must call this function only if you have disabled autosync for SYNC_TILE (see RDPQ_CFG_AUTOSYNCTILE).
void rdpq_sync_load | ( | void | ) |
Schedule a RDP SYNC_LOAD command.
This command must be sent before loading an area of TMEM if the RDP is currently drawing using that same area.
Normally, you do not need to call this function because rdpq automatically emits sync commands whenever necessary. You must call this function only if you have disabled autosync for SYNC_LOAD (see RDPQ_CFG_AUTOSYNCLOAD).
|
inline |
Enqueue a SET_FILL_COLOR RDP command.
This command is used to configure the color used by RDP when running in FILL mode (rdpq_set_mode_fill) and normally used by rdpq_fill_rectangle.
Notice that rdpq_set_mode_fill automatically calls this function, because in general it makes no sense to configure the FILL mode without also setting a FILL color.
[in] | color | The color to use to fill |
Enqueue a SET_FILL_COLOR RDP command to draw a striped pattern.
This command is similar to rdpq_set_fill_color, but allows to configure two colors, and creates a fill pattern that alternates horizontally between them every 2 pixels (creating vertical stripes).
This command relies on a low-level hack of how RDP works in filling primitives, so there is no configuration knob: it only works with RGBA 16-bit target buffers, it only allows two colors, and the vertical stripes are exactly 2 pixel width.
[in] | color1 | Color of the first vertical stripe |
[in] | color2 | Color of the second vertical stripe |
|
inline |
Set the RDP FOG blender register.
This function sets the internal RDP FOG register, part of the blender unit. As the name implies, this register is normally used as part of fog calculation, but it is actually a generic color register that can be used in custom blender formulas.
Another similar blender register is the BLEND register, configured via rdpq_set_blend_color.
See RDPQ_BLENDER and RDPQ_BLENDER2 on how to configure the blender (typically, via rdpq_mode_blender).
[in] | color | Color to set the FOG register to |
|
inline |
Set the RDP BLEND blender register.
This function sets the internal RDP BLEND register, part of the blender unit. As the name implies, this register is normally used as part of fog calculation, but it is actually a generic color register that can be used in custom blender formulas.
Another similar blender register is the FOG register, configured via rdpq_set_fog_color.
See RDPQ_BLENDER and RDPQ_BLENDER2 on how to configure the blender (typically, via rdpq_mode_blender).
[in] | color | Color to set the BLEND register to |
|
inline |
Set the RDP PRIM combiner register (color only) (RDP command: SET_PRIM_COLOR)
This function sets the internal RDP PRIM register, part of the color combiner unit. Naming aside, it is a generic color register that can be used in custom color combiner formulas.
Another similar blender register is the ENV register, configured via rdpq_set_env_color.
See RDPQ_COMBINER1 and RDPQ_COMBINER2 on how to configure the color combiner (typicall, via rdpq_mode_combiner).
If you wish to set PRIM LOD or PRIM MIN LOD values of the PRIM register, see rdpq_set_prim_lod_frac, rdpq_set_detail_factor or rdpq_set_prim_register_raw.
[in] | color | Color to set the PRIM register to |
|
inline |
Set the detail/sharpen blending factor (RDP command: SET_PRIM_COLOR (partial))
This function sets the internal minimum clamp for LOD fraction, that is used for determining the interpolation blend factor of a detail or sharpen texture at high magnification.
Range is [0..1] where 0 means no influence, and 1 means full influence. The range is internally inverted and converted to [0..31] for the RDP hardware
[in] | value | Value to set the register to in range [0..1] |
|
inline |
Set the RDP PRIM LOD FRAC combiner register (RDP command: SET_PRIM_COLOR (partial))
This function sets the internal Level of Detail fraction for primitive register, that is used for custom linear interpolation between any two colors in a Color Combiner.
See RDPQ_COMBINER1 and RDPQ_COMBINER2 on how to configure the color combiner (typicall, via rdpq_mode_combiner).
If you wish to set PRIM MIN LOD value, see rdpq_set_detail_factor.
[in] | value | Value to set the PRIM LOD register to in range [0..255] |
|
inline |
Set the RDP PRIM combiner register (raw version) (RDP command: SET_PRIM_COLOR)
This function sets the internal RDP PRIM register, part of the color combiner unit. Naming aside, it is a generic color register that can be used in custom color combiner formulas.
It also sets the PRIM LOD FRAC and PRIM MIN LOD FRAC values for the PRIM register For more information, see rdpq_set_prim_lod_frac, rdpq_set_detail_factor.
Another similar blender register is the ENV register, configured via rdpq_set_env_color.
See RDPQ_COMBINER1 and RDPQ_COMBINER2 on how to configure the color combiner (typicall, via rdpq_mode_combiner).
If you wish to set PRIM COLOR or PRIM LOD or PRIM MIN LOD values individually, see rdpq_set_prim_lod_frac, rdpq_set_detail_factor or rdpq_set_prim_color.
[in] | color | Color to set the PRIM register to |
[in] | minlod | Minimum LOD fraction to set the PRIM register to |
[in] | primlod | Primitive LOD fraction to set the PRIM register to |
|
inline |
Set the RDP ENV combiner register (RDP command: SET_ENV_COLOR)
This function sets the internal RDP ENV register, part of the color combiner unit. Naming aside, it is a generic color register that can be used in custom color combiner formulas.
Another similar blender register is the PRIM register, configured via rdpq_set_prim_color.
See RDPQ_COMBINER1 and RDPQ_COMBINER2 on how to configure the color combiner (typically, via rdpq_mode_combiner).
[in] | color | Color to set the ENV register to |
|
inline |
Set a fixed Z value to be used instead of a per-pixel value (RDP command; SET_PRIM_DEPTH)
When using z-buffering, normally the Z value used for z-buffering is calculated by interpolating the Z of each vertex onto each pixel. The RDP allows for usage of a fixed Z value instead, for special effects like particles or decals.
This function allows to configure the RDP register that holds the fixed Z value. It is then necessary to activate this special RDP mode: either manually turning on SOM_ZSOURCE_PRIM via rdpq_change_other_modes_raw.
For beginners, it is suggested to use the mode API instead, via rdpq_mode_zoverride.
[in] | prim_z | Fixed Z value (in range 0..0x7FFF) |
[in] | prim_dz | Delta Z value (must be a signed power of two). Pass 0 initially, and increment to next power of two in case of problems with objects with the same Z. |
rdpq_set_prim_depth
).
|
inline |
Load a palette of colors into TMEM (RDP command: LOAD_TLUT)
This command is used to load a palette into TMEM. TMEM can hold up to 256 16-bit colors in total to be used as palette, and they must be stored in the upper half of TMEM. These colors are arranged as a single 256-color palette when drawing FMT_CI8 images, or 16 16-colors palettes when drawing FMT_CI4 images.
Storage of colors in TMEM is a bit wasteful, as each color is replicated four times (in fact, 256 colors * 16-bit + 4 = 2048 bytes, which is in fact half of TMEM). This command should be preferred for palette loading as it automatically handles this replication.
Loading a palette manually is a bit involved. It requires configuring the palette in RDRAM via rdpq_set_texture_image, and also configure a tile descriptor with the TMEM destination address (via rdpq_set_tile). Instead, prefer using the simpler rdpq texture API (rdpq_tex.h), via rdpq_tex_upload_tlut.
[in] | tile | Tile descriptor to use (TILE0-TILE7). This is used to extract the destination TMEM address (all other fields of the descriptor are ignored). |
[in] | color_idx | Index of the first color to load into TMEM (0-255). This is a 16-bit offset into the RDRAM buffer set via rdpq_set_texture_image. |
[in] | num_colors | Number of colors to load (1-256). |
|
inline |
Configure the extents of a tile descriptor – fixed point version (RDP command: SET_TILE_SIZE)
This function is similar to rdpq_set_tile_size, but coordinates must be specified using fixed point numbers (10.2).
tile | Tile descriptor (TILE0-TILE7) | |
[in] | s0 | Top-left X texture coordinate to store in the descriptor (fx 10.2) |
[in] | t0 | Top-left Y texture coordinate to store in the descriptor (fx 10.2) |
[in] | s1 | Bottom-right exclusive X texture coordinate to store in the descriptor (fx 10.2) |
[in] | t1 | Bottom-right exclusive Y texture coordinate to store in the descriptor (fx 10.2) |
|
inline |
Load a texture image into TMEM with a single contiguous memory transfer (RDP command: LOAD_BLOCK)
This is a command alternative to rdpq_load_tile to load data from RDRAM into TMEM. It is faster than rdpq_load_tile but only allows to transfer a consecutive block of data; the block can cover multiple lines, but not a sub-rectangle of the texture image.
Before calling rdpq_load_block, the tile must have been configured using rdpq_set_tile to specify the TMEM address, and the texture in RDRAM must have been set via rdpq_set_texture_image.
After the call to rdpq_load_block, it is not possible to reuse the tile descriptor for performing a draw. So a new tile descriptor should be configured from scratch using rdpq_set_tile.
The maximum number of texels that can be transferred by a single call is
[in] | tile | Tile descriptor (TILE0-TILE7) |
[in] | s0 | Top-left X texture coordinate to load |
[in] | t0 | Top-left Y texture coordinate to load |
[in] | num_texels | Number of texels to load (max: 2048) |
[in] | tmem_pitch | Pitch of the texture in TMEM (in bytes) |
|
inline |
Load a portion of a texture into TMEM – fixed point version (RDP command: LOAD_TILE)
This function is similar to rdpq_load_tile, but coordinates can be specified in fixed point format (0.10.2). Refer to rdpq_load_tile for increased performance
[in] | tile | Tile descriptor to use (TILE0-TILE7). |
[in] | s0 | Upper-left X coordinate of the portion of the texture to load (fx 0.10.2). Range: 0-4096 |
[in] | t0 | Upper-left Y coordinate of the portion of the texture to load (fx 0.10.2), Range: 0-4096 |
[in] | s1 | Bottom-right X coordinate of the portion of the texture to load (fx 0.10.2), Range: 0-4096 |
[in] | t1 | Bottom-right Y coordinate of the portion of the texture to load (fx 0.10.2), Range: 0-4096 |
|
inline |
Low-level function to change the RDP combiner.
This function enqueues a low-level SET_COMBINE RDP command that changes the RDP combiner, setting it to a new value. You can use RDPQ_COMBINER1 and RDPQ_COMBINER2 to create the combiner settings for respectively a 1-pass or 2-pass combiner.
comb | The new combiner setting |
|
inline |
Low-level function to set the rendering mode register.
This function enqueues a low-level SET_OTHER_MODES RDP command that changes the RDP render mode, setting it to a new value
This function is very low level and requires very good knowledge of internal RDP state management. Moreover, it completely overwrites any existing configuration for all bits, so it must be used with caution within a block.
mode | The new render mode. See the RDP_RM |
|
inline |
Low-level function to partly change the rendering mode register.
This function is very low level and requires very good knowledge of internal RDP state management.
It allows to partially change the RDP render mode register, enqueuing a command that will modify only the requested bits. This function is to be preferred to rdpq_set_other_modes_raw as it preservers existing render mode for all the other bits, so it allows for easier composition.
[in] | mask | Mask of bits of the SOM register that must be changed |
[in] | val | New value for the bits selected by the mask. |
|
inline |
Low-level version of rdpq_set_color_image, with address lookup capability.
This is a low-level version of rdpq_set_color_image, that exposes the address lookup capability. It allows to either pass a direct buffer, or to use a buffer already stored in the address lookup table, adding optionally an offset. See rdpq_set_lookup_address for more information.
RDP a physical constraint of 64-byte alignment for render targets, so make sure to respect that while configuring a buffer. The validator will flag such a mistake.
index | Index in the rdpq lookup table of the buffer to set as render target. |
offset | Byte offset to add to the buffer stored in the lookup table. Notice that if index is 0, this can be a physical address to a buffer (use PhysicalAddr to convert a C pointer to a physical address). |
format | Format of the buffer. Only FMT_RGBA32, FMT_RGBA16 or FMT_I8 are possible to use as a render target. |
width | Width of the buffer in pixel |
height | Height of the buffer in pixel |
stride | Stride of the buffer in bytes (length of a row) |
|
inline |
Low-level version of rdpq_set_z_image, with address lookup capability.
This is a low-level version of rdpq_set_z_image, that exposes the address lookup capability. It allows to either pass a direct buffer, or to use a buffer already stored in the address lookup table, adding optionally an offset. See rdpq_set_lookup_address for more information.
RDP a physical constraint of 64-byte alignment for render targets, so make sure to respect that while configuring a buffer. The validator will flag such a mistake.
index | Index in the rdpq lookup table of the buffer to set as render target. |
offset | Byte offset to add to the buffer stored in the lookup table. Notice that if index is 0, this can be a physical address to a buffer (use PhysicalAddr to convert a C pointer to a physical address). |
|
inline |
Low-level version of rdpq_set_texture_image, with address lookup capability.
This is a low-level version of rdpq_set_texture_image, that exposes the address lookup capability. It allows to either pass a direct buffer, or to use a buffer already stored in the address lookup table, adding optionally an offset. See rdpq_set_lookup_address for more information.
RDP a physical constraint of 8-byte alignment for textures, so make sure to respect that while configuring a buffer. The validator will flag such a mistake.
index | Index in the rdpq lookup table of the buffer to set as texture image. |
offset | Byte offset to add to the buffer stored in the lookup table. Notice that if index is 0, this can be a physical address to a buffer (use PhysicalAddr to convert a C pointer to a physical address). |
format | Format of the texture (tex_format_t) |
width | Width of the texture in pixel (max 1024) |
height | Height of the texture in pixel (max 1024) |
|
inline |
Store an address into the rdpq lookup table.
This function is for advanced usages, it is not normally required to call it.
This function modifies the internal RDPQ address lookup table, by storing an address into on of the available slots.
The lookup table is used to allow for an indirect access to surface pointers. For instance, some library code might want to record a block that manipulates several surfaces, but without saving the actual surface pointers within the block. Instead, all commands referring to a surface, will actually refer to 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.
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.
index | Index of the slot in the table. Available slots are 1-15 (slot 0 is reserved). |
rdram_addr | Pointer of the buffer to store into the address table. |
|
inline |
Enqueue a RDP SET_TILE command (full version)
[in] | tile | Tile descriptor index (0-7) |
[in] | format | Texture format for the tile. Cannot be 0. Should correspond to X_get_format in surface_t or sprite_t; |
[in] | tmem_addr | Address in tmem where the texture is (or will be loaded). Must be multiple of 8; |
[in] | tmem_pitch | Pitch of the texture in tmem in bytes. Must be multiple of 8. Should correspond to srtide in surface_t; |
[in] | parms | Additional optional parameters for the tile. Can be left NULL or all 0. More information about the struct is in rdpq_tileparms_t |
bool __rdpq_inited = false |
True if rdpq_init was called.
True if the rdpq module was inited.