Avoid locking DirectX texture?

Started by
11 comments, last by CarlML 15 years, 2 months ago
Hi, Is there a way to get access to the color bits part of a LPDIRECT3DTEXTURE9 and alter them without the need to lock and unlock the texture? Currently I'm doing this:
LPDIRECT3DTEXTURE9 pd3dTex; //Holds the texture, previously initialized

D3DLOCKED_RECT d3dlr;
HRESULT hr = pd3dTex->LockRect(0, &d3dlr, NULL, 0);
COLOR *colors = (COLOR *)d3dlr.pBits;

//alter colors

pd3dTex->UnlockRect(0);
I'd like to avoid the locking part and just constantly have access to the pBits in memory (while still being able to draw the texture in the graphics device)
Advertisement
You can't directly modify a texture's contents without locking it, no way around that. If your modifications are something you can do in a pixel shader, then you might want to consider using render targets. Each time you need to make modifications you could sample the source texture and render results to another. Then next time you just switch the textures.
Quote:Original post by CarlML
Hi,

Is there a way to get access to the color bits part of a LPDIRECT3DTEXTURE9 and alter them without the need to lock and unlock the texture?

Currently I'm doing this:

*** Source Snippet Removed ***

I'd like to avoid the locking part and just constantly have access to the pBits in memory (while still being able to draw the texture in the graphics device)
Nope, there's no way. If there was, then the GPU would have to lock the texture every frame, and you'd get a horrific frame rate. You need to lock resources so that the GPU and CPU don't fight over it.

So long as you create the texture as D3DUSAGE_DYNAMIC and use the appropriate locking flags, performance shouldn't be much of a problem though so long as you lock, fill and unlock no more than once per frame.

See The Documentation for more details.
Ok thanks for the info, that is too bad. Currently I'm doing the lock/unlock twice per code cycle. Once to alter the pixels, then the texture is rendered and then I lock/unlock again to reset the texture. The locking part for a 4096x4096 texture takes around 50 milliseconds in my tests so doing it twice takes 0.1 seconds, which is quite a lot for something that is supposed to be interactive. I think I will need to redesign some parts so I can skip the second lock/unlock.
Quote:Original post by CarlML
Ok thanks for the info, that is too bad. Currently I'm doing the lock/unlock twice per code cycle. Once to alter the pixels, then the texture is rendered and then I lock/unlock again to reset the texture. The locking part for a 4096x4096 texture takes around 50 milliseconds in my tests so doing it twice takes 0.1 seconds, which is quite a lot for something that is supposed to be interactive. I think I will need to redesign some parts so I can skip the second lock/unlock.
1. Never read from a resource, keep a copy of the data in system memory and read from that instead.
2. Never ever lock a texture more than once per frame - you can only render once per frame, so why do you need to lock more than once?
3. Always use dynamic textures if you're locking them every frame. If you don't, you'll be severely hurting the GPU/driver.
4. If you can avoid locking the entire texture, do so. If you're only updating a few pixels in the texture, record the min and max X and Y coordinates, and only lock the rectangle between min and max. That way there's less data to transfer on Unlock()
5. Do you really need a 4096x4096 texture? A lot of cards don't support that (2 of the 3 cards I have don't).
In addition to Steve's comments:

1. If you must write to the pixels to start, consider two options:
a. A single 'master' texture that you write once at start and then render from texture->texture each frame with no locking.
b. If you must have dynamic/different pixels each frame consider a ring buffer where you have a 4-5 staging textures of the same size and whilst you render one you can lock the others

(1a) requires a render-target which can restrict your texture formats
(1b) requires much more memory and some tolerance to latency

In the case of (1a) you can use IDirect3DDevice9::Clear() for lightning fast reset operations (provided you're not doing HDRI).

When considering algorithmic alternatives you need to realise two things about performance. Firstly, the latency between physical hardware over the PCI-E is an obvious and trivial consideration; secondly, by having two processing units depend on the same resource you risk forcing synchronisation. The latter is less obvious but often the main problem - GPU rendering tends to be at its peak when it can operate independently of the CPU, so the more 'involved' the CPU and main app is with resources (and general pipeline control) the less room the GPU has to just run away and do its own thing.

Quote:. Do you really need a 4096x4096 texture? A lot of cards don't support that (2 of the 3 cards I have don't)
True, but just think how pathetic they'll seem with the D3D11 generation requiring 16,384x16,384 textures at a minimum [razz]

hth
Jack

<hr align="left" width="25%" />
Jack Hoxley <small>[</small><small> Forum FAQ | Revised FAQ | MVP Profile | Developer Journal ]</small>

Quote:Original post by jollyjeffers
Quote:. Do you really need a 4096x4096 texture? A lot of cards don't support that (2 of the 3 cards I have don't)
True, but just think how pathetic they'll seem with the D3D11 generation requiring 16,384x16,384 textures at a minimum [razz]
I still want my 65536x? textures for 16-bit lookups [sad]
Thanks for the feedback.

I was able to redesign away the need to have the second lock/unlock so that is good.

I'm testing with a 4096x4096 texture since that is in the upper range of what might be used. The texture size is choosen by the user of the program (a painting application, as a plugin for a 3d program). Anyway things are working ok now, I can live with one lock/unlock.:)
A question though... what happens if you lock the texture, alter pixel data and then display the texture without unlocking? Then unlock after displaying. In my test there doesn't seem to be any issue with doing that and the texture is displayed ok. Could it lead to issues?
Quote:Original post by CarlML
A question though... what happens if you lock the texture, alter pixel data and then display the texture without unlocking? Then unlock after displaying. In my test there doesn't seem to be any issue with doing that and the texture is displayed ok. Could it lead to issues?
Rendering will probably fail, or may display corrupt. If it works on your card, then that's just luck. If the texture is locked, it means that the CPU is accessing it. Because the GPU can be up to 2.5 frames behind the CPU, the GPU might not read the texture at the time you render, but might try to read it at some other point, after you've modified the texture again, or even in the middle of a memcpy() operation.

The Debug Runtimes will probably shout at you for doing that, and I suppose it's technically possible to crash the video card driver and bring down the whole system in an absolute worst case scenario.

This topic is closed to new replies.

Advertisement