Azur/azur/include/azur/gint/render.h

375 lines
15 KiB
C

//---
// azur.render: Specialized rendering pipeline for fx-CG gint
//
// On-chip ILRAM and DSP memory bring out the full power of the SH4AL-DSP.
// Therefore, optimal performance in a game's renderer will rely on using on-
// chip memory instead of standard RAM for graphics data.
//
// The obvious limitation of on-chip memory is its size (20 kiB total), which
// is much smaller than a full-resolution image (~177 kiB). This prompts for a
// technique known as "frame streaming", where fragments (strips of VRAM) of
// each frame are rendered and transferred to the display in sequence.
// Reasonable efficiency and suitable display driver settings can prevent
// tearing even though data is not being sent continuously.
//
// The main components of this rendering pipeline are the command queue and
// fragment shaders.
//
// The command queue stores all rendering commands, split into fragments. Each
// fragment needs to read through a number of commands, and the order does not
// match the order of API calls because each API call typically impacts several
// fragments. Therefore commands need to be stored.
//
// Fragment shaders are the programs that render commands into graphics data
// for each fragments. They are pretty similar to OpenGL shaders, in that they
// receive vectors of objects (command parameters) and produce graphics data to
// fragments (even though each fragment is a strip of VRAM, not a pixel), hence
// the name.
//
// The prefix for this module is [azrp] for "azur rendering pipeline".
//---
#pragma once
#include <azur/defs.h>
AZUR_BEGIN_DECLS
#include <gint/display.h>
#include <gint/image.h>
#include <libprof.h>
/* arzp_shader_t: Type of shader functions
* [uniforms] is a pointer to any data the shader might use as uniform.
* [command] is a structure of the shader's command type.
* [fragment] is a pointer to azrp_frag. */
typedef void azrp_shader_t(void *uniforms, void *command, void *fragment);
/* Video memory fragment used as rendering target (in XRAM). */
extern uint16_t *azrp_frag;
/* Maximum number of commands that can be queued. (This is only one of two
limits, the other being the size of the command data.) */
#define AZRP_MAX_COMMANDS 1024
/* Maximum number of shaders that can be defined. (This is a loose limit). */
#define AZRP_MAX_SHADERS 32
//---
// High and low-level pipeline functions
//
// The process of rendering a frame with azrp has four steps:
// 1. Clear the command queue
// 2. Queue commands with command generation functions
// 3. Sort the command queue
// 4. Render fragments in on-chip memory and send them to the display driver
//
// The command queue is empty when the program starts. The azrp_update()
// performs steps 3 and 4, rendering a frame; then clears the command queue
// again. Therefore, azrp_update() can be used more or less like dupdate().
//
// Functions for command generation are listed in the shader API below, and can
// be extended with custom shaders.
//
// Applications that want to render the same frame several time such as to
// save screenshots, or reuse commands, or add new commands and use the
// already-sorted base, can use the low-level functions below which implement
// steps 1, 3 and 4 individually.
//
// There are optional configuration calls that can be performed within step 1;
// the configuration is retained from frame to frame, so it may be enough to
// set it only once.
//---
/* azrp_update(): Sort commands, render a frame, and starts another one */
void azrp_update(void);
/* azrp_clear_commands(): Clear the command queue (step 1) */
void azrp_clear_commands(void);
/* azrp_sort_commands(): Sort the command queue (step 3) */
void azrp_sort_commands(void);
/* azrp_render_fragments(): Render and send fragments to the dislay (step 4) */
void azrp_render_fragments(void);
//---
// Configuration calls
//
// The following configuration options can be changed during step 1, so either
// along with arzp_sort_commands(), or just after azrp_update().
//
// Changing display settings usually requires updating the uniforms of shaders.
// See the details of each shader.
//
// Most settings are exposed as global variables. This is for read-only access;
// if you modify the variables directly you will get garbage.
//---
/* Current super-scaling factor. */
extern int azrp_scale;
/* Width and height of display (based on scale). */
extern int azrp_width, azrp_height;
/* Number of fragments in a frame (affected by configuration). */
extern int azrp_frag_count;
/* Offset of first fragment. */
extern int azrp_frag_offset;
/* Height of fragments. */
extern int azrp_frag_height;
/* dwindow settings for the display ({ 0, 0, azrp_width, azrp_height }). */
extern struct dwindow azrp_window;
/* azrp_config_scale(): Select the renderer's super-scaling factor
This pipeline supports integer upscaling by factors of x1, x2 and x3. Unlike
the traditional VRAM approach, upscaling in this pipeline is fundamentally
faster on every level, since every bit of graphics data can be handled on an
actually smaller resolution, leaving the pixel duplication to the display
transfer. This is because efficient transfers to the display in this system
are performed by CPU, which is much more versatile than the DMA.
The settings on each mode are as follow:
* x1: Display resolution: 396x224
Fragment size: 16 rows (12672 bytes)
Number of fragments: 28 (29 if an offset is used)
Total size of graphics data: 177'408 bytes
* x2: Display resolution: 198x112
Fragment size: 16 rows (6336 bytes) # TODO: increase
Number of fragments 7 (8 if an offset if used)
Total size of graphics data: 44'352 bytes
* x3: Display resolution: 132x75 (last row only has 2/3 pixels)
Fragment size: 16 rows (4224 bytes) # TODO: increase
Number of fragments: 5 (sometimes 6 if an offset is used)
Total size of graphics data: 19'800 bytes
As one would know when playing modern video games, super-resolution is one
of the most useful ways to increase performance. The reduced amount of
graphics data (either 4 or 9 times the fullscreen amount) has a huge impact
on the rendering process. */
void azrp_config_scale(int scale);
/* azrp_config_frag_offset(): Offset fragments along the y-axis
This call changes the alignment of fragments along the y-axis, so that the
first fragments starts somewhere above the screen. This tends to add one
additional fragment for the whole screen to be covered.
The primary use of this feature is to align grid-based frames with
fragments. As a prototypical example, top-down games using a tileset spend
most of the display surface showing the tiled map, so it's pretty beneficial
to align fragments on map rows so that each fragment handles only one row,
which makes the shader simpler and faster, uses less commands, and even
simplifies memory access patterns a little bit.
Another use is to align the x3 mode roughly to the center of the screen, to
emulate the 128x64 resolution of black-and-white models with 4 fragments.
@offset Fragment offset along the y-axis (0 ... height of fragment-1). */
void azrp_config_frag_offset(int offset);
//---
// Hooks
//---
/* Hook called before a fragment is sent to the display. The fragment can be
accessed and modified freeely (however, the time spent in the hook is
counted as overhead and only part of [azrp_perf_render]). */
typedef void azrp_hook_prefrag_t(int id, void *fragment, int size);
/* Get or set the prefrag hook. */
azrp_hook_prefrag_t *azrp_hook_get_prefrag(void);
void azrp_hook_set_prefrag(azrp_hook_prefrag_t *);
//---
// Standard shaders
//---
/* Clears the entire output with a single color */
extern uint8_t AZRP_SHADER_CLEAR;
/* Renders gint images with various dynamic effects */
extern uint8_t AZRP_SHADER_IMAGE_RGB16;
extern uint8_t AZRP_SHADER_IMAGE_P8;
extern uint8_t AZRP_SHADER_IMAGE_P4;
/* azrp_clear(): Clear output [ARZP_SHADER_CLEAR] */
void azrp_clear(uint16_t color);
/* azrp_image(): Queue image command [AZRP_SHADER_IMAGE_*] */
void azrp_image(int x, int y, bopti_image_t const *image);
/* azrp_subimage(): Queue image subsection command [AZRP_SHADER_IMAGE_*] */
void azrp_subimage(int x, int y, bopti_image_t const *image,
int left, int top, int width, int height, int flags);
void azrp_triangle(int x1, int y1, int x2, int y2, int x3, int y3, int color);
/* azrp_line(): Draw a line with clipping to the screen resolution between point (x1,y1) and (x2,y2) */
void azrp_line( int x1, int y1, int x2, int y2, uint16_t color );
/* azrp_circle() : Draw a circle with clipping to the screen resolution with a center (xc,yc) and a radius rad */
void azrp_circle( int xc, int yx, uint16_t rad, uint16_t color );
/* azrp_filledcircle() : Draw a filled circle with clipping to the screen resolution with a center (xc,yc) and a radius rad */
void azrp_filledcircle( int xc, int yx, uint16_t rad, uint16_t color );
/* azrp_poly() : Draw a polygon with clipping*/
void azrp_poly(int *x, int *y, int nb_vertices, uint16_t color);
/* azrp_filledpoly() : Draw a filled polygon with clipping*/
void azrp_filledpoly(int *x, int *y, int nb_vertices, uint16_t color);
/* See below for more detailed image functions. Dynamic effects are provided
with the same naming convention as gint. */
/* Functions to update uniforms for these shaders. You should call them when:
* AZRP_SHADER_CLEAR: Changing super-scaling settings.
* AZRP_SHADER_IMAGE_*: Changing super-scaling or or fragment offsets. */
void azrp_shader_clear_configure(void);
void azrp_shader_image_rgb16_configure(void);
void azrp_shader_image_p8_configure(void);
void azrp_shader_image_p4_configure(void);
void azrp_shader_triangle_configure(void);
void azrp_shader_line_configure(void);
void azrp_shader_circle_configure(void);
void azrp_shader_filledcircle_configure(void);
void azrp_shader_filledpoly_configure(void);
//---
// Performance indicators
//
// The following performance counters are run through by the rendering module
// in most stages of the rendering process. The module updates them but doesn't
// use them, so they are safe to write to and reset when they're not running.
//---
/* This counter runs during command generation and queue operations. */
extern prof_t azrp_perf_cmdgen;
/* This counter runs during the command sorting step. */
extern prof_t azrp_perf_sort;
/* This counter runs during shader executions in arzp_render_fragments(). */
extern prof_t azrp_perf_shaders;
/* This counter runs during CPU transfers to the R61524 display. */
extern prof_t azrp_perf_r61524;
/* This counter runs during rendering; it is the sum of shaders and r61524,
plus some logic overhead. */
extern prof_t azrp_perf_render;
/* azrp_perf_clear(): Clear all performance counters
Generally you want to do this before azrp_update(). */
void azrp_perf_clear(void);
//---
// Definitions for custom shaders
//---
/* azrp_register_shader(): Register a new command type and its shader program
This function adds the specified shader program to the program array, and
returns the corresponding command type. Adding new shaders is useful for
specialized rendering options (eg. tiles with fixed size) or new graphical
effects.
If the maximum number shaders is exceeded, returns -1. */
int azrp_register_shader(azrp_shader_t *program);
/* azrp_set_uniforms(): Set a shader's uniforms pointer
If the shader has less than 4 bytes of uniform data, an integer may be
passed as the address; there is no requirement that the pointer be aligned
or even points to valid memory. */
void azrp_set_uniforms(int shader_id, void *uniforms);
/* azrp_queue_command(): Add a new command to be rendered next frame
The command must be a structure starting with an 8-bit shader ID. The
command is added for all fragments in range [fragment..fragment+count); its
data can be updated between fragments by the shader program. Returns true on
success, false if the maximum amount of commands or command memory is
exceeded. */
bool azrp_queue_command(void *command, size_t size, int fragment, int count);
/* azrp_queue_image(): Split and queue a gint image command
The command must have been completely prepared with gint_image_mkcmd() and
have had its color effect sections filled. This function sets the shader ID
and adjusts the command for fragmented rendering. */
void azrp_queue_image(struct gint_image_box *box, image_t const *img,
struct gint_image_cmd *cmd);
//---
// Internal R61524 functions
//---
void azrp_r61524_fragment_x1(void *fragment, int size);
void azrp_r61524_fragment_x2(void *fragment, int width, int height);
//---
// Internal functions for the image shader
//
// We use gint's image rendering API but replace some of the core loops with
// Azur-specific versions that are faster in the CPU-bound context of this
// rendering engine. Some of the main loops from Azur actually perform better
// in RAM than bopti used to do, and are already in gint.
//---
/* azrp_image_effect(): Generalized azrp_image() with dynamic effects */
#define azrp_image_effect(x, y, img, eff, ...) \
azrp_image_effect(x, y, img, 0, 0, (img)->width, (img)->height, eff, \
##__VA_ARGS__)
/* azrp_subimage_effect(): Generalized azrp_subimage() with dynamic effects */
void azrp_subimage_effect(int x, int y, image_t const *img,
int left, int top, int w, int h, int effects, ...);
/* Specific versions for each format */
#define AZRP_IMAGE_SIG1(NAME, ...) \
void azrp_image_ ## NAME(int x, int y, image_t const *img,##__VA_ARGS__); \
void azrp_subimage_ ## NAME(int x, int y, image_t const *img, \
int left, int top, int w, int h, ##__VA_ARGS__);
#define AZRP_IMAGE_SIG(NAME, ...) \
AZRP_IMAGE_SIG1(rgb16 ## NAME, ##__VA_ARGS__) \
AZRP_IMAGE_SIG1(p8 ## NAME, ##__VA_ARGS__) \
AZRP_IMAGE_SIG1(p4 ## NAME, ##__VA_ARGS__)
AZRP_IMAGE_SIG(_effect, int effects, ...)
AZRP_IMAGE_SIG(, int effects)
AZRP_IMAGE_SIG(_clearbg, int effects, int bg_color_or_index)
AZRP_IMAGE_SIG(_swapcolor, int effects, int source, int replacement)
AZRP_IMAGE_SIG(_addbg, int effects, int bg_color)
AZRP_IMAGE_SIG(_dye, int effects, int dye_color)
#define azrp_image_rgb16_effect(x, y, img, eff, ...) \
azrp_subimage_rgb16_effect(x, y, img, 0, 0, (img)->width, (img)->height, \
eff, ##__VA_ARGS__)
#define azrp_image_p8_effect(x, y, img, eff, ...) \
azrp_subimage_p8_effect(x, y, img, 0, 0, (img)->width, (img)->height, \
eff, ##__VA_ARGS__)
#define azrp_image_p4_effect(x, y, img, eff, ...) \
azrp_subimage_p4_effect(x, y, img, 0, 0, (img)->width, (img)->height, \
eff, ##__VA_ARGS__)
#undef AZRP_IMAGE_SIG
#undef AZRP_IMAGE_SIG1
/* Main loop provided by Azur; as usual, these are not real functions; their
only use is as the [.loop] field of a command. */
void azrp_image_shader_rgb16_normal(void);
void azrp_image_shader_rgb16_clearbg(void);
void azrp_image_shader_rgb16_swapcolor(void);
void azrp_image_shader_rgb16_dye(void);
void azrp_image_shader_p8_normal(void);
void azrp_image_shader_p8_swapcolor(void);
void azrp_image_shader_p4_normal(void);
void azrp_image_shader_p4_clearbg(void);
AZUR_END_DECLS