News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

Vertical Retrace Sync with GDI/Win32

Started by johnsa, July 13, 2015, 11:36:46 PM

Previous topic - Next topic

johnsa

Hey all,

I know in theory there is no provision for this at all, there is no way to wait for Vertical Blanking without using Direct3D/DXGI, OpenGL (both indirectly through it's present functions) or legacy DirectDraw which is slow and deprecated. That said I did come across the following:
Win7 DWM can be switched off but for most users it's on, Win8+ it's always on. As such you have access to DwmFlush and DwmGetCompositionTimingInfo functions (from dwmapi.lib)
The composition timing info gives you back
qpcVBlank = The query performance counter value before the vertical blank.

There are a number of other fields in the structure returned:

rateRefresh =The monitor refresh rate.
qpcRefreshPeriod = The monitor refresh period.

So in theory using that value it should be possible to determine the next point for a vertical retrace start, DwmFlush in theory might also be useable.
I've tested DwmFlush and it definitely limits my FPS to 60 I'm just not sure on the best way to use it.

I was wondering if anyone else had experimented with these as a way to gear tear-free/vsync'ed software rendering in a window or from GDI etc.

I found some bugzilla threads where it appears Chromium and FF have used or considered to use this approach for their Silk and smooth scrolling implementations.

Zen

#1
Hi, JOHNSA,   
When I first read your post, I wasn't sure what you were talking about,...
So,...I Googled it:   
Detecting Vertical Retrace in Microsoft Windows
Understanding Synchronization in Real-Time 3D Graphics

...I was wondering why this was a significant issue,...and the author of the first article explained it:
QuoteTearing occurs when an object's position changes and electron guns of the CRT update the new and/or old areas of the object during the blit operation. If that happens, the computer display will show a situation where the top part of the image is updated and the bottom part that lags a frame behind (assuming that the blit goes top-down). This situation will only exist for a fraction of a second: if the vertical refresh rate of the monitor is 60 Hz, the display is updated 17 ms later. However, it exists long enough for us to perceive an irregularity in the object's shape, possibly due to phosphor afterglow. To avoid tearing, you should do blit as much as possible during the vertical retrace period. Therefore, it is necessary to be able to detect the start of the vertical retrace.

johnsa

That's exactly it.

The compuphase article pretty much covers why you would want to do it, however it's way out of date and using the standard VGA compatible status register read is no longer really valid (How I miss the DOS days..).

It requires:
a) a driver for ring0 port-reading and
b) most likely unsupported on any modern graphics card that supports multiple monitors especially if it's been fully configured by a driver and not running in a legacy "vga-compatible for boot" mode.

Some days I really think we have regressed.. compare intialising direct2D to the good old:
mov ax,13
int 10h

Any way.. the point is using a graphics API like DirectX allows you to enable swap chains/buffer presentation with or without waiting for vertical retrace and will obviously also support the new GSync style of adaptive retraces that are coming in to play.

For some applications however you really don't need or want DirectX/OGL.. Simple software based rendering is absolutely fine and apart from the legacy DirectDraw there is no "standard" way to detect a retrace.
Using these two DWM functions (from my research) seems to be the only possibly viable option to setup some sort of software timer that is in some way synchronised to the hardware event,
I'm assuming we would want to setup a thread which sleeps and fires up at a set point in time derived from the refresh rate into and qpcVBlank counter value.

Zen

I'll bet drunk people never even notice it,...:bgrin:


Zen

To be honest, I haven't found any information anywhere.
My only suggestion is to check the developer sections of the major graphics chipset manufacturers:
AMD Developer Central
NVIDIA Developer

...And, if you REALLY want to drive yourself nuts,...check this:
Ke-Sen Huang's Home Page
That page contains links to all the SIGGRAPH, Eurographics, IEEE, and ACM graphics papers available on the Internet,...
They make for some pretty obtuse, abstract reading, though,...:bgrin:

johnsa

https://code.google.com/p/chromium/issues/detail?id=467617

I've got the functions calling from a test app, but for some reason they give me back an NTSTATUS error code of Invalid parameter (0xc000000d).

