If locking a DDraw Surface is so slow...

Started by
8 comments, last by Arek the Absolute 22 years, 5 months ago
How would one go about reading / writing the contents of a surface without locking it? It must be possible, as the Blt function doesn''t seem to lock any surfaces, but I''m afraid that''s probably much dirtier work than I''m capable of at the moment... Do any of you know of a way around locking?
-Arek the Absolute"The full quartet is pirates, ninjas, zombies, and robots. Create a game which involves all four, and you risk being blinded by the sheer level of coolness involved." - Superpig
Advertisement
You could probably lock it once, and keep using that pointer over and over - but it''s not a safe thing to do.

If you lock buffer, be sure to use a triple buffer, and you should be fine. The performance issue comes into play if the buffer is locked, and Dx decides it''s time to flip.

Magmai Kai Holmlor
- Not For Rent
- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
The performance hit of locking a surface comes from having to copy the block of vram to system memory so it can be modified by your program. When the surface is unlocked the block of system memory is copied back to the video card, and the memory can then be reused, which invalidates the surface pointer.

The benefit of that is faster drawing of the surface, a very fast video->video copy, which is what Blt does if both surfaces are in vram.

Generally, if you create a surface in system memory you can keep reusing the same surface pointer, although in theory DirectX is free to move the data wherever it wants. The penalty for non-vram surfaces is slower draw speed, but if you''re locking a particular surface every frame (e.g. alpha blending) then it''s usually faster to keep it in system memory.
I don''t think that DX copies the data from VRAM to system memory. Todays hardware allows access to the whole videomemory directly.
Your best bet for handling this without fear of losing the VRAM pointer is to do one lock/unlock each frame, if needed. For example, you might structure your code something like this:

do background blits
lock surface
draw alpha blended sprites/lines/circles/etc...
unlock surface
do foreground blits

Of course, this is a general case. There may be instances where
you need to lock two or three times based on z-order. The idea is to keep locks down to a minimum.

YMMV.
I would have thought that the most efficient way to access data in vram would be to copy it out and back in a chunk, otherwise you''d have to read/write pixels across the AGP bus, which is slower than main memory, and is also slower to read back than write.

Never mind the issues of mapping vram into the programs address space...

I could of course be utterly wrong, if anyone knows better please feel free
I was under the impression that using Blt implicitly locks the surface. Unless I''m mistaken, you can''t access memory in a surface unless you lock it, and Blt does this automatically. Locking a surface has little overhead; it''s the grabbing of pixels from video memory that slows your system to a crawl, like Ruval said. I''ve tested this myself with a homemade alpha blending algorithm.

Keep your stuff in system memory if you want to manipulate your image data directly. Slower performance is the price you pay for having that much control over your graphics. And if you have a chance to use Visual Basic, check out the function GetLockedArray, which gives you read-write memory access as though it''s an array of raw pixel data.

GDNet+. It's only $5 a month. You know you want it.

I thought some real numbers might clear things up, by measuring the lock/unlock overhead in cycles.

        //This is the code I timedif( dds->Lock(0, &ddsd, DDLOCK_SURFACEMEMORYPTR, 0) )    goto dderror;if( dds->Unlock(ddsd.lpSurface) )    goto dderror;  

The call overhead for lock/unlock is 150 cycles the rest is all taken up by directx.

2200 cycles, DDSCAPS_OFFSCREENPLAIN
2200 cycles, DDSCAPS_VIDEOMEMORY
800 cycles, DDSCAPS_OFFSCREENPLAIN|DDSCAPS_SYSTEMMEMORY
800 cycles, DDSCAPS_SYSTEMMEMORY;

For reference here is some code that runs in about the same amount of time.

800 cycles, for(int i=400; i; i--);
2200 cycles, for(int i=1100; i; i--);

So what does it all mean:

1. Use DDSCAPS_OFFSCREENPLAIN|DDSCAPS_SYSTEMMEMORY if you lock
2. Only lock the surface once per frame
3. Lock only if you have to
4. Don't worry be happy, lock/unlock is pretty cheap


Edited by - burp on November 10, 2001 1:07:03 PM
Oky.... guess i''ve to poke my nose into this.

I''ve found a way to skip that pesky lock/unlock.

In windowed mode.... itz as simple as anything cuz all u have to do is lock the backbuffer once while initializing DirectX and keep the pointer in some global varaible which may read like (g_pBackBufferPointer).

In fullscreen mode... things are a lil tricky. In FullScreen the surfaces are flipped, the address pointer of the backbuffer and frontbuffer are swapped rather than blitting ( Which is done by DirectX itself). So what i do is, I lock both frontbuffer and backbuffer once while DirectX loads and keep the pointer as global variables. Then whenever the surfaces are flipped , I swap the global variables as well. for example:

VOID * g_pBackBufferPointer;
VOID * g_pFrontBufferPointer;

HRESULT Present()
{

HRESULT hr;
hr = g_pDDFrontBuffer->Flip(NULL,DDFLIP_WAIT);
if(FAILED(hr)) return hr;
VOID *pTempPointer;
pTempPointer = g_pBackBufferPointer;
g_pBackBufferPointer = g_pFrontBufferPointer;
g_pFrontBufferPointer = pTempPointer;

}

This makes your g_pBackBufferPointer always pointing towards the CURRENT BACKBUFFER !!! Hence you dont have to lock your
surfaces everytime you want to write someting on it . Simple huh ..
And tell you what.... it really works .
Z
Good idea, reminds me of the good old days, when programers were in total control of the graphics hardware.

Accessing video memory (read,write) is still slower than system memory, I say if you have to draw directly on the surface, use system memory.

One danger with that method would be if you lost the surface before drawing to it.

msdn quote: "Surfaces can be lost because the display mode was changed or because another application received exclusive access to the display card and freed all of the surface memory currently allocated on the card."

This topic is closed to new replies.

Advertisement