Jump to content
  • Advertisement
zmic

DX12 CopyTextureRegion to backbuffer of swapchain does nothing

Recommended Posts

Hi programmers,

I have this problem in DirectX 12.

I have one thread that runs a compute shader in a loop. This compute shader generates a 2d image in a buffer (D3D12_HEAP_TYPE_DEFAULT, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS). The buffer gets updated, say, a hundred times a second.

In the main thread, I copy this buffer to the back buffer of the swap chain when WM_PAINT is called. WM_PAINT keeps getting called because I never do the BeginPaint/Endpaint. For this copy operation I use a graphical CommandQueue/CommandList. Here's the pseudo-code for this paint operation:

... reset CommandQueue/CommandList

swapchain->GetBuffer(back_buffer)
commandlist->CopyTextureRegion(back_buffer, 0, 0, 0, computed_buffer, nullptr);
commandlist->ResourceBarrier( back_buffer, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PRESENT));

... execute CommandList and wait for finish using fence
... swapchain->Present(..)

I use a good old CriticalSection to make sure the compute CommandList and the graphical CommandList don't run at the same time.

When I start the program this runs fine in a normal window. I see the procedurally generated buffer animated in real time. However, when I switch to full screen (and resize the swapchain), nothing gets rendered. The screen stays black. When I leave fullscreen (again resize swapchain), same problem. The screen just stays black. Other than that the application runs stable. No directX warnings in the debug output, no nothing. I checked that the WM_PAINT messages keep coming and I checked that the compute thread keeps computing.

Note that I don't do anything else with the graphical commandlist. I set no pipeline, or root signature because I have no 3d rendering to do. Can this be a problem?

I suppose I could retrieve the computed buffer with a readback buffer and paint it with an ordinary GDI function, but that seems silly with the data already being on the GPU.

EDIT: I ran the code on another PC and on there the window stays black right from the start. So the resizing doesn't seem to be the problem.

Any ideas appreciated!

Edited by zmic

Share this post


Link to post
Share on other sites
Advertisement

EDIT2: I removed the compute thread altogether. Still the same problem. So it's not some concurrency problem.

I guess my question simply boils down to: I have a non-texture buffer with RGBA data on the GPU. How do I blit it to the backbuffer of the swapchain? (without running an actual 3D rendering pipeline, if possible),

 

Edited by zmic

Share this post


Link to post
Share on other sites

I am able to copy to the backbuffer (I used CopyResource not CopyTextureRegion, but I doubt that is the problem)

Remember to switch your backbuffer to D3D12_RESOURCE_STATE_COPY_DEST before copying to it

 

What happens if you resize the window not by switching to fullscreen but instead by dragging the edge of the window bigger ? (assuming you call your resize swapchain code here as well)

The problem may be something you have missed when resizing (for eample if you were using a pixel shader you have to recreate rendertargetviews when you resize the swapchain as the old texture buffers dont exist anymore so pointers to them are garbage)

The same applies to your uav on the texture your compute shader is writing to if you have recreated that texture on a resize.

Edited by CortexDragon

Share this post


Link to post
Share on other sites
2 hours ago, zmic said:

I use a good old CriticalSection to make sure the compute CommandList and the graphical CommandList don't run at the same time

You can submit work on two different queues at the same time - there's no need for a mutex there. Also, queues just enqueue work for the GPU to perform later - a mutex around your queues won't affect when the GPU actually executes the commands. Also, in this situation, a single queue would be fine instead of having two queues. 

Does your graphics command list contain the right barriers to transition the texture from UAV to copy-src and back again? How do you keep track of your backbuffer descriptors? Do you create your device with the debug flag set? 

Share this post


Link to post
Share on other sites
54 minutes ago, CortexDragon said:

I am able to copy to the backbuffer (I used CopyResource not CopyTextureRegion, but I doubt that is the problem)

Remember to switch your backbuffer to D3D12_RESOURCE_STATE_COPY_DEST before copying to it

 

What happens if you resize the window not by switching to fullscreen but instead by dragging the edge of the window bigger ? (assuming you call your resize swapchain code here as well)

The problem may be something you have missed when resizing (for eample if you were using a pixel shader you have to recreate rendertargetviews when you resize the swapchain as the old texture buffers dont exist anymore so pointers to them are garbage)

The same applies to your uav on the texture your compute shader is writing to if you have recreated that texture on a resize.

Thanks for your input! I was able to reproduce your suggestion... I can CopyResource a texture to the backbuffer (having the same size) and it works nicely in full-screen and windowed mode.

However, the buffer that is calculated by the compute thread is not a texture buffer but a plain D3D12_RESOURCE_DIMENSION_BUFFER so I cannot CopyResource it -- debugger complains that backbuffer and compute buffer are of different type. I need to use CopyTextureRegion with the buffer "wrapped" inside a D3D12_TEXTURE_COPY_LOCATION structure.

Maybe I can let the compute thread write into a texture buffer rather than a plain buffer.. gonna try that first.

 

 

 

Share this post


Link to post
Share on other sites
10 hours ago, Hodgman said:

