English Amiga Board


Go Back   English Amiga Board > Coders > Coders. Language > Coders. C/C++

 
 
Thread Tools
Old 26 May 2019, 23:58   #1
balrogsoft
Registered User
 
Join Date: May 2006
Location: Spain
Age: 37
Posts: 56
Game development using C and sys functions, double buffer problem

Hi.
I'm working on my game engine again, and I have performance problems when it runs on stock A600 (whithout acceleration) with Kickstart 2.0. For this system the double buffer is implemented doubling the height of screen and performing a ScrollVPort to the actual frame. When I use this trick, the frame rate drops to the 50%, 25 fps only.

Any idea about why this problem occurs? If I don't use double buffering, I have tried also a basic example blitting a tilemap with vertical scroll from my engine, and there isn't enough time every frame to draw it using amigaos functions, and it only makes a few blittings every frame, it blit the next tile row meanwhile the scroll is running, not a complete row, I must forget something, it's a simple tilemap.

Code:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <exec/execbase.h>
#include <exec/memory.h>  
#include <cybergraphx/cybergraphics.h>
#include <graphics/gfxbase.h>
#include <devices/timer.h>

#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/graphics.h>
#include <proto/cybergraphics.h>
#include <proto/timer.h>   

#define WIDTH 320
#define HEIGHT 256
#define DEPTH 5

extern struct ExecBase *SysBase;
struct GfxBase *GfxBase;
struct Library *CyberGfxBase= NULL;
struct Library *TimerBase;      /* to get at the time comparison functions */
static struct IORequest timereq;

UWORD __chip EmptyPointer[] = {0, 0, 0, 0};

ULONG timer(void)
{
  static struct timeval tt;
  struct timeval a, b;

  GetSysTime(&a);
  b = a;
  SubTime(&b, &tt);
  tt = a;

  return b.tv_secs*1000 + b.tv_micro/1000;
}

int main(void)
{
    struct Screen *screen;
    struct Window *window;
    struct DimensionInfo dimsinfo;

        ULONG mtimer = 0;
        int elapsed=0;
        WORD fps=0;
        WORD fps_val=0;
   
    LONG oscan_height,frame=0,frameOffset=0;
        UBYTE numstr[16];
                        
    OpenDevice("timer.device", 0, &timereq, 0);
        TimerBase = timereq.io_Device;
    GfxBase = (struct GfxBase *) OpenLibrary( "graphics.library", 0 );
                    
    if (CyberGfxBase = OpenLibrary("cybergraphics.library",0))
    {
        CloseLibrary(CyberGfxBase);
    }
    
        
    GetDisplayInfoData(FindDisplayInfo(LORES_KEY), (UBYTE *)&dimsinfo,
                               sizeof(struct DimensionInfo), DTAG_DIMS,
                               NULL);
                               
    oscan_height = dimsinfo.MaxOScan.MaxY - dimsinfo.MaxOScan.MinY + 1;

    screen = OpenScreenTags(NULL,
                           SA_DisplayID, PAL_MONITOR_ID|LORES_KEY,
                           SA_Width,     WIDTH,
                           SA_Height,    oscan_height<<1,
                           SA_Depth,     DEPTH, 
                           SA_Type,      CUSTOMSCREEN,
                           SA_Quiet,     TRUE,
                           SA_Draggable, FALSE,
                           SA_Exclusive, TRUE,
                           SA_Interleaved, TRUE,
                           SA_AutoScroll, FALSE,
                           TAG_DONE);
        
    window = OpenWindowTags(NULL,
                            WA_CustomScreen, screen,
                            WA_Flags, WFLG_BORDERLESS | WFLG_ACTIVATE,
                            WA_IDCMP, 0,
                            TAG_DONE);
        
       
    SetPointer(window, EmptyPointer, 1L, 1L, 0L, 0L);

        elapsed = timer();
    while((*(UBYTE *)0xBFE001) & 0x40)
    {  
        frame ^= 1;
        frameOffset = frame*oscan_height;                 // <--- Comment this line to disable double buffer
                
                fps++;
                        
                elapsed = timer();
                
                mtimer+=elapsed;
                if (mtimer>1000)
                {
                        fps_val = fps;
                        fps = 0;
                        mtimer=mtimer-1000;
                }
                
                sprintf(numstr, "%d fps", fps_val);
                                        
                SetAPen(&screen->RastPort, 31);
                Move(&screen->RastPort, 10, 10+frameOffset);
                Text(&screen->RastPort, numstr, strlen(numstr));
                
        screen->ViewPort.RasInfo->RyOffset = frameOffset; //  <--- Comment this line to disable double buffer
        ScrollVPort(&screen->ViewPort);                   //  <--- Comment this line to disable double buffer
    
        WaitTOF();
    }; 
    
        CloseWindow(window);
    CloseScreen(screen);

    CloseDevice(&timereq);
        
    if(GfxBase)
        CloseLibrary((struct Library *)GfxBase);
        
    return 0;
}
balrogsoft is offline  
Old 27 May 2019, 21:36   #2
balrogsoft
Registered User
 
