sdl2 flickering unless I don't use createrenderer - c

I have a somewhat basic rendering loop which blits a scaled image to the screen as fast as I can process the event loop. I created a minimal example that recreates the flickering on pastebin here.
If I don't use "SDL_CreateRenderer", and instead leave renderer to NULL, it works. I just can't clear the screen first. If I set the renderer, I get this crazy fast flickering.
// if I comment this out in my init_sdl(), no flickering...
renderer = SDL_CreateRenderer(window, -1, 0);
assert(renderer != NULL);
my draw function happens at the end of the event loop:
void draw()
{
SDL_SetRenderDrawColor(renderer, 255, 0, 128, 255);
SDL_RenderClear(renderer);
SDL_Rect dstrect = {
.x = 50,
.y = 50,
.h = 100,
.w = 100,
};
SDL_BlitScaled(img, NULL, screen, &dstrect);
SDL_UpdateWindowSurface(window);
SDL_RenderPresent(renderer);
}
I've seen this potential duplicate question, but the problem was that they had their RenderPresent in the wrong place. You can see I'm calling SDL_RenderPresent at the end of all drawing operations, which was my takeaway from that. It is still happening.
I'm using msys2 (mingw_x64), gcc, windows 10, SDL2.

Related

Simple Stretched Raylib Fullscreen Option?

The code for a simple Raylib program is listed below (based on the Raylib example shapes_logo_raylib). Running the program shows a version of the Raylib logo: a black square outline which fills about a third of the (800x450) window.
It's not hard to make a fullscreen version of the program, with calls such as GetCurrentMonitor(), SetWindowSize(), GetMonitorWidth(), GetMonitorHeight()
SetConfigFlags(FLAG_WINDOW_RESIZABLE), or ToggleFullscreen(). But then, while the black square remains a similar size as before, it occupies (top left) a much smaller proportion of the larger (fullscreen) window. Is there an option to display a larger "stretched" version of the original windowed image on the fullscreen window?
#include "raylib.h"
int main(void)
{
InitWindow(800, 450, "raylib [shapes] example - raylib logo using shapes");
while (!WindowShouldClose())
{
BeginDrawing()
ClearBackground(RAYWHITE);
DrawRectangle(screenWidth/2 - 128, screenHeight/2 - 128, 256, 256, BLACK);
DrawRectangle(screenWidth/2 - 112, screenHeight/2 - 112, 224, 224, RAYWHITE);
DrawText("raylib", screenWidth/2 - 44, screenHeight/2 + 48, 50, BLACK);
EndDrawing();
}
CloseWindow();
return 0;
}
I made the following additions to your code to make it work on any window size. It draws the stuff you want to draw onto a RenderTexture2D and then draws said texture onto the screen. I've only tested it with resizable windows, but it should work in any window mode, including exclusive fullscreen.
In short:
Request render texture using LoadRenderTexture(int, int)
Use BeginTextureMode(RenderTexture2D) and EndTextureMode() to draw onto the texture
Draw the texture using DrawTexturePro(Texture2D, Rectangle, Rectangle, Vector2, float, Color), the first rectangle is the size of the texture, the second the size of the screen. If it looks mirrored, invert the height of the input texture.
Unload the render texture when done.
I added comments to all my additions/changes to highlight what needs to be changed.
#include "raylib.h"
// This was just missing from your code, but I wanted to show a compilable version of the code
int screenWidth = 800;
int screenHeight = 450;
int main(void)
{
InitWindow(800, 450, "raylib [shapes] example - raylib logo using shapes");
// This should use the flag FLAG_FULLSCREEN_MODE which results in a possible ToggleFullscreen() call later on
SetWindowState(FLAG_WINDOW_RESIZABLE);
// Request a texture to render to. The size is the screen size of the raylib example.
RenderTexture2D renderTexture = LoadRenderTexture(screenWidth, screenHeight);
while (!WindowShouldClose())
{
// Instead of using BeginDrawing() we render to the render texture. Everything else stays unchanged
BeginTextureMode(renderTexture);
ClearBackground(RAYWHITE);
DrawRectangle(screenWidth / 2 - 128, screenHeight / 2 - 128, 256, 256, BLACK);
DrawRectangle(screenWidth / 2 - 112, screenHeight / 2 - 112, 224, 224, RAYWHITE);
DrawText("raylib", screenWidth / 2 - 44, screenHeight / 2 + 48, 50, BLACK);
// We need to end the texture mode separately
EndTextureMode();
// Let's draw the texture. The source rect is the size of the texture, the destination rect is of the same size as the screen. For some reason, the texture was flipped vertically, so I had to invert the source rects "height" to flip the UV.
BeginDrawing();
DrawTexturePro(
renderTexture.texture,
Rectangle{ 0, 0, static_cast<float>(renderTexture.texture.width), static_cast<float>(-renderTexture.texture.height) },
Rectangle{ 0, 0, static_cast<float>(GetScreenWidth()), static_cast<float>(GetScreenHeight()) },
Vector2{ 0, 0 },
0,
WHITE);
EndDrawing();
}
// Unload the texture handle again to make a clean exit.
UnloadRenderTexture(renderTexture);
CloseWindow();
return 0;
}
I hope this answers your question.

