Sync plugin GUI refresh to screen?
-
- KVRAF
- Topic Starter
- 7383 posts since 17 Feb, 2005
Is there a programmatic way to receive a windows message that the screen has been refreshed, or some similar mechanism? Would the use of a timer be bad? I understand that jitter will be a problem due to asynchronous calls between the GUI and the process function. What is more important is to get the plugin GUI refresh as constant as possible. I'm thinking of how to build a windowed histogram and have it display it's values with the least jitter possible, as that kind of skews how "accurate" it is.
-
- KVRAF
- 2256 posts since 29 May, 2012
This is for directx 9:
Have a look athttps://msdn.microsoft.com/en-us/librar ... s.85).aspx (linked from https://msdn.microsoft.com/en-us/librar ... s.85).aspx)
D3DPRESENT_INTERVAL_ONE seems to be the flag that enable vertical sync on d3d9 device's Present() api call.
For this the work, I guess you'll need a separete thread for the plugin GUI (i.e. cannot loop for rendering for your plugin in the DAW's GUI thread and still hope the DAW's GUI to work.).
As for what goes in that loop, have a look at http://celestialcoding.com/directx/d3d9 ... llo-world/ - this is a typical windows message handling loop, but directx calls are added for each iteration.
The relevant part is:
My understanding is that pDevice->Present() will wait for vertical sync if you specify D3DPRESENT_INTERVAL_ONE in the related settings - that's why you can't share the GUI thread of the DAW and still achieve vertical synchronization in this way. As for the "GetMessage(&msg,NULL,0,0))" part and other related message handling functions that are called, you should probably remove them all if the plugin window logically belongs to the DAW's GUI, but you may still be able to do rendering in another thread. This leaves:
Have a look athttps://msdn.microsoft.com/en-us/librar ... s.85).aspx (linked from https://msdn.microsoft.com/en-us/librar ... s.85).aspx)
D3DPRESENT_INTERVAL_ONE seems to be the flag that enable vertical sync on d3d9 device's Present() api call.
For this the work, I guess you'll need a separete thread for the plugin GUI (i.e. cannot loop for rendering for your plugin in the DAW's GUI thread and still hope the DAW's GUI to work.).
As for what goes in that loop, have a look at http://celestialcoding.com/directx/d3d9 ... llo-world/ - this is a typical windows message handling loop, but directx calls are added for each iteration.
The relevant part is:
Code: Select all
MSG msg; //declare a MSG local variable for the GetMessage of the while loop
while(GetMessage(&msg,NULL,0,0)) //GetMessage reffer to the wndProc() function
{
pDevice->Clear(0,0,D3DCLEAR_TARGET,D3DCOLOR_XRGB(0,0,0),1.0f,0.0f); //Clear the screen with black, basically since we only draw text here it's like your background change the D3DCOLOR_XRGB(0,0,0) to change the color the 3 number additioned must make 255 maximum
if(FAILED(render())) //call the function render() and verify if it worked
{
Emsg("Render failed"); //error pop-up for debug purpose
}
pDevice->Present(NULL,NULL,NULL,NULL); //display your buffer on screen
TranslateMessage(&msg); //translate the msg of the GetMessage of your while loop
DispatchMessage(&msg); //dispath the msg of the GetMessage of your while loop
}
Code: Select all
while(your_plugin_is_alive)
{
pDevice->Clear(0,0,D3DCLEAR_TARGET,D3DCOLOR_XRGB(0,0,0),1.0f,0.0f); //Clear the screen with black, basically since we only draw text here it's like your background change the D3DCOLOR_XRGB(0,0,0) to change the color the 3 number additioned must make 255 maximum
if(FAILED(render())) //call the function render() and verify if it worked
{
Emsg("Render failed"); //error pop-up for debug purpose
}
pDevice->Present(NULL,NULL,NULL,NULL); //display your buffer on screen
}
~stratum~
- KVRAF
- 7868 posts since 12 Feb, 2006 from Helsinki, Finland
Direct3D doesn't play well with GDI so expect some graphical glitches in some hosts. I'd just use OpenGL since that'll avoid these problems.stratum wrote:directx and opengl can wait for vertical sync. not that I spend whole my time with these apis and can tell immediately how, though.
You also want to put your VSynced rendering in a separate thread (to avoid causing a situation where multiple plugins take turns waiting for vsync), but you probably want the actual window to be owned by the host thread anyway, 'cos otherwise you'll get some weirdness with keyboard focus and such.
Also keep in mind that DWM (the composition manager) will actually composite with vsync (so you won't get any tearing either way) and running "vsync" in a composited window actually just forces your drawing to be synchronised with the composition... which add something like 2 (or so?) frames extra latency on top of whatever latency DWM has to begin with.
Personally... I don't think it's really worth it just for the purpose of trying to make your visualisation smoother (those work fine with regular WM_TIMER), but like .. up to you.
-
- KVRAF
- Topic Starter
- 7383 posts since 17 Feb, 2005
To avoid a separate thread, I was googling for an answer on a possible mechanism to control my window's invalidation. I noticed that if I invalidate my window right after it's painted (inside the wm_paint branch of the wndproc), it can be a very high refresh rate, faster than 1000hz... I don't understand how but that's what it did! That's obviously not acceptable though as it caused problems with the host. Idle() is just too slow for my need, and finding a way to avoid frame drops due to asynchronous timers is my main priority. I think double buffering might help to avoid that?
According to what I found searching, windows doesn't provide a vsync interrupt or a communication layer for it either. DirectX seems to poll the video driver and sets a flag, which some articles I found said was a terrible way to do it. Not sure how opengl does it. DWM isn't ideal because I am stubborn
According to what I found searching, windows doesn't provide a vsync interrupt or a communication layer for it either. DirectX seems to poll the video driver and sets a flag, which some articles I found said was a terrible way to do it. Not sure how opengl does it. DWM isn't ideal because I am stubborn
- KVRAF
- 7868 posts since 12 Feb, 2006 from Helsinki, Finland
Either use a multimedia timer or a separate thread. WM_TIMER only gets generated once there's nothing else in the GUI that needs updating, which is what you want if you run in the host thread, because then you won't slow down rest of the GUI. They are quite accurate enough for animation purposes anyway, then just use timeGetTime() to adjust animation speed in case the CPU gets too busy and you start missing frames.
The only real downside of WM_TIMER is that some retarded hosts (eg. Renoise) do their own animation in message loop idle, so in those hosts (eg. Renoise) you should really be using editor idle for animation. Unfortunately those hosts (eg. Renoise) are the only ones that actually call editor idle often enough to be useful, so .. like.. have fun.
If you really think you need better accuracy than the timers, then you really need a separate thread, because as long as you run in the host thread you don't really have much control over how much time the host and other plugins spend on doing stuff.
As far as vsync goes, it's actually useless because what you are really interested is the graphics drivers interpretation of when the presentation occurs, so really if you wanted "strict" sync you'd want to set SwapInterval to 1, then do glFinish() or an event-query (with a loop to wait) after presentation, at which point you know the driver claims to have put the stuff on screen. This kills much of the GPU pipelining (and CPU/GPU concurrency) though so it's usually not done even by games.
The only real downside of WM_TIMER is that some retarded hosts (eg. Renoise) do their own animation in message loop idle, so in those hosts (eg. Renoise) you should really be using editor idle for animation. Unfortunately those hosts (eg. Renoise) are the only ones that actually call editor idle often enough to be useful, so .. like.. have fun.
If you really think you need better accuracy than the timers, then you really need a separate thread, because as long as you run in the host thread you don't really have much control over how much time the host and other plugins spend on doing stuff.
As far as vsync goes, it's actually useless because what you are really interested is the graphics drivers interpretation of when the presentation occurs, so really if you wanted "strict" sync you'd want to set SwapInterval to 1, then do glFinish() or an event-query (with a loop to wait) after presentation, at which point you know the driver claims to have put the stuff on screen. This kills much of the GPU pipelining (and CPU/GPU concurrency) though so it's usually not done even by games.
-
- KVRAF
- Topic Starter
- 7383 posts since 17 Feb, 2005
Except for the fact the timer is asynchronous with regard to the vblank of the monitor. But 25hz refresh isn't the goal, it's 60hz+.stratum wrote:If you can achieve 1000hz then with a timer of 25 frames per second, the possibility of having a flicker on a frame doesn't seem very high.
Somehow also having this vblank message would allow my histogram process to stop accumulating and pass it's result to the editor. It's almost like a callback, except in message form
The idea is since the monitor vblank clock is fairly consistent, if the process is guaranteed to finish within that interval, the window size of the histogram would be as consistent as the vblank, plus or minus the jitter introduced by the size of the processing buffers when the message is received.
The window size could be further increased (slowed), but it can only be as fast as the display interval.
There's another possibility that would at least insure that data did not get dropped from the visual display, and that's to display multiple fixed size histogram windows in order of time. Kind of like a large timeslice oscilloscope display. If a single window were to be dropped, there would be a gap in the display, but a row of windows would extend the availibility of a frame to display all the data. Kind of like watching two televisions at the same time, with one of them a second or two behind.
Not really sure if any of this is super important, the idea can work with slower windows, but it could be better if it were faster, no way to tell yet.
- KVRAF
- 7868 posts since 12 Feb, 2006 from Helsinki, Finland
You really can't get a useful vblank signal on a modern system (well, except maybe fullscreen and even then it's sketchy), so I'd just forget about it right away and start thinking about alternatives.
The way I'd normally do visualisation stuff is to keep last N samples of data from the audio thread in a (ring) buffer that the GUI can read them from, the just calculate the visualisation on demand from these. If you track how many of the samples are new (eg. written after the GUI last accessed the stuff) then you can even run IIR analysis and such in the GUI thread. While you can theoretically lose some data this way, it's not really an issue as long as the buffer is long enough, since in order to drop data with a 2 second buffer you need your GUI animation to pause for 2 seconds.. which is probably quite noticeable, whether or not you actually lose data.
The way I'd normally do visualisation stuff is to keep last N samples of data from the audio thread in a (ring) buffer that the GUI can read them from, the just calculate the visualisation on demand from these. If you track how many of the samples are new (eg. written after the GUI last accessed the stuff) then you can even run IIR analysis and such in the GUI thread. While you can theoretically lose some data this way, it's not really an issue as long as the buffer is long enough, since in order to drop data with a 2 second buffer you need your GUI animation to pause for 2 seconds.. which is probably quite noticeable, whether or not you actually lose data.
-
- KVRAF
- Topic Starter
- 7383 posts since 17 Feb, 2005
Vsync is out of the question now. There's too many pitfalls to implement it as a work-around. I mean, yes Vsync works fine but it doesn't act as a callback, and it probably shouldn't either, esp since windows is a multi-tasking OS. I think my idea was trying to implement single-buffered well-timed callbacks, but the "well-timed" part isn't guaranteed. Double buffering with historic value display is the only solution. The only problem that method could present is jerky animation.
-
- KVRAF
- 2256 posts since 29 May, 2012
Will rendering of a double buffered frame be faster than 1/1000 seconds? Under these conditions I don't think you really have a problem to solve. Double buffering was used when the drawing code was so slow that you could see it in action:)
~stratum~