render-cg: decently implement basic functions

This commit is contained in:
lephe 2019-05-03 17:11:43 +02:00
parent 04231ea5d6
commit f3c8964b84
12 changed files with 409 additions and 88 deletions

View file

@ -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 <gint/defs/types.h>
/* 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;

View file

@ -13,6 +13,15 @@
#include <gint/defs/types.h>
/* 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 */

View file

@ -7,21 +7,6 @@
#include <gint/defs/types.h>
/* 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 <gint/display-cg.h>
#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 */

View file

@ -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,

View file

@ -0,0 +1,38 @@
#define GINT_NEED_VRAM
#include <gint/display.h>
/* 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;
}
}

107
src/render-cg/dline.c Normal file
View file

@ -0,0 +1,107 @@
#define GINT_NEED_VRAM
#include <gint/defs/util.h>
#include <gint/display.h>
/* 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);
}

11
src/render-cg/dpixel.c Normal file
View file

@ -0,0 +1,11 @@
#define GINT_NEED_VRAM
#include <gint/display.h>
/* 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;
}

59
src/render-cg/drect.c Normal file
View file

@ -0,0 +1,59 @@
#define GINT_NEED_VRAM
#include <gint/defs/util.h>
#include <gint/display.h>
/* 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;
}
}

9
src/render-cg/dupdate.c Normal file
View file

@ -0,0 +1,9 @@
#define GINT_NEED_VRAM
#include <gint/display.h>
//#include <gint/drivers/r61524.h>
/* dupdate() - push the video RAM to the display driver */
void dupdate(void)
{
r61524_display(vram, 0, 224);
}

16
src/render-cg/dvram.c Normal file
View file

@ -0,0 +1,16 @@
#define GINT_NEED_VRAM
#include <gint/display.h>
/* 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;
}

View file

@ -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);

View file

@ -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);