src/ds/gx.c (view raw)
1/* Copyright (c) 2013-2017 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include <mgba/internal/ds/gx.h>
7
8#include <mgba/internal/ds/ds.h>
9#include <mgba/internal/ds/io.h>
10
11mLOG_DEFINE_CATEGORY(DS_GX, "DS GX");
12
13#define DS_GX_FIFO_SIZE 256
14#define DS_GX_PIPE_SIZE 4
15
16static void DSGXDummyRendererInit(struct DSGXRenderer* renderer);
17static void DSGXDummyRendererReset(struct DSGXRenderer* renderer);
18static void DSGXDummyRendererDeinit(struct DSGXRenderer* renderer);
19static void DSGXDummyRendererSetRAM(struct DSGXRenderer* renderer, struct DSGXVertex* verts, struct DSGXPolygon* polys, unsigned polyCount);
20static void DSGXDummyRendererDrawScanline(struct DSGXRenderer* renderer, int y);
21static void DSGXDummyRendererGetScanline(struct DSGXRenderer* renderer, int y, color_t** output);
22
23static const int32_t _gxCommandCycleBase[DS_GX_CMD_MAX] = {
24 [DS_GX_CMD_NOP] = 0,
25 [DS_GX_CMD_MTX_MODE] = 2,
26 [DS_GX_CMD_MTX_PUSH] = 34,
27 [DS_GX_CMD_MTX_POP] = 72,
28 [DS_GX_CMD_MTX_STORE] = 34,
29 [DS_GX_CMD_MTX_RESTORE] = 72,
30 [DS_GX_CMD_MTX_IDENTITY] = 38,
31 [DS_GX_CMD_MTX_LOAD_4x4] = 68,
32 [DS_GX_CMD_MTX_LOAD_4x3] = 60,
33 [DS_GX_CMD_MTX_MULT_4x4] = 70,
34 [DS_GX_CMD_MTX_MULT_4x3] = 62,
35 [DS_GX_CMD_MTX_MULT_3x3] = 56,
36 [DS_GX_CMD_MTX_SCALE] = 44,
37 [DS_GX_CMD_MTX_TRANS] = 44,
38 [DS_GX_CMD_COLOR] = 2,
39 [DS_GX_CMD_NORMAL] = 18,
40 [DS_GX_CMD_TEXCOORD] = 2,
41 [DS_GX_CMD_VTX_16] = 18,
42 [DS_GX_CMD_VTX_10] = 16,
43 [DS_GX_CMD_VTX_XY] = 16,
44 [DS_GX_CMD_VTX_XZ] = 16,
45 [DS_GX_CMD_VTX_YZ] = 16,
46 [DS_GX_CMD_VTX_DIFF] = 16,
47 [DS_GX_CMD_POLYGON_ATTR] = 2,
48 [DS_GX_CMD_TEXIMAGE_PARAM] = 2,
49 [DS_GX_CMD_PLTT_BASE] = 2,
50 [DS_GX_CMD_DIF_AMB] = 8,
51 [DS_GX_CMD_SPE_EMI] = 8,
52 [DS_GX_CMD_LIGHT_VECTOR] = 12,
53 [DS_GX_CMD_LIGHT_COLOR] = 2,
54 [DS_GX_CMD_SHININESS] = 64,
55 [DS_GX_CMD_BEGIN_VTXS] = 2,
56 [DS_GX_CMD_END_VTXS] = 2,
57 [DS_GX_CMD_SWAP_BUFFERS] = 784,
58 [DS_GX_CMD_VIEWPORT] = 2,
59 [DS_GX_CMD_BOX_TEST] = 206,
60 [DS_GX_CMD_POS_TEST] = 18,
61 [DS_GX_CMD_VEC_TEST] = 10,
62};
63
64static const int32_t _gxCommandParams[DS_GX_CMD_MAX] = {
65 [DS_GX_CMD_MTX_MODE] = 1,
66 [DS_GX_CMD_MTX_POP] = 1,
67 [DS_GX_CMD_MTX_STORE] = 1,
68 [DS_GX_CMD_MTX_RESTORE] = 1,
69 [DS_GX_CMD_MTX_LOAD_4x4] = 16,
70 [DS_GX_CMD_MTX_LOAD_4x3] = 12,
71 [DS_GX_CMD_MTX_MULT_4x4] = 16,
72 [DS_GX_CMD_MTX_MULT_4x3] = 12,
73 [DS_GX_CMD_MTX_MULT_3x3] = 9,
74 [DS_GX_CMD_MTX_SCALE] = 3,
75 [DS_GX_CMD_MTX_TRANS] = 3,
76 [DS_GX_CMD_COLOR] = 1,
77 [DS_GX_CMD_NORMAL] = 1,
78 [DS_GX_CMD_TEXCOORD] = 1,
79 [DS_GX_CMD_VTX_16] = 2,
80 [DS_GX_CMD_VTX_10] = 1,
81 [DS_GX_CMD_VTX_XY] = 1,
82 [DS_GX_CMD_VTX_XZ] = 1,
83 [DS_GX_CMD_VTX_YZ] = 1,
84 [DS_GX_CMD_VTX_DIFF] = 1,
85 [DS_GX_CMD_POLYGON_ATTR] = 1,
86 [DS_GX_CMD_TEXIMAGE_PARAM] = 1,
87 [DS_GX_CMD_PLTT_BASE] = 1,
88 [DS_GX_CMD_DIF_AMB] = 1,
89 [DS_GX_CMD_SPE_EMI] = 1,
90 [DS_GX_CMD_LIGHT_VECTOR] = 1,
91 [DS_GX_CMD_LIGHT_COLOR] = 1,
92 [DS_GX_CMD_SHININESS] = 32,
93 [DS_GX_CMD_BEGIN_VTXS] = 1,
94 [DS_GX_CMD_SWAP_BUFFERS] = 1,
95 [DS_GX_CMD_VIEWPORT] = 1,
96 [DS_GX_CMD_BOX_TEST] = 3,
97 [DS_GX_CMD_POS_TEST] = 2,
98 [DS_GX_CMD_VEC_TEST] = 1,
99};
100
101static struct DSGXRenderer dummyRenderer = {
102 .init = DSGXDummyRendererInit,
103 .reset = DSGXDummyRendererReset,
104 .deinit = DSGXDummyRendererDeinit,
105 .setRAM = DSGXDummyRendererSetRAM,
106 .drawScanline = DSGXDummyRendererDrawScanline,
107 .getScanline = DSGXDummyRendererGetScanline,
108};
109
110static void _pullPipe(struct DSGX* gx) {
111 if (CircleBufferSize(&gx->fifo) >= sizeof(struct DSGXEntry)) {
112 struct DSGXEntry entry = { 0 };
113 CircleBufferRead8(&gx->fifo, (int8_t*) &entry.command);
114 CircleBufferRead8(&gx->fifo, (int8_t*) &entry.params[0]);
115 CircleBufferRead8(&gx->fifo, (int8_t*) &entry.params[1]);
116 CircleBufferRead8(&gx->fifo, (int8_t*) &entry.params[2]);
117 CircleBufferRead8(&gx->fifo, (int8_t*) &entry.params[3]);
118 CircleBufferWrite8(&gx->pipe, entry.command);
119 CircleBufferWrite8(&gx->pipe, entry.params[0]);
120 CircleBufferWrite8(&gx->pipe, entry.params[1]);
121 CircleBufferWrite8(&gx->pipe, entry.params[2]);
122 CircleBufferWrite8(&gx->pipe, entry.params[3]);
123 }
124 if (CircleBufferSize(&gx->fifo) >= sizeof(struct DSGXEntry)) {
125 struct DSGXEntry entry = { 0 };
126 CircleBufferRead8(&gx->fifo, (int8_t*) &entry.command);
127 CircleBufferRead8(&gx->fifo, (int8_t*) &entry.params[0]);
128 CircleBufferRead8(&gx->fifo, (int8_t*) &entry.params[1]);
129 CircleBufferRead8(&gx->fifo, (int8_t*) &entry.params[2]);
130 CircleBufferRead8(&gx->fifo, (int8_t*) &entry.params[3]);
131 CircleBufferWrite8(&gx->pipe, entry.command);
132 CircleBufferWrite8(&gx->pipe, entry.params[0]);
133 CircleBufferWrite8(&gx->pipe, entry.params[1]);
134 CircleBufferWrite8(&gx->pipe, entry.params[2]);
135 CircleBufferWrite8(&gx->pipe, entry.params[3]);
136 }
137}
138
139static void _fifoRun(struct mTiming* timing, void* context, uint32_t cyclesLate) {
140 struct DSGX* gx = context;
141 uint32_t cycles;
142 bool first = true;
143 while (true) {
144 if (gx->swapBuffers) {
145 break;
146 }
147
148 DSRegGXSTAT gxstat = gx->p->memory.io9[DS9_REG_GXSTAT_LO >> 1];
149 int pvMatrixPointer = DSRegGXSTATGetPVMatrixStackLevel(gxstat);
150 int projMatrixPointer = DSRegGXSTATGetProjMatrixStackLevel(gxstat);
151
152 if (CircleBufferSize(&gx->pipe) <= 2 * sizeof(struct DSGXEntry)) {
153 _pullPipe(gx);
154 }
155
156 struct DSGXEntry entry = { 0 };
157 CircleBufferDump(&gx->pipe, (int8_t*) &entry.command, 1);
158 cycles = _gxCommandCycleBase[entry.command];
159
160 if (first) {
161 first = false;
162 } else if (cycles > cyclesLate) {
163 break;
164 }
165 CircleBufferRead8(&gx->pipe, (int8_t*) &entry.command);
166 CircleBufferRead8(&gx->pipe, (int8_t*) &entry.params[0]);
167 CircleBufferRead8(&gx->pipe, (int8_t*) &entry.params[1]);
168 CircleBufferRead8(&gx->pipe, (int8_t*) &entry.params[2]);
169 CircleBufferRead8(&gx->pipe, (int8_t*) &entry.params[3]);
170
171 switch (entry.command) {
172 case DS_GX_CMD_MTX_MODE:
173 if (entry.params[0] < 4) {
174 gx->mtxMode = entry.params[0];
175 } else {
176 mLOG(DS_GX, GAME_ERROR, "Invalid GX MTX_MODE %02X", entry.params[0]);
177 }
178 break;
179 case DS_GX_CMD_MTX_IDENTITY:
180 switch (gx->mtxMode) {
181 case 0:
182 DSGXMtxIdentity(&gx->projMatrix);
183 break;
184 case 2:
185 DSGXMtxIdentity(&gx->vecMatrix);
186 // Fall through
187 case 1:
188 DSGXMtxIdentity(&gx->posMatrix);
189 break;
190 case 3:
191 DSGXMtxIdentity(&gx->texMatrix);
192 break;
193 }
194 break;
195 case DS_GX_CMD_MTX_PUSH:
196 switch (gx->mtxMode) {
197 case 0:
198 memcpy(&gx->projMatrixStack, &gx->projMatrix, sizeof(gx->projMatrix));
199 ++projMatrixPointer;
200 break;
201 case 2:
202 memcpy(&gx->vecMatrixStack[pvMatrixPointer & 0x1F], &gx->vecMatrix, sizeof(gx->vecMatrix));
203 // Fall through
204 case 1:
205 memcpy(&gx->posMatrixStack[pvMatrixPointer & 0x1F], &gx->posMatrix, sizeof(gx->posMatrix));
206 ++pvMatrixPointer;
207 break;
208 case 3:
209 mLOG(DS_GX, STUB, "Unimplemented GX MTX_PUSH mode");
210 break;
211 }
212 break;
213 case DS_GX_CMD_MTX_POP: {
214 int8_t offset = entry.params[0];
215 offset <<= 2;
216 offset >>= 2;
217 switch (gx->mtxMode) {
218 case 0:
219 projMatrixPointer -= offset;
220 memcpy(&gx->projMatrix, &gx->projMatrixStack, sizeof(gx->projMatrix));
221 break;
222 case 1:
223 pvMatrixPointer -= offset;
224 memcpy(&gx->posMatrix, &gx->posMatrixStack[pvMatrixPointer & 0x1F], sizeof(gx->posMatrix));
225 break;
226 case 2:
227 pvMatrixPointer -= offset;
228 memcpy(&gx->vecMatrix, &gx->vecMatrixStack[pvMatrixPointer & 0x1F], sizeof(gx->vecMatrix));
229 memcpy(&gx->posMatrix, &gx->posMatrixStack[pvMatrixPointer & 0x1F], sizeof(gx->posMatrix));
230 break;
231 case 3:
232 mLOG(DS_GX, STUB, "Unimplemented GX MTX_POP mode");
233 break;
234 }
235 break;
236 }
237 case DS_GX_CMD_SWAP_BUFFERS:
238 gx->swapBuffers = true;
239 break;
240 default:
241 mLOG(DS_GX, STUB, "Unimplemented GX command %02X:%02X %02X %02X %02X", entry.command, entry.params[0], entry.params[1], entry.params[2], entry.params[3]);
242 break;
243 }
244
245 gxstat = DSRegGXSTATSetPVMatrixStackLevel(gxstat, pvMatrixPointer);
246 gxstat = DSRegGXSTATSetProjMatrixStackLevel(gxstat, projMatrixPointer);
247 gxstat = DSRegGXSTATTestFillMatrixStackError(gxstat, projMatrixPointer || pvMatrixPointer >= 0x1F);
248 gx->p->memory.io9[DS9_REG_GXSTAT_LO >> 1] = gxstat;
249
250 if (cyclesLate >= cycles) {
251 cyclesLate -= cycles;
252 }
253 if (!CircleBufferSize(&gx->pipe)) {
254 cycles = 0;
255 break;
256 }
257 }
258 DSGXUpdateGXSTAT(gx);
259 if (cycles && !gx->swapBuffers) {
260 mTimingSchedule(timing, &gx->fifoEvent, cycles - cyclesLate);
261 }
262}
263
264void DSGXInit(struct DSGX* gx) {
265 gx->renderer = &dummyRenderer;
266 CircleBufferInit(&gx->fifo, sizeof(struct DSGXEntry) * DS_GX_FIFO_SIZE);
267 CircleBufferInit(&gx->pipe, sizeof(struct DSGXEntry) * DS_GX_PIPE_SIZE);
268 gx->fifoEvent.name = "DS GX FIFO";
269 gx->fifoEvent.priority = 0xC;
270 gx->fifoEvent.context = gx;
271 gx->fifoEvent.callback = _fifoRun;
272}
273
274void DSGXDeinit(struct DSGX* gx) {
275 DSGXAssociateRenderer(gx, &dummyRenderer);
276 CircleBufferDeinit(&gx->fifo);
277 CircleBufferDeinit(&gx->pipe);
278}
279
280void DSGXReset(struct DSGX* gx) {
281 CircleBufferClear(&gx->fifo);
282 CircleBufferClear(&gx->pipe);
283 DSGXMtxIdentity(&gx->projMatrix);
284 DSGXMtxIdentity(&gx->texMatrix);
285 DSGXMtxIdentity(&gx->posMatrix);
286 DSGXMtxIdentity(&gx->vecMatrix);
287
288 DSGXMtxIdentity(&gx->projMatrixStack);
289 DSGXMtxIdentity(&gx->texMatrixStack);
290 int i;
291 for (i = 0; i < 32; ++i) {
292 DSGXMtxIdentity(&gx->posMatrixStack[i]);
293 DSGXMtxIdentity(&gx->vecMatrixStack[i]);
294 }
295 gx->swapBuffers = false;
296 gx->bufferIndex = 0;
297 gx->mtxMode = 0;
298
299 memset(gx->outstandingParams, 0, sizeof(gx->outstandingParams));
300 memset(gx->outstandingCommand, 0, sizeof(gx->outstandingCommand));
301}
302
303void DSGXAssociateRenderer(struct DSGX* gx, struct DSGXRenderer* renderer) {
304 gx->renderer->deinit(gx->renderer);
305 gx->renderer = renderer;
306 gx->renderer->init(gx->renderer);
307}
308
309void DSGXUpdateGXSTAT(struct DSGX* gx) {
310 uint32_t value = gx->p->memory.io9[DS9_REG_GXSTAT_HI >> 1] << 16;
311 value = DSRegGXSTATIsDoIRQ(value);
312
313 size_t entries = CircleBufferSize(&gx->fifo) / sizeof(struct DSGXEntry);
314 // XXX
315 if (gx->swapBuffers) {
316 entries++;
317 }
318 value = DSRegGXSTATSetFIFOEntries(value, entries);
319 value = DSRegGXSTATSetFIFOLtHalf(value, entries < (DS_GX_FIFO_SIZE / 2));
320 value = DSRegGXSTATSetFIFOEmpty(value, entries == 0);
321
322 if ((DSRegGXSTATGetDoIRQ(value) == 1 && entries < (DS_GX_FIFO_SIZE / 2)) ||
323 (DSRegGXSTATGetDoIRQ(value) == 2 && entries == 0)) {
324 DSRaiseIRQ(gx->p->ds9.cpu, gx->p->ds9.memory.io, DS_IRQ_GEOM_FIFO);
325 }
326
327 value = DSRegGXSTATSetBusy(value, mTimingIsScheduled(&gx->p->ds9.timing, &gx->fifoEvent) || gx->swapBuffers);
328
329 gx->p->memory.io9[DS9_REG_GXSTAT_HI >> 1] = value >> 16;
330}
331
332static void DSGXUnpackCommand(struct DSGX* gx, uint32_t command) {
333 gx->outstandingCommand[0] = command;
334 gx->outstandingCommand[1] = command >> 8;
335 gx->outstandingCommand[2] = command >> 16;
336 gx->outstandingCommand[3] = command >> 24;
337 if (gx->outstandingCommand[0] >= DS_GX_CMD_MAX) {
338 gx->outstandingCommand[0] = 0;
339 }
340 if (gx->outstandingCommand[1] >= DS_GX_CMD_MAX) {
341 gx->outstandingCommand[1] = 0;
342 }
343 if (gx->outstandingCommand[2] >= DS_GX_CMD_MAX) {
344 gx->outstandingCommand[2] = 0;
345 }
346 if (gx->outstandingCommand[3] >= DS_GX_CMD_MAX) {
347 gx->outstandingCommand[3] = 0;
348 }
349 gx->outstandingParams[0] = _gxCommandParams[gx->outstandingCommand[0]];
350 gx->outstandingParams[1] = _gxCommandParams[gx->outstandingCommand[1]];
351 gx->outstandingParams[2] = _gxCommandParams[gx->outstandingCommand[2]];
352 gx->outstandingParams[3] = _gxCommandParams[gx->outstandingCommand[3]];
353}
354
355static void DSGXWriteFIFO(struct DSGX* gx, struct DSGXEntry entry) {
356 if (gx->outstandingParams[0]) {
357 entry.command = gx->outstandingCommand[0];
358 --gx->outstandingParams[0];
359 if (!gx->outstandingParams[0]) {
360 // TODO: improve this
361 memmove(&gx->outstandingParams[0], &gx->outstandingParams[1], sizeof(gx->outstandingParams[0]) * 3);
362 memmove(&gx->outstandingCommand[0], &gx->outstandingCommand[1], sizeof(gx->outstandingCommand[0]) * 3);
363 gx->outstandingParams[3] = 0;
364 }
365 } else {
366 gx->outstandingCommand[0] = entry.command;
367 gx->outstandingParams[0] = _gxCommandParams[entry.command];
368 if (gx->outstandingParams[0]) {
369 --gx->outstandingParams[0];
370 }
371 }
372 uint32_t cycles = _gxCommandCycleBase[entry.command];
373 if (!cycles) {
374 return;
375 }
376 if (CircleBufferSize(&gx->fifo) == 0 && CircleBufferSize(&gx->pipe) < (DS_GX_PIPE_SIZE * sizeof(entry))) {
377 CircleBufferWrite8(&gx->pipe, entry.command);
378 CircleBufferWrite8(&gx->pipe, entry.params[0]);
379 CircleBufferWrite8(&gx->pipe, entry.params[1]);
380 CircleBufferWrite8(&gx->pipe, entry.params[2]);
381 CircleBufferWrite8(&gx->pipe, entry.params[3]);
382 } else if (CircleBufferSize(&gx->fifo) < (DS_GX_FIFO_SIZE * sizeof(entry))) {
383 CircleBufferWrite8(&gx->fifo, entry.command);
384 CircleBufferWrite8(&gx->fifo, entry.params[0]);
385 CircleBufferWrite8(&gx->fifo, entry.params[1]);
386 CircleBufferWrite8(&gx->fifo, entry.params[2]);
387 CircleBufferWrite8(&gx->fifo, entry.params[3]);
388 } else {
389 mLOG(DS_GX, STUB, "Unimplemented GX full");
390 }
391 if (!gx->swapBuffers && !mTimingIsScheduled(&gx->p->ds9.timing, &gx->fifoEvent)) {
392 mTimingSchedule(&gx->p->ds9.timing, &gx->fifoEvent, cycles);
393 }
394}
395
396uint16_t DSGXWriteRegister(struct DSGX* gx, uint32_t address, uint16_t value) {
397 uint16_t oldValue = gx->p->memory.io9[address >> 1];
398 switch (address) {
399 case DS9_REG_DISP3DCNT:
400 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value);
401 break;
402 case DS9_REG_GXSTAT_LO:
403 value = DSRegGXSTATIsMatrixStackError(value);
404 if (value) {
405 oldValue = DSRegGXSTATClearMatrixStackError(oldValue);
406 oldValue = DSRegGXSTATClearProjMatrixStackLevel(oldValue);
407 }
408 value = oldValue;
409 break;
410 case DS9_REG_GXSTAT_HI:
411 value = DSRegGXSTATIsDoIRQ(value << 16) >> 16;
412 gx->p->memory.io9[address >> 1] = value;
413 DSGXUpdateGXSTAT(gx);
414 value = gx->p->memory.io9[address >> 1];
415 break;
416 default:
417 if (address < DS9_REG_GXFIFO_00) {
418 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value);
419 } else if (address <= DS9_REG_GXFIFO_1F) {
420 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value);
421 } else if (address < DS9_REG_GXSTAT_LO) {
422 struct DSGXEntry entry = {
423 .command = (address & 0x1FC) >> 2,
424 .params = {
425 value,
426 value >> 8,
427 }
428 };
429 if (entry.command < DS_GX_CMD_MAX) {
430 DSGXWriteFIFO(gx, entry);
431 }
432 } else {
433 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value);
434 }
435 break;
436 }
437 return value;
438}
439
440uint32_t DSGXWriteRegister32(struct DSGX* gx, uint32_t address, uint32_t value) {
441 switch (address) {
442 case DS9_REG_DISP3DCNT:
443 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%08X", address, value);
444 break;
445 case DS9_REG_GXSTAT_LO:
446 value = (value & 0xFFFF0000) | DSGXWriteRegister(gx, DS9_REG_GXSTAT_LO, value);
447 value = (value & 0x0000FFFF) | (DSGXWriteRegister(gx, DS9_REG_GXSTAT_HI, value >> 16) << 16);
448 break;
449 default:
450 if (address < DS9_REG_GXFIFO_00) {
451 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%08X", address, value);
452 } else if (address <= DS9_REG_GXFIFO_1F) {
453 if (gx->outstandingParams[0]) {
454 struct DSGXEntry entry = {
455 .command = gx->outstandingCommand[0],
456 .params = {
457 value,
458 value >> 8,
459 value >> 16,
460 value >> 24
461 }
462 };
463 DSGXWriteFIFO(gx, entry);
464 } else {
465 DSGXUnpackCommand(gx, value);
466 }
467 } else if (address < DS9_REG_GXSTAT_LO) {
468 struct DSGXEntry entry = {
469 .command = (address & 0x1FC) >> 2,
470 .params = {
471 value,
472 value >> 8,
473 value >> 16,
474 value >> 24
475 }
476 };
477 DSGXWriteFIFO(gx, entry);
478 } else {
479 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%08X", address, value);
480 }
481 break;
482 }
483 return value;
484}
485
486void DSGXSwapBuffers(struct DSGX* gx) {
487 mLOG(DS_GX, STUB, "Unimplemented GX swap buffers");
488 gx->swapBuffers = false;
489
490 // TODO
491 DSGXUpdateGXSTAT(gx);
492 if (CircleBufferSize(&gx->fifo)) {
493 mTimingSchedule(&gx->p->ds9.timing, &gx->fifoEvent, 0);
494 }
495}
496
497static void DSGXDummyRendererInit(struct DSGXRenderer* renderer) {
498 UNUSED(renderer);
499 // Nothing to do
500}
501
502static void DSGXDummyRendererReset(struct DSGXRenderer* renderer) {
503 UNUSED(renderer);
504 // Nothing to do
505}
506
507static void DSGXDummyRendererDeinit(struct DSGXRenderer* renderer) {
508 UNUSED(renderer);
509 // Nothing to do
510}
511
512static void DSGXDummyRendererSetRAM(struct DSGXRenderer* renderer, struct DSGXVertex* verts, struct DSGXPolygon* polys, unsigned polyCount) {
513 UNUSED(renderer);
514 UNUSED(verts);
515 UNUSED(polys);
516 UNUSED(polyCount);
517 // Nothing to do
518}
519
520static void DSGXDummyRendererDrawScanline(struct DSGXRenderer* renderer, int y) {
521 UNUSED(renderer);
522 UNUSED(y);
523 // Nothing to do
524}
525
526static void DSGXDummyRendererGetScanline(struct DSGXRenderer* renderer, int y, color_t** output) {
527 UNUSED(renderer);
528 UNUSED(y);
529 *output = NULL;
530 // Nothing to do
531}