Reading one pixel from texture to CPU in DX11

Started by
9 comments, last by Dawoodoz 13 years, 2 months ago
When using D3D11_CPU_ACCESS_READ for textures, I am not allowed to read or write using the GPU so that it is completely useless by itself. :blink:
How do I copy the immutable texture to a staged resource and read one pixel from it?
Advertisement
CopySubresourceRegion and then ID3D11DeviceContext::Map on the staging texture.
Thanks, I will try that. :)
I haven't figured out how to set the dimensions and offsets. Everything I have tried returned (0,0,0,0).


D3D11_BOX SrcBox;
SrcBox.left = ?;
SrcBox.right = ?;
SrcBox.top = ?;
SrcBox.bottom = ?;
SrcBox.front = 0;
SrcBox.back = 1;
m_pImmediateContext->CopySubresourceRegion(Surface->CPUAccessBuffer,0, ?,?,?, InputResource,0,&SrcBox);
You should be able to do it like this:

D3D11_BOX srcBox;
srcBox.left = pixel X coordinate;
srcBox.right = srcBox.left + 1;
srcBox.top = pixel Y coordinate;
srcBox.bottom = srcBox.top + 1;
srcBox.front = 0;
srcBox.back = 1;

pContext->CopySubresourceRegion(p1x1StagingTexture, 0, 0, 0, 0, pSourceTexture, 0, &srcBox);

D3D11_MAPPED_SUBRESOURCE msr;
pContext->Map(p1x1StagingTexture, 0, D3D11_MAP_READ, 0, &msr);
BYTE *pixel = msr.pData;
// copy data
pContext->Unmap(p1x1StagingTexture, 0);


And create the 1x1 texture with the same format as the source texture, staging usage, 0 bind flags, and D3D11_CPU_ACCESS_READ for the cpu access.
The issue seems to be when I try to use BYTE *pixel = msr.pData;
with the type DXGI_FORMAT_R32G32B32A32_FLOAT / D3DXVECTOR4.

// CPU Access buffer
D3D11_TEXTURE2D_DESC StagedDesc = {
1,//UINT Width;
1,//UINT Height;
1,//UINT MipLevels;
1,//UINT ArraySize;
DXGI_FORMAT_R32G32B32A32_FLOAT,//DXGI_FORMAT Format;
1, 0,//DXGI_SAMPLE_DESC SampleDesc;
D3D11_USAGE_STAGING,//D3D11_USAGE Usage;
0,//UINT BindFlags;
D3D11_CPU_ACCESS_READ,//UINT CPUAccessFlags;
0//UINT MiscFlags;
};
m_pd3dDevice->CreateTexture2D( &StagedDesc, NULL, &Surface->CPUAccessBuffer );



// Select the pixel
D3D11_BOX SrcBox;
SrcBox.left = X; // Minimum X
SrcBox.right = X+1; // Maximum X
SrcBox.top = Y; // Minimum Y
SrcBox.bottom = Y+1; // Maximum Y
SrcBox.front = 0; // Always 0 for 2D textures
SrcBox.back = 1; // Always 1 for 2D textures

// Get the texture pointer
if (Surface->HaveExtraColorBuffer == false || Surface->SwapState == true) {
InputResource = Surface->OriginalColorBuffer;
} else {
InputResource = Surface->ExtraColorBuffer;
}

// Copy the pixel to the staging buffer
m_pImmediateContext->CopySubresourceRegion(Surface->CPUAccessBuffer,0,0,0,0,InputResource,0,&SrcBox);

// Lock the memory
D3D11_MAPPED_SUBRESOURCE MappingDesc;
m_pImmediateContext->Map(Surface->CPUAccessBuffer,0,D3D11_MAP_READ,0,&MappingDesc);

// Get the data
if (MappingDesc.pData == NULL) {
::MessageBox(NULL, L"DrawSurface_GetPixelColor: Could not read the pixel color because the mapped subresource returned NULL.", L"Error!", NULL);
} else {
Result = *((D3DXVECTOR4*)MappingDesc.pData);
}

// Unlock the memory
m_pImmediateContext->Unmap(Surface->CPUAccessBuffer,0);
Is your source texture the exact same format, and not multisampled?
Use the D3D11_CREATE_DEVICE_DEBUG flag when creating your device and run with the debugger, and see if you get any debug output. If there's something wrong with the copy you will get a warning.
I had no warnings in debug mode.

I don't know if the texture is multisampled since I don't use it.
Here is the constructor for my draw surface.

