Archived

This topic is now archived and is closed to further replies.

DirectDraw Flip() woes

This topic is 5006 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hey all. I''ve been having a problem with Flip() forever, and no one has been able to help me. I figure I''ll try again and see if anyone has seen this problem. This is a legacy DX8 app that uses DirectDraw. The problem I''m having is, if I use Flip() to get the backbuffer surface to the front, it blocks until the next screen refresh (60hz on my monitor). This is unacceptable in my app. If I use Blt() to copy the backbuffer to the primary surface, it runs as fast as I''d like, but introduces motion artifacts, since it''s not drawing on a retrace. So, is there a way to use Flip() in such a way that it doesn''t block, but still draws on the retrace? Thanks, -Joe

Share this post


Link to post
Share on other sites
I''m a little hazy, but I think you can do this:

if(lpDDSPrimary->GetFlipStatus(DDGFS_CANFLIP)==DD_OK)
{
lpDDSPrimary->Flip();
}

Here is the MSDN page.

Are you trying to set up your render loop so that if the primary surface is not ready you can do AI or physics instead of waiting for vsync?

Share this post


Link to post
Share on other sites
"Are you trying to set up your render loop so that if the primary surface is not ready you can do AI or physics instead of waiting for vsync?"

Something like it. It''s a console emulator, so it''s got a set speed it should run at. The engine generates video frames at about 60hz (actually 59.something), and there''s tons of processing that needs to be done between frames. Basically I''d like to draw the scene and flip it, regardless of where in the VSYNC cycle I am. That way I can do other engine processing while it''s waiting to Flip(). I know I''m running fast enough, because when I use Blt() instead of Flip(), I get about 255 fps.

Do you think triple buffering could help me out? I haven''t done much reading on it, yet.

Thanks,

-Joe

Share this post


Link to post
Share on other sites
My experience is up to DX7, but I don''t think anything has changed much in DDraw.

The flip call shouldn''t block unless you tell it to with the DDFLIP_WAIT flag. Without that flag, it should either setup the flip and return, or return an error.

All that the flip call should do is setup the flip to occur at the next retrace. It doesn''t block until the retrace, even with the FLIP_WAIT flag. The wait flag forces it to block until the flip can be setup. I think the only real time that a flip can''t be setup immediately is when you''re trying to flip, and the previous flip is still waiting to be done (i.e. you''re program is way ahead of the flipping cycle time, which will occur if it can run at 255fps).

My memory of triple buffering is fuzzy, but I don''t think you''ll get much out of it. Its intended more for applications that are just barely missing the retrace (running too slow). As long you are doing all the program logic first in the game loop, followed by rendering to the back buffer, I think you''re running as fast as you can. But since you''re running too fast already, I don''t see the problem.

Also, you can always tell the flip call to flip immediately instead of waiting for the retrace using DDFLIP_NOVSYNC, but that will get you back to screen tearing.

Share this post


Link to post
Share on other sites
When using Flip() you can use the DDFLIP_NOVSYNC as flag to avoid it waiting for vsync, but then it can, of course, suffer from artifacts... Perhaps less severe than Blt though, because the change is instantaneous...

Like CodeMunkie said, to do this, you have to use a way to know if you can flip... There are a couple of ways:

Either you:

1. Try to flip when you know you''re ready and specify the flag DDFLIP_DONOTWAIT, and if it returns DDERR_WASSTILLDRAWING, continue your process and retry flipping until it works, because this way, it should flip when no flip is still pending or return the error, if the last flip is still not finished. It should not block your program like this.

or

2. Use CodeMunkie''s GetFlipStatus, which will, more or less, do the same thing as previous.

Share this post


Link to post
Share on other sites
I am specifying DDFLIP_DONOTWAIT. It still blocks until the retrace. How do I know? It runs 255fps with Blt(). It runs 60fps with Flip(DDFLIP_DONOTWAIT). I changed my monitor''s refresh rate to 75hz, and lo and behold, my emulator ran at 75fps.

I''ve also tried:

if (lpddsprimary->GetFlipStatus(DDGFS_CANFLIP) == DD_OK)
{
hr = lpddsprimary->Flip(NULL, DDFLIP_DONOTWAIT);
}
else
{
Log("Cannot flip");
}

And I never see "Cannot flip" in my logfile.

Could it be in the way I create the surfaces? Here''s my creation code (extensive error checking removed for clarity):

lpdd->SetDisplayMode(dwWidth, dwHeight, 16, 0, 0);

