1 /*---------------------------------------------------------------------------*
2   Project:  TwlSDK - GX - demos - UnitTours/Sub_Double3D
3   File:     main.c
4 
5   Copyright 2003-2008 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-01-13#$
14   $Rev: 9815 $
15   $Author: nakasima $
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     For details, see 8.5.2 Displaying Captured Images [Required] in the Programming Guidelines.
89 
90 
91 *How to avoid problems
92     You must display images captured in VRAM on both the upper and lower screens instead of displaying 3D images directly.
93     To handle this, this demo displays the VRAM with its captured data unchanged.
94 ---------------------------------------------------- */
95 static int operationMode = 0;
96 
Color(int idx)97 static void Color(int idx)
98 {
99     G3_Color(gCubeColor[idx]);
100 }
101 
Vtx(int idx)102 static void Vtx(int idx)
103 {
104     G3_Vtx(gCubeGeometry[idx * 3], gCubeGeometry[idx * 3 + 1], gCubeGeometry[idx * 3 + 2]);
105 }
106 
Quad(int idx0,int idx1,int idx2,int idx3)107 static void Quad(int idx0, int idx1, int idx2, int idx3)
108 {
109     Vtx(idx0);
110     Vtx(idx1);
111     Vtx(idx2);
112     Vtx(idx3);
113 }
114 
ColVtxQuad(int idx0,int idx1,int idx2,int idx3)115 static void ColVtxQuad(int idx0, int idx1, int idx2, int idx3)
116 {
117     Color(idx0);
118     Vtx(idx0);
119     Color(idx1);
120     Vtx(idx1);
121     Color(idx2);
122     Vtx(idx2);
123     Color(idx3);
124     Vtx(idx3);
125 }
126 
drawLeftCube(u16 Rotate)127 static void drawLeftCube(u16 Rotate)
128 {
129     G3_PushMtx();
130 
131     // Rotate and translate
132     G3_Translate(-3 << (FX32_SHIFT - 1), 0, 0);
133     {
134         fx16    s = FX_SinIdx(Rotate);
135         fx16    c = FX_CosIdx(Rotate);
136 
137         G3_RotX(s, c);
138         G3_RotY(s, c);
139         G3_RotZ(s, c);
140     }
141 
142     G3_MaterialColorDiffAmb(GX_RGB(31, 31, 31), // diffuse
143                             GX_RGB(16, 16, 16), // ambient
144                             FALSE      // use diffuse as vtx color if TRUE
145         );
146 
147     G3_MaterialColorSpecEmi(GX_RGB(16, 16, 16), // specular
148                             GX_RGB(0, 0, 0),    // emission
149                             FALSE      // use shininess table if TRUE
150         );
151 
152     G3_PolygonAttr(GX_LIGHTMASK_NONE,  // disable lights
153                    GX_POLYGONMODE_MODULATE,     // modulation mode
154                    GX_CULL_BACK,       // cull back
155                    0,                  // polygon ID(0 - 63)
156                    31,                 // alpha(0 - 31)
157                    attr                // OR of GXPolygonAttrMisc's value
158         );
159 
160     //---------------------------------------------------------------------------
161     // Draw a cube:
162     // Specify different colors for the planes.
163     //---------------------------------------------------------------------------
164 
165     G3_Begin(GX_BEGIN_QUADS);
166 
167     {
168         Color(3);
169         Quad(2, 0, 4, 6);
170 
171         Color(4);
172         Quad(7, 5, 1, 3);
173 
174         Color(5);
175         Quad(6, 4, 5, 7);
176 
177         Color(2);
178         Quad(3, 1, 0, 2);
179 
180         Color(6);
181         Quad(5, 4, 0, 1);
182 
183         Color(1);
184         Quad(6, 7, 3, 2);
185     }
186 
187     G3_End();
188 
189     G3_PopMtx(1);
190 }
191 
drawRightCube(u16 Rotate)192 static void drawRightCube(u16 Rotate)
193 {
194     G3_PushMtx();
195 
196     // Rotate and translate
197     G3_Translate(3 << (FX32_SHIFT - 1), 0, 0);
198 
199     {
200         fx16    s = FX_SinIdx(Rotate);
201         fx16    c = FX_CosIdx(Rotate);
202 
203         G3_RotX(s, c);
204         G3_RotY(s, c);
205         G3_RotZ(s, c);
206     }
207 
208     G3_MaterialColorDiffAmb(GX_RGB(31, 31, 31), // diffuse
209                             GX_RGB(16, 16, 16), // ambient
210                             FALSE      // use diffuse as vtx color if TRUE
211         );
212 
213     G3_MaterialColorSpecEmi(GX_RGB(16, 16, 16), // specular
214                             GX_RGB(0, 0, 0),    // emission
215                             FALSE      // use shininess table if TRUE
216         );
217 
218     G3_PolygonAttr(GX_LIGHTMASK_NONE,  // disable lights
219                    GX_POLYGONMODE_MODULATE,     // modulation mode
220                    GX_CULL_BACK,       // cull back
221                    0,                  // polygon ID(0 - 63)
222                    31,                 // alpha(0 - 31)
223                    attr                // OR of GXPolygonAttrMisc's value
224         );
225 
226     //---------------------------------------------------------------------------
227     // Draw a cube:
228     // Specify different colors for the vertices.
229     //---------------------------------------------------------------------------
230     G3_Begin(GX_BEGIN_QUADS);
231     {
232         ColVtxQuad(2, 0, 4, 6);
233         ColVtxQuad(7, 5, 1, 3);
234         ColVtxQuad(6, 4, 5, 7);
235         ColVtxQuad(3, 1, 0, 2);
236         ColVtxQuad(5, 4, 0, 1);
237         ColVtxQuad(6, 7, 3, 2);
238     }
239     G3_End();
240 
241     G3_PopMtx(1);
242 
243 }
244 
setupFrame2N(void)245 static void setupFrame2N(void)
246 {
247 
248     GX_SetDispSelect(GX_DISP_SELECT_MAIN_SUB);
249 
250     (void)GX_ResetBankForSubOBJ();
251     GX_SetBankForSubBG(GX_VRAM_SUB_BG_128_C);
252     GX_SetBankForLCDC(GX_VRAM_LCDC_D);
253     GX_SetCapture(GX_CAPTURE_SIZE_256x192,
254                   GX_CAPTURE_MODE_A,
255                   GX_CAPTURE_SRCA_2D3D, (GXCaptureSrcB)0, GX_CAPTURE_DEST_VRAM_D_0x00000, 16, 0);
256     switch (operationMode)
257     {
258     case 0:
259         GX_SetGraphicsMode(GX_DISPMODE_VRAM_D, GX_BGMODE_0, GX_BG0_AS_3D); // Method that uses deflickering
260         break;
261     case 1:
262         GX_SetGraphicsMode(GX_DISPMODE_GRAPHICS, GX_BGMODE_0, GX_BG0_AS_3D); // Method that does not use deflickering
263         break;
264     }
265     GX_SetVisiblePlane(GX_PLANEMASK_BG0);
266     G2_SetBG0Priority(0);
267 
268     GXS_SetGraphicsMode(GX_BGMODE_5);
269     GXS_SetVisiblePlane(GX_PLANEMASK_BG2);
270     G2S_SetBG2ControlDCBmp(GX_BG_SCRSIZE_DCBMP_256x256,
271                            GX_BG_AREAOVER_XLU, GX_BG_BMPSCRBASE_0x00000);
272     G2S_SetBG2Priority(0);
273     G2S_BG2Mosaic(FALSE);
274 }
275 
setupFrame2N_1(void)276 static void setupFrame2N_1(void)
277 {
278     GX_SetDispSelect(GX_DISP_SELECT_SUB_MAIN);
279     (void)GX_ResetBankForSubBG();
280     GX_SetBankForSubOBJ(GX_VRAM_SUB_OBJ_128_D);
281     GX_SetBankForLCDC(GX_VRAM_LCDC_C);
282     GX_SetCapture(GX_CAPTURE_SIZE_256x192,
283                   GX_CAPTURE_MODE_A,
284                   GX_CAPTURE_SRCA_2D3D, (GXCaptureSrcB)0, GX_CAPTURE_DEST_VRAM_C_0x00000, 16, 0);
285 
286     switch (operationMode)
287     {
288     case 0:
289         GX_SetGraphicsMode(GX_DISPMODE_VRAM_C, GX_BGMODE_0, GX_BG0_AS_3D); // Method that uses deflickering
290         break;
291     case 1:
292         GX_SetGraphicsMode(GX_DISPMODE_GRAPHICS, GX_BGMODE_0, GX_BG0_AS_3D); // Method that does not use deflickering
293         break;
294     }
295     GX_SetVisiblePlane(GX_PLANEMASK_BG0);
296     G2_SetBG0Priority(0);
297 
298     GXS_SetGraphicsMode(GX_BGMODE_5);
299     GXS_SetVisiblePlane(GX_PLANEMASK_OBJ);
300 }
301 
setupSubOAM(void)302 static void setupSubOAM(void)
303 {
304     int     i;
305     int     x, y;
306     int     idx = 0;
307 
308     GXS_SetOBJVRamModeBmp(GX_OBJVRAMMODE_BMP_2D_W256);
309 
310     for (i = 0; i < 128; ++i)
311     {
312         sOamBak[i].attr01 = 0;
313         sOamBak[i].attr23 = 0;
314     }
315 
316     for (y = 0; y < 192; y += 64)
317     {
318         for (x = 0; x < 256; x += 64, idx++)
319         {
320             G2_SetOBJAttr(&sOamBak[idx],
321                           x,
322                           y,
323                           0,
324                           GX_OAM_MODE_BITMAPOBJ,
325                           FALSE,
326                           GX_OAM_EFFECT_NONE,
327                           GX_OAM_SHAPE_64x64, GX_OAM_COLOR_16, (y / 8) * 32 + (x / 8), 15, 0);
328         }
329     }
330 
331     DC_FlushRange(&sOamBak[0], sizeof(sOamBak));
332     /* I/O register is accessed using DMA operation, so cache wait is not needed */
333     // DC_WaitWriteBufferEmpty();
334     GXS_LoadOAM(&sOamBak[0], 0, sizeof(sOamBak));
335 
336 }
337 
338 //---------------------------------------------------------------------------
339 // Enable the SwapBuffers execution flag
340 //---------------------------------------------------------------------------
SetSwapBuffersflag(void)341 static void SetSwapBuffersflag(void)
342 {
343     OSIntrMode old = OS_DisableInterrupts();    // interrupts disabled
344     G3_SwapBuffers(GX_SORTMODE_AUTO, GX_BUFFERMODE_Z);
345     swap = TRUE;
346     (void)OS_RestoreInterrupts(old);
347 }
348 
IgnoreRemoval(void)349 static BOOL IgnoreRemoval(void)
350 {
351     return FALSE;
352 }
353 
354 
355 #ifdef SDK_TWL
TwlMain(void)356 void TwlMain(void)
357 #else
358 void NitroMain(void)
359 #endif
360 {
361 //#define CPU_TEST                      // Puts a load on the CPU (for testing)
362 //#define GE_TEST                      // Puts a load on the Geometry Engine (for testing)
363 
364 //#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
365                                         //
366 
367     u16     Rotate = 0;                // For rotating cubes (0-65535)
368 
369     int     i = 0;
370 
371 #if defined(CPU_TEST) || defined(GE_TEST)
372     MATHRandContext32 rnd;             // Variable to generate random number that applies load to CPU (For testing)
373 #endif
374     //---------------------------------------------------------------------------
375     // Initialize:
376     // Enable IRQ interrupts, initialize VRAM, and set BG #0 to 3D mode
377     //---------------------------------------------------------------------------
378     DEMOInitCommon();
379     DEMOInitVRAM();
380     CARD_SetPulledOutCallback(IgnoreRemoval);
381 
382     OS_InitThread();
383     OS_CreateThread(&thread1, proc1, NULL, stack1 + STACK_SIZE / sizeof(u32), STACK_SIZE,
384                     THREAD1_PRIO);
385     OS_WakeupThreadDirect(&thread1);
386 
387     G3X_Init();
388     G3X_InitTable();
389     G3X_InitMtxStack();
390     G3X_AntiAlias(TRUE);
391     setupSubOAM();
392 
393     G3_SwapBuffers(GX_SORTMODE_AUTO, GX_BUFFERMODE_Z);
394     G3X_SetClearColor(GX_RGB(0, 0, 0), // Clear color
395                       0,               // Clear alpha
396                       0x7fff,          // Clear depth
397                       63,              // Clear polygon ID
398                       FALSE            // Fog
399         );
400 
401     G3_ViewPort(0, 0, 255, 191);       // Viewport
402 
403     //---------------------------------------------------------------------------
404     // Set up the projection matrix
405     //---------------------------------------------------------------------------
406     {
407         fx32    right = FX32_ONE;
408         fx32    top = FX32_ONE * 3 / 4;
409         fx32    near = FX32_ONE;
410         fx32    far = FX32_ONE * 400;
411 
412         //---------------------------------------------------------------------------
413         // Switch MTXMODE to GX_MTXMODE_PROJECTION, and
414         // set a projection matrix on the current projection matrix on the matrix stack
415         //---------------------------------------------------------------------------
416         G3_Perspective(FX32_SIN30, FX32_COS30,  // Sine and cosine of FOVY
417                        FX32_ONE * 4 / 3,        // Aspect
418                        near,           // Near
419                        far,            // Far
420                        NULL            // A pointer to a matrix if you use one
421             );
422 
423         G3_StoreMtx(0);
424     }
425 
426 #if defined(SET_FOG)
427     /* Fog configuration */
428     {
429         u32     fog_table[8];
430         int     i;
431 
432         // Fog attribute settings
433         G3X_SetFog(TRUE,               // Enable fog
434                    GX_FOGBLEND_COLOR_ALPHA,     // Apply fog to color and alpha
435                    GX_FOGSLOPE_0x2000, // Fog gradient settings
436                    0x4800);            // Fog calculation depth value
437         G3X_SetFogColor(GX_RGB(31, 31, 31), 0); // Fog color settings
438 
439         // Fog table settings (increasing the values thickens the fog)
440         for (i = 0; i < 8; i++)
441         {
442             fog_table[i] = (u32)(((i * 16) << 0) |
443                                  ((i * 16 + 4) << 8) |
444                                  ((i * 16 + 8) << 16) | ((i * 16 + 12) << 24));
445         }
446         G3X_SetFogTable(&fog_table[0]);
447     }
448 #endif
449 
450     // The foreground (BG0) will be blended for a combination of 2D and 3D
451     G2_SetBlendAlpha(GX_BLEND_PLANEMASK_BG0,
452                      GX_BLEND_PLANEMASK_BG0|GX_BLEND_PLANEMASK_BG1|GX_BLEND_PLANEMASK_BG2|GX_BLEND_PLANEMASK_BG3|GX_BLEND_PLANEMASK_OBJ|GX_BLEND_PLANEMASK_BD,
453                      16,
454                      0
455         );
456 
457     DEMOStartDisplay();
458 
459 #if defined(CPU_TEST) || defined(GE_TEST)
460     MATH_InitRand32(&rnd, OS_GetVBlankCount()); // Generates a random number that applies load to CPU (for testing)
461 #endif
462 
463     while (1)
464     {
465         DEMOReadKey();
466 
467         if(DEMO_IS_TRIG(PAD_BUTTON_Y))
468         {
469             operationMode = (operationMode+1)%2; // Switch the operating mode
470         }
471 
472         G3X_Reset();
473 
474         Rotate += 256;
475 
476         //---------------------------------------------------------------------------
477         // Set up camera matrix
478         //---------------------------------------------------------------------------
479         {
480             VecFx32 Eye = { 0, 0, FX32_ONE * 5 };       // Eye Position
481             VecFx32 at = { 0, 0, 0 };  // Viewpoint
482             VecFx32 vUp = { 0, FX32_ONE, 0 };   // Up
483 
484             G3_LookAt(&Eye, &vUp, &at, NULL);
485         }
486         G3_PushMtx();
487 
488 #if defined(SET_FOG)
489         if(DEMO_IS_TRIG(PAD_BUTTON_A))
490         {
491             if(attr)
492             {
493                 // Disable fog
494                 attr = GX_POLYGON_ATTR_MISC_NONE;
495             }
496             else
497             {
498                 // Enable fog
499                 attr = GX_POLYGON_ATTR_MISC_FOG;
500             }
501         }
502 #endif
503 
504         if (flip_flag)
505             drawRightCube(Rotate);
506         else
507             drawLeftCube(Rotate);
508         G3_PopMtx(1);
509 
510 #ifdef GE_TEST
511         while (GX_GetVCount() != 191)
512         {;
513         }
514 #endif
515 
516 #ifdef CPU_TEST
517         // Puts a load on the CPU with a random number (for testing)
518         if (DEMO_IS_PRESS(PAD_BUTTON_R))
519         {
520             OS_SpinWait(MATH_Rand32(&rnd, 1000000));
521         }
522 #endif
523 
524 #ifdef GE_TEST
525         // Puts a load on the Geometry Engine with a random number (for testing)
526         while (i <= MATH_Rand32(&rnd, 1000000))
527         {
528             G3_PushMtx();
529             G3_PopMtx(1);
530             i++;
531         }
532 #endif
533 
534 #if defined(SET_FOG)
535         // Wait until the V-Count is 180
536         // (It is necessary to ensure that the fog process completes after this wait and before the start of the V-Blank.
537         //  For now, an ample setting of 180 lines was used.)
538         while (GX_GetVCount() != 180)
539         {;
540         }
541 
542         // Only issue in frames where the SwapBuffers command will not end in a processing falloff.
543         // (Processing won't hang if the rendering is completed within a V-Count of 180.)
544         if (!G3X_IsGeometryBusy())
545         {
546             SetSwapBuffersflag();          // Issue the SwapBuffers command
547 
548             if (flip_flag)                 // Flip switch flag
549             {
550                 // Set the fog color for the lower screen
551                 G3X_SetFogColor(GX_RGB(0, 0, 0), 0);
552             }
553             else
554             {
555                 // Set the fog color for the upper screen
556                 G3X_SetFogColor(GX_RGB(31, 31, 31), 0);
557             }
558         }
559 #else
560         SetSwapBuffersflag();          // Enable the SwapBuffers execution flag
561 #endif
562         OS_WaitVBlankIntr();           // Waiting for the end of the V-Blank interrupt
563         switch (operationMode)
564         {
565         case 1:
566             OS_WaitVBlankIntr();           // Waiting for the end of the V-Blank interrupt
567             break;
568         }
569     }
570 }
571 
572 //---------------------------------------------------------------------------
573 // V-Blank interrupt function:
574 //
575 // Interrupt handlers are registered on the interrupt table by OS_SetIRQFunction.
576 // OS_EnableIrqMask selects IRQ interrupts to enable, and
577 // OS_EnableIrq enables IRQ interrupts.
578 // Notice that you have to call OS_SetIrqCheckFlag to check a V-Blank interrupt.
579 //---------------------------------------------------------------------------
VBlankIntr(void)580 void VBlankIntr(void)
581 {
582     OS_SetIrqCheckFlag(OS_IE_V_BLANK); // Checking V-Blank interrupt
583 
584     //---- Start thread 1
585     OS_WakeupThreadDirect(&thread1);
586 }
587 /*
588 ---------------------------------------------------------------------------------------------------
589 
590 An explanation of the SwapBuffers command and the Geometry Engine follows
591 
592 - The SwapBuffers command will be stored in the geometry command FIFO queue when the G3_SwapBuffers function is called.
593   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.
594   (Basically, it can only be executed when a V-Blank starts.)
595 
596 
597   - 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).
598   During this time, operations such as rendering graphics or swapping cannot be done, so the image from the last frame is displayed.
599 
600 
601 
602 s: Issue SwapBuffers command
603 S: Mount the buffer swap operation
604 w: Wait for the SwapBuffers command to run before beginning the buffer swap operation
605 G: The Geometry Engine is busy rendering, etc.
606 
607                           |
608 -V-Blank(end)------------------------------------------
609                +-----+    |     +-----+
610                |     |    |     |     |
611                |     |    |     |     |
612                |  G  |    |     |  G  |
613                |     |    |     |     |
614                |     |    |     |     |
615                |     |    |     |     |
616                |     |    |     |     |
617                |     |    |     |     |
618                +-----+    |     |     |
619                |  s  |    |     |     |
620                +-----+    |     |     |
621                |  w  |    |     |     |
622 -V-Blank(start)+-----+----+-----+     +-----------
623   * 784(cycle) |  S  |    |     |     |            * The number of CPU cycles taken by this swap operation is 784 cycles
624                +-----+    |     +-----+
625      * check ->           |     |  s  |            * This check uses the G3X_IsGeometryBusy function to determine whether the swap operation has finished
626                           |     +-----+
627 -V-Blank(end)-------------+-----+     +---------
628                           |     |     |
629                           |     |     |
630                           |     |     |
631                           |     |     |
632                           |     |     |
633                           |     |     |
634                           |     |  w  |
635                           |     |     |
636                           |     |     |
637                           |     |     |
638                           |     |     |
639                           |     |     |
640                           |     |     |
641                           |     |     |
642                           |     |     |
643 -V-Blank(start)-----------+-----+-----+----------
644                           |     |  S  |
645                           |     +-----+
646                           |
647                           |
648 -V-Blank(end)-------------+-----------------------
649 
650 
651 * Conditions that generate problems
652     - 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.
653 
654 
655 To avoid this problem, you must do the following within the V-Blank:
656     - Confirm that the next image has finished being rendered before entering a V-Blank.
657       If rendering has finished, the buffers will be swapped at the start of the next V-Blank.
658     - Confirm that the buffer swap operation has completed within the V-Blank.
659       If the swap operation is finished, switch the upper and lower screens.
660 
661     Therefore:
662     - 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.
663     - The buffer swap operation completes within the V-Blank.
664     You must confirm both of the above.
665 
666 **Specific processes run in a V-Blank
667 
668         -  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.
669 
670 
671 
672 
673         - OS_SpinWait(784):                        Waits 784 cycles.
674 
675         - if (!G3X_IsGeometryBusy() && swap):      If the SwapBuffers command has been run before the VBlank and the Geometry Engine is not busy
676                                                    (Not running a buffer swap operation or rendering graphics)
677 
678 
679 [] Meaning of the operation
680     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.
681     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 VBlank was delayed.
682 -> In this case neither the swap operation nor a switch between the upper and lower screens should be run.
683     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.
684 -> Since the render and swap operations have both finished, you should switch the upper and lower screens.
685     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).
686 
687 
688 
689 
690 
691 
692 
693 
694 
695 Although this method cannot be used when no polygons are being rendered, here is another possibility:
696 
697 void VBlankIntr(void)
698 {
699     if ( (G3X_GetVtxListRamCount() == 0) && swap )
700     {
701         if (flag)  // flip switch
702         {
703             setupFrame2N_1();
704         }
705         else
706         {
707             setupFrame2N();
708         }
709 
710 
711 Making modifications as shown above will also prevent the issue.
712 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.
713 
714 NOTE: If an extremely long interrupt occurs before a V-Blank interrupt and the V-Blank starts late, and 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 only may show the previous image.
715 
716 
717 
718 
719 
720 
721 
722 ---------------------------------------------------------------------------------------------------
723 */
proc1(void * p1)724 void proc1(void *p1)
725 {
726 #pragma unused(p1)
727 
728     while (1)
729     {
730 #define SPIN_WAIT                      // Switches between the method using the OS_SpinWait function and the method not using it
731 
732     // Image rendering also checks whether the swap operation for the buffer has completed
733 #ifdef SPIN_WAIT
734 
735     if (GX_GetVCount() <= 193)
736     {
737 #ifdef SDK_TWL
738         OS_SpinWaitSysCycles(784);
739 #else
740         OS_SpinWait(784);
741 #endif
742     }
743     if (!G3X_IsGeometryBusy() && swap)
744 
745 #else
746 
747     if ((G3X_GetVtxListRamCount() == 0) && swap)
748 
749 #endif
750     // If the rendering and swap operations are both finished, the upper and lower screens switch
751     {
752         if (flip_flag)                 // Flip switch (operation to switch the upper and lower screens)
753         {
754             setupFrame2N_1();
755         }
756         else
757         {
758             setupFrame2N();
759         }
760         swap = FALSE;
761         flip_flag = !flip_flag;
762     }
763     OS_SleepThread(NULL);   // Puts this thread to sleep. It will next wake when OS_WakeupThreadDirect is called.
764     }
765 }
766