English Amiga Board


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

 
 
Thread Tools
Old 01 December 2019, 15:17   #1
balrogsoft
Registered User
 
Join Date: May 2006
Location: Spain
Age: 39
Posts: 67
Flickering graphics using BltBitMap function of graphics.library

I made some progress with my game code, tilemaps, collisions, moving objects by the player. And it works fine, but when I try to run it on a slow machine like an A500, I have some flickering graphics when the blitter is so busy to reach 50 fps, so when there are slowdowns, the graphics start to flicker. My start code is based on this page to obtain a double buffer system:

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

This code, and executable example, this is only a part of my code to reproduce this problem. Emulating an A500 also reproduces the problem.

http://amigaskool.net/download/Game.lha

Here is the main code

Code:
int main(void)
{
    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;
    
    Bitmap* tiles_bm;
    WORD offsetx_tiles[92], offsety_tiles[92];
    BPTR file_ptr = 0;
    WORD fps=0;
    WORD fps_val=0;
    ULONG mtimer = 0, elapsed = 0, d;
    LONG frame=0;
    UBYTE numstr[16];
    WORD x, y, i = 0;
    
    UBYTE* tiles = (UBYTE*)AllocMem(TILES_SIZE, MEMF_CHIP);
    UBYTE* mod = (UBYTE*)AllocMem(MOD_SIZE, MEMF_CHIP);
    UBYTE* map = (UBYTE*)AllocMem(MAP_SIZE, 0L);
    UWORD* pal = (UWORD*)AllocMem(32, 0L);
    
    if (file_ptr = Open("data/intro1.mod", MODE_OLDFILE)) 
    {
            Read(file_ptr, mod, MOD_SIZE);
            Close(file_ptr);
    }
    
    if (file_ptr = Open("data/map.dat", MODE_OLDFILE)) 
    {
            Read(file_ptr, map, MAP_SIZE);
            Close(file_ptr);
    }
    
    if (file_ptr = Open("data/pal.dat", MODE_OLDFILE)) 
    {
            Read(file_ptr, pal, 32);
            Close(file_ptr);
    }
    
    if (file_ptr = Open("data/tiles2.dat", MODE_OLDFILE)) 
    {
            Read(file_ptr, tiles, TILES_SIZE);
            Close(file_ptr);
    }

    GfxBase = (struct GfxBase *) OpenLibrary( "graphics.library", 0 );

    for (i = 0; i < 91; i++) {
        offsetx_tiles[i] = (i<<TILE_BITS)%208;
        offsety_tiles[i] = ((i<<TILE_BITS)/208)<<TILE_BITS;
    }
    
    tiles_bm = bm_create(208, 112, 4, tiles);
    bm_createMask(tiles_bm, 0);
    
    oldview = GfxBase->ActiView;

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

    for (d=0; d<DEPTH; d++)
    {
        bitMap1.Planes[d] = (PLANEPTR)AllocRaster(WIDTH, HEIGHT);
        bitMap2.Planes[d] = (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);
    LoadRGB4(&viewPort, pal, 16);
    LOCpr1 = view.LOFCprList;
    SHCpr1 = view.SHFCprList;
    view.LOFCprList = 0;
    view.SHFCprList = 0;
    
    rasInfo.BitMap = &bitMap2;
    MakeVPort(&view, &viewPort);
    MrgCop(&view);
    LoadRGB4(&viewPort, pal, 16);
    LOCpr2 = view.LOFCprList;
    SHCpr2 = view.SHFCprList;

    myTask = FindTask(NULL);
    oldPri = SetTaskPri(myTask, 127);    
    
    Forbid();
    Disable();
    
    custom->dmacon =  BITCLR|DMAF_SPRITE;
    
    LoadView(&view);
    WaitTOF();

    mt_install_cia(custom, 0, 1);
    mt_init(custom, mod, NULL, 0);
    mt_musicmask(custom, 0xf);
    mt_Enable = 1;
    
    startTimer();
    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;
        }
                 
        LoadView(&view);
        
        i = 0;       
        for (y = 0; y < 64-1; y += TILE) 
        {
                for (x = 0; x < WIDTH-1; x+= TILE) 
                {
                    WORD tile = map[i]-1;
                    if (tile>-1)
                        BltBitMap(tiles_bm->bitmap,  offsetx_tiles[tile], offsety_tiles[tile] ,rastPort->BitMap,
                                                        x, y, TILE, TILE,  0xC0, 0xff, NULL);

                    i++;  
                }
        }
        BltBitMap(tiles_bm->bitmap, offsetx_tiles[4], offsety_tiles[4] ,rastPort->BitMap,
                                    10, 10, TILE, TILE,  0xC0, 0xff, NULL);
        
        BltBitMap(tiles_bm->bitmap, offsetx_tiles[4], offsety_tiles[4] ,rastPort->BitMap,
                                    20, 20, TILE, TILE,  0xC0, 0xff, NULL);
        WaitBlit();
        mtimer+=timer();
        if (mtimer>=1000)
        {
                fps_val = fps;
                fps = 0;
                mtimer = mtimer - 1000;
        }
        
        sprintf(numstr, "%d fps", fps_val);
                                
        SetAPen(rastPort, 31);
        Move(rastPort, 10, 100);
        Text(rastPort, numstr, strlen(numstr));
            
        frame ^= 1;
        fps++;
          
    }; 
    
    WaitTOF();
    
    closeTimer();
    
    mt_end(custom);
    mt_remove_cia(custom);
    
    custom->dmacon = BITSET|DMAF_SPRITE;
    
    Enable();
    Permit();
    
    LoadView(oldview);
    
    FreeCprList(LOCpr1);
    FreeCprList(LOCpr2);
    FreeCprList(SHCpr1);
    FreeCprList(SHCpr2);
    
    FreeVPortCopLists(&viewPort); 

    for(d=0; d<DEPTH; d++)
    {
        if (bitMap1.Planes[d])
            FreeRaster(bitMap1.Planes[d], WIDTH, HEIGHT);
        if (bitMap2.Planes[d])
            FreeRaster(bitMap2.Planes[d], WIDTH, HEIGHT);
    }
    
    
    bm_dealloc(tiles_bm);
        
    FreeMem(mod, MOD_SIZE);
    FreeMem(tiles, TILES_SIZE);
    
    FreeMem(map, MAP_SIZE);
    FreeMem(pal, 32);
    
    if(GfxBase)
        CloseLibrary((struct Library *)GfxBase);
        
    return 0;
}