You can submit work on two different queues at the same time - there's no need for a mutex there. Also, queues just enqueue work for the GPU to perform later - a mutex around your queues won't affect when the GPU actually executes the commands. Also, in this situation, a single queue would be fine instead of having two queues. 

Does your graphics command list contain the right barriers to transition the texture from UAV to copy-src and back again? How do you keep track of your backbuffer descriptors? Do you create your device with the debug flag set? 

Thanks for your input Hodgman!

You are right about the queuing. Those mutexes don't make a difference so I threw them out again.

Yeah the debug flag is set and I think the state transitions are ok. Whenever there's something wrong with those barriers I get spammed instantly in the debug output.

 

Edited by zmic

Share this post


Link to post
Share on other sites

CopyTextureRegion is extremely difficult to use, because you have to make sure that the formats match. I used a similar approach as you in a performance test: copying a loaded texture directly to swap chain back buffer of my output window. Some textures would work, others not. That could explain why your app does not work on the other PC at all.

I guess the only good way to get the image from your compute shader back to the output window is the thing you didn't want to: Use the texture in a regular draw call, because that will convert the different formats for you. I know it sounds silly to program a shader for a simple copy to output window, but at least I cold not find another way.

Bottom line: CopyTextureRegion is fine to copy areas between your own buffers that have the same format, but has not much use in copying to the swap chain back buffer, because that buffer type will depend on the machine you run your program on.

Share this post


Link to post
Share on other sites
18 minutes ago, clemensx said:

CopyTextureRegion is extremely difficult to use, because you have to make sure that the formats match. I used a similar approach as you in a performance test: copying a loaded texture directly to swap chain back buffer of my output window. Some textures would work, others not. That could explain why your app does not work on the other PC at all.

I guess the only good way to get the image from your compute shader back to the output window is the thing you didn't want to: Use the texture in a regular draw call, because that will convert the different formats for you. I know it sounds silly to program a shader for a simple copy to output window, but at least I cold not find another way.

Bottom line: CopyTextureRegion is fine to copy areas between your own buffers that have the same format, but has not much use in copying to the swap chain back buffer, because that buffer type will depend on the machine you run your program on.

Thanks for answering!

In the meantime I stumbled on some kludge that works for whatever reason... the kludge being that I add 16 extra pixels to the size of the backbuffer on resize. I have no idea why this works, but the program only has to run on one machine so I'll take it... for now. When I have some more time I will look into it again.

    _iswapchain3->GetDesc(&desc);
    if (!minimized)
    {
        THROW_FAIL(_iswapchain3->ResizeBuffers(2, X + 16, Y + 16, desc.BufferDesc.Format, desc.Flags));
    }

Without the 16, it doesn't work. With the 16, it does. Yeah, really :)

By the way the backbuffer is created with DXGI_FORMAT_R8G8B8A8_UNORM and my own buffer has the same format internally, so just I assumed they would be compatible that way.

 

Edited by zmic

Share this post


Link to post
Share on other sites
3 hours ago, zmic said:

Thanks for answering!

In the meantime I stumbled on some kludge that works for whatever reason... the kludge being that I add 16 extra pixels to the size of the backbuffer on resize. I have no idea why this works, but the program only has to run on one machine so I'll take it... for now. When I have some more time I will look into it again.


    _iswapchain3->GetDesc(&desc);
    if (!minimized)
    {
        THROW_FAIL(_iswapchain3->ResizeBuffers(2, X + 16, Y + 16, desc.BufferDesc.Format, desc.Flags));
    }

Without the 16, it doesn't work. With the 16, it does. Yeah, really :)

By the way the backbuffer is created with DXGI_FORMAT_R8G8B8A8_UNORM and my own buffer has the same format internally, so just I assumed they would be compatible that way.

 

 

Hello zmic,

 

I have absolutely no experience in doing what you are trying to do (copy data directly to swap chain buffer) but I just noticed one thing in your last comment if I read what clemensx said. I'm not sure if clemensx meant that formats have to be the exact same or only the bytes alignment of the formats. If the formats really have to be exactly the same, are you sure that the buffer of your swap chain is not actually B8G8R8A8_UNORM instead of R8G8B8A8_UNORM? I've seen swap chains using the B8G8R8A8_UNORM, R11G11B10_FLOAT and R16G16B16A16_FLOAT formats but never R8G8B8A8_UNORM like common textures.

 

Also I've had to do the same thing as you in the past but I've done it with an extra draw call and a quad covering the whole buffer after reading everywhere that it was normal to have to do such thing.

Edited by ChuckNovice

Share this post


Link to post
Share on other sites

yes ChuckNovice, this is exactly what I meant. A simple format change from B8G8... to R8G8... will not work. Some details can be found in the official docu at https://msdn.microsoft.com/de-de/library/windows/desktop/dn903862(v=vs.85).aspx

I ended up doing the same thing as you, using a very simple triangle setup that spans the whole screen and copying the texture there using normal shader code. Kind of what you need to do anyway for a number of reasons, like blur effects and many more.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!