Nice review of the issues, Grey Alien.
I say bring back the `vertical blank interrupt` from the Amiga. :-)
One thing that came to mind is the part where Windows, or any other o/s, fights with your app to take away some of the CPU time for whatever it needs to do. In the old days on the Amiga, for example, you could take over the whole machine, shut down the o/s (disable multitasking), open your own screen and draw directly to the video memory. You also had a vertical blank interrupt which you could put all your graphics drawing code inside so that it would always be called at exactly the right time - or that you could use to do hardware flipping of the front/back buffers. Perfectly smooth all the time so long as your system had enough oomph to always finish drawing all the gfx in time.
So what you have now is Windows has multitasking switched on and is trying to do some other tasks. Either you have to turn multitasking off, which might not be feasible, or you have to allow for the fact that it IS going to pre-empt your application for various reasons at who-knows-what time and for who-knows-how-long. Maybe what we need is a command that returns control to the o/s toward the end of a frame, but pulls it back from the o/s when the time comes to do a flip. Or maybe a form of Flip that, while waiting for the VWait or software timing, hands back control to the o/s for a bit. Then the o/s can do its thing and not have to fight so hard to interrupt your app. That said, who knows if this would even satisfy it, maybe it would still interrupt you at any given moment. Ideally if you could switch off multitasking you'd be all set.
I also wonder if the FlipHook would help. If you know that the flip is probably vertically synchronized (Flip -1/1), then you could put all your game code inside the FlipHook function so that it gets called immediately after the flip. This would almost be similar to a vertical blank interrupt. Not sure of the benefits of that though.
Another issue that came to mind for me, that you didn't mention, is that in OpenGL, or probably DX also, although you've drawn stuff using the interface command it doesnt necessarily mean that the gfx card has flushed that and drawn it to the buffer. This doesn't usually matter because you don't see the drawing, due to a backbuffer, but what might affect things is that the gfx card might not be actually doing the drawing until you call Flip() or glFlush() or similar, then it has a sudden rush to draw all the gfx in one go, whereas this could've been happening much sooner if the gfx card were drawing in parallel to the CPU processing the game. This could cause extra delay and slowdown of the overall game. We should look into how/when to render, where in your loop to put the gfx code, and how to get the gfx card to be working hard throughout the whole frame evenly.
Just to clarify also, the vertical tear is caused as you said by the video beam/update overtaking or being overtaken by the copying of the back buffer to the front buffer. This is all due to either drawing stuff direct to the front buffer, or copying the backbuffer to the front buffer (Flip). What I wondered is if it is worth trying to implement your own copying routine that is more friendly with regards to how the video beam is likely to behave, to avoid tearing. What if you copied the back buffer to the front in sections? What if instead of copying the whole buffer from the top left to the bottom right a row at a time, you copied in thin vertical strips from left to right? If the video beam overlapped the drawing of one of those strips, tearing would be restrained to only the width of the strip and probably hardly noticeable (or maybe the tear would bounce up and down the screen more?). I know that at least in OpenGL you could use glReadBuffer() glWriteBuffer() and glCopyPixels() to do the blitting.
I also wondered if there is a way to `time` the frame, how long it's taking, or how long it ideally takes given the hz rate, and then if you know things are v-sync'd, time your flip or time your gfx drawing to be in better harmony with the video beam? Not sure what I'm saying there.
Overall what I'm finding also is that there ISN't at this time a reliable certain solution. There is no reliable way of finding out for sure exactly what hardware is available, exactly where the vertical blank is, exactly what buffers the hardware supports. It also is not possible on some systems to open a context requesting certain buffers and have it open without crashing out when the buffers don't exist in that mode, so you can't assume ANYTHING is going to be certain. All you can assume is that on most systems, some most basic modes will work right. Beyond that you could provide the user with some options to `try` but that's as good as it gets.
For example if your desktop is 16-bit, normally you would not get a depth buffer/stencil buffer/alpha buffer. Same is true of fullscreen. Yet on some systems, if you take the dare and request those buffers you actually get them, AND due to some wierd internal handling of what you've requested, it might actually run much faster than without those buffers, perphaps due to how it rearranges the alignment of data in memory, or how it avoids certain software-based routines that the OpenGL driver uses in certain modes. You can never quite tell what the graphics card supports, in what modes, with what buffers, and which is going to work right or faster or better or be available.
I think all you can do is have some of the most basic `safe` `most predictable` modes, and offer any enhanced features ie extra buffers used etc as switches the user can try. If they fail, chances are the app will bomb out. That's the best you can hope for. What I'm doing is aiming for the user to have at least one decent 32-bit mode, because in 32-bit you can *probably* be fairly sure that it will support all the buffers - stencil/alpha/depth/accum. Then if it's anything less than 32-bit, assume it has no extra buffers, cus on different platforms it either does or does not support it. Then let them select to try turning on these modes, and risk crashing. It's pretty precarious and unreliable and frustrating, but that too is the nature of the hardware/driver/support beast at this time.
Anyway, good stuff.
I say bring back the `vertical blank interrupt` from the Amiga. :-)
One thing that came to mind is the part where Windows, or any other o/s, fights with your app to take away some of the CPU time for whatever it needs to do. In the old days on the Amiga, for example, you could take over the whole machine, shut down the o/s (disable multitasking), open your own screen and draw directly to the video memory. You also had a vertical blank interrupt which you could put all your graphics drawing code inside so that it would always be called at exactly the right time - or that you could use to do hardware flipping of the front/back buffers. Perfectly smooth all the time so long as your system had enough oomph to always finish drawing all the gfx in time.
So what you have now is Windows has multitasking switched on and is trying to do some other tasks. Either you have to turn multitasking off, which might not be feasible, or you have to allow for the fact that it IS going to pre-empt your application for various reasons at who-knows-what time and for who-knows-how-long. Maybe what we need is a command that returns control to the o/s toward the end of a frame, but pulls it back from the o/s when the time comes to do a flip. Or maybe a form of Flip that, while waiting for the VWait or software timing, hands back control to the o/s for a bit. Then the o/s can do its thing and not have to fight so hard to interrupt your app. That said, who knows if this would even satisfy it, maybe it would still interrupt you at any given moment. Ideally if you could switch off multitasking you'd be all set.
I also wonder if the FlipHook would help. If you know that the flip is probably vertically synchronized (Flip -1/1), then you could put all your game code inside the FlipHook function so that it gets called immediately after the flip. This would almost be similar to a vertical blank interrupt. Not sure of the benefits of that though.
Another issue that came to mind for me, that you didn't mention, is that in OpenGL, or probably DX also, although you've drawn stuff using the interface command it doesnt necessarily mean that the gfx card has flushed that and drawn it to the buffer. This doesn't usually matter because you don't see the drawing, due to a backbuffer, but what might affect things is that the gfx card might not be actually doing the drawing until you call Flip() or glFlush() or similar, then it has a sudden rush to draw all the gfx in one go, whereas this could've been happening much sooner if the gfx card were drawing in parallel to the CPU processing the game. This could cause extra delay and slowdown of the overall game. We should look into how/when to render, where in your loop to put the gfx code, and how to get the gfx card to be working hard throughout the whole frame evenly.
Just to clarify also, the vertical tear is caused as you said by the video beam/update overtaking or being overtaken by the copying of the back buffer to the front buffer. This is all due to either drawing stuff direct to the front buffer, or copying the backbuffer to the front buffer (Flip). What I wondered is if it is worth trying to implement your own copying routine that is more friendly with regards to how the video beam is likely to behave, to avoid tearing. What if you copied the back buffer to the front in sections? What if instead of copying the whole buffer from the top left to the bottom right a row at a time, you copied in thin vertical strips from left to right? If the video beam overlapped the drawing of one of those strips, tearing would be restrained to only the width of the strip and probably hardly noticeable (or maybe the tear would bounce up and down the screen more?). I know that at least in OpenGL you could use glReadBuffer() glWriteBuffer() and glCopyPixels() to do the blitting.
I also wondered if there is a way to `time` the frame, how long it's taking, or how long it ideally takes given the hz rate, and then if you know things are v-sync'd, time your flip or time your gfx drawing to be in better harmony with the video beam? Not sure what I'm saying there.
Overall what I'm finding also is that there ISN't at this time a reliable certain solution. There is no reliable way of finding out for sure exactly what hardware is available, exactly where the vertical blank is, exactly what buffers the hardware supports. It also is not possible on some systems to open a context requesting certain buffers and have it open without crashing out when the buffers don't exist in that mode, so you can't assume ANYTHING is going to be certain. All you can assume is that on most systems, some most basic modes will work right. Beyond that you could provide the user with some options to `try` but that's as good as it gets.
For example if your desktop is 16-bit, normally you would not get a depth buffer/stencil buffer/alpha buffer. Same is true of fullscreen. Yet on some systems, if you take the dare and request those buffers you actually get them, AND due to some wierd internal handling of what you've requested, it might actually run much faster than without those buffers, perphaps due to how it rearranges the alignment of data in memory, or how it avoids certain software-based routines that the OpenGL driver uses in certain modes. You can never quite tell what the graphics card supports, in what modes, with what buffers, and which is going to work right or faster or better or be available.
I think all you can do is have some of the most basic `safe` `most predictable` modes, and offer any enhanced features ie extra buffers used etc as switches the user can try. If they fail, chances are the app will bomb out. That's the best you can hope for. What I'm doing is aiming for the user to have at least one decent 32-bit mode, because in 32-bit you can *probably* be fairly sure that it will support all the buffers - stencil/alpha/depth/accum. Then if it's anything less than 32-bit, assume it has no extra buffers, cus on different platforms it either does or does not support it. Then let them select to try turning on these modes, and risk crashing. It's pretty precarious and unreliable and frustrating, but that too is the nature of the hardware/driver/support beast at this time.
Anyway, good stuff.