Zen

JOHNSA,
As you well know,...these are such extremely small time intervals that they are in the range of the time it takes to execute the instructions to detect it.
If you figure it out,...we should probably give you a genius award,...:bgrin:

...If it was me, I would try to find a way to skip the draw call which occurs during the moment of the Retrace. The frame frequency varies, though, depending on the code pipeline structure. Maybe, you should consult a quantum mechanics physicist,...:bgrin:

johnsa

Success!



invoke CreateDC,NULL,CStr("\\.\DISPLAY1"),NULL,NULL
lea rcx,adapterInfo
mov [rcx],rax
invoke D3DKMTOpenAdapterFromHdc, rcx

and then...

lea rcx,adapterInfo
lea rdx,vblankInfo
mov eax,[rcx+8]
mov [rdx],eax
mov eax,[rcx+20]
mov [rdx+8],eax
invoke D3DKMTWaitForVerticalBlankEvent, rdx

... 60fps.. perfectly synced, no jitter or dropped frames.. now to code some Windows copper effects ;)


adeyblue

That github sample doesn't release the DC nor close the adapter when its done. Those are things that are important to do.

You probably have them, but just in case.

Siekmanski

Nice, didn't know this D3DKMT_WAITFORVERTICALBLANKEVENT function.
Can't wait to see your copper effects.  :t
Creative coders use backward thinking techniques as a strategy.

rrr314159

FWIW,

I don't get any "tearing" when blting from hDCMem (back frame) to the front hDC (Device Context). My programs produce frames at a rate from 7 ms (faster than the 60 FPS refresh rate) to over 100 ms, the blting operation takes a couple milliseconds I think. When I first was doing this I assumed I'd have to synch with the screen refesh, but in fact I don't. I had been supposing Windows was taking care of it for me, but this thread implies otherwise. You may be thinking a possible explanation is:

Quote from: Zen on July 14, 2015, 06:28:33 AM
I'll bet drunk people never even notice it,...:bgrin:

- but I can assure you, that's not the explanation! - because many non-drunk people* have also stared at it for hours - there is never any "tearing" or any such artifact. BTW I have seen lots of this problem in the old days (with CRT phosphor monitors); and also when I was drawing directly to the front frame; so I know what it looks like. I usually use an i5 with 4 cores, but the problem also never occurs on much slower computers, such as Pentium using 1 core.

FWIW.

* including young people with sharper eyes - altho admittedly only for minutes, their attention spans are much shorter
I am NaN ;)

johnsa

I've included a releasedc and closeadapter call in my code, good spotting!

I can't really explain why you wouldn't get tearing, my software blitting/rendering code generally runs at up to 500fps at 1920x1080 with some basic fills/screen clears etc. And 500fps simply HAS to tear although as you say I generally don't notice it (as in seeing visible tears in the screen). That said however what I do find is what the browser people refer to as "jank" where you get occasional stutters and dropped frames.

My suspicion would be if you're doing software rendering/blitting and are running at least Vista.. that the DWM(Desktop Window Manager) already only composites the windows and presents them at vblank, so you won't see tearing but what you would get is dropped frames.

Are you running Vista+ ?

avcaballero

It seems that in GDI it is not an easy way to get vert sync. Nevertheless, the main point is don't try drawing wildly, don't draw the same point more than one time in the same frame. In many cases using buffers may help. Another point is to achieve joining the path, for example when you draw a rotating image, in other case, bleep will occur whether you achieve vertical sync or not.

A tiny example of copperbars in GDI.

Siekmanski

You can prevent jerky animations if you calculate a "smooth factor".
Calculate the time between the new and last vertical blank, and use that value to multiply with your animation update speed.

See the RenderD3D proc ( you can find it in Common\Direct3D9_Main.Asm), where I calculate the FrameTimeDelta.
See the Update_frame proc in 3D_colormap.Asm where I use the FrameTimeDelta (smooth factor) to update the cube rotation.
And see, no jerky animation.

Marinus
Creative coders use backward thinking techniques as a strategy.