Last edited by balrogsoft; 01 December 2019 at 16:07.
balrogsoft is offline  
Old 01 December 2019, 19:33   #2
hitchhikr
Registered User

 
Join Date: Jun 2008
Location: somewhere else
Posts: 437
Dunno, at a first glance i'd say i would put the LoadView at the end of the loop so that the blitting would be made on the back buffer.
hitchhikr is offline  
Old 01 December 2019, 19:41   #3
balrogsoft
Registered User
 
Join Date: May 2006
Location: Spain
Age: 39
Posts: 67
Quote:
Originally Posted by hitchhikr View Post
Dunno, at a first glance i'd say i would put the LoadView at the end of the loop so that the blitting would be made on the back buffer.
I have tried it before, with the same results. All blitting operations are performed on the bitmap when they are not displayed. I'm struggling with this code some days but I can't make it work as expected, so the best solution at this moment is make the game running at 25 fps on slow machines. Two WaitTOF() functions instead one, and it runs ok.
balrogsoft is offline  
Old 07 December 2019, 07:51   #4
Bruce Abbott
Registered User

Bruce Abbott's Avatar
 
Join Date: Mar 2018
Location: Hastings, New Zealand
Posts: 820
I tried running your executable on my OS1.3 A500, but it crashed because the OS2.x function AllocVec() is called in bm_create(). I then tried compiling the source code but 'cop.o' is missing.

Can you supply the missing object file or the source code that created it?
Bruce Abbott is offline  
Old 07 December 2019, 09:51   #5
jotd
This cat is no more
jotd's Avatar
 
Join Date: Dec 2004
Location: FRANCE
Age: 50
Posts: 5,775
I'm currently tring to do the same thing (C++ code with blitter stuff). WaitTOF didn't help me much. In my case, the update is too slow.

I removed the main background blit and the flickering is gone. My background is static so no need to redraw it completely. My strategy now will be to draw the background around the moving objects first, then draw the objects (and don't full redraw), applying a rounding of 16 pixels of course, and trying to avoid to refresh the same zone several times (maybe more difficult/costly) by sorting the rectangles/other... didn't figure that out yet.

What about removing the loop and putting that in a VBLANK int server?

I notice that you're calling WaitBlit() after 2 blits (and not after each blit), but not in between. Wouldn't that cause issues too?

Also I didn't see any OwnBlitter()/DisownBlitter() calls. Not doing this allows the OS to use the blitter (unless the OS blit calls perform that, well, it's very likely after all)

Last edited by jotd; 07 December 2019 at 11:50.
jotd is offline  
Old 10 December 2019, 09:52   #6
balrogsoft
Registered User
 
