Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
External rendering SDL2
#1
Question 
I’ve been trying to wrap my head around the external rendering feature of Tilengine lately to no avail. I just don’t understand how it’s supposed to work despite reading the documentation and looking at examples. I tried poking around Tilengine’s window.c file to see how it’s done, but attempting to recreate what’s being done in window.c on my own ended with failure. I’m sure it’s just my lack of knowledge in rendering/surfaces to begin with, but I could really use some help on the topic, as I’ve been spinning my wheels here for days. If someone could give me a short example on the proper and most simple way to get TLN_SetRenderTarget working with an SDL_Surface it would be much appreciated. Thank you and have a great day!
Reply
#2
Hi!

It would be better if you post here source code of your current attempt, so I can check why it's failing.

The "tricky" part of setting up the external rendering is not in tilengine -as it takes just one function-, but on the host side.

In SDL2, at the beginning you must:
  1. create the window
  2. create the renderer
  3. create 32-bit ARGB  backbuffer texture with "streaming" attribute (so it gets optimized for frequent writes):
Code:
SDL_Window* window = SDL_CreateWindow(window_title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, wnd_width, wnd_height, 0);
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED + SDL_RENDERER_PRESENTVSYNC);
SDL_Texture* backbuffer = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, wnd_params.width, wnd_params.height);

Note:
Tilengine supports window scaling, so internal framebuffer resolution is not the same than target window size
  • wnd_width and wnd_height are target window dimensions after scaling
  • wnd_params.width and wnd.params.height are original framebuffer size (the one you set with TLN_Init())
For each rendered frame, you must:

  1. Gain write access to framebuffer by locking the texture
  2. Set render target to the locked texture
  3. Render the frame
  4. Unlock the backbuffer texture so SDL has access again
  5. Copy the backbuffer texture to the renderer, scaling it to fit window size
  6. Present the renderer to the screen:
Code:
// setup render target
static uint8_t* rt_pixels; // pointer to pixel data
static int rt_pitch;       // bytes per scanline
SDL_LockTexture (backbuffer, NULL, (void**)&rt_pixels, &rt_pitch);
TLN_SetRenderTarget (rt_pixels, rt_pitch);

// render frame
TLN_UpdateFrame(frame);

// present to screen
SDL_UnlockTexture(backbuffer);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, backbuffer, NULL, &dstrect);
SDL_RenderPresent(renderer);

This is basically what the built-in tilengine window does, you can find it in window.c source code. As you can see it is mostly SDL2 stuff, not tilengine stuff. This is different for each framework where you want to embed external rendering.

Hope this helps!
Reply
#3
(06-27-2021, 04:32 PM)megamarc Wrote: Hi!

It would be better if you post here source code of your current attempt, so I can check why it's failing.

The "tricky" part of setting up the external rendering is not in tilengine -as it takes just one function-, but on the host side.

In SDL2, at the beginning you must:
  1. create the window
  2. create the renderer
  3. create 32-bit ARGB  backbuffer texture with "streaming" attribute (so it gets optimized for frequent writes):
Code:
SDL_Window* window = SDL_CreateWindow(window_title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, wnd_width, wnd_height, 0);
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED + SDL_RENDERER_PRESENTVSYNC);
SDL_Texture* backbuffer = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, wnd_params.width, wnd_params.height);

Note:
Tilengine supports window scaling, so internal framebuffer resolution is not the same than target window size
  • wnd_width and wnd_height are target window dimensions after scaling
  • wnd_params.width and wnd.params.height are original framebuffer size (the one you set with TLN_Init())
For each rendered frame, you must:

  1. Gain write access to framebuffer by locking the texture
  2. Set render target to the locked texture
  3. Render the frame
  4. Unlock the backbuffer texture so SDL has access again
  5. Copy the backbuffer texture to the renderer, scaling it to fit window size
  6. Present the renderer to the screen:
Code:
// setup render target
static uint8_t* rt_pixels; // pointer to pixel data
static int rt_pitch;       // bytes per scanline
SDL_LockTexture (backbuffer, NULL, (void**)&rt_pixels, &rt_pitch);
TLN_SetRenderTarget (rt_pixels, rt_pitch);

