Bitmap blit speeds

Miscellaneous Forums/General Discussion/Bitmap blit speeds

I've been writing different tilemap engines over the years, but I never quite understood the speed differences from using big images vs. small.

When making a map with tiles, you save memory space by building a greater picture with smaller blocks, instead of loading a big file with the whole picture at once.

But what I'm unclear on is if it's viable to mix small tiles with big ones, as in blitting speeds.

If I have a big tree for example, 128x64 pixels, while the rest of my tiles are 32x32, will the tree render faster or slower than 8 small tiles?

I know there's bound to be unessesary blitting when it's partly obscured, or 90% outside screen space.

Today's computers probably doesn't care, but I would like it to be as efficient as possible :)

I'm not 100% sure either, coz no matter which one applies, you are still filling the same screen with it.

I do know one thing though... If you have a big scrolling level, it does help if you only render the tiles that are currently local to the screen area.

I guess it depends on your hardware, but last time I looked it was a lot faster to blit one big image than to build a tilemap, so using big tiles where possible should benefit speed, but it depends how much logic overhead this adds to your tile engine.

Back in the retro days where platforms like the Amiga had quite a limited amount of throughput for drawing graphics, this kind of thing was studied by many people trying to squeeze the best speed out of games. For example one thing which made a game like `Shadow of the Beast 2` smooth scrolling was that it used really quite large tiles (at the time), like 128 or 64 pixels wide, compared to the usual 32 or 16 or even 8 pixels width which were used by many other people.

Now obviously you're drawing the same amount of pixels on the screen IF the whole tile is being displayed, but if to draw that tile you have to handle smaller pieces of images then you are introducing extra overhead. The individual small tiles might not be stored close to each other in memory which decreases cache performance. Also in order to manage multiple smaller tiles you'd probably have one nested drawing loop (an X loop inside a Y loop) for each small tile. That loop has to be `set up` for each tile which can mean going to figure out the address in memory of where the new piece of image is stored, and also calculating a new address for where it should be written to. The more pieces you add the more that overhead increases. Drawing 64 8x8 tiles is going to be in general slower than drawing 1 64x64 tile, not because the write's to memory are slower but because the set-up, loop management and reading is going to be slower.

On top of that you have to consider what technique you are using for scrolling. In the old days, one of the benefits was that you got to keep the contents of the backbuffer, so you'd only have to draw the new strip of tiles which were coming into view as the screen scrolled, and you probably had hardware scrolling to move the existing content. In that scenario, there is really no loss to drawing large tiles which might only be barely visible, because you are getting more drawing done in less time and you only have to draw it once. Then the hardware scrolling can scroll the new tiles into view. With that kind of a scroll-engine you are better off using larger tiles than small ones. Your scrolling can potentially be faster moving as well as a result of more free time to draw more tiles.

Today it really depends on your scroll engine as to whether that pays off. There are different factors to consider. If you are going to be redrawing your whole screen every frame then you want to minimize time spent trying to draw tiles which are not visible, and in that regard smaller tiles at the edges of the screen would be better. But then it would be helpful to draw the visible tiles as large tiles. Also on modern graphics cards there is geometry and matrix math involved in drawing an image, like in BlitzMax via OpenGL or DirectX, it would be better to minimize the amount of vertices needing to be defined.

I think ideally what you might do is store your tiles as if they are big tiles, say 128 or 64 size, and then make your blit routine able to handle a gap at the end of each row so that it can skip to the start of the next row or a position within the graphic, so that you can draw only the visible portion of the tile - that way you'd be adapting your drawing to either draw a minimal amount of a partially visible tile, or would draw a large tile area in one go if the whole thing is visible.

You might find also that the CPU is kept waiting for the GPU to do its drawing so time that you thought you saved being efficient in handling small or large tiles might go to waste anyway. It's hard to say that on modern computers there is much benefit at all to trying to use larger tiles, but it is faster if you don't have to keep swapping textures or defining unnecessarily complex geometry.

Since you're talking about `blitting` which implies a software-based tile engine, drawing pixels to some buffer using the CPU, in that case you probably could consider what I said above, but if you're using GL or DX I would say just use reasonably large tiles and don't worry about it. The GPU will clip pixels that are outside of the screen anyway.

Scrolling can be done much faster if you use intermediate buffers; great for blitz3d but blitzmax doesnt support them and the various hacks people have put together to do it keep dissapearing; its time buffers were added to bmax.
Any modern system should draw 64x64 as fast as it can draw 8x8 and even if they cant the overhead on filling a screen with 8x8 tiles is massively more than using 64x64.

For scrolling, just redraw the big image back onto it's self, but offset it and draw the new border tiles onto the big image.

I believe it's called triple buffering?

Sorry if any of this is doubling up on imaginary's post, but i can't be bothered reading it all :D

Problem of doing that without an intermediate buffer is that it has all the sprites on it.

In triple buffering you have three screen buffers. You draw to the backmost one and then you have like an interrupt timer which triggers off interrupt code to do a Flip(0) ie copy or display the backbuffer to the visible screen in sync with the vertical blank, but inbetween the time that the current buffer is drawing and the vertical blank starts you have some time to spare so you start working on a second buffer. The idea is to use all the processing time rather than waste it waiting for the vblank.

It doesn't really have anything to do with these `intermediate buffers` that you're talking about. I think you mean that you have an off-screen buffer that you keep and which never actually flips, and so you can do old-school scrolling techniques, and thus copy the existing content over itself at a new offset to scroll efficiently, as explained by RossC. You could do the same in OpenGL and DirectX easily enough, it's just that it's not a standard Max feature.

Thanks guys for all your knowledge.

I'm going to make a tile engine for a side scroller with mixed tilesizes.

I would just code it using simple "render every tile visible on screen" logic. I also have coded a tile engine with individual tile sizes in Bmax. Depending on the tile size, screen size, your speed will vary, but in my tests using 800x600 with 2-3 layers of tiles, I was able to get acceptable frame rates on older machines. For timing/rendering I would suggest a method where your logic runs at a stable rate and the game renders as often as possible. ;)

There is a more efficient rendering method than 'draw all the tiles one-by-one'. Whether it is feasible, desirable or practical with Blitz3D or BlitzMax is another matter.

Harken back to the Amiga days. Smooth scrolling was facilitated by making best use of the hardware features. You set up a screen twice as wide as the display (for a horizontal-only scroller), drew your initial tiles and then used the hardware to offset the view portal. The magic of how you managed to string this out into a level of arbitrary size is ... well, suffice to say I'm not going to try to use my clumsy brain to describe it here. Most ex-Amiga coders reading this thread know how it was done. Every tile-width boundry (usually 16 or 32 pixels) required a blit of a vertical strip of tiles, you could even stretch this out to drawing only one tile per frame (to reduce the chance of a jitter at the boundary) with a bit of creative thinking. This was clearly the most efficient use of the system, as it reduced blitting to a minimum per frame.

I'm quite sure the same methodology could be applied to the case in question. Render the intial tileset to an image buffer twice as wide as the display. Blit that one image to the screen, which you can offset. This would give you the ability to easily scroll left & right. Every 16-pixel boundry (or whatever your tilesize is) you need only render a vertical strip of new tiles to the image buffer.

I guess it depends on the hardware and the implementation as to how efficient this ends up being.

I'm quite sure the same methodology could be applied to the case in question. Render the intial tileset to an image buffer twice as wide as the display. Blit that one image to the screen, which you can offset. This would give you the ability to easily scroll left & right. Every 16-pixel boundry (or whatever your tilesize is) you need only render a vertical strip of new tiles to the image buffer.

Simulating hardware scrolling is an interesting (read thorny*) idea but for the longest time it has been received wisdom not to blit any image larger than the screen in B3D; within Max you will have to use pixmaps, incurring a speed hit, and depending on the resolution who knows if the vid card will bug out with the final texture or not -- I wouldn't trust it.

*Really, solving the problem of how to hardware scroll efficiently on a platform that doesn't actually have or need it is no solution to something that isn't a problem.

Today's computers probably doesn't care, but I would like it to be as efficient as possible :)

If you want to use your time as efficiently as possible I would concentrate on making your code readable and maintainable, because it's 2008 and you can afford to. (Hey, someone had to mention it!)