Join Date: May 2006
Location: Spain
Age: 39
Posts: 67
Quote:
Originally Posted by Bruce Abbott View Post
I tried running your executable on my OS1.3 A500, but it crashed because the OS2.x function AllocVec() is called in bm_create(). I then tried compiling the source code but 'cop.o' is missing.

Can you supply the missing object file or the source code that created it?

Ooops, it's my fault, I'm sorry, you can delete cop.o object from the smakefile file, it was a test and it is not used in the current example. Anyway, I think I will start using the hardware directly since I found some limitations using only the functions of graphics.library.
balrogsoft is offline  
Old 10 December 2019, 10:30   #7
balrogsoft
Registered User
 
Join Date: May 2006
Location: Spain
Age: 39
Posts: 67
Quote:
Originally Posted by jotd View Post
I'm currently tring to do the same thing (C++ code with blitter stuff). WaitTOF didn't help me much. In my case, the update is too slow.

I removed the main background blit and the flickering is gone. My background is static so no need to redraw it completely. My strategy now will be to draw the background around the moving objects first, then draw the objects (and don't full redraw), applying a rounding of 16 pixels of course, and trying to avoid to refresh the same zone several times (maybe more difficult/costly) by sorting the rectangles/other... didn't figure that out yet.
In my original code I did something similar, my example only reproduces this problem. But two moving objects are enough to have problems with my code, which not only restores the background, it also has objects that are in front of these moving objects, so there are at least 8-10 blitter operations each frame.


Quote:
Originally Posted by jotd View Post
What about removing the loop and putting that in a VBLANK int server?
Well, probably I try this solution this week, I hope it works better.


Quote:
Originally Posted by jotd View Post
I notice that you're calling WaitBlit() after 2 blits (and not after each blit), but not in between. Wouldn't that cause issues too?
It was a test that I didn't remove from the example. Autodocs says that after leaving the blitter functions, the last blit performed may not have ended, I was simply ensuring that the blitter ended before making the double buffer change.


Quote:
Originally Posted by jotd View Post
Also I didn't see any OwnBlitter()/DisownBlitter() calls. Not doing this allows the OS to use the blitter (unless the OS blit calls perform that, well, it's very likely after all)
As far as I know, OwnBlitter/DisownBlitter functions are only needed if you use the blitter directly. I made all blitter operations through graphics.library
balrogsoft is offline  
Old 10 December 2019, 10:59   #8
balrogsoft
Registered User
 
Join Date: May 2006
Location: Spain
Age: 39
Posts: 67
I am thinking about the BMF INTERLEAVED flag mentioned in AllocBirMap autodocs may be the problem.


Code:
BMF_INTERLEAVED tells graphics that you would like your
        bitmap to be allocated with one large chunk of display
        memory for all bitplanes. This minimizes color flashing
        on deep displays. If there is not enough contiguous RAM
        for an interleaved bitmap, graphics.library will fall
        back to a non-interleaved one.

I don't use AllocBitMap, I'm using InitBitMap instead, I know that my code is currently not compatible with kickstart 1.3, but I want to make it compatible.


What is the correct way to initialize a bitmap with InitBitmap so that the planes are interleaved? Should I set BMF_INTERLEAVED flag on the BitMap structure after calling InitBitMap and allocating the planes in a block of continuous memory? As explained in autodocs in the initbitmap function, flags field should not be used when using InitBitmap

Code:
 The Flagsh and pad fields are reserved for future use and
    should not be used by application programs.

Last edited by balrogsoft; 10 December 2019 at 11:42.
balrogsoft is offline  
Old 10 December 2019, 15:53   #9
roondar
Registered User

 
Join Date: Jul 2015
Location: The Netherlands
Posts: 3,022
I may be misinterpreting your code. But if I understand it correctly, the problem might be found in the code below.
Code:
/* stuff removed */
        if (frame==0) {
            view.LOFCprList = LOCpr1;
            view.SHFCprList = SHCpr1;
            rastPort = &rastPort1;
        }
        else {
            view.LOFCprList = LOCpr2;
            view.SHFCprList = SHCpr2;
            rastPort = &rastPort2;
        }
                 
        LoadView(&view);
       
/* stuff removed */
       
        BltBitMap(tiles_bm->bitmap,  offsetx_tiles[tile], offsety_tiles[tile] ,
                  rastPort->BitMap, x, y, TILE, TILE,  0xC0, 0xff, NULL);