// render frame
TLN_UpdateFrame(frame);

// present to screen
SDL_UnlockTexture(backbuffer);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, backbuffer, NULL, &dstrect);
SDL_RenderPresent(renderer);

This is basically what the built-in tilengine window does, you can find it in window.c source code. As you can see it is mostly SDL2 stuff, not tilengine stuff. This is different for each framework where you want to embed external rendering.

Hope this helps!

Hey Marc, I'm finally getting around to trying to get external rendering working again, but I've ran into some issues again.

I've sent up things nearly identically to the window.c in Tilengine, only I am using a C++ class instead. I thought I had everything right, but then the background was not updating at all, so I double checked and saw I was missing a few things, and now I'm sure it is setup properly, however when I go to call TLN_UpdateFrame() I get a Segmentation fault. What could I be doing wrong?

this is the code I am running in my gameloop to update the graphics:
Code:
const int CURRENT_TIME_MS = SDL_GetTicks();
       int ELAPSED_TIME_MS = CURRENT_TIME_MS - LAST_UPDATE_TIME;

       graphics.beginWindowFrame();
       TLN_UpdateFrame(std::min(ELAPSED_TIME_MS, MAX_FRAME_TIME));
       //this->update(std::min(ELAPSED_TIME_MS, MAX_FRAME_TIME));
       LAST_UPDATE_TIME = CURRENT_TIME_MS;
       graphics.endWindowFrame();

and these are the functions I am using in my graphics class:
Code:
void Graphics::endWindowFrame() {
   SDL_UnlockTexture(this->_backbuffer); // Tilengine stuff
   SDL_RenderClear(this->_renderer);
   SDL_RenderCopy(this->_renderer, this->_backbuffer, NULL, &this->dstrect);
   SDL_RenderPresent(this->_renderer);
}

void Graphics::beginWindowFrame() {
   SDL_LockTexture (this->_backbuffer, NULL, (void**)&this->_rt_pixels, &this->_rt_pitch);
   TLN_SetRenderTarget (this->_rt_pixels, this->_rt_pitch);
}

The graphics class constructor:
Code:
this->_screenWidth = 432;
   this->_screenHeight = 240;
   this->_resWidth = globals::HRES;
   this->_resHeight = globals::VRES;
   this->_maxSprites = 256;
   this->_colorCycles = 2;
   
   this->dstrect.x = 0;
   this->dstrect.y = 0;
this->dstrect.w = globals::SCREEN_WIDTH * (int)globals::SPRITE_SCALE;
this->dstrect.h = globals::SCREEN_HEIGHT * (int)globals::SPRITE_SCALE;
   this->_window = SDL_CreateWindow("Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, globals::SCREEN_WIDTH * globals::SPRITE_SCALE, globals::SCREEN_HEIGHT * globals::SPRITE_SCALE, 0);
   this->_renderer = SDL_CreateRenderer(this->_window, -1, SDL_RENDERER_ACCELERATED + SDL_RENDERER_PRESENTVSYNC);
   this->_backbuffer = SDL_CreateTexture(this->_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, this->_resWidth, this->_resHeight);
   SDL_SetTextureAlphaMod(this->_backbuffer, 0);

If you could give me any insight on this it would be much appreciated! Thank you very much and happy new year to you!
Reply
#4
Happy new year!
How are you doing?
Without having the full source code of your attempt I can only guess, as I'm not expert on the quirks of C++. But there's a thing it doesn't smell well for me: the locked texture lives inside a "graphics" class, whereas TLN_UpdateFrame() is being called outside that class. I don't know how C++ handles this, but concerning access rights, you're trying that TLN_UpdateFrame() access private data of an external class, and that's forbidden. Maybe the segmentation fault is originated on this unallowed access, but that's just a guess.

Make sure that the target surface and _rt_pixels have public access, or call TLN_UpdateFrame() from inside Graphics class.

Let me know!
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)