![]() |
![]() |
#1 |
Registered User
Join Date: May 2006
Location: Spain
Age: 41
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. |
![]() |
![]() |
#2 |
Registered User
![]() Join Date: Jun 2008
Location: somewhere else
Posts: 449
|
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.
|
![]() |
![]() |
#3 |
Registered User
Join Date: May 2006
Location: Spain
Age: 41
Posts: 67
|
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.
|
![]() |
![]() |
#4 |
Registered User
![]() Join Date: Mar 2018
Location: Hastings, New Zealand
Posts: 1,751
|
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? |
![]() |
![]() |
#5 |
This cat is no more
![]() Join Date: Dec 2004
Location: FRANCE
Age: 51
Posts: 7,367
|
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. |
![]() |
![]() |
#6 | |
Registered User
Join Date: May 2006
Location: Spain
Age: 41
Posts: 67
|
Quote:
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. |
|
![]() |
![]() |
#7 | |||
Registered User
Join Date: May 2006
Location: Spain
Age: 41
Posts: 67
|
Quote:
Quote:
Quote:
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 |
|||
![]() |
![]() |
#8 |
Registered User
Join Date: May 2006
Location: Spain
Age: 41
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. |
![]() |
![]() |
#9 |
Registered User
![]() Join Date: Jul 2015
Location: The Netherlands
Posts: 3,319
|
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 */ If true, this would cause the kind of flickering you're observing. |
![]() |
![]() |
#10 | |
Registered User
Join Date: May 2006
Location: Spain
Age: 41
Posts: 67
|
Quote:
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. |
|
![]() |
![]() |
#11 | |
Registered User
![]() Join Date: Mar 2018
Location: Hastings, New Zealand
Posts: 1,751
|
Quote:
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. |
|
![]() |
![]() |
#12 | ||
Registered User
Join Date: May 2006
Location: Spain
Age: 41
Posts: 67
|
Quote:
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:
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. |
||
![]() |
![]() |
#13 |
Registered User
Join Date: May 2006
Location: Spain
Age: 41
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 |
![]() |
![]() |
#14 | |
botcher
![]() Join Date: Jun 2016
Location: Hamburg/Germany
Posts: 659
|
Quote:
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); |
|
![]() |
![]() |
#15 | |
Registered User
Join Date: May 2006
Location: Spain
Age: 41
Posts: 67
|
Quote:
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. |
|
![]() |
![]() |
#16 | |
botcher
![]() Join Date: Jun 2016
Location: Hamburg/Germany
Posts: 659
|
Quote:
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 |
|
![]() |
![]() |
#17 | |
Registered User
Join Date: May 2006
Location: Spain
Age: 41
Posts: 67
|
Quote:
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. |
|
![]() |
Currently Active Users Viewing This Thread: 1 (0 members and 1 guests) | |
Thread Tools | |
![]() |
||||
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 |
|
|