Technical Note : SLE0001 Author : Scott Evans Created/Modified : 16/9/97 Description : Describes the process of “double buffering” If you want to program the Yaroze then understanding how double buffering is used and how it works should make the job a little easier. This document describes double buffering the way I see it and should not be taken as an official document on the subject. What follows is a short description of double buffering and two sample Yaroze programs which show how to achieve double buffering using different techniques. Double buffering is used on the Yaroze for three things. The screen, ordering table and the primitive/packet buffer. Screen double buffering Two areas in the frame buffer are set up with each area big enough for one screen. One area is designated the display area while the other is the drawing area. Anything that gets put into the display area will be seen on the TV screen, but the contents of the drawing area are not seen. While the display area is being shown the next frame is being drawn at the same time in the drawing area. As soon as drawing is complete the two areas are swapped and the new image will be seen. The old display area will be used as the new drawing area and the old drawing area will be the new display area. This is repeated 50 (PAL) or 60 (NTSC) times a second and is how all Yaroze programs achieve “display”. The display and drawing areas are held in VRAM, which can be thought of as a 1024x512 pixel screen in which you can only see what is on this screen through the display area. So if you move the display area around in VRAM you will see the VRAM contents on your TV. The two screen buffer areas can be set up using the GsDefDispBuff() function or using the lower level functions PutDrawEnv() and PutDispEnv() functions. Once you have set up a display and drawing area you can then use GsSwapDispBuff() to swap the two areas. This is usually done after waiting for drawing to finish and waiting for the next frame using DrawSync()/ResetGraph() and VSync() functions. The PutDrawEnv() and PutDispEnv() functions can also be used to swap the two areas. Ordering tables and packet buffers The Yaroze also uses double buffering for ordering tables and packet buffers. The packet buffer is an area in which primitives are created, these primitives can then be executed directly by the GPU, examples of primitives are sprites (GsSPRITE), lines (GsLINE/GsGLINE) and filled rectangles (GsBOXF). You need two packet buffers because the packet buffer cannot be written to while being used by the GPU, so while one buffer is being used by the GPU the other buffer is used to create the new primitives for the next frame. The ordering table is simply a linked list of primitives. When the ordering table is drawn the primitives are drawn in the order they appear in the list. So primitives at the front of the list will appear to be behind primitives further along the list. This is used to implement Z sorting of primitives. The same applies to the ordering table as it does to the packet buffer. Primitives cannot be placed in the ordering table if it is being used for drawing, so to eliminate any delay two ordering tables are used. As before while one is being used for drawing the other can be built. Example programs The following are the simplest Yaroze programs. They demonstrate two different ways of achieving the double buffering talked about above. All they do is turn the screen red and display a white box in the centre of the screen. // Filename : Dbuff1.c // Coded by : Scott Evans // Created/Modified : 16/9/97 // Description : Demonstrates double buffering using high // level graphics functions // Header file for Yaroze library #include // The packet buffer must be large enough to store the // maximum number of primitives that will be used // In this program we are only using one primitive a GsBOXF #define PBSIZE 1*sizeof(GsBOXF) // Z resolution of ordering table #define OT_LENGTH 1 // Screen width and height (NTSC) #define SCRNW 320 #define SCRNH 240 // Define 2 ordering tables GsOT mainOT[2]; GsOT_TAG OTTags[2][1<>1; box.y=(SCRNH-box.h)>>1; box.r=box.g=box.b=0xff; // Main loop while(1) { // Find the active buffer (0 or 1) current_buffer=GsGetActiveBuff(); // Set the packet buffer for the current buffer GsSetWorkBase((PACKET *)PacketBuffer[current_buffer]); // Initialise the ordering table GsClearOt(0,0,&mainOT[current_buffer]); // Put the box primitive into the ordering table GsSortBoxFill(&box,&mainOT[current_buffer],0); // Wait for drawing and vertical blank to finish DrawSync(0); VSync(0); // Swap the buffers GsSwapDispBuff(); // Clear the screen in red GsSortClear(0x80,0x0,0x0,&mainOT[current_buffer]); // Execute the primitives in the ordering table GsDrawOt(&mainOT[current_buffer]); } exit(0); } // Filename : Dbuff2.c // Coded by : Scott Evans // Created/Modified : 16/9/97 // Description : Demonstrates double buffering using high // and low level graphics functions #include // Header file for yaroze.c my versions of commands not included in // Yaroze libraries #include #include #define NPKTS 1 #define PBSIZE NPKTS*sizeof(GsBOXF) #define OT_LENGTH 1 #define SCRNW 320 #define SCRNH 240 // 2 ordering tables and 2 packet buffers GsOT mainOT[2],*ot=mainOT; GsOT_TAG OTTags[2][1<length=(ot+1)->length=OT_LENGTH; ot->org=OTTags[0]; (ot+1)->org=OTTags[1]; // Set drawing area for buffer 0 and 1 SetDefDrawEnv(&dbp->drawenv,0,0,SCRNW,SCRNH); SetDefDrawEnv(&(dbp+1)->drawenv,0,SCRNH,SCRNW,SCRNH); // Set display area for buffer 0 and 1 SetDefDispEnv(&dbp->dispenv,0,SCRNH,SCRNW,SCRNH); SetDefDispEnv(&(dbp+1)->dispenv,0,0,SCRNW,SCRNH); // Set ordering table for buffer 0 and 1 dbp->ot=ot; (dbp+1)->ot=ot+1; // Set packet buffer for buffer 0 and 1 dbp->pb=pb; (dbp+1)->pb=pb+PBSIZE; // Dither off dbp->drawenv.dtd=(dbp+1)->drawenv.dtd=0; // Clear background on dbp->drawenv.isbg=(dbp+1)->drawenv.isbg=1; // Background colour=red setRGB0(&dbp->drawenv,0x80,0x0,0x0); setRGB0(&(dbp+1)->drawenv,0x80,0x0,0x0); // Set up a white box box.attribute=0; box.w=box.h=50; box.x=(SCRNW-box.w)>>1; box.y=(SCRNH-box.h)>>1; box.r=box.g=box.b=0xff; // Initialise graphics ResetGraph(0); // Turn on display SetDispMask(1); while(1) { // Swap buffers dbp=(dbp==db ? db+1 : db); // Set packet buffer for current buffer GsSetWorkBase(dbp->pb); // Initialise ordering table GsClearOt(0,0,dbp->ot); // Put primitive in ordering table GsSortBoxFill(&box,dbp->ot,0); // Wait for it all to finish VSync(0); // Halt drawing after vertical blank has returned // Similar to DrawSync() but does not wait for drawing to // finish, it just stops the drawing ResetGraph(1); // Set new drawing and display areas PutDrawEnv(&dbp->drawenv); PutDispEnv(&dbp->dispenv); // Execute the primitives in the ordering table GsDrawOt(dbp->ot); } exit(0); }