End of draw loop causing massive delay

I have multiple 'modes' which draw different things on the screen e.g. movement, inventory. When I switch between these, I am using a transition that fades black then fade back into the other mode.
The transition uses millis() and the time it started to determine how transparent to make a black rectangle that covers the screen.
However there is a massive delay at the end of the draw function which is disrupting this. The delay happens right after the mode that is being drawn behind the rectangle changes. It also only happens on the first transition made, not on subsequent ones.
What is causing the delay and how can I prevent it?
My code is very long and I have not been able to find what might be causing this. So I'm not sure exactly what code to post.
I've tried using processing's inbuilt debugger but the debugger doesn't pause millis() which makes it very unhelpful.
I've also tried adding several println() statements as timestamps but these only narrow the problem down to being between the end of draw() and the start of then next loop of draw()
// Transitions
if (p.transMode != ""){
if (timer == 0){
timer = millis();
} // If timer hasn't been started
ctime = millis() - timer; // current time
if (ctime < (transT*2/5)){
fill (0, 0, 0, map(ctime, 0, transT*2/5, 0, 255));
// Get darker
}
else if (ctime < transT/2){
p.transMode != p.mode){fill (0, 0, 0, 255);
p.mode = p.transMode;
// change mode in transition and move if able, fill black
}
else if (ctime < transT){
fill (0, 0, 0, map(ctime - transT/2, 0, transT/2, 255, 0));
// Get lighter
}
else { // If transition finished
timer = 0; // reset timer
p.transMode = ""; // turn off transition
fill(0, 0, 0, 0);
}
// draw transition rectangle
beginShape();
vertex(-100, -100, 200);
vertex(1100, -100, 200);
vertex(1100, 600, 200);
vertex(-100, 600, 200);
endShape();
}
Instead of fading back into the new mode it stays completely black for longer then suddenly cuts to the other mode (the timer doesn't let it enter the if statement that is making it fade back).

Blank screen after rendering text

I have opened a window which shows two rects on the screen then using SDL_TTF to show the mouse position on the screen.
The bit I am having hard time understanding is why after rendering text the the two rects before it do not show up.
I am using SDL_RenderFillRect to draw two rects on screen
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
SDL_RenderFillRect(renderer, rect1);
SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255);
SDL_RenderFillRect(renderer, rect2);
Code for rendering the text is
// define string with mouse x, y coords
sprintf(someString, "x: %d, y: %d", mouse.x, mouse.y);
SDL_Point textPos = {10, 10};
WriteText(renderer, font, someString, textPos, (SDL_Color){255, 255, 255, 255});
SDL_Surface *fontSurface = TTF_RenderText_Blended(font, someString, COLOR_BLACK); // create font surface
SDL_Texture *fontTexture = SDL_CreateTextureFromSurface(renderer, fontSurface); // create the texture
// get clip width and height from fontsurface clip rect
SDL_Rect *fontRect = &fontSurface->clip_rect;
fontRect->x = pos.x;
fontRect->y = pos.y;
SDL_RenderCopy(renderer, fontTexture, NULL, fontRect); // copy text to the renderer
// delete surface and texture
SDL_FreeSurface(fontSurface);
SDL_DestroyTexture(fontTexture);
It to shows the mouse positon top left corner of the window. However this makes the rest of the window blank.
To prevent this my work around is having to draw something on the screen after calling SDL_RendererCopy (and weirdly before calling SDL_DestroyTexture too) For example drawing single point
...
SDL_RenderCopy(renderer, fontTexture, NULL, fontRect); // copy text to the renderer
// why is this needed??
SDL_RenderDrawPoint(renderer, 0, 0);
// delete surface and texture
SDL_FreeSurface(fontSurface);
SDL_DestroyTexture(fontTexture); // have to draw a point before this
...
This then shows the two rects rendered before the text
If I set dstRect to NULL when calling SDL_RenderCopy then the text spans the whole window but I can see what was rendered before underneath the text.
Why am I having to draw a point after calling SDL_RenderCopy to stop what was rendered before from not showing up?
NOTE: Link to full source code https://pastebin.com/tRSFT0PV
This is a bug in SDL 2.0.10. It's fixed by https://hg.libsdl.org/SDL/rev/6ee12b88beed and this fix will ship with 2.0.11. Sorry about that!