memset(&ddsd, 0, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
ddsd.dwBackBufferCount = 1;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_COMPLEX | DDSCAPS_FLIP | DDSCAPS_VIDEOMEMORY;

lpdd->CreateSurface(&ddsd, &lpddsprimary, NULL);

ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;
lpddsprimary->GetAttachedSurface(&ddsd.ddsCaps, &lpddsback);

-Joe

Share this post


Link to post
Share on other sites
Well, no matter what, if you don't include the DDFLIP_NOVSYNC flag, the most fps you'll get is the monitor refresh rate, so that's no mystery. I wouldn't immediately assume the flip is blocking though. Stick a timer around it to see how long the call takes. It should not block.

Maybe a blitting call is blocking. If you tell the primary to flip, and then go and try to draw to the back surface, it will either block there or return an error (depending on what flag you use there I believe), because since the back buffer you want to draw to is still the primary (being displayed), DX won't let you draw to it. So that could block until the first flip physically occurs, and then you would draw to the back buffer and the next flip call would return OK immediately. You could use triple buffering to get one more frame ahead, but I don't see how that would really help any.

I still don't see what you are trying to accomplish. You are running way faster then the refresh already. Without turning off vertical syncing, you can't improve the framerate anymore. And therefore somewhere along the line you're program will need to wait for the retrace before continuing, whether you let DX block or do it in your app.

[edited by - Rock2000 on March 25, 2004 12:02:06 AM]

Share this post


Link to post
Share on other sites
Rock:

Well, the problem is, the emulator''s engine spits out just about 60 video frames per second. This means if Flip() is waiting for VSYNC, that leaves me very little time to do the engine processing between frames. This particular engine runs 14000 cpu cycles per video frame. That''s quite a bit of processing that needs to be done. In addition, the controllers are polled each video frame as well. So, when using Flip(), the emulator is noticeably slowed down.

What I''m trying to accomplish exactly is for the Flip() to just fail if it''s busy, or get performed asynchronously. I wouldn''t mind dropping 2 or 3 frames per second to get the performance I need. So, I''m not trying to get a faster frame rate, I''m trying to buy myself more time to process between frames.

Interesting point about the Blt(). I''m not too sure, though. The only time I perform a Blt() is *immediately* before Flipping the surface. The rest of my processing is done on a memory buffer. All my blts are performed DDBLT_ASYNC, as well.

Thanks for the suggestions,

-Joe

Share this post


Link to post
Share on other sites
As long as you are performing all non-ddraw logic after the flip, which it sounds like you are, I think you''re good. The only times I can think of that you will hit a wall and have to wait are when calling a Blt func, Lock, or Flip. And generally the Blt/Lock will want to block rather then the flip when using vsync, since they''re done first. In your case, I think triple buffers might help get a fraction ahead. In that case, after the Flip call, you should still be able to render to the new Back buffer, since that 3rd buffer isn''t being displayed, and isn''t queued to be displayed, its available. But when you go to Flip that one and the last flip is still waiting, you''ll have to wait there. So you can get get the extra performance of rendering to the back buffer earlier, which you couldn''t with just 2 buffers. At 60fps it shouldn''t be a problem, but just be aware that this increases the input latency a LITTLE, since the user is seeing a frame that you built 2 frames ago with the input they entered 2 frames ago, or something like that.

There are comparable wait flags for Blt (not sure if waiting or not waiting is the default) you can use. I''m not really sure what the ASYNC flag does. I don''t know what DX is supposed to do if you don''t specify it, but it isn''t meant as a wait flag. And I THINK the Blt error you''ll get when trying to render to the back buffer that is still being displayed is SURFACE_BUSY, which doesn''t look like its covered by the wait flags anyways. Make sure you check the return codes.

Like I said, I doubt the flip is blocking, but if you want to be sure who is blocking, but a few timers around some functions and log the time they take to return. That will clearly show who the culprit it. My only guess is Blt or Lock.

Share this post


Link to post
Share on other sites
I''ll try the timing thing, since I''m curious. I''ll post some results this evening, if I can get some time to work on it.

I''m using the DDBLT_DONOTWAIT and DDBLT_ASYNC flags on all my Blt()s. The DDBLT_ASYNC flag, according to MSDN:
"Performs this blit asynchronously through the first in, first out (FIFO) hardware in the order received. If no room is available in the FIFO hardware, the call fails."

I''m also using the DDLOCK_DONOTWAIT flag on my Lock() call.

I check return values on everything. According to my logfile, none of the DD functions are returning anything but DD_OK. Very strange. I''ll check the timings tonight, and double check my error handling logic, and we''ll be sure. There''s only 1 lock, 2 blts and a flip, so shouldn''t be too hard.

Is it possible for DirectDraw to affect DirectSound? I wouldn''t think so, but I''ve been surprised before.

-Joe

Nostalgia, an Intellivision Emulator
http://www.gotmaille.com/nostalgia/

Share this post


Link to post
Share on other sites
Rock,

Your guess was right on the money. It's not the Flip() that's taking forever, it's the Blt() immediately before it that's blocking. The Flip() takes .03ms to execute. The Blt() takes up to 15ms! I'm a little curious about this. Check out my logic below to see why (along with timings):

1. Process engine until it's time to draw a frame (1-3ms)
2. backbuffer->Blt(colorfill); (.03ms)
3. offscreen_surface->Lock(); (.003ms)
4. Copy this frame's bitmap into offscreen_surface pointer. (.08ms)
5. offscreen_surface->Unlock(); (.003ms)
6. backbuffer->Blt(offscreen_surface); (15.4ms)
7. primarysurface->Flip(); (.03ms)
8. Goto step 1

1/60 second = 16.67ms.

So, if the problem is that the Blt() is waiting for the backbuffer to be drawn at vsync, why doesn't the Blt() in step 2 take forever? All Blt()s are being called with DDBLT_ASYNC and DDBLT_DONOTWAIT. No function call fails. The size of the surface being Blitted is 318x192.

Incidentally, if I replace the Flip() with a Blt(), the Blt() in step 6 takes about 2ms. Still a lot longer than it should.

This is getting intersting. Any other suggestions?

-Joe

...edit to add engine processing time

[edited by - Nostalgia on March 26, 2004 8:05:34 PM]

[edited by - Nostalgia on March 26, 2004 8:06:47 PM]

Share this post


Link to post
Share on other sites
It is odd that its waiting if you pass in the don''t wait flag. I''d try to use neither wait flag and see what happens, just to see (I never understood why they added the don''t wait flag, since it wasn''t in DX5 and wasn''t needed). The first call could be OK if its just queueing the instruction. It probably is, especially since its just a color fill.

Maybe try using BltFast if you don''t need Blt functionality, see if it blocks or not. And as mentioned, if you are copying from system surface to the back buffer, it could take a long time. I''ve lost track of what type of numbers to expect, but 2 ms seems pretty good, depending on resolution and color depth.

But I think you can see that your program is probably waiting right where you''d want it to. Its got the next frame already to go, and it just needs to get the go-ahead to draw it.

Share this post


Link to post
Share on other sites
The offscreen surface *was* being created in System memory. Thanks for catching that. I was debugging something a loooong time ago, and guess I forgot to change it back :/

So, that changes my timings to:

07:52:33 -> Engine : 00.785ms
07:52:33 -> Blt() colorfill: 00.011ms
07:52:33 -> Lock() : 14.652ms
07:52:33 -> Buffer Copy : 01.021ms
07:52:33 -> Unlock() : 00.014ms
07:52:33 -> Blt() screen : 00.025ms
07:52:33 -> Flip() : 00.013ms

So now it's the Lock() on the offscreen buffer that's blocking. I'm using the DDLOCK_DONOTWAIT | DDLOCK_NOSYSLOCK flags.

I've tried multithreading before, and it didn't buy me much. Although I didn't know as much about the problem as I do now. If I have the time, I'll try it again. It'll require a bit of re-architation. Is that a word?

-Joe

[edited by - Nostalgia on March 29, 2004 7:59:20 AM]

Share this post


Link to post
Share on other sites
Interesting postscript. I just tried the code on a different machine. This one is Win2k instead of XP. On this machine, the colorfill Blt() at the beginning of my display routine DOES fail when the surface is busy. You can see here that it dropped 3 frames while waiting for the surface:

11:10:35 -> Engine : 01.187ms
11:10:35 -> Blt failed (Backbuffer colorfill) : DDERR_WASSTILLDRAWING
11:10:35 -> Engine : 01.172ms
11:10:35 -> Blt failed (Backbuffer colorfill) : DDERR_WASSTILLDRAWING
11:10:35 -> Engine : 01.347ms
11:10:35 -> Blt failed (Backbuffer colorfill) : DDERR_WASSTILLDRAWING
11:10:35 -> Engine : 85.742ms
11:10:35 -> Blt() colorfill: 00.031ms
11:10:35 -> Lock() : 00.006ms
11:10:35 -> Buffer Copy : 00.114ms
11:10:35 -> Unlock() : 00.003ms
11:10:35 -> Blt() screen : 02.378ms
11:10:35 -> Flip() : 00.016ms

The reason the Engine took 85 ms is that skipping the frames allowed the emulator to get too far ahead of the sound and needed to be throttled.

So I wonder if the video card on my other machine is just quirky.

-Joe

Share this post


Link to post
Share on other sites