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