libgui 2.01
libgui is a GUI library for the GameCube, Wii, and Wii U
 
Loading...
Searching...
No Matches
OgcVideoDriver.cpp
1/****************************************************************************
2 * Platform Abstraction Layer (OGC driver)
3 * Daryl Borth 2026
4 * OgcVideoDriver.cpp
5 ***************************************************************************/
6#include <gccore.h>
7#include <ogcsys.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11#include <malloc.h>
12#include <unistd.h>
13#include <ogc/machine/processor.h>
14
15#include "OgcVideoDriver.h"
16#include "../../libgui/Gui.h"
17
18#define DEFAULT_FIFO_SIZE 256 * 1024
19static Mtx GXmodelView2D;
20
21OgcVideoDriver::OgcVideoDriver() { }
22
23OgcVideoDriver::~OgcVideoDriver()
24{
25 delete imageRenderer;
26 delete glyphRenderer;
27}
28
29void OgcVideoDriver::init(int width, int height)
30{
31 VIDEO_Init();
32
33 #ifdef HW_RVL
34 if (CONF_GetAspectRatio() == CONF_ASPECT_16_9 && (*(u32*)(0xCD8005A0) >> 16) == 0xCAFE) // Wii U
35 {
36 write32(0xd8006a0, 0x30000004), mask32(0xd8006a8, 0, 2);
37 }
38 #endif
39
40 vmode = VIDEO_GetPreferredMode(nullptr); // get default video mode
41
42 #ifdef HW_RVL
43 if (CONF_GetAspectRatio() == CONF_ASPECT_16_9)
44 vmode->viWidth = 678;
45 else
46 vmode->viWidth = 672;
47
48 if((vmode->viTVMode >> 2) == VI_NTSC)
49 {
50 vmode->viXOrigin = (VI_MAX_WIDTH_NTSC - vmode->viWidth) / 2;
51 vmode->viYOrigin = (VI_MAX_HEIGHT_NTSC - vmode->viHeight) / 2;
52 }
53 else
54 {
55 vmode->viXOrigin = (VI_MAX_WIDTH_PAL - vmode->viWidth) / 2;
56 vmode->viYOrigin = (VI_MAX_HEIGHT_PAL - vmode->viHeight) / 2;
57 }
58 #endif
59
60 VIDEO_Configure (vmode);
61
62 xfb[0] = (uint32_t *) SYS_AllocateFramebuffer (vmode);
63 xfb[1] = (uint32_t *) SYS_AllocateFramebuffer (vmode);
64 DCInvalidateRange(xfb[0], VIDEO_GetFrameBufferSize(vmode));
65 DCInvalidateRange(xfb[1], VIDEO_GetFrameBufferSize(vmode));
66 xfb[0] = (uint32_t *) MEM_K0_TO_K1 (xfb[0]);
67 xfb[1] = (uint32_t *) MEM_K0_TO_K1 (xfb[1]);
68
69 VIDEO_ClearFrameBuffer (vmode, xfb[0], COLOR_BLACK);
70 VIDEO_ClearFrameBuffer (vmode, xfb[1], COLOR_BLACK);
71 VIDEO_SetNextFramebuffer (xfb[0]);
72
73 VIDEO_SetBlack (FALSE);
74 VIDEO_Flush ();
75 VIDEO_WaitVSync ();
76 if (vmode->viTVMode & VI_NON_INTERLACE)
77 VIDEO_WaitVSync ();
78
79 screenWidth = width;
80 screenHeight = height;
81
82 // Initialize GX
83 GXColor background = { 0, 0, 0, 0xff };
84 gp_fifo = memalign(32, DEFAULT_FIFO_SIZE);
85 memset(gp_fifo, 0, DEFAULT_FIFO_SIZE);
86 GX_Init(gp_fifo, DEFAULT_FIFO_SIZE);
87 GX_SetCopyClear (background, GX_MAX_Z24);
88 GX_SetDispCopyGamma (GX_GM_1_0);
89 GX_SetCullMode (GX_CULL_NONE);
90
91 resetVideoMenu();
92
93 imageRenderer = new OgcImageRenderer();
94 glyphRenderer = new OgcGlyphRenderer();
95}
96
98{
99 GX_AbortFrame();
100 GX_Flush();
101 OgcFreeRetiredMeshes(); // the abort left the GPU idle
102
103 VIDEO_SetBlack(TRUE);
104 VIDEO_Flush();
105}
106
108{
109 whichfb ^= 1; // flip framebuffer
110 GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE);
111 GX_SetColorUpdate(GX_TRUE);
112 GX_CopyDisp(xfb[whichfb],GX_TRUE);
113 GX_DrawDone();
114 OgcFreeRetiredMeshes();
115 VIDEO_SetNextFramebuffer(xfb[whichfb]);
116 VIDEO_Flush();
117 VIDEO_WaitForFlush();
118 ++frameTimer;
119}
120
122{
123 GXColor background = { color.r, color.g, color.b, color.a };
124 GX_SetCopyClear(background, GX_MAX_Z24);
125}
126
127void OgcVideoDriver::resetVideoMenu()
128{
129 Mtx44 p;
130 float yscale;
131 uint32_t xfbHeight;
132
133 // clears the bg to color and clears the z buffer
134 GXColor background = {0, 0, 0, 255};
135 GX_SetCopyClear (background, GX_MAX_Z24);
136
137 yscale = GX_GetYScaleFactor(vmode->efbHeight,vmode->xfbHeight);
138 xfbHeight = GX_SetDispCopyYScale(yscale);
139 GX_SetScissor(0,0,vmode->fbWidth,vmode->efbHeight);
140 GX_SetDispCopySrc(0,0,vmode->fbWidth,vmode->efbHeight);
141 GX_SetDispCopyDst(vmode->fbWidth,xfbHeight);
142 GX_SetCopyFilter(vmode->aa,vmode->sample_pattern,GX_TRUE,vmode->vfilter);
143 GX_SetFieldMode(vmode->field_rendering,((vmode->viHeight==2*vmode->xfbHeight)?GX_ENABLE:GX_DISABLE));
144
145 if (vmode->aa)
146 GX_SetPixelFmt(GX_PF_RGB565_Z16, GX_ZC_LINEAR);
147 else
148 GX_SetPixelFmt(GX_PF_RGB8_Z24, GX_ZC_LINEAR);
149
150 // setup the vertex descriptor
151 // tells the flipper to expect direct data
152 GX_ClearVtxDesc();
153 GX_InvVtxCache ();
154 GX_InvalidateTexAll();
155
156 GX_SetVtxDesc(GX_VA_TEX0, GX_NONE);
157 GX_SetVtxDesc(GX_VA_POS, GX_DIRECT);
158 GX_SetVtxDesc (GX_VA_CLR0, GX_DIRECT);
159
160 GX_SetVtxAttrFmt (GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0);
161 GX_SetVtxAttrFmt (GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
162 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0);
163 GX_SetZMode (GX_FALSE, GX_LEQUAL, GX_TRUE);
164
165 GX_SetNumChans(1);
166 GX_SetNumTexGens(1);
167 GX_SetTevOp (GX_TEVSTAGE0, GX_PASSCLR);
168 GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
169 GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);
170
171 guMtxIdentity(GXmodelView2D);
172 guMtxTransApply (GXmodelView2D, GXmodelView2D, 0.0F, 0.0F, -50.0F);
173 GX_LoadPosMtxImm(GXmodelView2D,GX_PNMTX0);
174
175 guOrtho(p,0,screenHeight-1,0,screenWidth-1,0,300);
176 GX_LoadProjectionMtx(p, GX_ORTHOGRAPHIC);
177
178 GX_SetViewport(0,0,vmode->fbWidth,vmode->efbHeight,0,1);
179 GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR);
180 GX_SetAlphaUpdate(GX_TRUE);
181}
182
183void* OgcImageRenderer::createTexture(int width, int height)
184{
185 int padWidth = width + (4 - width % 4) % 4;
186 int padHeight = height + (4 - height % 4) % 4;
187 int len = (padWidth * padHeight) * 4;
188 if (len % 32) len += (32 - len % 32);
189 return memalign(32, len);
190}
191
192void OgcImageRenderer::loadTextureData(void* texture, const uint8_t* rgba, int width, int height)
193{
194 if(!texture || !rgba) return;
195 uint8_t* dst = (uint8_t*)texture;
196 int padWidth = width + (4 - width % 4) % 4;
197 int padHeight = height + (4 - height % 4) % 4;
198
199 for (int y = 0; y < padHeight; y++) {
200 for (int x = 0; x < padWidth; x++) {
201 uint32_t offset = ((((y >> 2) * (padWidth >> 2) + (x >> 2)) << 5) + ((y & 3) << 2) + (x & 3)) << 1;
202 if (y >= height || x >= width) {
203 dst[offset] = 0; dst[offset+1] = 255; dst[offset+32] = 255; dst[offset+33] = 255;
204 } else {
205 const uint8_t* src = rgba + (y * width + x) * 4;
206 dst[offset] = src[3]; // A
207 dst[offset+1] = src[0]; // R
208 dst[offset+32] = src[1]; // G
209 dst[offset+33] = src[2]; // B
210 }
211 }
212 }
213
214 int len = (padWidth * padHeight) * 4;
215 if (len % 32) len += (32 - len % 32);
216 DCFlushRange(dst, len);
217}
218
220{
221 if(texture)
222 free(texture);
223}
224
225void OgcImageRenderer::drawTexture(void * texture, float xpos, float ypos, uint16_t width, uint16_t height, float degrees, float scaleX, float scaleY, uint8_t alpha)
226{
227 if(!texture)
228 return;
229
230 GXTexObj texObj;
231
232 GX_InitTexObj(&texObj, texture, width, height, GX_TF_RGBA8, GX_CLAMP, GX_CLAMP, GX_FALSE);
233 GX_LoadTexObj(&texObj, GX_TEXMAP0);
234 GX_InvalidateTexAll();
235
236 GX_SetTevOp (GX_TEVSTAGE0, GX_MODULATE);
237 GX_SetVtxDesc (GX_VA_TEX0, GX_DIRECT);
238
239 Mtx m,m1,m2, mv;
240 width >>= 1;
241 height >>= 1;
242
243 guMtxScale(m1, scaleX, scaleY, 1.0);
244 guVector axis = (guVector) {0 , 0, 1 };
245 guMtxRotAxisDeg (m2, &axis, degrees);
246 guMtxConcat(m2,m1,m);
247
248 guMtxTransApply(m,m, xpos+width,ypos+height,0);
249 guMtxConcat (GXmodelView2D, m, mv);
250 GX_LoadPosMtxImm (mv, GX_PNMTX0);
251
252 GX_Begin(GX_QUADS, GX_VTXFMT0,4);
253 GX_Position3f32(-width, -height, 0);
254 GX_Color4u8(0xFF,0xFF,0xFF,alpha);
255 GX_TexCoord2f32(0, 0);
256
257 GX_Position3f32(width, -height, 0);
258 GX_Color4u8(0xFF,0xFF,0xFF,alpha);
259 GX_TexCoord2f32(1, 0);
260
261 GX_Position3f32(width, height, 0);
262 GX_Color4u8(0xFF,0xFF,0xFF,alpha);
263 GX_TexCoord2f32(1, 1);
264
265 GX_Position3f32(-width, height, 0);
266 GX_Color4u8(0xFF,0xFF,0xFF,alpha);
267 GX_TexCoord2f32(0, 1);
268 GX_End();
269 GX_LoadPosMtxImm (GXmodelView2D, GX_PNMTX0);
270
271 GX_SetTevOp (GX_TEVSTAGE0, GX_PASSCLR);
272 GX_SetVtxDesc (GX_VA_TEX0, GX_NONE);
273}
274
275void OgcImageRenderer::drawRectangle(float x, float y, float width, float height, PixelColor color)
276{
277 long n = 4;
278 float x2 = x+width;
279 float y2 = y+height;
280 guVector v[] = {{x,y,0.0f}, {x2,y,0.0f}, {x2,y2,0.0f}, {x,y2,0.0f}, {x,y,0.0f}};
281
282 GX_Begin(GX_TRIANGLEFAN, GX_VTXFMT0, n);
283 for(long i=0; i<n; ++i)
284 {
285 GX_Position3f32(v[i].x, v[i].y, v[i].z);
286 GX_Color4u8(color.r, color.g, color.b, color.a);
287 }
288 GX_End();
289}
290
291void OgcImageRenderer::drawMesh(const MeshVertex* vertices, size_t count)
292{
293 // Same GX state as drawRectangle(): TEV pass-through of the per-vertex color,
294 // no texture coordinates, culling off. A GX_Begin() takes at most 65535
295 // vertices, which is also a multiple of 3, so a long list splits cleanly.
296 if(!vertices)
297 return;
298 count -= count % 3;
299
300 while(count > 0)
301 {
302 const uint16_t n = (uint16_t)(count > 65535 ? 65535 : count);
303
304 GX_Begin(GX_TRIANGLES, GX_VTXFMT0, n);
305 for(uint16_t i = 0; i < n; ++i)
306 {
307 const MeshVertex& v = vertices[i];
308 GX_Position3f32(v.x, v.y, 0.0f);
309 GX_Color4u8(v.c.r, v.c.g, v.c.b, v.c.a);
310 }
311 GX_End();
312
313 vertices += n;
314 count -= n;
315 }
316}
317
318// A mesh kept as a GX display list: the GX_Begin()/vertex/GX_End() commands recorded once, then
319// replayed by the GPU reading them straight from memory, so drawing one costs the CPU a matrix
320// load and a call command instead of 16 bytes through the FIFO for every vertex.
322{
323 void * list;
324 u32 size;
326};
327
328static OgcMesh * retiredMeshes = nullptr;
329
330void * OgcImageRenderer::createMesh(const MeshVertex* vertices, size_t count)
331{
332 if(!vertices)
333 return nullptr;
334 count -= count % 3;
335 if(count == 0)
336 return nullptr;
337
338 // Each GX_Begin() is a 3-byte command followed by 16 bytes per vertex (3 floats and RGBA8).
339 // A display list must start on a 32-byte boundary and be a multiple of 32 bytes long; the
340 // extra 32 is room for the padding GX_EndDispList() adds.
341 const size_t begins = (count + 65534) / 65535;
342 const size_t bytes = (begins * 3 + count * 16 + 63) & ~(size_t)31;
343
344 void * list = memalign(32, bytes);
345 if(!list)
346 return nullptr;
347
348 // Recorded from the current GX state, which is the menu's: the same vertex description and
349 // format drawMesh() relies on. Not tied to the matrix; that is loaded per draw.
350 DCInvalidateRange(list, bytes);
351 GX_BeginDispList(list, bytes);
352 const MeshVertex * v = vertices;
353 size_t left = count;
354 while(left > 0)
355 {
356 const uint16_t n = (uint16_t)(left > 65535 ? 65535 : left);
357
358 GX_Begin(GX_TRIANGLES, GX_VTXFMT0, n);
359 for(uint16_t i = 0; i < n; ++i)
360 {
361 GX_Position3f32(v[i].x, v[i].y, 0.0f);
362 GX_Color4u8(v[i].c.r, v[i].c.g, v[i].c.b, v[i].c.a);
363 }
364 GX_End();
365
366 v += n;
367 left -= n;
368 }
369 const u32 used = GX_EndDispList();
370 if(used == 0) // didn't fit
371 {
372 free(list);
373 return nullptr;
374 }
375 DCFlushRange(list, used);
376
377 OgcMesh * m = (OgcMesh *)malloc(sizeof(OgcMesh));
378 if(!m)
379 {
380 free(list);
381 return nullptr;
382 }
383 m->list = list;
384 m->size = used;
385 m->next = nullptr;
386 return m;
387}
388
390{
391 OgcMesh * m = (OgcMesh *)mesh;
392 if(!m)
393 return;
394
395 // The GPU may not have read the list yet; OgcFreeRetiredMeshes() frees it once it has
396 u32 level;
397 _CPU_ISR_Disable(level);
398 m->next = retiredMeshes;
399 retiredMeshes = m;
400 _CPU_ISR_Restore(level);
401}
402
403void OgcFreeRetiredMeshes()
404{
405 u32 level;
406 _CPU_ISR_Disable(level);
407 OgcMesh * m = retiredMeshes;
408 retiredMeshes = nullptr;
409 _CPU_ISR_Restore(level);
410
411 while(m)
412 {
413 OgcMesh * next = m->next;
414 free(m->list);
415 free(m);
416 m = next;
417 }
418}
419
420void OgcImageRenderer::drawMeshInstance(void * mesh, float x, float y, float scaleX, float scaleY)
421{
422 const OgcMesh * m = (const OgcMesh *)mesh;
423 if(!m)
424 return;
425
426 // Place the mesh with the position matrix, the way drawTexture() places a quad, instead of
427 // transforming every vertex on the CPU
428 Mtx t, mv;
429 guMtxScale(t, scaleX, scaleY, 1.0f);
430 guMtxTransApply(t, t, x, y, 0.0f);
431 guMtxConcat(GXmodelView2D, t, mv);
432 GX_LoadPosMtxImm(mv, GX_PNMTX0);
433 GX_CallDispList(m->list, m->size);
434 GX_LoadPosMtxImm(GXmodelView2D, GX_PNMTX0);
435}
Umbrella header for the platform-agnostic libgui core.
GX-backed GlyphRenderer for GuiTextRenderer.
GX-backed ImageRenderer for GuiImage/GuiImageData textures.
void loadTextureData(void *texture, const uint8_t *rgba, int width, int height) override
Loads raw RGBA8 pixels into the pre-allocated texture.
void * createTexture(int width, int height) override
Allocates an empty hardware-aligned texture buffer.
void destroyTexture(void *texture) override
Destroys a texture created by createTexture.
void drawMeshInstance(void *mesh, float x, float y, float scaleX, float scaleY) override
Draws a mesh from createMesh() fully opaque, as its vertex colors say.
void drawMesh(const MeshVertex *vertices, size_t count) override
Draws a triangle list: three vertices per triangle, each with its own color.
void * createMesh(const MeshVertex *vertices, size_t count) override
Hands a triangle list to the backend to keep in its own ready-to-draw form (a GX display list,...
void drawRectangle(float x, float y, float width, float height, PixelColor color) override
Draws a solid-color rectangle (alpha-blended using color.a).
void drawTexture(void *texture, float xpos, float ypos, uint16_t width, uint16_t height, float degrees, float scaleX, float scaleY, uint8_t alpha) override
Draws a texture created by createTexture.
void destroyMesh(void *mesh) override
Releases a mesh from createMesh().
void shutdown() override
Releases the video backend. Call after every other user of the renderers has finished.
void render() override
Flushes the current frame to the screen and swaps buffers.
void clearScreen(const PixelColor &color) override
Clears the current frame buffer.
void init(int width, int height) override
A single 2D vertex with its own color, in design-canvas coordinates.
Definition Mesh.h:27
float y
Y position (design-canvas pixels).
Definition Mesh.h:29
float x
X position (design-canvas pixels).
Definition Mesh.h:28
PixelColor c
Straight (non-premultiplied) RGBA color.
Definition Mesh.h:30
OgcMesh * next
Link in the retired list.
8-bit-per-channel RGBA color used by the video interfaces.
Definition Mesh.h:16
uint8_t g
Green color component.
Definition Mesh.h:18
uint8_t r
Red color component.
Definition Mesh.h:17
uint8_t b
Blue color component.
Definition Mesh.h:19
uint8_t a
Alpha component.
Definition Mesh.h:20