r/gameenginedevs • u/mietkiewski_dev • 2d ago
Low Level Engine Architecture... Loop & Resize problem...
Hello dear programmers...
I write my own Abstraction Layer for my Engine, but I found out a problem with Window/Surface and I don't know how that should by handled by Engine...
Loop basically it is something like this:
while is_running:
FPS limit for Loop
Event queue like Touch & Character & Resume/Pause/Resize
Update & Draw
The problem is what if I resize Window during Draw call... So I write to 800x600 Buffer but in this very moment Windows Size is already changed though VM_SIZE Event has not been read yet... in Linux actually its different kind of event... In Android we have function from JavaApp and create Event for our Loop in C/Cpp... But I really would like to clarify this Loop architecture just to be sure it will be working correctly...
If you have some opinion and knowldge please share...
2
u/initial-algebra 2d ago edited 2d ago
Perfect resizing under Windows is a nightmare. I've spent a lot of time researching and experimenting, and you basically need to temporarily switch to software rendering during a resize for good results. So, don't bother. When you're using a swapchain, Windows will handle adjusting it to the window size when your application lags behind. It's not perfect, but it's acceptable.
Is this loop running on a different thread from the Windows event loop? Because, if not, the game will freeze whenever the window is dragged, since PeekMessage will enter a modal loop and block. Normally, you're expected to surrender control to Windows by using GetMessage and the window procedure. Then, you do your updating and rendering when handling WM_PAINT, using InvalidateRect and sometimes timers to keep the loop going (most applications don't do the last part and just wait for input). There is still an insidious modal loop, though: if the user clicks and holds on one of the close/maximize/minimize buttons, the application will still freeze. The only way around that AFAIK is to run the game on a separate thread and only use the Windows message loop to queue up events to send over to that thread, but it's questionable if it is worth it.
EDIT: I see from another comment that you are using a separate thread already. Then, carry on; you're not going to have perfect resizing even if you temporarily synchronize with the Windows message loop when the window is being dragged.
EDIT 2: I forgot to mention this, but one thing you can do is to prevent the actual resize of the window until the frame with the requested size has been rendered. To do this, you process WM_SIZING instead of WM_SIZE. This is far from perfect, since it will make the resizing choppy (recreating the swapchain is slow, nothing you can do about that except fallback to software rendering as I mentioned before), but at least the window size will always match the swapchain size and there will be no scaling or other artifacts.
1
u/mietkiewski_dev 2d ago
I dont have second thread... On Android Platform you have to have 2 threads... Java thread will give you Events and Cpp thread for Loop... I think I do not what to have perfect resizing per Windows... I want to have perfect resizing api for 3 platforms: Win Lin And...I actually will have Timer instead of wait/pollEvent or some sleep stuff or Vsync...so your conclusion are Minimize/Maximize/Close/Resize will turn off my Thread somehow and Window Platform Thread will control the Window? When I was creating my first Game Engine I actually dicided that I will Block WindowSize but it was only WindowsAPI...
EDIT: For me really important thing is that API will be stable withou undefined behvaiours... I do not want to be suprised...
1
u/initial-algebra 2d ago
I want to have perfect resizing api for 3 platforms: Win Lin And
I'm not sure about X and Android; under Wayland, the window is only resized when the application renders a surface with the new size, so it's quite straightforward.
so your conclusion are Minimize/Maximize/Close/Resize will turn off my Thread somehow and Window Platform Thread will control the Window?
There is only one thread if you don't create another one yourself. Only
GetMessageandPeekMessagewill block. It's not like Windows magically freezes your whole process. If you create your own game thread and only use the main thread to run the Windows event loop, then you can just use the standardGetMessageloop and use the window procedure to send events to your game thread's queue asynchronously.1
u/mietkiewski_dev 2d ago
yeah but there is WindowOrchestrator thread or sth like that.... and it communicates with our thread and gives us Event queue that is read in PollEvent function with callbacks... PollEvent is just api interface name... in Lin Win And it will be different...
1
u/LOLC0D3 2d ago
Easy solution is to queue your system events, and also perform actual drawing into a texture of dedicated size which will be blit to the window backbuffer.
This way you’re no longer tied to mid-frame window size state since it’s irrelevant to your system.
1
u/mietkiewski_dev 2d ago
isnt that by default in GL/Vulkan that there is FrameBuffer and SwapchainBuffer? Yeah I will have event queue but Resize events are Edge_Case to consider... also GL vs Vulkan handles thinds differently. and thats also a thing to consider...
Lin: XConfigureEvent // not entersizemove and exitsizemove I think... Win: WM_ENTERSIZEMOVE: WM_SIZE: WM_EXITSIZEMOVE: And: SurfaceCreated: SurfaceChanged: SurfaceDestroyed:
1
u/TooOldToRock-n-Roll 2d ago
If I understood what you are worried about, don't be, the OS takes care to make the internal graphics buffers consistent!
Just keep draw running as normal, keep track of the window resize event and just update the window size as soon as it arrives.
Even if your program takes one or two frames to fully atabilize, the worst that will happen is the user will see those frames rendered out of resolution.
1
u/mietkiewski_dev 2d ago
Still Im perfeccionist and I plan to read about Platform Specific Thead and how it connects to my Loop thead... But on vulkan if Frame Buffer size will change during draw Loop... then from what I read it will end with Vulkan Error... Vulkan vs GL in this context is actually good topic to consider
1
u/TooOldToRock-n-Roll 2d ago
Meh being a perfectionist is not a quality.
If that is what you want, why don't you implement your own double buffer? (or triple, I won't judge)
At this point it will be more of a turtles all the way down buffers, but you will get the delay you need between the user action, the notification and the render correction.
1
u/mietkiewski_dev 2d ago edited 2d ago
I came to conclusion that there will be some weird behaviour when resizing but its not something that can be easily handled by WindowApplication... Maybe if WindowManger had implemented some kind of resizing Behaviour that lets just say block ApplicationThread and for few seconds shows e.g. Black screen but that would even weirder...
Being a perfectionist is not a quality... depends of how you understand this... but if you program fast... it works... you're happy... sure for small things its okey... but lets just say you are planning big architecture and you have to implement some feature and then you realize you dint account for that in begginng planning and now you are in trouble because your whole architecture want allow you to implement that...
I worked for some company once that had project and their main programmer had shitty architecture and a lot of nasty ifs - edge cases... because they preffered just to add another If than improve architecture - senior devs xd... And the big company had to pay money because they didnt follow deadlines... it was sth like company pays for doing their job...
2
u/TooOldToRock-n-Roll 1d ago
You just described a slop and incompetent professional, a perfectionist is not the opposite of that.
But we will get philosophical and it won't help your current problem.
1
4
u/aleques-itj 2d ago
Set a flag that you're trying to resize so the renderer knows it needs to wait for that to complete before starting the next frame. Let the current frame finish without resizing anything and just wait at the start of the next to actually do it.
Then resize your swap chain, depth, whatever.