Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
TLN_SetRenderTarget parameter
#1
I'll update the docs, but for this one I just wanted to check, because it's code;

On this page: http://tilengine.org/doc/page_render.html it gives TLN_SetRenderTarget a void pointer;
void* framebuffer = malloc (pitch * vres);
TLN_SetRenderTarget (framebuffer, pitch);

But this causes a compilation error, because it actually wants a uint8_t pointer.  It compiles if you cast it;
TLN_SetRenderTarget ((uint8_t*)framebuffer, pitch);

Is that correct?

------------------------------

Also I was thinking, even though the external rendering is meant to be quite flexible, for this page it'd be good to have a bare minimum example of how to actually link the Tilengine output to an SDL_Texture, since that seems to be the recommended usage?
Happy to add it... just trying to work out how to do it Smile Trying to piece something together from the example, and what's in Tilengine\Window.c...
Reply
#2
Yeah, this one should be normalized... in C, a void* pointer means "anything goes" (an opaque type thing), but in C++ it must be explicitly cast or an error is thrown.

The actual internal format of the framebuffer is 32-bit RGBA pixels, but you can ambiguously express it as 8-bit channel values, as it's just a countiguous chunk of memory. Parameter type should express the kind of data, but there's no standard way to express 32-bit RGBA. And the "anything goes" void* isn't true either... That's why I used uint8_t*, to state that there are just bytes.

What do you think that makes more sense?

----------

The exact place in Window.c with the framebuffer/SDL texture link is here:
https://github.com/megamarc/Tilengine/bl...dow.c#L978
  • The TLN_BeginWindowFrame() function locks the SDL_Texture backbuffer, acquiring direct acces to pixel data, and feeds Tilengine with this data in the next line.
https://github.com/megamarc/Tilengine/bl...ow.c#L1030
  • Once the frame is finished, the TLN_EndWindowFrame() function unlocks the backbuffer object, that has just get the framebuffer rendered. Now it's ready to be blitted to the window with regular SDL function calls.
Reply
#3
Yeah I think uint8_t* is clearer, I think I've seen it before to represent colour bytes. And yeah I found those parts of the code, no problem Smile but here's where I'm actually stuck...

Code:
            SDL_LockTexture (backbuffer, NULL, (void*)&rt_pixels, &rt_pitch);
            TLN_SetRenderTarget (rt_pixels, rt_pitch);
            TLN_BeginFrame (frame);

In that first line, I get a compile error "Can't convert void* to void**" so naturally the naive thing to do is change it to cast to (void**) - then it compiles, but I get a "Segmentation fault" during runtime and the program quits.

I'm actually not surprised to see this kind of error, because as far as I understand, we're using the void pointer to just start accessing the memory directly and we can easily overstep our bounds if we don't know what we're doing, which is obviously happening, looking at the error. I educated myself on void pointers recently. I get the general idea of it, but not sure what to do here specifically.

Here's the full code (not cleaned up yet);
Code:
//Using SDL and standard IO
#include<SDL2/SDL.h>
#include <stdio.h>
#include <Tilengine.h>

//Screen dimension constants
const int SCREEN_WIDTH = 320;
const int SCREEN_HEIGHT = 240;

int main( int argc, char* args[] )
{
    //The window we'll be rendering to
    SDL_Window* window = NULL;

    //The surface contained by the window
    SDL_Surface* screenSurface = NULL;

    //Initialize SDL
    if( SDL_Init( SDL_INIT_VIDEO ) < 0 )
    {
        printf( "SDL could not initialize! SDL_Error: %s\n", SDL_GetError() );
        return 1;
    }

    //Create window
    window = SDL_CreateWindow( "Tilengine with scaling and sound", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN );
    if( window == NULL )
    {
        printf( "Window could not be created! SDL_Error: %s\n", SDL_GetError() );
        return 1;
    }

    const int hres = 400;
    const int vres = 240;
    int frame = 0;
    /*
    const int pitch = hres * sizeof(int);
    void* framebuffer;
    // init and set framebuffer
    TLN_Init (hres, vres, 2, 80, 0);
    framebuffer = malloc (pitch * vres);
    TLN_SetRenderTarget ((uint8_t*)framebuffer, pitch);
    */

    SDL_Renderer* renderer = SDL_CreateRenderer (window, -1, 0);
    uint8_t*         rt_pixels;
    int             rt_pitch;
    SDL_Texture*     backbuffer;
    backbuffer = SDL_CreateTexture (renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, SCREEN_WIDTH,SCREEN_HEIGHT);

    //Main loop flag
    bool quit = false;
    //Event handler
    SDL_Event e;
     //While application is running
    while( !quit )
    {
        //Handle events on queue
        while( SDL_PollEvent( &e ) != 0 )
        { //User requests quit
            if( e.type == SDL_QUIT )
                quit = true;
            if( e.type == SDL_KEYDOWN )
            {
                if(e.key.keysym.sym == SDLK_ESCAPE)
                    quit = true;
            }

            SDL_LockTexture (backbuffer, NULL, (void**)&rt_pixels, &rt_pitch);
            TLN_SetRenderTarget (rt_pixels, rt_pitch);
            TLN_BeginFrame (frame);

            TLN_UpdateFrame (frame);
            frame += 1;
            SDL_UnlockTexture (backbuffer);

            SDL_RenderPresent (renderer);
        }
    }

    /* deallocate */
    //free (framebuffer);
    TLN_Deinit ();

    //Destroy window
    SDL_DestroyWindow( window );
    //Quit SDL subsystems
    SDL_Quit();

    return 0;
}
Reply
#4
Hi! Two issues here:
  • You've commented out TLN_Init(). The error comes from there. You can wipe out all the framebuffer thing because you are providing one, but not the TLN_Init().
  • It's also missing the blitting of the backbuffer texture to the renderer, once it is done with the SDL_RenderCopy() function
