1 /*---------------------------------------------------------------------------*
2 Project: TwlSDK - GX - demos - UnitTours/Sub_Double3D
3 File: main.c
4
5 Copyright 2003-2009 Nintendo. All rights reserved.
6
7 These coded instructions, statements, and computer programs contain
8 proprietary information of Nintendo of America Inc. and/or Nintendo
9 Company Ltd., and are protected by Federal copyright law. They may
10 not be disclosed to third parties or copied or duplicated in any form,
11 in whole or in part, without the prior written consent of Nintendo.
12
13 $Date:: 2009-06-04#$
14 $Rev: 10698 $
15 $Author: okubata_ryoma $
16 *---------------------------------------------------------------------------*/
17
18 //---------------------------------------------------------------------------
19 // A sample that displays moving 3D models on both LCDs.
20 //
21 // Both LCDs display a different rotating 3D cube.
22 // The sub 2D engine displays the captured image rendered by the main engine,
23 // making use of direct color bitmap BG/bitmap OBJ.
24 // Note that the output LCDs are swapped every frame.
25 //
26 // HOWTO:
27 // 1. Set up OAM for the sub 2D engine, to display captured images correctly.
28 // 2. (Frame 2N): Assign VRAM-C to BG of the sub 2D engine,
29 // and display the captured image on VRAM-C
30 // through direct color bitmap BG.
31 // Capture the 3D plane and store it on VRAM-D.
32 // (Frame 2N+1): Assign VRAM-D to OBJ of the sub 2D engine,
33 // and display the captured image on VRAM-D
34 // through bitmap OBJs.
35 // Capture the 3D plane and store it on VRAM-C.
36 //---------------------------------------------------------------------------
37
38 #ifdef SDK_TWL
39 #include <twl.h>
40 #else
41 #include <nitro.h>
42 #endif
43 #include "DEMO.h"
44
45 #define STACK_SIZE 1024
46 #define THREAD1_PRIO (OS_THREAD_LAUNCHER_PRIORITY - 6)
47 void proc1(void *p1);
48 OSThread thread1;
49 u32 stack1[STACK_SIZE / sizeof(u32)];
50
51 static GXOamAttr sOamBak[128];
52
53 BOOL flip_flag = TRUE; // Flip switch flag
54 BOOL swap = FALSE; // SwapBuffers execution flag
55 int attr = GX_POLYGON_ATTR_MISC_NONE;
56
57 s16 gCubeGeometry[3 * 8] = {
58 FX16_ONE, FX16_ONE, FX16_ONE,
59 FX16_ONE, FX16_ONE, -FX16_ONE,
60 FX16_ONE, -FX16_ONE, FX16_ONE,
61 FX16_ONE, -FX16_ONE, -FX16_ONE,
62 -FX16_ONE, FX16_ONE, FX16_ONE,
63 -FX16_ONE, FX16_ONE, -FX16_ONE,
64 -FX16_ONE, -FX16_ONE, FX16_ONE,
65 -FX16_ONE, -FX16_ONE, -FX16_ONE
66 };
67
68 GXRgb gCubeColor[8] = {
69 GX_RGB(31, 31, 31),
70 GX_RGB(31, 31, 0),
71 GX_RGB(31, 0, 31),
72 GX_RGB(31, 0, 0),
73 GX_RGB(0, 31, 31),
74 GX_RGB(0, 31, 0),
75 GX_RGB(0, 0, 31),
76 GX_RGB(0, 0, 0)
77 };
78
79 /* ---------------------------------------------------
80 **Operating modes
81 This demo runs in 2 modes each time the Y Button is pressed.
82 A 30-fps mode with deflickering is used first, followed by a 15-fps mode without deflickering.
83
84
85
86 *Flickering
87 Flickering occurs when a display capture feature is used to capture a 3D image and the original image and the captured image are displayed one after the other.
88
89
90 When the display capture feature is used to capture a 3D image, the least-significant bit of the original image's RGB (6:6:6) data will be 0, so the result will be RGB (5:5:5).
91
92 Therefore, alternating between displaying the original image and the captured image will cause flickering.
93
94 *How to avoid problems
95 You must display images captured in VRAM on both the upper and lower screens instead of displaying 3D images directly.
96 To handle this, this demo displays the VRAM with its captured data unchanged.
97
98 *Polygon Edge Flickering
99 Even if the aforementioned flickering countermeasures have been taken, some settings may cause polygon edges to flicker.
100 This is caused by alpha values.
101
102 An alpha value of 0 can be stored in the color buffer along with a valid RGB value according to the following formula: color buffer alpha = (polygon alpha x (antialias factor + correction value 1)) / 32.
103
104 During 3D captures afterward, 0 will also be stored in the VRAM alpha bit only when the capture data's alpha value is 0.
105
106 (You can actually dump VRAM and check polygon edges with an alpha value of 0.)
107
108 This alpha bit is disabled in VRAM display mode, so pixels corresponding to the polygon edge will be displayed.
109 The alpha bit is enabled for BG display (graphics display mode), so these bits will be handled as omitted and the backdrop color will be displayed.
110
111
112 This difference causes polygon edges to flicker.
113 (Although it is difficult to see when the clear color and the backdrop color are the same, if the clear color is changed, for example, it will be possible to see that these colors are displayed alternating as the background color.)
114
115
116 *How to avoid problems
117 If the value specified as argument 'a' to the GX_SetCapture function is GX_CAPTURE_SRCA_2D3D, the display will be the same in the aforementioned scenario and when 2D and 3D graphics are combined, allowing you to avoid flickering.
118
119 ---------------------------------------------------- */
120 static int operationMode = 0;
121
Color(int idx)122 static void Color(int idx)
123 {
124 G3_Color(gCubeColor[idx]);
125 }
126
Vtx(int idx)127 static void Vtx(int idx)
128 {
129 G3_Vtx(gCubeGeometry[idx * 3], gCubeGeometry[idx * 3 + 1], gCubeGeometry[idx * 3 + 2]);
130 }
131
Quad(int idx0,int idx1,int idx2,int idx3)132 static void Quad(int idx0, int idx1, int idx2, int idx3)
133 {
134 Vtx(idx0);
135 Vtx(idx1);
136 Vtx(idx2);
137 Vtx(idx3);
138 }
139
ColVtxQuad(int idx0,int idx1,int idx2,int idx3)140 static void ColVtxQuad(int idx0, int idx1, int idx2, int idx3)
141 {
142 Color(idx0);
143 Vtx(idx0);
144 Color(idx1);
145 Vtx(idx1);
146 Color(idx2);
147 Vtx(idx2);
148 Color(idx3);
149 Vtx(idx3);
150 }
151
drawLeftCube(u16 Rotate)152 static void drawLeftCube(u16 Rotate)
153 {
154 G3_PushMtx();
155
156 // Rotate and translate
157 G3_Translate(-3 << (FX32_SHIFT - 1), 0, 0);
158 {
159 fx16 s = FX_SinIdx(Rotate);
160 fx16 c = FX_CosIdx(Rotate);
161
162 G3_RotX(s, c);
163 G3_RotY(s, c);
164 G3_RotZ(s, c);
165 }
166
167 G3_MaterialColorDiffAmb(GX_RGB(31, 31, 31), // Diffuse
168 GX_RGB(16, 16, 16), // Ambient
169 FALSE // Use diffuse as vtx color if TRUE
170 );
171
172 G3_MaterialColorSpecEmi(GX_RGB(16, 16, 16), // Specular
173 GX_RGB(0, 0, 0), // Emission
174 FALSE // Use shininess table if TRUE
175 );
176
177 G3_PolygonAttr(GX_LIGHTMASK_NONE, // Disable lights
178 GX_POLYGONMODE_MODULATE, // Modulation mode
179 GX_CULL_BACK, // Cull back
180 0, // Polygon ID (0-63)
181 31, // Alpha (0-31)
182 attr // OR of GXPolygonAttrMisc's value
183 );
184
185 //---------------------------------------------------------------------------
186 // Draw a cube:
187 // Specify different colors for the planes
188 //---------------------------------------------------------------------------
189
190 G3_Begin(GX_BEGIN_QUADS);
191
192 {
193 Color(3);
194 Quad(2, 0, 4, 6);
195
196 Color(4);
197 Quad(7, 5, 1, 3);
198
199 Color(5);
200 Quad(6, 4, 5, 7);
201
202 Color(2);
203 Quad(3, 1, 0, 2);
204
205 Color(6);
206 Quad(5, 4, 0, 1);
207
208 Color(1);
209 Quad(6, 7, 3, 2);
210 }
211
212 G3_End();
213
214 G3_PopMtx(1);
215 }
216
drawRightCube(u16 Rotate)217 static void drawRightCube(u16 Rotate)
218 {
219 G3_PushMtx();
220
221 // Rotate and translate
222 G3_Translate(3 << (FX32_SHIFT - 1), 0, 0);
223
224 {
225 fx16 s = FX_SinIdx(Rotate);
226 fx16 c = FX_CosIdx(Rotate);
227
228 G3_RotX(s, c);
229 G3_RotY(s, c);
230 G3_RotZ(s, c);
231 }
232
233 G3_MaterialColorDiffAmb(GX_RGB(31, 31, 31), // Diffuse
234 GX_RGB(16, 16, 16), // Ambient
235 FALSE // Use diffuse as vtx color if TRUE
236 );
237
238 G3_MaterialColorSpecEmi(GX_RGB(16, 16, 16), // Specular
239 GX_RGB(0, 0, 0), // Emission
240 FALSE // Use shininess table if TRUE
241 );
242
243 G3_PolygonAttr(GX_LIGHTMASK_NONE, // Disable lights
244 GX_POLYGONMODE_MODULATE, // Modulation mode
245 GX_CULL_BACK, // Cull back
246 0, // Polygon ID (0-63)
247 31, // Alpha (0-31)
248 attr // OR of GXPolygonAttrMisc's value
249 );
250
251 //---------------------------------------------------------------------------
252 // Draw a cube:
253 // Specify different colors for the vertices.
254 //---------------------------------------------------------------------------
255 G3_Begin(GX_BEGIN_QUADS);
256 {
257 ColVtxQuad(2, 0, 4, 6);
258 ColVtxQuad(7, 5, 1, 3);
259 ColVtxQuad(6, 4, 5, 7);
260 ColVtxQuad(3, 1, 0, 2);
261 ColVtxQuad(5, 4, 0, 1);
262 ColVtxQuad(6, 7, 3, 2);
263 }
264 G3_End();
265
266 G3_PopMtx(1);
267
268 }
269
setupFrame2N(void)270 static void setupFrame2N(void)
271 {
272
273 GX_SetDispSelect(GX_DISP_SELECT_MAIN_SUB);
274
275 (void)GX_ResetBankForSubOBJ();
276 GX_SetBankForSubBG(GX_VRAM_SUB_BG_128_C);
277 GX_SetBankForLCDC(GX_VRAM_LCDC_D);
278 GX_SetCapture(GX_CAPTURE_SIZE_256x192,
279 GX_CAPTURE_MODE_A,
280 GX_CAPTURE_SRCA_2D3D, (GXCaptureSrcB)0, GX_CAPTURE_DEST_VRAM_D_0x00000, 16, 0);
281 switch (operationMode)
282 {
283 case 0:
284 GX_SetGraphicsMode(GX_DISPMODE_VRAM_D, GX_BGMODE_0, GX_BG0_AS_3D); // Method that uses deflickering
285 break;
286 case 1:
287 GX_SetGraphicsMode(GX_DISPMODE_GRAPHICS, GX_BGMODE_0, GX_BG0_AS_3D); // Method that does not use deflickering
288 break;
289 }
290 GX_SetVisiblePlane(GX_PLANEMASK_BG0);
291 G2_SetBG0Priority(0);
292
293 GXS_SetGraphicsMode(GX_BGMODE_5);
294 GXS_SetVisiblePlane(GX_PLANEMASK_BG2);
295 G2S_SetBG2ControlDCBmp(GX_BG_SCRSIZE_DCBMP_256x256,
296 GX_BG_AREAOVER_XLU, GX_BG_BMPSCRBASE_0x00000);
297 G2S_SetBG2Priority(0);
298 G2S_BG2Mosaic(FALSE);
299 }
300
setupFrame2N_1(void)301 static void setupFrame2N_1(void)
302 {
303 GX_SetDispSelect(GX_DISP_SELECT_SUB_MAIN);
304 (void)GX_ResetBankForSubBG();
305 GX_SetBankForSubOBJ(GX_VRAM_SUB_OBJ_128_D);
306 GX_SetBankForLCDC(GX_VRAM_LCDC_C);
307 GX_SetCapture(GX_CAPTURE_SIZE_256x192,
308 GX_CAPTURE_MODE_A,
309 GX_CAPTURE_SRCA_2D3D, (GXCaptureSrcB)0, GX_CAPTURE_DEST_VRAM_C_0x00000, 16, 0);
310
311 switch (operationMode)
312 {
313 case 0:
314 GX_SetGraphicsMode(GX_DISPMODE_VRAM_C, GX_BGMODE_0, GX_BG0_AS_3D); // Method that uses deflickering
315 break;
316 case 1:
317 GX_SetGraphicsMode(GX_DISPMODE_GRAPHICS, GX_BGMODE_0, GX_BG0_AS_3D); // Method that does not use deflickering
318 break;
319 }
320 GX_SetVisiblePlane(GX_PLANEMASK_BG0);
321 G2_SetBG0Priority(0);
322
323 GXS_SetGraphicsMode(GX_BGMODE_5);
324 GXS_SetVisiblePlane(GX_PLANEMASK_OBJ);
325 }
326
setupSubOAM(void)327 static void setupSubOAM(void)
328 {
329 int i;
330 int x, y;
331 int idx = 0;
332
333 GXS_SetOBJVRamModeBmp(GX_OBJVRAMMODE_BMP_2D_W256);
334
335 for (i = 0; i < 128; ++i)
336 {
337 sOamBak[i].attr01 = 0;
338 sOamBak[i].attr23 = 0;
339 }
340
341 for (y = 0; y < 192; y += 64)
342 {
343 for (x = 0; x < 256; x += 64, idx++)
344 {
345 G2_SetOBJAttr(&sOamBak[idx],
346 x,
347 y,
348 0,
349 GX_OAM_MODE_BITMAPOBJ,
350 FALSE,
351 GX_OAM_EFFECT_NONE,
352 GX_OAM_SHAPE_64x64, GX_OAM_COLOR_16, (y / 8) * 32 + (x / 8), 15, 0);
353 }
354 }
355
356 DC_FlushRange(&sOamBak[0], sizeof(sOamBak));
357 /* I/O register is accessed using DMA operation, so cache wait is not needed */
358 // DC_WaitWriteBufferEmpty();
359 GXS_LoadOAM(&sOamBak[0], 0, sizeof(sOamBak));
360
361 }
362
363 //---------------------------------------------------------------------------
364 // Enable the SwapBuffers execution flag
365 //---------------------------------------------------------------------------
SetSwapBuffersflag(void)366 static void SetSwapBuffersflag(void)
367 {
368 OSIntrMode old = OS_DisableInterrupts(); // Interrupts prohibited
369 G3_SwapBuffers(GX_SORTMODE_AUTO, GX_BUFFERMODE_Z);
370 swap = TRUE;
371 (void)OS_RestoreInterrupts(old);
372 }
373
IgnoreRemoval(void)374 static BOOL IgnoreRemoval(void)
375 {
376 return FALSE;
377 }
378
379
380 #ifdef SDK_TWL
TwlMain(void)381 void TwlMain(void)
382 #else
383 void NitroMain(void)
384 #endif
385 {
386 //#define CPU_TEST // Puts a load on the CPU (for testing)
387 //#define GE_TEST // Puts a load on the Geometry Engine (for testing)
388
389 //#define SET_FOG // Refer to the code shown when this define is enabled if you want to set different fogs for the two screens using Double3D
390 //
391
392 u16 Rotate = 0; // For rotating cubes (0-65535)
393
394 int i = 0;
395
396 #if defined(CPU_TEST) || defined(GE_TEST)
397 MATHRandContext32 rnd; // Variable to generate random number that applies load to CPU (for testing)
398 #endif
399 //---------------------------------------------------------------------------
400 // Initialize:
401 // Enable IRQ interrupts, initialize VRAM, and set background #0 for 3D mode.
402 //---------------------------------------------------------------------------
403 DEMOInitCommon();
404 DEMOInitVRAM();
405 CARD_SetPulledOutCallback(IgnoreRemoval);
406
407 OS_InitThread();
408 OS_CreateThread(&thread1, proc1, NULL, stack1 + STACK_SIZE / sizeof(u32), STACK_SIZE,
409 THREAD1_PRIO);
410 OS_WakeupThreadDirect(&thread1);
411
412 G3X_Init();
413 G3X_InitTable();
414 G3X_InitMtxStack();
415 G3X_AntiAlias(TRUE);
416 setupSubOAM();
417
418 G3_SwapBuffers(GX_SORTMODE_AUTO, GX_BUFFERMODE_Z);
419 G3X_SetClearColor(GX_RGB(0, 0, 0), // Clear color
420 0, // Clear alpha
421 0x7fff, // Clear depth
422 63, // Clear polygon ID
423 FALSE // Fog
424 );
425
426 G3_ViewPort(0, 0, 255, 191); // Viewport
427
428 //---------------------------------------------------------------------------
429 // Set up the projection matrix
430 //---------------------------------------------------------------------------
431 {
432 fx32 right = FX32_ONE;
433 fx32 top = FX32_ONE * 3 / 4;
434 fx32 near = FX32_ONE;
435 fx32 far = FX32_ONE * 400;
436
437 //---------------------------------------------------------------------------
438 // Switch MTXMODE to GX_MTXMODE_PROJECTION, and
439 // set a projection matrix on the current projection matrix on the matrix stack
440 //---------------------------------------------------------------------------
441 G3_Perspective(FX32_SIN30, FX32_COS30, // Sine and cosine of FOVY
442 FX32_ONE * 4 / 3, // Aspect
443 near, // Near
444 far, // Far
445 NULL // A pointer to a matrix if you use one
446 );
447
448 G3_StoreMtx(0);
449 }
450
451 #if defined(SET_FOG)
452 /* Fog configuration */
453 {
454 u32 fog_table[8];
455 int i;
456
457 // Fog attribute settings
458 G3X_SetFog(TRUE, // Enable fog
459 GX_FOGBLEND_COLOR_ALPHA, // Apply fog to color and alpha
460 GX_FOGSLOPE_0x2000, // Fog gradient settings
461 0x4800); // Fog calculation depth value
462 G3X_SetFogColor(GX_RGB(31, 31, 31), 0); // Fog color settings
463
464 // Fog table settings (increasing the values thickens the fog)
465 for (i = 0; i < 8; i++)
466 {
467 fog_table[i] = (u32)(((i * 16) << 0) |
468 ((i * 16 + 4) << 8) |
469 ((i * 16 + 8) << 16) | ((i * 16 + 12) << 24));
470 }
471 G3X_SetFogTable(&fog_table[0]);
472 }
473 #endif
474
475 // The foreground (BG0) will be blended for a combination of 2D and 3D
476 G2_SetBlendAlpha(GX_BLEND_PLANEMASK_BG0,
477 GX_BLEND_PLANEMASK_BG0|GX_BLEND_PLANEMASK_BG1|GX_BLEND_PLANEMASK_BG2|GX_BLEND_PLANEMASK_BG3|GX_BLEND_PLANEMASK_OBJ|GX_BLEND_PLANEMASK_BD,
478 16,
479 0
480 );
481
482 DEMOStartDisplay();
483
484 #if defined(CPU_TEST) || defined(GE_TEST)
485 MATH_InitRand32(&rnd, OS_GetVBlankCount()); // Generates a random number that applies load to CPU (for testing)
486 #endif
487
488 while (1)
489 {
490 DEMOReadKey();
491
492 if(DEMO_IS_TRIG(PAD_BUTTON_Y))
493 {
494 operationMode = (operationMode+1)%2; // Switch the operating mode
495 }
496
497 G3X_Reset();
498
499 Rotate += 256;
500
501 //---------------------------------------------------------------------------
502 // Set up camera matrix
503 //---------------------------------------------------------------------------
504 {
505 VecFx32 Eye = { 0, 0, FX32_ONE * 5 }; // Eye position
506 VecFx32 at = { 0, 0, 0 }; // Viewpoint
507 VecFx32 vUp = { 0, FX32_ONE, 0 }; // Up
508
509 G3_LookAt(&Eye, &vUp, &at, NULL);
510 }
511 G3_PushMtx();
512
513 #if defined(SET_FOG)
514 if(DEMO_IS_TRIG(PAD_BUTTON_A))
515 {
516 if(attr)
517 {
518 // Disable fog
519 attr = GX_POLYGON_ATTR_MISC_NONE;
520 }
521 else
522 {
523 // Enable fog
524 attr = GX_POLYGON_ATTR_MISC_FOG;
525 }
526 }
527 #endif
528
529 if (flip_flag)
530 drawRightCube(Rotate);
531 else
532 drawLeftCube(Rotate);
533 G3_PopMtx(1);
534
535 #ifdef GE_TEST
536 while (GX_GetVCount() != 191)
537 {;
538 }
539 #endif
540
541 #ifdef CPU_TEST
542 // Puts a load on the CPU with a random number (for testing)
543 if (DEMO_IS_PRESS(PAD_BUTTON_R))
544 {
545 OS_SpinWait(MATH_Rand32(&rnd, 1000000));
546 }
547 #endif
548
549 #ifdef GE_TEST
550 // Puts a load on the Geometry Engine with a random number (for testing)
551 while (i <= MATH_Rand32(&rnd, 1000000))
552 {
553 G3_PushMtx();
554 G3_PopMtx(1);
555 i++;
556 }
557 #endif
558
559 #if defined(SET_FOG)
560 // Wait until the V-Count is 180
561 // (It is necessary to ensure that the fog process completes after this wait and before the start of the V-Blank.
562 // For now, an ample setting of 180 lines was used.)
563 while (GX_GetVCount() != 180)
564 {;
565 }
566
567 // Only issue in frames where the SwapBuffers command will not end in a processing falloff.
568 // (Processing won't hang if the rendering is completed within a V-Count of 180.)
569 if (!G3X_IsGeometryBusy())
570 {
571 SetSwapBuffersflag(); // Issue the SwapBuffers command
572
573 if (flip_flag) // Flip switch flag
574 {
575 // Set the fog color for the lower screen
576 G3X_SetFogColor(GX_RGB(0, 0, 0), 0);
577 }
578 else
579 {
580 // Set the fog color for the upper screen
581 G3X_SetFogColor(GX_RGB(31, 31, 31), 0);
582 }
583 }
584 #else
585 SetSwapBuffersflag(); // Enable the SwapBuffers execution flag
586 #endif
587 OS_WaitVBlankIntr(); // Waiting for the end of the V-Blank interrupt
588 switch (operationMode)
589 {
590 case 1:
591 OS_WaitVBlankIntr(); // Waiting for the end of the V-Blank interrupt
592 break;
593 }
594 }
595 }
596
597 //---------------------------------------------------------------------------
598 // V-Blank interrupt function:
599 //
600 // Interrupt handlers are registered on the interrupt table by OS_SetIRQFunction.
601 // OS_EnableIrqMask selects IRQ interrupts to enable, and
602 // OS_EnableIrq enables IRQ interrupts.
603 // Notice that you have to call OS_SetIrqCheckFlag to check a V-Blank interrupt.
604 //---------------------------------------------------------------------------
VBlankIntr(void)605 void VBlankIntr(void)
606 {
607 OS_SetIrqCheckFlag(OS_IE_V_BLANK); // Checking V-Blank interrupt
608
609 //---- Start thread 1
610 OS_WakeupThreadDirect(&thread1);
611 }
612 /*
613 ---------------------------------------------------------------------------------------------------
614
615 An explanation of the SwapBuffers command and the Geometry Engine follows
616
617 - The SwapBuffers command will be stored in the geometry command FIFO queue when the G3_SwapBuffers function is called.
618 The buffers will be swapped at the start of the next V-Blank regardless of when the SwapBuffers command is called from the geometry command FIFO queue.
619 (Basically, it can only be executed when a V-Blank starts.)
620
621
622 - Therefore, if rendering or some other process overlaps with the V-Blank interval and the SwapBuffers command could not be run before the start of the V-Blank, the geometry engine will stay busy and wait until the next frame's V-Blank starts (approximately one frame).
623 During this time, operations such as rendering graphics or swapping cannot be done, so the image from the last frame is displayed.
624
625
626
627 s: Issue SwapBuffers command
628 S: Mount the buffer swap operation
629 w: Wait for the SwapBuffers command to run before beginning the buffer swap operation
630 G: The Geometry Engine is busy rendering, etc.
631
632 |
633 -V-Blank(end)-----------------------------------------
634 +-----+ | +-----+
635 | | | | |
636 | | | | |
637 | G | | | G |
638 | | | | |
639 | | | | |
640 | | | | |
641 | | | | |
642 | | | | |
643 +-----+ | | |
644 | s | | | |
645 +-----+ | | |
646 | w | | | |
647 -V-Blank(start)+-----+----+-----+ +-----------
648 * 784(cycle) | S | | | | * The number of CPU cycles taken by this swap operation is 784 cycles
649 +-----+ | +-----+
650 * check -> | | s | * This check uses the G3X_IsGeometryBusy function to determine whether the swap operation has finished
651 | +-----+
652 -V-Blank(end)-------------+-----+ +---------
653 | | |
654 | | |
655 | | |
656 | | |
657 | | |
658 | | |
659 | | w |
660 | | |
661 | | |
662 | | |
663 | | |
664 | | |
665 | | |
666 | | |
667 | | |
668 -V-Blank(start)-----------+-----+-----+----------
669 | | S |
670 | +-----+
671 |
672 |
673 -V-Blank(end)-------------+-----------------------
674
675
676 * Conditions that generate problems
677 - Both the upper and lower screens will display the same image if they were swapped despite the fact that the buffers did not finish swapping during the V-Blank.
678
679
680 To avoid this problem, you must do the following inside the V-Blank.
681 - Confirm that the next image has finished being rendered before entering a V-Blank.
682 If rendering has finished, the buffers will be swapped at the start of the next V-Blank.
683 - Confirm that the buffer swap operation has completed inside the V-Blank.
684 If the swap operation is finished, switch the upper and lower screens.
685
686 Therefore:
687 - The next render is completed before entering a V-Blank, thus ensuring that the swap operation will happen at the start of the V-Blank.
688 - The buffer swap operation completes inside the V-Blank.
689 You must confirm both of the above.
690
691 **Specific processes run in a V-Blank
692
693 - if(GX_GetVCount() <= 193): The swap operation doesn't even take 1 line, so if 193 lines have been reached at this point we can determine that the swap has finished without the need to wait 784 cycles with the OS_SpinWait function.
694
695
696
697
698 - OS_SpinWait(784): Waits 784 cycles.
699
700 - if (!G3X_IsGeometryBusy() && swap): If the SwapBuffers command has been run before the V-Blank and the Geometry Engine is not busy
701 (Not running a buffer swap operation or rendering graphics)
702
703
704 [] Meaning of the operation
705 784 (392 x 2) CPU cycles are required to swap the buffers (switch the data accessed by the rendering engine), so if the buffers started to swap at the start of a V-Blank, it can be inferred that they will have finished swapping after a 784-cycle wait.
706 If the geometry engine is busy following a 784-cycle wait from the start of the V-Blank, the buffers have not yet finished being swapped because the rendering of graphics before the V-Blank was delayed.
707 -> In this case, neither the swap operation nor a switch between the upper and lower screens should be run.
708 If the geometry engine is not busy following a 784-cycle wait from the start of the V-Blank, the buffers have finished being swapped, and the graphics have finished being rendered.
709 -> Because the render and swap operations have both finished, you should switch the upper and lower screens.
710 If you think that these 784 cycles during the V-Blank are a complete waste, add an operation of at least 784 cycles before calling OS_SpinWait(784).
711
712
713
714
715
716
717
718
719
720 Although this method cannot be used when no polygons are being rendered, here is another possibility.
721
722 void VBlankIntr(void)
723 {
724 if ( (G3X_GetVtxListRamCount() == 0) && swap )
725 {
726 if (flag) // Flip switch
727 {
728 setupFrame2N_1();
729 }
730 else
731 {
732 setupFrame2N();
733 }
734
735
736 Making modifications as shown above will also prevent the issue.
737 This method determines that the buffers have been swapped using the G3X_GetVtxListRamCount function (which returns the number of vertices stored in vertex RAM) to confirm that there are no vertices stored in vertex RAM when the buffers have been swapped at the start of a V-Blank.
738
739 Note: If an extremely long interrupt occurs before a V-Blank interrupt and the V-Blank starts late, the V-Blank time becomes extremely short (approximately 3 lines or fewer). The upper and lower screens may be switched mid-display, and that frame may show only the previous image.
740
741
742
743
744
745
746
747 ---------------------------------------------------------------------------------------------------
748 */
proc1(void * p1)749 void proc1(void *p1)
750 {
751 #pragma unused(p1)
752
753 while (1)
754 {
755 #define SPIN_WAIT // Switches between the method using the OS_SpinWait function and the method not using it
756
757 // Image rendering also checks whether the swap operation for the buffer has completed
758 #ifdef SPIN_WAIT
759
760 if (GX_GetVCount() <= 193)
761 {
762 #ifdef SDK_TWL
763 OS_SpinWaitSysCycles(784);
764 #else
765 OS_SpinWait(784);
766 #endif
767 }
768 if (!G3X_IsGeometryBusy() && swap)
769
770 #else
771
772 if ((G3X_GetVtxListRamCount() == 0) && swap)
773
774 #endif
775 // If the rendering and swap operations are both finished, the upper and lower screens switch
776 {
777 if (flip_flag) // Flip switch (operation to switch the upper and lower screens)
778 {
779 setupFrame2N_1();
780 }
781 else
782 {
783 setupFrame2N();
784 }
785 swap = FALSE;
786 flip_flag = !flip_flag;
787 }
788 OS_SleepThread(NULL); // Puts this thread to sleep. It will next wake when OS_WakeupThreadDirect is called
789 }
790 }
791