Jump to content
  • Advertisement
Sign in to follow this  
KaiserJohan

DX11 Reading one texel from depthbuffer

This topic is 672 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

I was trying to find something similiar to glReadPixel() and stumbled upon this post: https://www.gamedev.net/topic/594722-reading-one-pixel-from-texture-to-cpu-in-dx11/

 

Seemed reasonable, tried it but lo and behold didnt work.

 

Sample code;

{
	DX11DepthBufferReadback::DX11DepthBufferReadback(ID3D11DevicePtr device, ID3D11DeviceContextPtr context, ID3D11Texture2DPtr depthBuffer) :
		mContext(context),
		mDepthbuffer(depthBuffer),
		mStaging1x1(nullptr)
	{
		D3D11_TEXTURE2D_DESC depthTextureDesc;
		ZeroMemory(&depthTextureDesc, sizeof(depthTextureDesc));
		depthBuffer->GetDesc(&depthTextureDesc);

		depthTextureDesc.Usage = D3D11_USAGE_STAGING;
		depthTextureDesc.BindFlags = 0;
		depthTextureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
		device->CreateTexture2D(&depthTextureDesc, NULL, &mStaging1x1);
	}


	float DX11DepthBufferReadback::GetDepthValue(const WindowPosition& position) const
	{
		D3D11_BOX box;
		ZeroMemory(&box, sizeof(box));
		box.left = position.x;
		box.right = box.left + 1;
		box.top = position.y;
		box.bottom = box.top + 1;
		box.front = 0;
		box.back = 1;

		mContext->CopySubresourceRegion(mStaging1x1, 0, 0, 0, 0, mDepthbuffer, 0, &box);

		D3D11_MAPPED_SUBRESOURCE mappedRes;
		mContext->Map(mStaging1x1, 0, D3D11_MAP_READ, 0, &mappedRes);
		float depth = *static_cast<float*>(mappedRes.pData);
		mContext->Unmap(mStaging1x1, 0);

		return depth;
	}
}

This is the debug ouput I get:

