5
\$\begingroup\$

In my current project I need to fill a 256x224 screen with the pixel values from an array. The array is declared this way:

uint8_t *vram = new uint8_t[7168];

So as you might have noticed, every bit in this array represent one pixel in the screen (256 x 224 / 8 = 7168). My current attempt to render a screen looks like this: (it is executed at 60hz)

int x = 0;  
int y = 0;
for (int i = 0; i < 7168; i++)
{
    //read the byte
    uint8_t b = vram[i];
    for (int j = 7; j >= 0; j--)
    {
        //read 1 bit from the original byte
        bool pixel = (b >> j) & 1;
        SetPixel(x, y, pixel);

        //if end of line reached, go to next line
        if (((x + 1) % 256) == 0)
        {
            x = 0;
            y++;
        }
        else
        {
            x++;
        }
    }
}

and my SetPixel function:

void SetPixel(int x, int y, Uint32 pixel)
{
    Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * 4;
    *(Uint32 *)p = pixel;
}

My window and surface declarations:

window = SDL_CreateWindow("Window", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 256, 224, SDL_WINDOW_OPENGL);
surface = SDL_GetWindowSurface(window);

Currently I have two big problems:

  • My surface isn't been changed, doesn't matter what I try
  • I'm not even sure if this method is efficient, looking at profiling data from Visual Studio it doesn't seem to be

So, how could I render a black and white screen from an byte array in a somewhat efficient way?

Any help will be very appreciated!

\$\endgroup\$
6
  • \$\begingroup\$ What are you doing to test whether or not the surface has changed? Is it a screen surface that you're expecting will change the appearance of the window? Or are you directly checking the memory in a debugger? \$\endgroup\$
    – Sardonic
    Commented Feb 9, 2019 at 18:41
  • \$\begingroup\$ Yep, a screen surface in a window. I will clarify it in my question right now. Really forgot about it. \$\endgroup\$
    – salgado
    Commented Feb 9, 2019 at 18:46
  • 1
    \$\begingroup\$ Are you calling SDL_UpdateWindowSurface( window ) after making your changes to the surface pixels? \$\endgroup\$
    – Sardonic
    Commented Feb 9, 2019 at 19:07
  • 1
    \$\begingroup\$ No. I will add it right now to my code. Thanks for pointing it out! \$\endgroup\$
    – salgado
    Commented Feb 9, 2019 at 19:11
  • 1
    \$\begingroup\$ I'm working on an answer for you now, but I misread your question at first and am a bit stumped... I may post a partial answer for now with some pointers of where to go, and someone cleverer than I might swoop in and fill out the rest :) \$\endgroup\$
    – Sardonic
    Commented Feb 9, 2019 at 19:22

1 Answer 1

4
\$\begingroup\$

It seems like you have 2 questions here: 1. Why doesn't this code change the surface? 2. Is there a way to do this faster?

Why doesn't this code work?

For me, opening a window, grabbing the screen surface, and writing to the pixels with the function you have there works perfectly fine, so the problem is likely not in your SetPixels function. Instead, it's likely with your surface. There are a couple of possible problems here; most likely, however, is that you simply forgot to call SDL_UpdateWindowSurface( window ) after making your changes. If this doesn't fix it, make sure that:

  1. Your surface is from your window. Surfaces can come from a lot of different places in SDL, so make sure this surface is indeed the surface that backs the window. You can get this from SDL_GetWindowSurface( window )

  2. Depending on the surface, you may need to lock it and unlock it with SDL_LockSurface() and SDL_UnlockSurface(), modifying the pixels in between those two calls.

How can it be faster?

Ideally, by using more memory. The best thing to do would be to do away with the bit-array altogether and simply use a texture (or SDL_Surface) that is the same size as the window surface. Then, copy the whole thing, perhaps with an SDL_BlitSurface() or, even better, using an SDL_Renderer and SDL_RenderCopy(). The main thing slowing down the code is unpacking the bits. If you can store the whole texture at once, you'll be able to skip that step entirely. Then, when copying a giant texture all at once, SDL will be able to take advantage of all the CPU's efficient memory copying features (if using SDL_Surfaces) or all of the extreme texture-copying might of the GPU (if using an SDL_Renderer).

If this isn't possible, the more pixels you can copy at once, the better off you'll be.

The SDL docs demonstrate clearing an entire surface on the CPU using the call SDL_memset(surface->pixels, 0, surface->h * surface->pitch) (Source). In your case, SDL_memcpy may be a more useful call. You should try to expand the pixels out into a block of memory several bytes long (the more the better), and then call something like SDL_memcpy(surface->pixels, eightPixels, sizeof(Uint32_t) * 8) if you were copying eight pixels at once, for example. I don't have an exact working sample of this, but it should give you a useful direction to move in.

\$\endgroup\$
4
  • 1
    \$\begingroup\$ I'll add to this that attempting to micro-optimize by having each bit in an array be a pixel is only going to make things worse. This goes hand-in-hand with your "how can it be faster" section - most displays are 32-bit RGBX, so storing the source data in the same 32-bit format allows it to be quickly written from source to destination in a single operation, rather than having to go through intermediate bit-by-bit unpacking. This is one of those seemingly counter-intuitive things about graphics programming - using substantially more memory can make your program orders of magnitude faster. \$\endgroup\$ Commented Feb 10, 2019 at 12:14
  • \$\begingroup\$ @MaximusMinimus I added a bit about this to the answer. \$\endgroup\$
    – Sardonic
    Commented Feb 11, 2019 at 2:04
  • \$\begingroup\$ @MaximusMinumus I totally agree with you now, after some attempts to make this code batter. The major problem is that this bit array is a memory dump from an old hardware that rendered image like this so I really was biased towards this solution that worked well for the time it was used. It's really counter intuitive as you pointed out! \$\endgroup\$
    – salgado
    Commented Feb 11, 2019 at 11:06
  • \$\begingroup\$ @Sardonic Thank you for your answer! You helped me a lot! \$\endgroup\$
    – salgado
    Commented Feb 11, 2019 at 11:07

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .