Sync plugin GUI refresh to screen?

DSP, Plugin and Host development discussion.
Post Reply New Topic
RELATED
PRODUCTS

Post

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.

Post

directx and opengl can wait for vertical sync. not that I spend whole my time with these apis and can tell immediately how, though.
~stratum~

Post

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:

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
        }
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:

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~

Post

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

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. :)

Post

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 :)

Post

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.
~stratum~

Post

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.

Post

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

Post

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.

Post

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.

Post

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~

Post Reply

Return to “DSP and Plugin Development”