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