D3D11 ERROR: ID3D11DeviceContext::CopySubresourceRegion: If CopySubresourceRegion is used with Multisampled or D3D11_BIND_DEPTH_STENCIL Resources, then the whole Subresource must be copied. DstX, DstY, and DstZ must all be 0, while pSrcBox must be NULL. [ RESOURCE_MANIPULATION ERROR #280: COPYSUBRESOURCEREGION_INVALIDSOURCEBOX]

So this dosn't seem to work. Is there any similiarly easy/straight forward solution that works?

 

Share this post


Link to post
Share on other sites
Advertisement

You could write a single-thread, single-wave Compute Shader that reads a single sample from the texel you're interested in and writes that to a USAGE_DEFAULT buffer. You can then issue a CopySubresourceRegion to copy the single float from a USAGE_DEFAULT buffer to a USAGE_STAGING buffer and map that for readback instead. You can't call ResourceSubresource on an MSAA depth buffer, so you'll need to use a shader (CS or PS) to extract the data you're interested in and write that to another resource. If you'd like to avoid Compute Shaders you could write the extracted depth value to a 1x1 R32_FLOAT render target and copy that texture instead to a STAGING resource.

Share this post


Link to post
Share on other sites

You could write a single-thread, single-wave Compute Shader that reads a single sample from the texel you're interested in and writes that to a USAGE_DEFAULT buffer. You can then issue a CopySubresourceRegion to copy the single float from a USAGE_DEFAULT buffer to a USAGE_STAGING buffer and map that for readback instead. You can't call ResourceSubresource on an MSAA depth buffer, so you'll need to use a shader (CS or PS) to extract the data you're interested in and write that to another resource. If you'd like to avoid Compute Shaders you could write the extracted depth value to a 1x1 R32_FLOAT render target and copy that texture instead to a STAGING resource.

This is my depthbuffer, also used for my staging texture except for cpuAccess, usage and bindflags:

// create depth buffer
D3D11_TEXTURE2D_DESC depthStencilBufferDesc;
ZeroMemory(&depthStencilBufferDesc, sizeof(D3D11_TEXTURE2D_DESC));
depthStencilBufferDesc.ArraySize = 1;
depthStencilBufferDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_DEPTH_STENCIL;
depthStencilBufferDesc.Format = DXGI_FORMAT_R24G8_TYPELESS;
depthStencilBufferDesc.Width = backbufferTextureDesc.Width;
depthStencilBufferDesc.Height = backbufferTextureDesc.Height;
depthStencilBufferDesc.MipLevels = 1;
depthStencilBufferDesc.SampleDesc.Count = 1;
depthStencilBufferDesc.Usage = D3D11_USAGE_DEFAULT;

depthStencilBufferDesc.SampleDesc.Count = 1 is not multisampled right? (i.e it should work?)

Edited by KaiserJohan

Share this post


Link to post
Share on other sites

Correct, but the error message also says you can't copy just a sub-region of the depth buffer, you have to copy it at all. You appear to be trying to copy a 1x1 region.

 

You can either copy the entire thing back to system memory and just read the texel you're interested in, or you can extract the relevant texel on the GPU and just copy that back.

 

I'm not sure whether you might encounter difficulty copying back D24S8 too, I've never tried that. Given that some hardware doesn't pack D24 and S8 together into a single DWORD I don't know what the memory layout of the system memory copy of this surface would look like. It wouldn't surprise me if you encounter further validation errors.

Edited by Adam Miles

Share this post


Link to post
Share on other sites

Correct, but the error message also says you can't copy just a sub-region of the depth buffer, you have to copy it at all. You appear to be trying to copy a 1x1 region.

 

You can either copy the entire thing back to system memory and just read the texel you're interested in, or you can extract the relevant texel on the GPU and just copy that back.

 

I'm not sure whether you might encounter difficulty copying back D24S8 too, I've never tried that. Given that some hardware doesn't pack D24 and S8 together into a single DWORD I don't know what the memory layout of the system memory copy of this surface would look like. It wouldn't surprise me if you encounter further validation errors.

 

What do you estimate would be the most efficient way of doing it (copy entire depthbuffer and read one texel -VS- use a CS/PS to output to a 1x1 staging texture and read that?)

Share this post


Link to post
Share on other sites

There's only limited bandwidth back across the PCI-E bus, so ideally you'd be copying as little data as possible over that (which favours the CS/PS approach). A 1080p depth buffer is at least 8MB, heading up to 32MB by the time your resolution is 4K. With the single texel extraction you can copy back a fixed amount (4 bytes) irrespective of your resolution.

Share this post


Link to post
Share on other sites

I would suggest don't do it at all.  Aside from bandwidth, even a single pixel readback will cause the GPU and CPU to synchronize, potentially cutting your performance by up to two-thirds.  Think about what you want to do rather than how you want to do it - is there an alternative approach that doesn't involve a readback?

Share this post


Link to post
Share on other sites

Copying back the texel on a one frame delay shouldn't do much to impact the frame rate. So long as there's latency tolerance built into the system there's nothing particularly harmful about a small readback.

Share this post


Link to post
Share on other sites

I am doing this in my editor to raise/lower terrain (i.e increment/decrement values in a heightmap). To do that I first need to reconstruct the 3d point from 2d screen coords and for that I need depth for that pixel (afaik) and then somehow go from that 3d point to modify the correct hieghtmap texel (but that is a later problem).

Share this post


Link to post
Share on other sites

 

... If CopySubresourceRegion is used with Multisampled or D3D11_BIND_DEPTH_STENCIL Resources ...

 

Do you have a need for a stencil buffer? If not, try changing DXGI_FORMAT_R24G8_TYPELESS to DXGI_FORMAT_R32_TYPELESS (and no D3D11_BIND_DEPTH_STENCIL) and see if it works..?

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

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

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!