Posts: 136
Threads: 26
Joined: Aug 2017
Reputation:
5
07-18-2018, 01:56 AM
(This post was last modified: 07-18-2018, 02:31 AM by Domarius.)
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 Trying to piece something together from the example, and what's in Tilengine\Window.c...
Posts: 673
Threads: 33
Joined: Jan 1970
Reputation:
13
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.
Posts: 136
Threads: 26
Joined: Aug 2017
Reputation:
5
07-18-2018, 10:27 AM
(This post was last modified: 07-18-2018, 02:34 PM by Domarius.)
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 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;
}
Posts: 673
Threads: 33
Joined: Jan 1970
Reputation:
13
07-18-2018, 03:33 PM
(This post was last modified: 07-18-2018, 03:34 PM by megamarc.)
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.
Posts: 136
Threads: 26
Joined: Aug 2017
Reputation:
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;
}
Posts: 673
Threads: 33
Joined: Jan 1970
Reputation:
13
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
- 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;
}
Posts: 136
Threads: 26
Joined: Aug 2017
Reputation:
5
07-19-2018, 09:25 PM
(This post was last modified: 07-19-2018, 09:26 PM by Domarius.)
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!
Posts: 673
Threads: 33
Joined: Jan 1970
Reputation:
13
No problem looking at your code 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!
Posts: 136
Threads: 26
Joined: Aug 2017
Reputation:
5
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.
Posts: 673
Threads: 33
Joined: Jan 1970
Reputation:
13
Thanks man, you're very welcome to help spread the word and make it more well known and understood.
|