0
\$\begingroup\$

So, I'm new at C programming and SDL2 and I'm trying to get something working here to write some games. First, I would like to know if I'm doing the rendering stuff the right way.

I'm kind of confused about the software and hardware rendering. Here I'm using a SDL_Texture and SDL_LockTexture to write raw pixels into it. I would like to know if I should use SDL_Surface instead, and, how can I get the best performance out of the hardware.

I allocate the memory for the pixels in the SDL_WINDOWEVENT_EXPOSED, which is called the first time a window appears, and everytime the window is resized, so then I can reallocate a new memory for the new window size.

For that to work, I need to SDL_DestroyTexture and free(pixels) to allocate newer ones in a new resized SDL_Texture, at least I think I need. The problem is... I'm getting a Segmentation Fault right in the free() call, according to GDB (GNU Debugger), and I don't know why, it's probably some stupid beginner error.

It is important to mention that, in the first while(running) iteration, the function free() is not called, so, the SegFault only happens the first time I resize the window. Here is the code:

#include <stdio.h> // fprintf, printf

#include <SDL2/SDL.h>

typedef struct
{
  SDL_Texture* texture;
  void* pixels;
  int width;
  int height;
  int pitch;
  int bytes_per_pixel;

} Render_Buffer;

void rendering_testing(Render_Buffer* render_buffer)
{
  if(SDL_LockTexture(render_buffer->texture,
                     NULL,
                     &render_buffer->pixels,
                     &render_buffer->pitch) != 0)
  {
    fprintf(stderr, "Failed to lock SDL texture: %s\n", SDL_GetError());
  }
  else
  {
    uint8* row_first_byte = (uint8*)render_buffer->pixels;

    for(uint16 y = 0; y < render_buffer->height; ++y)
    {
      uint32* pixel = (uint32*)row_first_byte;

      for(uint16 x = 0; x < render_buffer->width; ++x)
      {
        *pixel++ = 0x00ff00ff;
      }

      row_first_byte += render_buffer->pitch;
    }

    SDL_UnlockTexture(render_buffer->texture);
  }
}

int main(int argc, char* argv[])
{
  if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) != 0)
  {
    fprintf(stderr, "Failed to initialize SDL: %s\n", SDL_GetError());
    return -1;
  }

  int window_width = 640;
  int window_height = 480;

  SDL_Window* window = SDL_CreateWindow("Breakout",
                                        SDL_WINDOWPOS_CENTERED,
                                        SDL_WINDOWPOS_CENTERED,
                                        window_width,
                                        window_height,
                                        SDL_WINDOW_RESIZABLE
#ifndef DEBUG
                                        |SDL_WINDOW_FULLSCREEN_DESKTOP
#endif
                                        );
  if(window == NULL)
  {
    fprintf(stderr, "Failed to create SDL window: %s\n", SDL_GetError());
    SDL_Quit();
    return -1;
  }

  SDL_Renderer* renderer = SDL_CreateRenderer(window, -1,
                                              SDL_RENDERER_ACCELERATED|
                                              SDL_RENDERER_PRESENTVSYNC);
  if(renderer == NULL)
  {
    fprintf(stderr, "Failed to create SDL renderer: %s\n", SDL_GetError());
    SDL_DestroyWindow(window);
    SDL_Quit();
    return -1;
  }

  Render_Buffer render_buffer = {0};
  render_buffer.bytes_per_pixel = 4;

  bool8 running = true;
  while(running)
  {
    SDL_Event event;
    while(SDL_PollEvent(&event))
    {
      switch(event.type)
      {
        case SDL_QUIT:
        {
          running = false;
        } break;

        case SDL_WINDOWEVENT:
        {
          switch(event.window.event)
          {
            case SDL_WINDOWEVENT_EXPOSED:
            {
              if(render_buffer.texture)
              {
                SDL_DestroyTexture(render_buffer.texture);
              }

              SDL_GetWindowSize(window, &window_width, &window_height);
#ifdef DEBUG
              printf("window width: %d\nwindow height: %d\n", window_width, window_height);
#endif
              render_buffer.width = window_width;
              render_buffer.height = window_height;

              render_buffer.texture = SDL_CreateTexture(renderer,
                                                        SDL_PIXELFORMAT_RGBA8888,
                                                        SDL_TEXTUREACCESS_STREAMING,
                                                        render_buffer.width,
                                                        render_buffer.height);

              if(render_buffer.texture == NULL)
              {
                fprintf(stderr, "Failed to create SDL texture: %s\n", SDL_GetError());
                SDL_DestroyRenderer(renderer);
                SDL_DestroyWindow(window);
                SDL_Quit();
                running = false;
                return -1;
              }

              if(render_buffer.pixels)
              {
                free(render_buffer.pixels); // SEGFAULT HAPPENS HERE!!!
              }

              render_buffer.pixels = malloc(render_buffer.width *
                                            render_buffer.height *
                                            render_buffer.bytes_per_pixel);

              render_buffer.pitch = render_buffer.width * render_buffer.bytes_per_pixel;

            } break;

            default:
            {
              // TODO
            }
          }
        } break;

        default:
        {
          // TODO
        }
      }
    }

    rendering_testing(&render_buffer);

    SDL_RenderCopy(renderer, render_buffer.texture, NULL, NULL);
    SDL_RenderPresent(renderer);
  }

  SDL_DestroyTexture(render_buffer.texture);
  SDL_DestroyRenderer(renderer);
  SDL_DestroyWindow(window);
  SDL_Quit();

  return 0;
}