/* rest of code */
I might be misreading something, but the above seems to be incorrect. The call to LoadView seems to show the same bitmap as the one you are blitting to with the calls to BltBitMap.

If true, this would cause the kind of flickering you're observing.
roondar is offline  
Old 10 December 2019, 17:08   #10
balrogsoft
Registered User
 
Join Date: May 2006
Location: Spain
Age: 39
Posts: 67
Quote:
Originally Posted by roondar View Post
]I might be misreading something, but the above seems to be incorrect. The call to LoadView seems to show the same bitmap as the one you are blitting to with the calls to BltBitMap.

If true, this would cause the kind of flickering you're observing.

Yes, you're right, it seems like a mistake, I probably did it after hours trying to make it work, but changing the rastport pointers to point the bitmap that is not on screen, makes more flicker graphics. Anyway if you move the LoadView function at the end, where it was originally, the result is exactly the same.

Code:
        WaitTOF();
        if (frame==0) {
            view.LOFCprList = LOCpr1;
            view.SHFCprList = SHCpr1;
            rastPort = &rastPort1;
        }
        else {
            view.LOFCprList = LOCpr2;
            view.SHFCprList = SHCpr2;
            rastPort = &rastPort2;
        }
        
        BltBitMap(tiles_bm->bitmap, offsetx_tiles[4], offsety_tiles[4] ,rastPort->BitMap,
                                    10, 10, TILE, TILE,  0xC0, 0xff, NULL);

        LoadView(&view);
        frame ^= 1;
        fps++;

Last edited by balrogsoft; 10 December 2019 at 17:42.
balrogsoft is offline  
Old 13 December 2019, 18:25   #11
Bruce Abbott
Registered User

Bruce Abbott's Avatar
 