Code:
// first initialise SDL window, SDL_Renderer and SDL_Texture just as you do
// ...
TLN_Init(SCREEN_WIDTH,SCREEN_HEIGHT, 2, 80, 0);

// And this inside your main loop:
SDL_LockTexture (backbuffer, NULL, (void**)&rt_pixels, &rt_pitch);
TLN_SetRenderTarget (rt_pixels, rt_pitch);
TLN_BeginFrame (frame);
TLN_UpdateFrame (frame++);
SDL_UnlockTexture (backbuffer);
SDL_RenderCopy (renderer, backbuffer, NULL, &dstrect);

Let me know!

I can see now that the "frame" parameter may be redundant somewhere in the API... I'll check this out.
Reply
#5
Oh $*%)#... yes I forgot to put TLN_Init() back after commenting out the framebuffer stuff...

Ok I've made those changes, now it runs, but when I quit the loop, it again prints "Segmentation fault" at the console. So it's great that it's running now, but an error on quit is obviously not good.

And do I need to "SDL_RenderPresent" at the end?

Here's my current code;

Code:
//Using SDL and standard IO
#include<SDL2/SDL.h>
#include <stdio.h>
#include <Tilengine.h>

//Screen dimension constants
const int SCREEN_WIDTH = 320;
const int SCREEN_HEIGHT = 240;

int main( int argc, char* args[] )
{
//The window we'll be rendering to
SDL_Window* window = NULL;

//Initialize SDL
if( SDL_Init( SDL_INIT_VIDEO ) < 0 )
{
printf( "SDL could not initialize! SDL_Error: %s\n", SDL_GetError() );
return 1;
}

//Create window
   window = SDL_CreateWindow( "Tilengine with scaling and sound", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN );
   if( window == NULL )
   {
       printf( "Window could not be created! SDL_Error: %s\n", SDL_GetError() );
       return 1;
   }

   const int hres = 400;
   const int vres = 240;
   int frame = 0;
   /*
   const int pitch = hres * sizeof(int);
   void* framebuffer;
   // init and set framebuffer
   TLN_Init (hres, vres, 2, 80, 0);
   framebuffer = malloc (pitch * vres);
   TLN_SetRenderTarget ((uint8_t*)framebuffer, pitch);
   */

   TLN_Init (hres, vres, 2, 80, 0);
   SDL_Renderer* renderer = SDL_CreateRenderer (window, -1, 0);
   uint8_t* rt_pixels;
   int rt_pitch;
   SDL_Texture* backbuffer;
backbuffer = SDL_CreateTexture (renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, SCREEN_WIDTH,SCREEN_HEIGHT);

//Main loop flag
bool quit = false;
//Event handler
SDL_Event e;
//While application is running
   while( !quit )
   {
       //Handle events on queue
       while( SDL_PollEvent( &e ) != 0 )
       { //User requests quit
           if( e.type == SDL_QUIT )
               quit = true;
           if( e.type == SDL_KEYDOWN )
           {
               if(e.key.keysym.sym == SDLK_ESCAPE)
                   quit = true;
           }

           SDL_LockTexture (backbuffer, NULL, (void**)&rt_pixels, &rt_pitch);
           TLN_SetRenderTarget (rt_pixels, rt_pitch);
           TLN_BeginFrame (frame);
           TLN_UpdateFrame (frame++);
           SDL_UnlockTexture (backbuffer);
           SDL_RenderCopy (renderer, backbuffer, NULL, NULL);
           SDL_RenderPresent (renderer);
       }
   }

   /* deallocate */
   //free (framebuffer);
   TLN_Deinit ();

//Destroy window
SDL_DestroyWindow( window );
//Quit SDL subsystems
SDL_Quit();

return 0;
}
Reply
#6
Hi again,