Should I use malloc() and free() or something else instead?

What can I do to solve this SegFault error? What can I do the get the best performance out of the renderer while writing pixels manually into the memory, and what is the right way to do that? What can I do better that I'm not doing here?

For now, thanks so much for the attention!

\$\endgroup\$
  • 1
    \$\begingroup\$ Your segfault is happening because you're calling free on already freed memory. SDL_DestroyTexture frees the memory managed by the SDL_Texture that you 'Destroy'. \$\endgroup\$ – Joshua Hedges Apr 24 at 0:28
  • 1
    \$\begingroup\$ Also you shouldn't be destroying your renderer, window and quitting SDL until you're out of that loop, you're currently doing each twice, under certain scenarios, you should be fine to just leave the calls to those functions at the end of your main() for now, where you have another set of them. In general you shouldn't need more than one call to SDL_Init(). Also one call to SDL_Quit(), and only after you're completely done destroying/freeing up everything else related to SDL. \$\endgroup\$ – Joshua Hedges Apr 24 at 0:32
  • \$\begingroup\$ @JoshuaHedges you are dam right! Thank you very much! \$\endgroup\$ – serafimeid Apr 24 at 2:58
  • \$\begingroup\$ Oh, but, if I manage to write only one SDL_Quit() at the end of the main() function, I will need to write a bunch of if's and else's. Do you think it's a better programming practice to not write a bunch of return in the middle of the code and instead use a return_value int variable to use at the end of the program? I dont think it's such a big deal to do that. Actually, the SDL wiki always call return when checking for a feature initialization in the middle of the code. \$\endgroup\$ – serafimeid Apr 24 at 3:17
  • \$\begingroup\$ Hey @JoshuaHedges, the SDL_DestroyTexture is not freeing my pixels memory. When I resize the window and look to the memory usage in the task manager, it only gets bigger. If pixels were freed, the memory usage should increase and decrease when resizing the window. \$\endgroup\$ – serafimeid Apr 24 at 3:49
1
\$\begingroup\$

After the SDL_UnlockTexture the pointer to the pixels gets invalidated. From SDL documentation

As an optimization, the pixels made available for editing don't necessarily contain the old texture data. This is a write-only operation, and if you need to keep a copy of the texture data you should do that at the application level.

Also is explained in LazyFoo's documentation.

After the texture is unlocked the pixel pointer is invalid so we set it to NULL.

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.