Search code examples
csdl-2

Pixel plotting AND partial screen updates in SDL2


I have an existing library for 2-bit (CGA) graphics in MS-DOS, and I would like to use the same API to support graphics using the same 2-bit assets on a modern system using SDL2. Basically I want an easy way to port past and future MS-DOS projects to Linux/Windows/Mac, and maybe to develop new MS-DOS projects under Linux (because debugging under MS-DOS is a pain).

My library stores a 2-bit buffer off screen and does partial screen updates as and when required. The process for dealing with a call to a graphical operation is basically: (1) draw to the 2-bit buffer and add the screen coordinates that need updating to a list; (2) when the list reaches capacity, or an explicit update is requested, transfer the changed parts of the buffer to the screen. This works nicely and efficiently in the original MS-DOS library.

My approach with SDL has been to retain this 2-bit buffer [1], and copy updates from it (pixel by pixel, unless someone can suggest a better way) onto an SDL_Renderer, then use SDL_RenderPresent to transfer that to the screen.

The problem with this I have found, is that "The backbuffer should be considered invalidated after each present; do not assume that previous contents will exist between frames," as the documentation says. And it's right: every other frame, something goes missing from the screen [2]. The documentation recommends using SDL_RenderClear(), but this isn't suitable for my application as I do partial screen updates; everything on the screen would disappear apart from the latest update.

So using a Renderer seems to be the right way to plot pixels individually, but the Renderer doesn't like partial screen updates that I need. From what I've read, the only way to do what I want is to copy the entire 2-bit buffer to a renderer pixel-by-pixel every time there's an update (no matter how small) required to the screen. I expect this would slow things down unacceptably.

So what's the proper way to achieve what I want?

--

[1] For those who are going to ask why... Because all the drawing operations write to this buffer, using the same code that handles drawing operations to off-screen 2-bit bitmaps, and it will be easier to port from DOS/CGA to SDL if I keep those drawing operations the same, and just worry about SDL when I transfer this buffer's contents to the screen.

[2] Strangely, I have got the graphics to update properly by putting in a 1-second pause between each "frame", which seems to allow SDL to keep the Renderer updated after each present, for reasons I don't understand. But obviously 1fps is unsuitable, even for the turn-based games I am developing. I've tried using a SDL_PollEvent() loop in its place, but that doesn't work.


Solution

  • So, after a few weeks, I have the closest thing available to an answer. SDL2 doesn't support 2-bit graphics; for that I'll have to wait for SDL3.

    If I need 2-bit graphics, the correct way seems to be to transfer all 320x200 pixels from my 2-bit back buffer to an SDL_Renderer, pixel by pixel, each time there's an update, and SDL_RenderPresent that onto my window. This is slow, but it appears to work. It runs at an acceptable speed for turn-based game at least when not running valgrind (I've not yet tried with gdb).

    My library (and the CGA hardware) does support 1-bit graphics at 640x200, twice the resolution of the 2-bit graphics. So until SDL3 is properly released, I can use a workaround: force 1-bit mode and use a 1-bit SDL_Surface that SDL2 does support, blitting 1-bit updates from my 1-bit back buffer to the 1-bit SDL_Surface. This won't allow me to do a release quality port (i.e. in colour) for Linux/Windows but it will allow me to develop under Linux, taking advantage of the debugging tools that Linux offers.