Join Date: Mar 2018
Location: Hastings, New Zealand
Posts: 820
Quote:
Originally Posted by balrogsoft View Post
changing the rastport pointers to point the bitmap that is not on screen, makes more flicker graphics. Anyway if you move the LoadView function at the end, where it was originally, the result is exactly the same.
LoadView() takes effect at the next top of frame. If you do it immediately after WaitTOF() the old bitmap will still be displayed for the rest of that frame, so you should render to the 'new' bitmap in the new view (which isn't active yet). However if rendering time extends past the next top of frame that bitmap becomes visible, and then you should render to the other bitmap. If you can't do everything in one frame then you should do WaitTOF() immediately after LoadView(), then render to the 'old' bitmap.

I finally got your code working my A500 and the frame rate was only 13fps, compared to 50fps on my 50MHz A1200. To find out what was taking up so much time on the A500 I removed the fps display code and replaced it with pokes to color 0 at various places in the code. From this I was able to estimate how much frame time was used. Blitting 10 16x16 tiles took about half the frame, while blitting 10 32x32 tiles only took 25% longer. This suggests a large overhead in BltBitmapRastPort(). Manipulating the blitter registers directly might be faster if you can do it more efficiently.
Bruce Abbott is offline  
Old 16 December 2019, 10:25   #12
balrogsoft
Registered User
 
Join Date: May 2006
Location: Spain
Age: 39
Posts: 67
Quote:
Originally Posted by Bruce Abbott View Post
LoadView() takes effect at the next top of frame. If you do it immediately after WaitTOF() the old bitmap will still be displayed for the rest of that frame, so you should render to the 'new' bitmap in the new view (which isn't active yet). However if rendering time extends past the next top of frame that bitmap becomes visible, and then you should render to the other bitmap. If you can't do everything in one frame then you should do WaitTOF() immediately after LoadView(), then render to the 'old' bitmap.

First of all, thanks a lot to take your time testing my code. I have tested any combination of LoadView - WaitTOF, this code implements a double buffer explained on amigaos.net (https://wiki.amigaos.net/wiki/Classi...ffered_Display). And the expected execution in pseudo code is:

Code:
load view 2
waittof
frame = 0

do 
    waittof

    if frame = 0
         copperlist view pointer set to view1 copper list
         rastport set to view1 to operate in bitmap1
    else
         copperlist view pointer set to view2 copper list
         rastport set to view2 to operate in bitmap2

    load view

    blit bitmap to rastport
while (condition)




So the execution frame by frame is something like:

frame 0
load view 2
wait tof

(start game loop)
wait tof 
point to view1
load view1
blit to bitmap1

frame 1
wait tof (then view1 is shown)
point to view2
load view2
blit to bitmap2

frame 0
wait tof (then view2 is shown)
point to view1
load view1
blit to bitmap1

...

I don't think I'm missing anything, it should work, but who knows, maybe I'm doing something totally wrong ...





Quote:
Originally Posted by Bruce Abbott View Post
I finally got your code working my A500 and the frame rate was only 13fps, compared to 50fps on my 50MHz A1200. To find out what was taking up so much time on the A500 I removed the fps display code and replaced it with pokes to color 0 at various places in the code. From this I was able to estimate how much frame time was used. Blitting 10 16x16 tiles took about half the frame, while blitting 10 32x32 tiles only took 25% longer. This suggests a large overhead in BltBitmapRastPort(). Manipulating the blitter registers directly might be faster if you can do it more efficiently.

This code tries to reproduce the problem by doing many blitting operations per frame, don't expect to see an optimized code.
Finally I have changed my approach to use hardware directly, there are many limits using graphics.library and system functions, I implementing first level of Agony using C, just for fun and to see if it's possible.
balrogsoft is offline  
Old 03 February 2020, 22:29   #13
balrogsoft
Registered User
 
Join Date: May 2006
Location: Spain
Age: 39
Posts: 67
I finally found the problem, the blitter functions of graphics.library are sooo slow. I have changed these functions to use the blitter directly, and it works perfectly on Amiga 500. But I couldn't find a way to optimize it further and configure the graphics to be interleaved using the graphics.library in KS 1.3, so now I am working directly with the hardware using C.

In case someone finds useful the code I has developed using the graphics.library, the following code works on Amiga 500 with KS 1.3


https://github.com/balrogsoft/amiga500-game-engine
balrogsoft is offline  
Old 04 February 2020, 13:42   #14
bebbo
botcher

 
Join Date: Jun 2016
Location: Hamburg/Germany
Posts: 555
Quote:
Originally Posted by balrogsoft View Post
I finally found the problem, the blitter functions of graphics.library are sooo slow.

Maybe you forgot to clear the layer and allocate a matching tmpRas?
Without a ras->TmpRas a temporary is allocated per call...

And for the layer an Obtain/Release-Semaphore is called...

do outside

struct Layer * l = rp->Layer;
ObtainSemaphore(&l->Lock);
rp->Layer = NULL;

// loop

rp->Layer = l;
ReleaseSemaphore(&l->Lock);
bebbo is offline  
Old 04 February 2020, 14:25   #15
balrogsoft
Registered User
 
Join Date: May 2006
Location: Spain
Age: 39
Posts: 67
Quote:
Originally Posted by bebbo View Post
Maybe you forgot to clear the layer and allocate a matching tmpRas?

If I'm not wrong, tmpRas is not necessary for blitting operations. I know that the text function uses tmpRas, but all the functions that could use tmpRas have been eliminated and the problem persists, so I don't think that tmpRas will make it work, but who knows, maybe I will give it a chance.
balrogsoft is offline  
Old 04 February 2020, 14:44   #16
bebbo
botcher

 
Join Date: Jun 2016
Location: Hamburg/Germany
Posts: 555
Quote:
Originally Posted by balrogsoft View Post
If I'm not wrong, tmpRas is not necessary for blitting operations. I know that the text function uses tmpRas, but all the functions that could use tmpRas have been eliminated and the problem persists, so I don't think that tmpRas will make it work, but who knows, maybe I will give it a chance.

it's not necessary, because the graphics function will allocate one per call, which means
ObtainSemaphore
AllocMem
WaitBlit (can't free tempras before)

FreeMem
ReleaseSemaphore
bebbo is offline  
Old 04 February 2020, 14:54   #17
balrogsoft
Registered User
 
Join Date: May 2006
Location: Spain
Age: 39
Posts: 67
Quote:
Originally Posted by bebbo View Post
it's not necessary, because the graphics function will allocate one per call, which means
ObtainSemaphore
AllocMem
WaitBlit (can't free tempras before)

FreeMem
ReleaseSemaphore

I will try, although my current code is not based on system functions because I can't setup a interleaved screen bitmap with system calls using KS 1.3, which is the most optimal scenario for blitting operations, so now I'm using hardware directly.
balrogsoft 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
graphics library v40.25 Schlachtwerk request.Apps 0 14 February 2015 20:47
Open-source graphics library Don_Adan Coders. System 32 15 January 2013 22:15
graphics.library 40.25 beta series Cosmos Coders. General 337 22 July 2011 18:15
graphics.library private LVO's AmiCoder Coders. General 12 30 December 2009 04:24
Can't open version 36 of graphics.library Lambizkit support.Apps 1 07 November 2007 08:00

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:31.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2021, vBulletin Solutions Inc.
Page generated in 0.10098 seconds with 13 queries