Does this SDL_gfx code involve a race condition?

When I run the following code on my machine, it doesn't behave deterministically. The triangle it should draw only appears sometimes:
#include <SDL/SDL.h>
#include <SDL/SDL_gfxPrimitives.h>
int main(int argc, char* args[])
{
int xres = 250;
int yres = 250;
SDL_Surface* screen = SDL_SetVideoMode(xres, yres,
0, SDL_SWSURFACE | SDL_DOUBLEBUF | SDL_NOFRAME);
SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
filledTrigonRGBA(screen, 10, 10, 170, 170, 75, 100, 255, 0, 255, 255);
//SDL_Delay(1); // this fixes some race condition?
SDL_Flip(screen);
SDL_Delay(1000);
SDL_Quit();
return 0;
}
But if I uncomment the first SDL_Delay() call, the triangle always appears. I have also observed this when using SDL 2.
Is there a race condition in one of the libraries here, or is something wrong with my computer?
There are many things involved, especially if you have compositing window manager. Like if you flip your resulting image but window wasn't visible at that moment. With compositing it is even worse since it implements its own double buffering.
Just repeatedly draw it a loop, like every single example does. If you absolutely have to, you can redraw only on window events (mostly 'exposed' one).

Is it possible to create an XOR pen like DrawFocusRect()?

The Win32 GDI DrawFocusRect(HDC, const RECT*) function draws the dotted outline of a rectangle on the desired devince context. The cool thing about this function is it draws the dots using an XOR function so that when you call it a second time on the same device context and rectangle, it erases itself:
RECT rc = { 0, 0, 100, 100 };
DrawFocusRect(hdc, &rc); // draw rectangle
DrawFocusRect(hdc, &rc); // erase the rectangle we just drew
I want to achieve the same dotted line effect as DrawFocusRect() but I just want a line, not a whole rectangle. I tried doing this by passing a RECT of height 1 to DrawFocusRect() but this doesn't work because it XORs the "bottom line" of the rectange on top of the top line so nothing gets painted.
Can I create a plain HPEN that achieves the same effect as DrawFocusRect() so I can draw just a single line?
As #IInspectable commented, you want to use SetROP2(). The other half of the battle is creating the correct pen. Here is how the whole thing shakes out:
HPEN create_focus_pen()
{
LONG width(1);
SystemParametersInfo(SPI_GETFOCUSBORDERHEIGHT, 0, &width, 0);
LOGBRUSH lb = { }; // initialize to zero
lb.lbColor = 0xffffff; // white
lb.lbStyle = BS_SOLID;
return ExtCreatePen(PS.GEOMETRIC | PS.DOT, width, &lb, 0, 0);
}
void draw_focus_line(HDC hdc, HPEN hpen, POINT from, POINT to)
{
HPEN old_pen = SelectObject(hdc, hpen);
int old_rop = SetROP2(R2_XORPEN);
MoveToEx(hdc, from.x, from.y, nullptr);
LineTo(hdc, to.x, to.y);
SelectObject(hdc, old_pen);
SetROP2(old_rop);
}

Resources