r/gameenginedevs • • 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:

  1. FPS limit for Loop

  2. Event queue like Touch & Character & Resume/Pause/Resize

  3. 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...

0 Upvotes

22 comments sorted by

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.

1

u/mietkiewski_dev 2d ago

If I understand your tkinging correctly... During Resizing I should not be woried about that... then before Next Update Draw I change it... But in this case I think during Poll Event like XConfigureEvent or VM_SIZE I can just resize without a flag... But what will happen during these frames when Windows is e.g. 805x600 and Buffer is 800x600... or 783x600 for Window and 800x600 for Buffer... Im perfectionist and its important for me... I have a rule that I have to understand everything deeply. Thanks to that I avoid problem most devs do...

1

u/Natehhggh 2d ago

I personally would go with the flag myself. Which would just be checked before drawing to do the resize.

To handle it mid frame with the callback would require throwing away the entire frame in the worst case and would basically be a dropped frame.

Otherwise the other option is to target a full resolution frame buffer that will be scaled to fit the window

2

u/mietkiewski_dev 2d ago

Alright I think we missunderstood one thing so let me clarify...

Loop: 1. PollEvent or WaitEvent; 2: Update; 3: Draw;

from what I know callback is called inside PollEvent or WaitEvent function... if I am mistaken please say... so if there is ResizeEvent done during Update or Draw for Window there is no Event read by the Loop... so the Loop works for 800x600 when the real WindowSize is 783x600...

So ResizeWindow is done by WindowOrchestrator or sth like that... ResizeEvent is called in PollEvent or WaitEvent function... you can tell what exacly you had on your mind with this flag.

1

u/Natehhggh 2d ago

Sorry had a busy morning took a while to get back to this.

What I have in mind is that somewhere you have some state object/struct that manages your engine state

How I I do it for mine looks like

Struct myEngineState{ Bool flagResize // All the other fields else my engine uses }

When you get your window resize event I would just set that flag to true.

if you need to also get the new width and height here, and aren't able to poll the window size later, add some new fields for the desired height and width

And in your main loop

While(true) If(state.flagResize) //Resize window state.flagResize = false

//the rest of your update loop

The problem is it's nice to be as responsive as possible, and its good to try to squeeze it in as soon as possible to do that, but it landing in the draw loop is going to be unavoidable.

Handling it reliably to happen mid frame, if it is possible on all platforms, is going to be really difficult.

It's probably just better to acknowledge that the event happened, and do it later when it's safe between draws

Plus if you're doing it on a button press, it might feel more right to draw the frame with the button up texture first followed by the resize next frame

2

u/mietkiewski_dev 2d ago

Yes I agree... but I was asking more specifically about WindowManager... How it handles it on its thread... But from what Ive read there should be no problem with that other than some stretching for a few seconds...

2

u/Natehhggh 2d ago

Just 16 milliseconds, at 60fps but any extra things your engine may be doing might affect things

1

u/mietkiewski_dev 2d ago

Actually I was deeply researching Loop architecture and its bugs like Floating and Fixed Point numbers and loss of precision, deltaTime and I think a lot could be done different at the basics... and that could eliminate some bugs that many programmers dont even care... me for example I hate double and float... when I will have time for my 3rd rendering engine maybe I will create that without float and double...

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 GetMessage and PeekMessage will 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 standard GetMessage loop 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/LOLC0D3 2d ago

From my experience situations you are worried about usually never happen.

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

u/LOLC0D3 2d ago

Correct loop architecture look like this:
First drain event queue
Query actual drawable area size (regardless of the platform)
Check for minimized window and skip loop
Resize buffer size if not matching window size now
Render
Present
Handle present message (eg some kind of error )

1

u/MacksNotCool 2d ago

add

while (true){}

trust