void EngineCore::DrawSurface_SetSize(DrawSurface_Struct* Surface, int NewWidth, int NewHeight, bool FinalSurface, bool HaveDepthBuffer, bool HaveExtraColorBuffer) {
if (NewWidth < 1 || NewHeight < 1) {
::MessageBox(NULL, L"DrawSurface_SetSize: A draw surface can't have non positive dimensions.", L"Error!", NULL);
} else if (Surface->CurrentWidth != NewWidth || Surface->CurrentHeight != NewHeight) {
// Release any old data to avoid memory leaks
DrawSurface_Struct_Release(Surface);

// Store dimensions
Surface->CurrentWidth = NewWidth;
Surface->CurrentHeight = NewHeight;

if (FinalSurface) {
// Update the swap chain
m_pSwapChain->ResizeBuffers(1,NewWidth,NewHeight,DXGI_FORMAT_R8G8B8A8_UNORM,0);
if (HaveExtraColorBuffer) {
::MessageBox(NULL, L"DrawSurface_SetSize: The final draw surface may not have an extra color buffer.", L"Error!", NULL);
}
}

Surface->HaveExtraColorBuffer = HaveExtraColorBuffer;

// Color buffer
D3D11_TEXTURE2D_DESC TextureDescription = {
NewWidth,//UINT Width;
NewHeight,//UINT Height;
1,//UINT MipLevels;
1,//UINT ArraySize;
DXGI_FORMAT_R32G32B32A32_FLOAT,//DXGI_FORMAT Format;
1, 0,//DXGI_SAMPLE_DESC SampleDesc;
D3D11_USAGE_DEFAULT,//D3D11_USAGE Usage;
D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET,//UINT BindFlags;
0,//UINT CPUAccessFlags;
0//UINT MiscFlags;
};

// Color buffer
m_pd3dDevice->CreateTexture2D( &TextureDescription, NULL, &Surface->OriginalColorBuffer );
// Extra color buffer
if (HaveExtraColorBuffer) {
m_pd3dDevice->CreateTexture2D( &TextureDescription, NULL, &Surface->ExtraColorBuffer );
}
// CPU Access buffer
D3D11_TEXTURE2D_DESC StagedDesc = {
1,//UINT Width;
1,//UINT Height;
1,//UINT MipLevels;
1,//UINT ArraySize;
DXGI_FORMAT_R32G32B32A32_FLOAT,//DXGI_FORMAT Format;
1, 0,//DXGI_SAMPLE_DESC SampleDesc;
D3D11_USAGE_STAGING,//D3D11_USAGE Usage;
0,//UINT BindFlags;
D3D11_CPU_ACCESS_READ,//UINT CPUAccessFlags;
0//UINT MiscFlags;
};
m_pd3dDevice->CreateTexture2D( &StagedDesc, NULL, &Surface->CPUAccessBuffer );

// Color output
D3D11_SHADER_RESOURCE_VIEW_DESC TextureOutputDescription = { TextureDescription.Format, D3D11_SRV_DIMENSION_TEXTURE2D, 0, 0 };
TextureOutputDescription.Texture2D.MipLevels = 1;
m_pd3dDevice->CreateShaderResourceView( Surface->OriginalColorBuffer, &TextureOutputDescription, &Surface->OriginalColorOutput );
if (HaveExtraColorBuffer) { // Extra surface
m_pd3dDevice->CreateShaderResourceView( Surface->ExtraColorBuffer, &TextureOutputDescription, &Surface->ExtraColorOutput );
}

// Color input
if (FinalSurface) {
ID3D11Texture2D* pBackBuffer = NULL;
m_pSwapChain->GetBuffer( 0, __uuidof( ID3D11Texture2D ), ( LPVOID* )&pBackBuffer );
m_pd3dDevice->CreateRenderTargetView( pBackBuffer, NULL, &Surface->OriginalColorInput );
pBackBuffer->Release();
} else {
D3D11_RENDER_TARGET_VIEW_DESC TextureInputDescription;
TextureInputDescription.Format = TextureDescription.Format;
TextureInputDescription.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
TextureInputDescription.Texture2D.MipSlice = 0;
m_pd3dDevice->CreateRenderTargetView(Surface->OriginalColorBuffer, &TextureInputDescription, &Surface->OriginalColorInput );
if (HaveExtraColorBuffer) { // Extra surface
m_pd3dDevice->CreateRenderTargetView(Surface->ExtraColorBuffer, &TextureInputDescription, &Surface->ExtraColorInput );
}
}

DrawSurface_SetSwapState(Surface,false);

Surface->HaveDepthBuffer = HaveDepthBuffer;
if (HaveDepthBuffer) {
// Depth buffer
D3D11_TEXTURE2D_DESC DepthTextureDescription = TextureDescription;
DepthTextureDescription.Format = DXGI_FORMAT_R32_TYPELESS;
DepthTextureDescription.BindFlags = D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE;
m_pd3dDevice->CreateTexture2D( &DepthTextureDescription, NULL, &Surface->DepthBuffer );

// Depth I/O
D3D11_DEPTH_STENCIL_VIEW_DESC DepthIODescription = {
DXGI_FORMAT_D32_FLOAT,
D3D11_DSV_DIMENSION_TEXTURE2D,
0
};
m_pd3dDevice->CreateDepthStencilView( Surface->DepthBuffer, &DepthIODescription, &Surface->DepthInputAndOutput );
}
}
}
Are you sure your texture contains the correct information?
Use PIX to confirm that the buffer isn't full of zeroes unless you're already drawing the buffer, and put a break-point before CopySubresourceRegion and check the values, so you're not copying from ExtraColorBuffer when you want to copy from the other one or something like that.
I don't see anything wrong with your code at a glance, but I can't say for certain. The method should definitely work however, what feature level are you running on?
I run on DirectX 10.0 hardware and use shader model 4.0.
I can see the image because it is used in a post effect but I will try the method with other input.

I usually solve this type of bugs while sleeping, so I might wake up with the answer tomorrow. :)

This topic is closed to new replies.

Advertisement