With 3D graphics cards, you can create tiles using vertexes (polygons), put the image as a texture at UV coordinates. The images will reside in video ram, so you can have thousands of sprites floating around at 400 fps or more without any issues on Blitz3D. This can be achievable with fastImage, or with Draw3D, or possibly Sprite Candy.

Sledge: I hadn't even considered a scrolling game in Blitz before being intrigued by this thread. I wasn't even going to post, due to a subconciouss belief that trying the method I described would end up messier and slower than really necessary, due both to Blitz' intrinsic problems doing anything out of the ordainary with image buffers and the extra head-work needed to grok the basic method of hardware scrolling (and then applying it in software).

Still, I had to put the idea out there, even if just so someone (like you) could definitively slap it down with good reasoning ;)

Not sure what language you're using but in BlitzMax if you were using OpenGL for example, without any extensions, you are not guaranteed that the contents of the backbuffer remain intact after a Flip. Sometimes it is, sometimes it isn't. So you can't really treat the backbuffer as a permanent storage place for the previous frame, and besides that the backbuffer has to be the same size as the screen resolution so you can't create extra room for scrolling. And there is no hardware scrolling.

To get it to work you need a separate graphics buffer, which would entail using GL extensions, to create like a framebuffer object and/or render target to render directly to it and then switch it with the backbuffer. Hopefully that switch might not entail a full-screen copy, otherwise it's pointless. But then your buffer can be bigger than the screen and you could feasibly easily achieve 360-degree tilemap scroll engines just like in the Amiga days with minimal redraw.

One thing to consider though is that preserving the background and then drawing moving objects on top of it means that you have to `restore` the background also, so you have to have double-buffering of your buffers (ie to large buffers). If your restoration of graphics is to be efficient you have to use like `dirty rectangles`. But then dirty rects are only efficient up to a point. When there is a lot of stuff going on on-screen graphically, lots of animation etc, it is possibly more efficient to just redraw the whole background rather than restore the graphics, and then you also can skip having to draw extra strips of tiles for scrolling, and also can skip needing large image buffers. So it sort of depends on the visual needs of your game. Trying to copy sections of image buffer from one place to another would have to entail use of making one buffer be a texture and using it to render to another texture, otherwise it'd be too slow to use the `copyrect` routine.

There are also added benefits to updating the whole screen at once. There are no `playfields` on modern graphics cards since they are chunky-format based rather than planar. So if you want to do parallax scrolling you either have to do some clever stencil masking or redraw the whole screen anyway. Also when you redraw the whole screen you get animation capabilities for every single tile `for free`. If you want your tile map background to be highly an animated then this would be the way to go. Also once you get into more advanced stuff like lighting or semi-transparency or foreground/background layers etc it starts to become less desirable to try saving parts of the screen - especially when different areas of the screen start to scroll independently of each other. And what about if you want to start rotating things?

If you just want a minimally-animating one-layer tilemap, maybe two-layers if you're clever, perhaps something like a 2D Mario platform game, you might see some speed gains from using these old techniques, but otherwise I don't see that there is a lot of appeal anymore. The benefits of redrawing everything I think are far more appealing than the slight loss of speed that it might entail.

Also consider the low-end specification of graphics GPU's these days. Probably most computers out there have enough speed and hardware acceleration to run a decent tilemap game with full screen redraw. Like, even 4-5 years ago an iMac had a GeForce4 GPU which is fast enough to run most games like that.

"You set up a screen twice as wide as the display (for a horizontal-only scroller)..."

ugh...I did something similar in software on the ST. Like IH said, the added flexability of tile map animation/fx when drawing every tile is superior. Remember, we don't need the game to run at 100's of frames per second. 30-60 is fine. ;)

If you really really need the speed you could make it work with GL extensions but with eye-candy-focussed games these days it's probably not worth it I think. That said, for certain games where you want to preserve a huge destructible environment in every pixel of detail it could be a good idea to use like a `superbitmap`.

I used to do 8 way scrolling using 2.5x screen memory but it only works if you have the ability to draw to buffers. years a go i did 2 layer parallax scrolling with 200 sprites on a p233/matrox mystique 4mb using this method.