Linear motion stutters

I created a simple, frame-independent, variable time step, linear movement in Direct3D9 using ID3DXSprite . Most users cannot notice this, but on some (including my) computers this happens often, and sometimes it stutters a lot.

  • Attenuation occurs when VSync on and off.

  • I realized that this is happening in the OpenGL renderer.

  • Not a floating point problem.

  • It seems that the problem exists only in the AERO Transparent Glass window mode (excellent or at least much less noticeable in full-screen, borderless full screen or with the aeron off), which is even worse when the window has lost focus.

    / li>

EDIT:

The delta time of the frame leaves no boundaries of 16 .. 17 ms even when stuttering.

It looks like my delta time error log has been tapped. I fixed it now.

  • Usually, with the VSync frame turned on, 17 ms is displayed, but sometimes (perhaps at startup) it jumps to 25-30 ms.

(I leave the log only once at application exit, and not at runtime, rendering, so it does not affect performance)

  device->Clear(0, 0, D3DCLEAR_TARGET, D3DCOLOR_ARGB(255, 255, 255, 255), 0, 0); device->BeginScene(); sprite->Begin(D3DXSPRITE_ALPHABLEND); QueryPerformanceCounter(&counter); float time = counter.QuadPart / (float) frequency.QuadPart; float deltaTime = time - currentTime; currentTime = time; position.x += velocity * deltaTime; if (position.x > 640) velocity = -250; else if (position.x < 0) velocity = 250; position.x = (int) position.x; sprite->Draw(texture, 0, 0, &position, D3DCOLOR_ARGB(255, 255, 255, 255)); sprite->End(); device->EndScene(); device->Present(0, 0, 0, 0); 

Timer fixed thanks to Eduard Wirch and Ben Voigt (although it does not fix the initial problem)

 float time() { static LARGE_INTEGER start = {0}; static LARGE_INTEGER frequency; if (start.QuadPart == 0) { QueryPerformanceFrequency(&frequency); QueryPerformanceCounter(&start); } LARGE_INTEGER counter; QueryPerformanceCounter(&counter); return (float) ((counter.QuadPart - start.QuadPart) / (double) frequency.QuadPart); } 

EDIT # 2:

So far I have tried three update methods:

1) Variable time step

  x += velocity * deltaTime; 

2) Fixed time step

  x += 4; 

3) Fixed time step + Interpolation

  accumulator += deltaTime; float updateTime = 0.001f; while (accumulator > updateTime) { previousX = x; x += velocity * updateTime; accumulator -= updateTime; } float alpha = accumulator / updateTime; float interpolatedX = x * alpha + previousX * (1 - alpha); 

All methods work almost the same way, a fixed time step looks better, but it does not completely depend on the frame rate and does not solve the problem completely (still rarely (stutters) rarely).

For now, disabling AERO Transparent Glass or switching to full-screen mode is only a significant positive change.

I use NVIDIA latest GeForce 332.21 Driver and Windows 7 x64 Ultimate drivers.

+7
c ++ game-physics movement directx-9
source share
2 answers

Part of the solution was a simple data type task with high accuracy. Replace the speed calculation with a constant and you will see extremely smooth motion. Analysis of the calculation showed that you save the result from QueryPerformanceCounter() inside the float. QueryPerformanceCounter() returns a number that looks like this on my computer: 724032629776 . This number requires storing at least 5 bytes. Be that as it may, float uses 4 bytes (and only 24 bits for the actual number) to store the value. Therefore, accuracy is lost when converting the result of a QueryPerformanceCounter() to a float . And sometimes this leads to a deltaTime zero, causing a stutter.

This explains why some users do not experience this problem. It all depends on whether the result QueryPerformanceCounter() .

The solution to this part of the problem: use double (or, as Ben Voigt suggested: save the initial performance counter and subtract this from the new values ​​before converting to float . This will give you at least more but may eventually hit the float resolution limit again when the application is running for a long time (depends on the growth rate of the performance counter).

After correcting this stuttering was much less, but did not disappear completely. Analysis of the behavior during operation showed that the frame is skipped now and then. The GPU application command buffer is flushed using Present , but the current command remains in the application queue until the next vsync (even if Present was called long before vsync (14ms)). Further analysis revealed that the reverse process (f.lux) told the system to set up a gamma ramp from time to time. This command required the full GPU queue to work before it was executed. Probably to avoid side effects. This flash for the GPU was launched just before the "real" command was transferred to the GPU queue. The system blocked video scheduling until the GPU dries. This took until the next vsync. Thus, the current packet was not moved to the GPU queue until the next frame. The visible effect of this is stuttering.

It is not true that you are using f.lux on your computer. But you probably come across a similar background. You will need to look for the source of the problem in your system yourself. I wrote a blog post on how to diagnose frame drops: Frame Diagnostics skips and stutters in DirectX applications . You will also find the entire history of the diagnosis of f.lux as the culprit.

But even if you find that the source of your frame is skipped, I doubt that you will achieve stable 60 frames per second, and the composition of the dwm windows is on. The reason is that you are not drawing directly on the screen. But instead, you draw on a common dwm surface. Since this is a shared resource, it can be blocked by other users for an arbitrary time, which makes it impossible to maintain frame rate stability for your application. If you really need a stable frame rate, go to full screen mode or disable window composition (in Windows 7. Windows 8 does not allow you to disable window composition):

 #include <dwmapi.h> ... HRESULT hr = DwmEnableComposition(DWM_EC_DISABLECOMPOSITION); if (!SUCCEEDED(hr)) { // log message or react in a different way } 
+7
source share

I took a look at your source code and noticed that you only process one message box in each frame. For me, it caused a stutter in the past.

I would recommend a loop on PeekMessage until it returns zero to indicate that the message queue has run out. After that, take a shot.

So change:

 if (PeekMessageW(&message, 0, 0, 0, PM_REMOVE)) 

to

 while (PeekMessageW(&message, 0, 0, 0, PM_REMOVE)) 

Edit:

I compiled and ran the code (with a different texture) and it displayed the movement smoothly for me. I do not have aero (Windows 8).

One thing I noticed: you set D3DCREATE_SOFTWARE_VERTEXPROCESSING . Have you tried setting this to D3DCREATE_HARDWARE_VERTEXPROCESSING ?

+2
source share

All Articles