Join Date: May 2006
Location: Spain
Age: 37
Posts: 56
I got the answer, one view, two bitmaps, creating two copperlist for each bitmap and changing the copper list of the view every frame, as explained here, good reading indeed:


https://wiki.amigaos.net/wiki/Classi...ffered_Display


Here is my implementation of this double buffer technique, it works on AOS 2.0 running at 50 fps.Anyway, any attempt to call ScrollVPort decreases performance at 25 fps, so this technique will not give you 50 fps for games that use scroll, I do not know if this is the best you can get using the system functions.

Code:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <exec/execbase.h>
#include <exec/memory.h>  
#include <graphics/gfxbase.h>
#include <devices/timer.h>

#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/intuition.h>
#include <proto/graphics.h>
#include <proto/timer.h>   


#define WIDTH   320
#define HEIGHT  256*2+64
#define DEPTH   5

extern struct ExecBase *SysBase;
struct GfxBase *GfxBase;
struct Library *CyberGfxBase= NULL;
struct Library *TimerBase;      /* to get at the time comparison functions */
static struct IORequest timereq;

ULONG timer(void)
{
  static struct timeval tt;
  struct timeval a, b;

  GetSysTime(&a);
  b = a;
  SubTime(&b, &tt);
  tt = a;

  return b.tv_secs*1000 + b.tv_micro/1000;
}

