From f3c8964b84a86d1a23f3ea3a6d4b40926a13da60 Mon Sep 17 00:00:00 2001 From: lephe Date: Fri, 3 May 2019 17:11:43 +0200 Subject: [PATCH] render-cg: decently implement basic functions --- include/gint/display-cg.h | 119 ++++++++++++++++++++++++++++++++++++-- include/gint/display-fx.h | 17 ++++-- include/gint/display.h | 21 +------ src/r61524/r61524.c | 45 +++++--------- src/render-cg/dclear.c | 38 ++++++++++++ src/render-cg/dline.c | 107 ++++++++++++++++++++++++++++++++++ src/render-cg/dpixel.c | 11 ++++ src/render-cg/drect.c | 59 +++++++++++++++++++ src/render-cg/dupdate.c | 9 +++ src/render-cg/dvram.c | 16 +++++ src/render-fx/dline.c | 53 ++++++++--------- src/render-fx/dupdate.c | 2 +- 12 files changed, 409 insertions(+), 88 deletions(-) create mode 100644 src/render-cg/dline.c create mode 100644 src/render-cg/dpixel.c create mode 100644 src/render-cg/drect.c create mode 100644 src/render-cg/dupdate.c create mode 100644 src/render-cg/dvram.c diff --git a/include/gint/display-cg.h b/include/gint/display-cg.h index 2466800..01292c2 100644 --- a/include/gint/display-cg.h +++ b/include/gint/display-cg.h @@ -4,6 +4,8 @@ // This module covers all 16-bit opaque rendering functions. For // gamma-related functions, color composition, check out a color library. // +// All the functions in this module work on a 396x224 resolution - gint +// lets you use the full surface! //--- #ifndef GINT_DISPLAY_CG @@ -11,10 +13,119 @@ #ifdef FXCG50 -/* Screen dimensions on fxcg50 - never mind the borders, gint lets you use the - full surface! */ -#define DWIDTH 396 -#define DHEIGHT 224 +#include + +/* Expose the VRAM variable if GINT_NEED_VRAM is defined. It must always point + to a 32-aligned buffer of size 177408. Any function can use it freely to + perform rendering or store data when not drawing. Triple buffering is + already implemened in gint, see the dvram() function below. + + In this module, colors are in the 16-bit R5G6B5 format, as it is the format + used by the display controller. */ + +#ifdef GINT_NEED_VRAM +extern uint16_t *vram; +#endif + +//--- +// Video RAM management +//--- + +/* dvram() - Control video RAM address and triple buffering + + Normal rendering under gint uses double-buffering: there is one image + displayed on the screen and one in memory, in a region called the video RAM + (VRAM). The application draws frames in the VRAM then sends them to the + screen only when they are finished, using dupdate(). + + On fxcg50, the performance bottleneck is almost always the graphical + rendering (especially in games) because the high amount of data, 173 kB per + frame in full-resolution, makes graphics manipulation computationally + expensive. The transfer also takes about 10 ms in itself. + + Since gint transfers data to the screen using the DMA, it is possible to run + the application while the finished frame is being transferred. However, + writing to the VRAM during this period it is still begin read by the DMA. + Changing the contents of the VRAM too soon would alter the frame being sent. + + The solution to this is to use triple-buffering with the display and two + VRAMs that are alternately begin written to while the other is being + transferred. The VRAM switching is handled by dupdate() and is activated + whenever two VRAMs are configured. + + By default gint uses triple buffering with one VRAM in the user stack and + a second one in the system stack. + + VRAMs must be contiguous, 32-aligned, (2*396*224)-byte buffers. + + @main Main VRAM area, used alone if [secondary] is NULL + @secondary Additional VRAM area, enables triple buffering if non-NULL */ +void dvram(uint16_t *main, uint16_t *secondary); + +/* dupdate() - push the video RAM to the display driver + This function makes the contents of the VRAM visible on the screen. It is + the direct equivalent of Bdisp_PutDisp_DD(). + + If triple buffering is enabled (this is the default, and disabled only if + dvram() is used to setup double buffering instead), it also swaps buffers. + Also waits for the previous dupdate() call to finish before executing. */ +void dupdate(void); + +//--- +// Area rendering functions +//--- + +/* dclear() - fill the screen with a single color + This function clears the screen by painting all the pixels in a single, + opaque color. + + @color Any R5G6B5 color */ +void dclear(uint16_t color); + +/* drect() - fill a rectangle of the screen + This functions paints a rectangle in an opaque color. The endpoints (x1 y1) + and (x2 y2) are included in the rectangle. + + @x1 @y1 @x2 @y2 Bounding rectangle (drawn area). + @color Any R5G6B5 color */ +void drect(int x1, int y1, int x2, int y2, uint16_t color); + +//--- +// Point drawing functions +//--- + +/* dpixel() - change a pixel's color + Paints the selected pixel with an opaque color. Setting pixels individually + is a slow method for rendering. Other functions that draw lines, rectangles, + images or text will take advantage of possible optimizations to make the + rendering faster: check them out first. + + @x @y Coordinates of the pixel to repaint + @color Any R5G6B5 color */ +void dpixel(int x, int y, uint16_t color); + +/* dline() - render a straight line + This function draws a line using a Bresenham-style algorithm. Please note + that the affected pixels may not be exactly the same when using dline() and + Bdisp algorithms. + + dline() has optimization facilities for horizontal and vertical lines. The + first kind is about twice as fast, while the second avoids some computation + (the optimization gain is not as significant as on fx9860g). dline() is not + able to clip the line without calculating all the pixels, so drawing a line + from (-1e6,0) to (1e6,395) will work, but will be veeery slow. + + @x1 @y1 @x2 @y2 End points of the line (both included). + @color Any R5G6B5 color */ +void dline(int x1, int y1, int x2, int y2, uint16_t color); + +//--- +// Image rendering (bopti) +//--- + +//--- +// Text rendering (topti) +//--- typedef void font_t; diff --git a/include/gint/display-fx.h b/include/gint/display-fx.h index c6fcba6..d052191 100644 --- a/include/gint/display-fx.h +++ b/include/gint/display-fx.h @@ -13,6 +13,15 @@ #include +/* Expose the VRAM variable if GINT_NEED_VRAM is defined. It must always point + to a 4-aligned buffer of size 1024. Any function can use it freely to: + - Use another video ram area (triple buffering or more, gray engine); + - Implement additional drawing functions; + - Store data when not drawing. */ +#ifdef GINT_NEED_VRAM +extern uint32_t *vram; +#endif + /* color_t - colors available for drawing The following colors are defined by the library: @@ -50,9 +59,9 @@ typedef enum // Area drawing functions //--- -/* dupdate() - pushes the video RAM to the display driver +/* dupdate() - push the video RAM to the display driver This function makes the contents of the VRAM visible on the screen. It is - the direct equivalent of Bdisp_PutDisp_DD() */ + the direct equivalent of Bdisp_PutDisp_DD(). */ void dupdate(void); /* dclear() - fill the screen with a single color @@ -100,7 +109,7 @@ void dpixel(int x, int y, color_t color); void dline(int x1, int y1, int x2, int y2, color_t color); //--- -// Image rendering (bopti) +// Image rendering (bopti) //--- /* image_t - image files encoded for bopti @@ -126,7 +135,7 @@ typedef struct } GPACKED(4) image_t; //--- -// Text rendering (topti) +// Text rendering (topti) //--- /* font_t - font data encoded for topti */ diff --git a/include/gint/display.h b/include/gint/display.h index b50c9b5..3e832e5 100644 --- a/include/gint/display.h +++ b/include/gint/display.h @@ -7,21 +7,6 @@ #include -/* Expose the VRAM variable if GINT_NEED_VRAM is defined. This address is used - as the VRAM basis by all of gint's drawing functions, and must point to a - suitable buffer: - [fx9860g] A 4-aligned buffer of size 1024. - [fxcg50] A 4-aligned buffer of size 177408. - - This variable is primarily meant to be exposed to gint functions, but - add-ins may use it or change it freely: - - To use another video ram area (triple buffering or more, gray engine); - - To implement additional drawing functions; - - When not drawing, as additional RAM (especially on fxcg50). */ -#ifdef GINT_NEED_VRAM -extern uint32_t *vram; -#endif - /* As you would expect, display on fx9860g and display on fxcg50 are completely different worlds. As a consequence, so are the rendering functions ^^ */ @@ -33,10 +18,7 @@ extern uint32_t *vram; #include #endif -//--- -// Parameter access -//--- - +#if 0 /* dinfo_t - summary of information provided by this module */ typedef struct { @@ -56,5 +38,6 @@ typedef struct @info Pointer to allocated dinfo_t structure, will be filled. */ void dinfo(dinfo_t *info); +#endif #endif /* GINT_DISPLAY */ diff --git a/src/r61524/r61524.c b/src/r61524/r61524.c index b5e5d3f..9060699 100644 --- a/src/r61524/r61524.c +++ b/src/r61524/r61524.c @@ -121,7 +121,7 @@ void r61524_win_set(uint16_t HSA, uint16_t HEA, uint16_t VSA, uint16_t VEA) // Driver functions //--- -void r61524_test(void) +/* void r61524_test(void) { uint16_t device_name; uint16_t doc; @@ -198,34 +198,15 @@ void r61524_test(void) Bdisp_PutDisp_DD(); getkey(); +} */ -/* Bdisp_AllClr_VRAM(); - print(1, 1, "MSTPCR0=????????"); - print_hex(9, 1, POWER.MSTPCR0.lword, 8); +/* TODO: r61524: update, backlight, brightness, gamma */ - print(1, 2, "DMAOR=????"); - print_hex(7, 2, DMA.OR.word, 4); - - print(1, 3, "SAR=????????"); - print_hex(5, 3, DMA.DMA0.SAR, 8); - print(1, 4, "DAR=????????"); - print_hex(5, 4, DMA.DMA0.DAR, 8); - print(1, 5, "TCR=????????"); - print_hex(5, 5, DMA.DMA0.TCR, 8); - print(1, 6, "CHCR=????????"); - print_hex(6, 6, DMA.DMA0.CHCR, 8); - - Bdisp_PutDisp_DD(); - getkey(); */ -} - -void r61524_dma_test(uint16_t *vram) +void r61524_display(uint16_t *vram, int start, int height) { - int y1 = 0; - int height = 224; /* Move the window to the desired region, then select address 0 */ - r61524_win_set(0, 395, y1, y1 + height - 1); + r61524_win_set(0, 395, start, start + height - 1); select(ram_address_horizontal); write(0); select(ram_address_vertical); @@ -234,15 +215,17 @@ void r61524_dma_test(uint16_t *vram) /* Bind address 0xb4000000 to the data write command */ select(write_data); - void *src = vram; + void *src = vram + 396 * start; void *dst = (void *)0xb4000000; - /* The thing is, 396 is not a multiple of 32. - To make things simple, we can choose to always send a multiple of 8 - rows, which makes the 32 factor appear. */ - int blocks = 198 * (height >> 3); + /* The amount of data sent per row, 396*2, is not a multiple of 32. For + now I assume [height] is a multiple of 4, which makes the factor 32 + appear. */ + int blocks = 99 * (height >> 2); + /* Now roll! */ dma_transfer(DMA_32B, blocks, src, DMA_INC, dst, DMA_FIXED); + /* Wait for any previous DMA transfer to finish */ dma_transfer_wait(); } @@ -279,7 +262,7 @@ static void ctx_restore(void *buf) #ifdef GINT_BOOT_LOG /* r6524_status() - status string */ -static const char *r6524_status(void) +static const char *r61524_status(void) { select(device_code_read); uint16_t dev = read(); @@ -298,7 +281,7 @@ static const char *r6524_status(void) gint_driver_t drv_r61524 = { .name = "R61524", .init = NULL, - .status = GINT_DRIVER_STATUS(r6524_status), + .status = GINT_DRIVER_STATUS(r61524_status), .ctx_size = sizeof(ctx_t), .sys_ctx = &sys_ctx, .ctx_save = ctx_save, diff --git a/src/render-cg/dclear.c b/src/render-cg/dclear.c index e69de29..b8877d6 100644 --- a/src/render-cg/dclear.c +++ b/src/render-cg/dclear.c @@ -0,0 +1,38 @@ +#define GINT_NEED_VRAM +#include + +/* dclear() - fill the screen with a single color */ +void dclear(uint16_t color) +{ + /* TOOD: render-cg: dclear(): Consider using the DMA */ + + /* Operate in longwords to clear faster. Also start at the end of the + VRAM to take advantage of pre-decrement writes. */ + uint32_t *v = (void *)(vram + 88704); + uint32_t op = (color << 16) | color; + + /* Split the VRAM into 2772 blocks of 16 longwords to reduce the + overhead of the loop */ + for(int blocks = 2772; blocks; blocks--) + { + *--v = op; + *--v = op; + *--v = op; + *--v = op; + + *--v = op; + *--v = op; + *--v = op; + *--v = op; + + *--v = op; + *--v = op; + *--v = op; + *--v = op; + + *--v = op; + *--v = op; + *--v = op; + *--v = op; + } +} diff --git a/src/render-cg/dline.c b/src/render-cg/dline.c new file mode 100644 index 0000000..25a6fd5 --- /dev/null +++ b/src/render-cg/dline.c @@ -0,0 +1,107 @@ +#define GINT_NEED_VRAM +#include +#include + +/* dhline() - optimized drawing of a horizontal line + @x1 @x2 @y Coordinates of endpoints of line (both included) + @color Any R5G6B5 color */ +static void dhline(int x1, int x2, int y, uint16_t color) +{ + /* Order and bounds */ + if((uint)y >= 224) return; + if(x1 > x2) swap(x1, x2); + if(x1 < 0) x1 = 0; + if(x2 >= 396) x2 = 395; + + int offset = 396 * y; + + /* Use longwords to do the copy, but first paint the endpoints to heed + for odd x1 and x2. Checking the parity may be a waste of time. */ + vram[offset + x1] = color; + vram[offset + x2] = color; + + /* Now round to longword boundaries and copy everything in-between with + longwords */ + x1 = x1 + (x1 & 1); + x2 = (x2 + 1) & ~1; + + uint32_t *start = (void *)(vram + offset + x1); + uint32_t *end = (void *)(vram + offset + x2); + uint32_t op = (color << 16) | color; + + while(end > start) *--end = op; +} + +/* dvline() - optimized drawing of a vertical line + @y1 @y2 @x Coordinates of endpoints of line (both included) + @color Any R5G6B5 color */ +static void dvline(int y1, int y2, int x, uint16_t color) +{ + /* Order and bounds */ + if((uint)x >= 395) return; + if(y1 > y2) swap(y1, y2); + if(y1 < 0) y1 = 0; + if(y2 >= 224) y2 = 223; + + uint16_t *v = vram + 396 * y1 + x; + int height = y2 - y1 + 1; + + while(height--) *v = color, v += 396; +} + +/* dline() - Bresenham line drawing algorithm + Remotely adapted from MonochromeLib code by Pierre "PerriotLL" Le Gall. + Relies on dhline() and dvline() for optimized situations. + @x1 @y1 @x2 @y2 Coordinates of endpoints of line (included) + @color Any R5G6B5 color */ +void dline(int x1, int y1, int x2, int y2, uint16_t color) +{ + /* Possible optimizations */ + if(y1 == y2) + { + dhline(x1, x2, y1, color); + return; + } + if(x1 == x2) + { + dvline(y1, y2, x1, color); + return; + } + + /* Brensenham line drawing algorithm */ + + int i, x = x1, y = y1, cumul; + int dx = x2 - x1, dy = y2 - y1; + int sx = sgn(dx), sy = sgn(dy); + + dx = abs(dx), dy = abs(dy); + + dpixel(x1, y1, color); + + if(dx >= dy) + { + /* Start with a non-zero cumul to even the overdue between the + two ends of the line (for more regularity) */ + cumul = dx >> 1; + for(i = 1; i < dx; i++) + { + x += sx; + cumul += dy; + if(cumul > dx) cumul -= dx, y += sy; + dpixel(x, y, color); + } + } + else + { + cumul = dy >> 1; + for(i = 1; i < dy; i++) + { + y += sy; + cumul += dx; + if(cumul > dy) cumul -= dy, x += sx; + dpixel(x, y, color); + } + } + + dpixel(x2, y2, color); +} diff --git a/src/render-cg/dpixel.c b/src/render-cg/dpixel.c new file mode 100644 index 0000000..574be5e --- /dev/null +++ b/src/render-cg/dpixel.c @@ -0,0 +1,11 @@ +#define GINT_NEED_VRAM +#include + +/* dpixel() - change a pixel's color */ +void dpixel(int x, int y, uint16_t color) +{ + /* Coordinate checks */ + if((uint)x >= 396 || (uint)y >= 224) return; + + vram[396 * y + x] = color; +} diff --git a/src/render-cg/drect.c b/src/render-cg/drect.c new file mode 100644 index 0000000..510d0ee --- /dev/null +++ b/src/render-cg/drect.c @@ -0,0 +1,59 @@ +#define GINT_NEED_VRAM +#include +#include + +/* drect() - fill a rectangle of the screen */ +void drect(int x1, int y1, int x2, int y2, uint16_t color) +{ + if(x1 > x2) swap(x1, x2); + if(y1 > y2) swap(y1, y2); + + /* Order and bounds */ + if(x1 >= 396 || x2 < 0 || y1 >= 224 || y2 < 0) return; + if(x1 < 0) x1 = 0; + if(x2 >= 396) x2 = 395; + if(y1 < 0) y1 = 0; + if(y2 >= 224) y2 = 223; + + /* The method is exactly like dhline(). I first handle odd endpoints, + then write longwords for the longest section */ + + uint16_t *base = vram + 396 * y1; + int height = y2 - y1 + 1; + + /* Do borders first if there are at an odd position */ + + if(x1 & 1) + { + uint16_t *v = base; + for(int h = height; h; h--) + { + v[x1] = color; + v += 396; + } + x1++; + } + + if(x2 & 1) x2++; + else + { + uint16_t *v = base; + for(int h = height; h; h--) + { + v[x2] = color; + v += 396; + } + } + + /* Now copy everything that's left as longwords */ + + uint32_t *v = (void *)(base + x1); + uint32_t op = (color << 16) | color; + int width = (x2 - x1) >> 1; + + for(int h = height; h; h--) + { + for(int w = 0; w < width; w++) v[w] = op; + v += 198; + } +} diff --git a/src/render-cg/dupdate.c b/src/render-cg/dupdate.c new file mode 100644 index 0000000..a59a177 --- /dev/null +++ b/src/render-cg/dupdate.c @@ -0,0 +1,9 @@ +#define GINT_NEED_VRAM +#include +//#include + +/* dupdate() - push the video RAM to the display driver */ +void dupdate(void) +{ + r61524_display(vram, 0, 224); +} diff --git a/src/render-cg/dvram.c b/src/render-cg/dvram.c new file mode 100644 index 0000000..184fd4d --- /dev/null +++ b/src/render-cg/dvram.c @@ -0,0 +1,16 @@ +#define GINT_NEED_VRAM +#include + +/* Put both VRAMs in the system stack! */ +static uint16_t *main = (void *)0xac0f0000; +static uint16_t *scnd = (void *)0xac11b500; + +/* Shared VRAM pointer, the one exposed by GINT_NEED_VRAM */ +uint16_t *vram = (void *)0xac0f0000; + +/* dvram() - Control video RAM address and triple buffering */ +void dvram(uint16_t *new_main, uint16_t *new_secondary) +{ + main = new_main; + scnd = new_secondary; +} diff --git a/src/render-fx/dline.c b/src/render-fx/dline.c index 0347b41..93d23a9 100644 --- a/src/render-fx/dline.c +++ b/src/render-fx/dline.c @@ -6,42 +6,37 @@ /* dhline() - optimized drawing of a horizontal line using a rectangle mask @x1 @x2 @y Coordinates of endpoints of line (both included) @color Allowed colors: white, black, none, reverse */ -static void dhline(int x1, int x2, int y, color_t color) +void dhline(int x1, int x2, int y, color_t color) { if((uint)y >= 64) return; if(x1 > x2) swap(x1, x2); - uint32_t *lword = vram + (y << 2) + 4; - uint32_t m[4]; - /* Get the masks for the [x1, x2] range */ + uint32_t m[4]; masks(x1, x2, m); - switch(color) + uint32_t *data = vram + (y << 2); + + if(color == color_white) { - case color_white: - *--lword &= ~m[3]; - *--lword &= ~m[2]; - *--lword &= ~m[1]; - *--lword &= ~m[0]; - break; - - case color_black: - *--lword |= m[3]; - *--lword |= m[2]; - *--lword |= m[1]; - *--lword |= m[0]; - break; - - case color_reverse: - *--lword ^= m[3]; - *--lword ^= m[2]; - *--lword ^= m[1]; - *--lword ^= m[0]; - break; - - default: - return; + data[0] &= ~m[0]; + data[1] &= ~m[1]; + data[2] &= ~m[2]; + data[3] &= ~m[3]; + } + else if(color == color_black) + { + data[0] |= m[0]; + data[1] |= m[1]; + data[2] |= m[2]; + data[3] |= m[3]; + } + else if(color == color_reverse) + { + data[0] ^= m[0]; + data[1] ^= m[1]; + data[2] ^= m[2]; + data[3] ^= m[3]; } } @@ -50,7 +45,7 @@ static void dhline(int x1, int x2, int y, color_t color) of the vram cannot be used here. @y1 @y2 @x Coordinates of endpoints of line (both included) @color Allowed colors: black, white, none, reverse */ -static void dvline(int y1, int y2, int x, color_t operator) +void dvline(int y1, int y2, int x, color_t operator) { if((uint)x >= 128) return; if(y1 > y2) swap(y1, y2); diff --git a/src/render-fx/dupdate.c b/src/render-fx/dupdate.c index c6047de..f673d79 100644 --- a/src/render-fx/dupdate.c +++ b/src/render-fx/dupdate.c @@ -8,7 +8,7 @@ GSECTION(".bss") static uint32_t fx_vram[256]; /* Here is the definition of the VRAM pointer */ GDATA uint32_t *vram = fx_vram; -/* dupdate() - pushes the video RAM to the display driver */ +/* dupdate() - push the video RAM to the display driver */ void dupdate(void) { t6k11_display(vram, 0, 64, 16);