I've found some issues in your sample, I've fixed them and here you have a working one
  • At startup: two sets of resolutions used: 400x240 for tilengine and 320x240 for the SDL backbuffer. They must match. That was providing Tilengine a smaller work area than it needed, so buffer overflow was happening. segfault Smile
  • At deinitialize: missing SDL_DestroyTexture(backbuffer) and SDL_DestroyRenderer(renderer) before SDL_DestroyWindow(window).
  • Inside the main loop: the rendering part was inside the loop that polled events on the SDL. So frames were only generated in response to SDL events, but they should be generated always (game logic doesn't wait for user input to update state).
Check if this works now for you:


Code:
//Using SDL and standard IO
#include <SDL2/SDL.h>
#include <stdio.h>
#include <Tilengine.h>

//Screen dimension constants
const int SCREEN_WIDTH = 320;
const int SCREEN_HEIGHT = 240;

int main( int argc, char* args[] )
{
    //The window we'll be rendering to
    SDL_Window* window = NULL;

    //Initialize SDL
    if( SDL_Init( SDL_INIT_VIDEO ) < 0 )
    {
        printf( "SDL could not initialize! SDL_Error: %s\n", SDL_GetError() );
        return 1;
    }

    //Create window
    window = SDL_CreateWindow( "Tilengine with scaling and sound", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN );
    if( window == NULL )
    {
        printf( "Window could not be created! SDL_Error: %s\n", SDL_GetError() );
        return 1;
    }

    int frame = 0;

    TLN_Init (SCREEN_WIDTH, SCREEN_HEIGHT, 2, 80, 0);
    SDL_Renderer* renderer = SDL_CreateRenderer (window, -1, 0);
    uint8_t* rt_pixels;
    int rt_pitch;
    SDL_Texture* backbuffer;
    backbuffer = SDL_CreateTexture (renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, SCREEN_WIDTH,SCREEN_HEIGHT);

    //Main loop flag
    bool quit = false;
    //Event handler
    SDL_Event e;
    //While application is running
    while( !quit )
    {
        //Handle events on queue
        while( SDL_PollEvent( &e ) != 0 )
        {
            //User requests quit
            if( e.type == SDL_QUIT )
                quit = true;
            if( e.type == SDL_KEYDOWN )
            {
                if(e.key.keysym.sym == SDLK_ESCAPE)
                    quit = true;
            }
        }

        SDL_LockTexture (backbuffer, NULL, (void**)&rt_pixels, &rt_pitch);
        TLN_SetRenderTarget (rt_pixels, rt_pitch);
        TLN_BeginFrame (frame);
        TLN_UpdateFrame (frame++);
        SDL_UnlockTexture (backbuffer);
        SDL_RenderCopy (renderer, backbuffer, NULL, NULL);
        SDL_RenderPresent (renderer);
    }

    /* deallocate */
    //free (framebuffer);
    TLN_Deinit ();

    //Destroy window
    SDL_DestroyTexture(backbuffer);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow( window );
    //Quit SDL subsystems
    SDL_Quit();

    return 0;
}
Reply
#7
Urgh... yeah... that was all stuff I could've fixed myself given enough time, I had a feeling that was the case.  I was going to have another look at it today but I had to put it on hold.  I'll certainly be having more confidence in myself that I can tackle this with enough attention to detail, even though it's a kind of new area in some ways - so moving forward I'll be giving the issues a lot more time before giving up and posting here! Because really I'd like to keep posting about legit issues to help improve Tilengine.

Thank you for looking at it - yes it works just fine now, no segmentation fault!
Reply
#8
No problem looking at your code Smile it's more efficient if I can review your code when you get stuck at some point, instead of trying to guess you alone where's the problem. Tilengine is not thoroughly tested and may contain bugs or quirks. At least you're fiddling with it and trying to do things, I appreciate your effort!
Reply
#9
Ah my pleasure. I feel like Tilengine was made for me. I hope it becomes more well known, perhaps tutorials and videos will help with that! I'll be plugging it on my site when I put up the little games I have planned.
Reply
#10
Thanks man, you're very welcome to help spread the word and make it more well known and understood.
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)