int main(void)
{
    struct DimensionInfo dimsinfo;

        struct View view, *oldview=NULL; 
        struct ViewPort viewPort;

        struct BitMap bitMap1;
        struct BitMap bitMap2;
        struct RastPort rastPort1;
        struct RastPort rastPort2;
        struct RastPort *rastPort;

        struct RasInfo rasInfo;

        struct cprlist *LOCpr1;
        struct cprlist *SHCpr1;
        struct cprlist *LOCpr2;
        struct cprlist *SHCpr2;
        
        WORD fps=0;
        WORD fps_val=0;
        ULONG mtimer = 0, elapsed=0, depth;
    LONG oscan_height,frame=0;
        UBYTE numstr[16];


    OpenDevice("timer.device", 0, &timereq, 0);
        TimerBase = timereq.io_Device;
        
    GfxBase = (struct GfxBase *) OpenLibrary( "graphics.library", 0 );
                    
    GetDisplayInfoData(FindDisplayInfo(LORES_KEY), (UBYTE *)&dimsinfo,
                               sizeof(struct DimensionInfo), DTAG_DIMS,
                               NULL);
                               
    oscan_height = dimsinfo.MaxOScan.MaxY - dimsinfo.MaxOScan.MinY + 1;

        oldview = GfxBase->ActiView;

        InitView(&view); 

        InitBitMap(&bitMap1, DEPTH, WIDTH, HEIGHT);
            
        InitBitMap(&bitMap2, DEPTH, WIDTH, HEIGHT);

        for (depth=0; depth<DEPTH; depth++)
        {
            bitMap1.Planes[depth] = (PLANEPTR)AllocRaster(WIDTH, HEIGHT);
            bitMap2.Planes[depth] = (PLANEPTR)AllocRaster(WIDTH, HEIGHT);
        }

        
        InitRastPort(&rastPort1);
        rastPort1.BitMap = &bitMap1;
        SetRast(&rastPort1, 0);
        
        InitRastPort(&rastPort2);
        rastPort2.BitMap = &bitMap2;
        SetRast(&rastPort2, 0);
        
        rasInfo.BitMap = &bitMap1;
        rasInfo.RxOffset = 0;
        rasInfo.RyOffset = 0;
        rasInfo.Next = NULL;

        InitVPort(&viewPort);
        view.ViewPort = &viewPort;
        viewPort.RasInfo = &rasInfo;
        viewPort.DWidth = WIDTH;
        viewPort.DHeight = HEIGHT;

        MakeVPort(&view, &viewPort);

        MrgCop(&view);
        
        LOCpr1 = view.LOFCprList;
        SHCpr1 = view.SHFCprList;
        
        
        view.LOFCprList = 0;
        view.SHFCprList = 0;
        
        rasInfo.BitMap = &bitMap2;
        

        MakeVPort(&view, &viewPort);

        MrgCop(&view);
        
        LOCpr2 = view.LOFCprList;
        SHCpr2 = view.SHFCprList;
        
        LoadView(&view);

        elapsed = timer();
    while((*(UBYTE *)0xBFE001) & 0x40)
    {  
        WaitTOF();
                if (frame==0) {
                    view.LOFCprList = LOCpr1;
                    view.SHFCprList = SHCpr1;
                    rastPort = &rastPort1;
                }
                else {
                    view.LOFCprList = LOCpr2;
                    view.SHFCprList = SHCpr2;
                    rastPort = &rastPort2;
                }
                        
                elapsed = timer();
                
                mtimer+=elapsed;
                if (mtimer>1000)
                {
                        fps_val = fps;
                        fps = 0;
                        mtimer = mtimer - 1000;
                }
                
                sprintf(numstr, "%d fps", fps_val);
                                        
                SetAPen(rastPort, 31);
                Move(rastPort, 10, 10);
                Text(rastPort, numstr, strlen(numstr));
                
                LoadView(&view);
                
        frame ^= 1;
                fps++;
          
    }; 

        LoadView(oldview);
        
        WaitTOF();
        
        FreeCprList(LOCpr1);
        FreeCprList(LOCpr2);
        FreeCprList(SHCpr1);
        FreeCprList(SHCpr2);
        
        FreeVPortCopLists(&viewPort); 

        for(depth=0; depth<DEPTH; depth++)
        {
            if (bitMap1.Planes[depth])
                FreeRaster(bitMap1.Planes[depth], WIDTH, HEIGHT);
            if (bitMap2.Planes[depth])
                FreeRaster(bitMap2.Planes[depth], WIDTH, HEIGHT);
        }
    CloseDevice(&timereq);
        
        
    if(GfxBase)
        CloseLibrary((struct Library *)GfxBase);
        
    return 0;
}

Last edited by balrogsoft; 27 May 2019 at 22:27.
balrogsoft is offline  
Old 27 May 2019, 22:38   #3
Astrofra
Amos Basic
Astrofra's Avatar
 
Join Date: Feb 2013
Location: Orleans | France
Age: 45
Posts: 63
Ahaha, amazing.

I was about to answer, but you finally found a technique that is far more efficient than everything I tried.

As you probably know, there is no official support for double buffering in AmigaOS 1.3/2.0.

In my own project (an adventure game for the OCS range of Amigas), I'm using the MakeScreen()/RethinkDisplay() technique, but it is known for being the slowest method ever (the autodocs say RethinkDisplay() takes milliseconds).

