Writing to a texture while it's being read

Started by
12 comments, last by pcmaster 4 years, 6 months ago

Hello, I should say I'm new to DX12 and I have the following use case.

I'm making a "simple" font rendering library that caches glyphs from DirectWrite into 2D textures/atlases as they're being requested by the user on the fly.

I create multiple textures with fixed sizes so that I don't have to resize them if there's not enough space for a font's glyphs. A new texture is created if there's not enough space.

Now, the problem is that there can be a command list being executed that is sampling these textures (because of a previous text render) while I need to insert some glyph to the atlas(es) (write to a texture region).

Obviously, it is guaranteed that the texture data read by the previous render will not be modified since the glyph insert process only writes to free space.

So my question is, is it possible to write to a texture while it's being read in this case, and how? If not, what's the best way to do what I want (or some good alternatives)?

 

And another question: using this method I might end up having to use multiple textures for a draw call. How do I make the shader choose which texture to sample from depending on input data? There can be a dynamic number of textures in this case, but they're all the same size.

Or is it better to split this into multiple draw calls and change resources between them?

Advertisement

In DX12 the user is responsible to sync computations, so as long as you're not writing to the same memory as you're reading, you should be fine. However proper sync to not produce race conditions or similar will probably be a good choice.

In theory what you're describing should be possible to do with a UAV resource, possibly in a compute shader.

For the sync, what Baemz said, look at the ID3D12GraphicsCommandList::ResourceBarrier() method.

For multiple-textures, assuming they all have the same HLSL type (i.e. `Texture2D`) - you can use an unbounded array of textures, then index them dynamically inside the shader. It should looks something like this:


Texture2D gTex[] : register(t0);
SamplerState gSampler : register(s0);
float4 ps(float2 texC : TEXCOORD) : SV_TARGET0
{
    uint arrayIndex = calculateTextureIndexBasedOnWhatever();
    return gTex[arrayIndex].Sample(...);
}

 

14 hours ago, satanir said:

For the sync, what Baemz said, look at the ID3D12GraphicsCommandList::ResourceBarrier() method.

For multiple-textures, assuming they all have the same HLSL type (i.e. `Texture2D`) - you can use an unbounded array of textures, then index them dynamically inside the shader. It should looks something like this:



Texture2D gTex[] : register(t0);
SamplerState gSampler : register(s0);
float4 ps(float2 texC : TEXCOORD) : SV_TARGET0
{
    uint arrayIndex = calculateTextureIndexBasedOnWhatever();
    return gTex[arrayIndex].Sample(...);
}

 

Thank you both.

Is a transition barrier for a texture into a copy destination state safe if there's another command list executing that uses that texture, as long as I don't overwrite existing data with CopyTextureRegion? If true that means that write-only states like "copy destination" prevent access to a resource until the state changes into a read state right?

And what's the difference between a transition to D3D12_RESOURCE_STATE_UNORDERED_ACCESS and creating a resource with D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS? They both seem to allow read and write at the same time.

I don't think you want to use D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS, that's intended for multi-device or at least multi-queue scenarios. I think you only have only a single queue, right? All command lists executed with that queue get serialised.

Transitioning a resource to D3D12_RESOURCE_STATE_UNORDERED_ACCESS means that you'll be able to use it as a RWTexture/RWBuffer/etc and the correct caches will be flushed for you, based on what the state was previously. Similarly with all the other transitions.

OK that clears up some things.

Can I write to a D3D12_RESOURCE_STATE_UNORDERED_ACCESS resource from the CPU? Because that's what I need in this case: write from CPU, read from GPU. However, the docs specify that for CopyTextureRegion a resource must be in copy destination state. If it's not possible then I guess I just might use regular texture and barriers each time I need to write to it (even though barriers seem unnecessary to me when I'm not overwriting any data).

No, you can't. Unordered access means UAV (unordered access view) which maps to HLSL RWTexture2D, for example. Just like SRV (shader resource view) maps to HLSL Texture2D, for example.

Neither has anything to do with the CPU. These states are about GPU-to-GPU synchronisation. Not CPU-to-GPU.

CopyTextureRegion is also a GPU-to-GPU operation - the GPU will at some later unspecified time (when it gets to processing the "CopyTextureRegion" command) copy from some GPU-visible resource to some other GPU-visible resource. There is no CPU involved whatsoever.

Uploading from CPU is done differently, you have to use the upload heaps.

Yes, I had upload heaps in mind. UAV seems useless to me in this case then.

I'm going to be more specific, what I want to do is to update a texture in the default heap from the CPU, without using any barriers or CPU<=>GPU synchronization for performance reasons. These updates are rare and when they happen they don't overwrite any existing data (functioning like an ever-increasing buffer of limited size). A pixel shader will sample this texture since it's used to render text.

However from what I understand now, it's not possible without using transition barriers since CopyTextureRegion expects a copy destination state.

Somebody more versed with DX12 could shed some light on uploading to the default heap.
I'm getting a bit lost :) What will the CopyTextureRegion copy from and to where?

1 hour ago, pcmaster said:

Somebody more versed with DX12 could shed some light on uploading to the default heap.
I'm getting a bit lost :) What will the CopyTextureRegion copy from and to where?

I'm going to create a texture in the upload heap that will be filled with glyphs from DirectWrite whenever they don't already exist in GPU memory. Then CopyTextureRegion to that place where it doesn't exist (a texture in the default heap).

This topic is closed to new replies.

Advertisement