The ScrollVPort() trick is supposed to be faster, but as the source code of this 3.1 version is approx. 300 lines of asm, so I guess this might be why it is a bit slower than what we expected


for the records, here is my double buffer routine (works on AmigaDOS1.3, but hell, it is SO SLOW) :



Code:
void flipBuffers(buffered_screen *screen)
{
	/* Swap the physical and logical bitmaps */
	screen->physical = (USHORT)(screen->physical)^1;
	screen->screen->RastPort.BitMap	= screen->bitmaps[screen->physical];
	screen->screen->ViewPort.RasInfo->BitMap = screen->bitmaps[screen->physical];
#ifdef DEBUG_MACROS	
	printf("flipBuffers() : physical screen: %d\n", screen->physical);
	printf("flipBuffers() : logical screen: %d\n", getLogicalBitmapIndex(screen));
#endif	
}

void presentScreen(buffered_screen *screen)
{
	/* Update the physical display to match the recently updated bitmap. */
	MakeScreen(screen->screen);
	RethinkDisplay();
#ifdef DEBUG_MACROS	
	printf("presentScreen()\n");
#endif	
}
Astrofra is offline  
Old 29 May 2019, 19:05   #4
balrogsoft
Registered User
 
Join Date: May 2006
Location: Spain
Age: 37
Posts: 56
RethinkDisplay is probably one of the worst options for games. My code doesn't run under AmigaOS 1.3, but I will take a look to see if it can work.
balrogsoft is offline  
Old 20 June 2019, 21:46   #5
bebbo
botcher

 
Join Date: Jun 2016
Location: Hamburg/Germany
Posts: 429
just some additional info:

ScrollVPort(&screen->ViewPort);

is calling

WaitBOVP(&screen->ViewPort);

so a WaitTOF(); after that might be the cause to skip a frame.

And there is this comment to WaitBOVP(&screen->ViewPort);:

Quote:
BUGS
Horrors! This function currently busy waits waiting for the
beam to get to the right place. It should use the copper
interrupt to trigger and send signals like WaitTOF does.
=> ScrollVPort(&screen->ViewPort); is a bad idea too
bebbo is offline  
Old 20 June 2019, 22:20   #6
meynaf
son of 68k
meynaf's Avatar
 
Join Date: Nov 2007
Location: Lyon / France
Age: 46
Posts: 3,617
Alternatively it's possible to open two screens and perform the frame flipping with ScreenToFront -- if the game does not need to multitask.

But under OS i would rather use a back-buffer (i.e. mem copy to do the refresh). This is better for performance if only part of the screen needs updating.
meynaf is online now  
Old 20 June 2019, 22:31   #7
bebbo
botcher

 
Join Date: Jun 2016
Location: Hamburg/Germany
Posts: 429
Quote:
Originally Posted by meynaf View Post
Alternatively it's possible to open two screens and perform the frame flipping with ScreenToFront -- if the game does not need to multitask.

But under OS i would rather use a back-buffer (i.e. mem copy to do the refresh). This is better for performance if only part of the screen needs updating.

ScreenToFront is calling RethinkDisplay
bebbo is offline  
 


Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools

Similar Threads
Thread Thread Starter Forum Replies Last Post
Request: Long double (80bit) functions in x86/x64 assembly for WinUAE Toni Wilen request.Other 2 20 April 2018 09:50
ScrollVPort double buffer problem with garbage pixels balrogsoft Coders. General 5 29 May 2014 13:31
Double buffer copper?? h0ffman Coders. General 8 19 July 2011 20:10
Vsync Fullscreen and Double Buffer, incorrect frame rate? rsn8887 support.WinUAE 1 07 April 2011 21:43
Big Brother 0.51 Non-Sys Monitor Problem WintermuteX support.WinUAE 6 19 October 2003 17:19

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT +2. The time now is 18:08.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2019, vBulletin Solutions Inc.
Page generated in 0.06994 seconds with 15 queries