• Advertisement
Sign in to follow this  

Get Raw Data from D3D11Texture2D C++

Recommended Posts

Hello everyone,

 

I'm trying to extract buffer of pixel BGRA from a D3D11Texture2D.

Here is the code I started with : https://code.msdn.microsoft.com/windowsdesktop/Desktop-Duplication-Sample-da4c696a/sourcecode?fileId=42782&pathId=1384140008

In this function I would like to extract the raw data from Data->Frame :

DUPL_RETURN DISPLAYMANAGER::ProcessFrame(_In_ FRAME_DATA* Data, _Inout_ ID3D11Texture2D* SharedSurf, INT OffsetX, INT OffsetY, _In_ DXGI_OUTPUT_DESC* DeskDesc)

Here is my extract code :

BYTE* DISPLAYMANAGER::GetImageData(ID3D11Texture2D* texture2D, D3D11_TEXTURE2D_DESC Desc)
{
	if (texture2D != NULL)
	{
		D3D11_TEXTURE2D_DESC description;
		texture2D->GetDesc(&description);
		description.BindFlags = 0;
		description.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
		description.Usage = D3D11_USAGE_STAGING;
		description.Format = DXGI_FORMAT_B8G8R8A8_UNORM;

		ID3D11Texture2D* texTemp = NULL;
		HRESULT hr = m_Device->CreateTexture2D(&description, NULL, &texTemp);
		if (FAILED(hr))
		{
			if (texTemp)
			{
				texTemp->Release();
				texTemp = NULL;
			}
			return NULL;
		}
		m_DeviceContext->CopyResource(texTemp, texture2D);

		D3D11_MAPPED_SUBRESOURCE mapped;
		unsigned int subresource = D3D11CalcSubresource(0, 0, 0);
		hr = m_DeviceContext->Map(texTemp, subresource, D3D11_MAP_READ_WRITE, 0, &mapped);
		if (FAILED(hr))
		{
			texTemp->Release();
			texTemp = NULL;
			return NULL;
		}

		unsigned char *m_CaptureData = new unsigned char[Desc.Width * Desc.Height * 4];
		RtlZeroMemory(m_CaptureData, Desc.Width * Desc.Height * 4);
		const int pitch = Desc.Width;
		unsigned char* source = static_cast<unsigned char*>(mapped.pData);
		unsigned char* dest = m_CaptureData;
		for (int i = 0; i < Desc.Width; i++) {
			memcpy(dest, source, Desc.Height * 4);
			source += pitch;
			dest += pitch;
		}
		for (int i = 0; i < 1200; i++) {
			trace(L"Pixel[%d] = %x\n", i, source[i]);
		}

		m_DeviceContext->Unmap(texTemp, 0);
		return dest;
	}
	else
		return NULL;
}

On the print I got value '0' in every index. I don't know why ..

Please can you help me ? Thank you !

Share this post


Link to post
Share on other sites
Advertisement

There's a few obvious errors in the code you've written which are worth fixing:

  1. The pitch of the source (read pointer) should be incremented by mapped.RowPitch, not Desc.Width. Desc.Width is the number of pixels the texture is wide, and is not only measured in the wrong units (pixels, instead of bytes) but the pitch is likely something other than "Desc.Width * 4".
  2. The pitch of the destination (dest) should be incremented by Desc.Width * 4 (bytes) since 'dest' is an unsigned char*.
  3. Your for loop attempts to copy the data row by row, but should be iterating "for(int i = 0; i < Desc.Height; ..." rather than Desc.Width.
  4. The amount memcpy'ed out per row should be Desc.Width * 4, not Desc.Height * 4.
  5. I'm not sure what the relevance of '1200' is, but you're printing out just the first 1200 colour channels (RGBA 300 times). So if the first 300 pixels are transparent black, then you'll get 0 printed out all the time. Try iterating over every pixel just to be sure.

Share this post


Link to post
Share on other sites

Hello Adam Miles,

Thank you very much for your help.

I still have full of '0' value after the change. Here is my new code :

BYTE* DISPLAYMANAGER::GetImageData(ID3D11Texture2D* texture2D, D3D11_TEXTURE2D_DESC Desc)
{
	if (texture2D != NULL)
	{
		D3D11_TEXTURE2D_DESC description;
		texture2D->GetDesc(&description);
		description.BindFlags = 0;
		description.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
		description.Usage = D3D11_USAGE_STAGING;
		description.Format = DXGI_FORMAT_B8G8R8A8_UNORM;

		ID3D11Texture2D* texTemp = NULL;
		HRESULT hr = m_Device->CreateTexture2D(&description, NULL, &texTemp);
		if (FAILED(hr))
		{
			if (texTemp)
			{
				texTemp->Release();
				texTemp = NULL;
			}
			return NULL;
		}
		m_DeviceContext->CopyResource(texTemp, texture2D);

		D3D11_MAPPED_SUBRESOURCE mapped;
		unsigned int subresource = D3D11CalcSubresource(0, 0, 0);
		hr = m_DeviceContext->Map(texTemp, subresource, D3D11_MAP_READ_WRITE, 0, &mapped);
		if (FAILED(hr))
		{
			texTemp->Release();
			texTemp = NULL;
			return NULL;
		}

		unsigned char *captureData = new unsigned char[Desc.Width * Desc.Height * 4];
		RtlZeroMemory(captureData, Desc.Width * Desc.Height * 4);
		const int pitch = mapped.RowPitch;
		unsigned char *source = static_cast<unsigned char*>(mapped.pData);
		unsigned char *dest = captureData;
		for (int i = 0; i < Desc.Height; i++) {
			memcpy(captureData, source, Desc.Width * 4);
			source += pitch;
			captureData += Desc.Width * 4;
		}
		for (int i = 0; i < Desc.Width * Desc.Height * 4; i++) {
			trace(L"Pixel[%d] = %x\n", i, dest[i]);
		}

		m_DeviceContext->Unmap(texTemp, 0);
		return dest;
	}
	else
		return NULL;
}

I don't know if I did well, but now I memcpy on "captureData" because I got an access violation pointer when I was copying on "dest" on the print.

Result is like the following :

...
Pixel[4379306] = 0
Pixel[4379307] = 0
Pixel[4379308] = 0
Pixel[4379309] = 0
Pixel[4379310] = 0
Pixel[4379311] = 0
Pixel[4379312] = 0
Pixel[4379313] = 0
Pixel[4379314] = 0
Pixel[4379315] = 0
Pixel[4379316] = 0
Pixel[4379317] = 0
Pixel[4379318] = 0
Pixel[4379319] = 0
Pixel[4379320] = 0
Pixel[4379321] = 0
Pixel[4379322] = 0
Pixel[4379323] = 0

...

Share this post


Link to post
Share on other sites

The only small quirk left is that you map it for READ_WRITE rather than just READ, but that shouldn't be a problem. You can remove CPU_ACCESS_WRITE from the temp texture creation as well if you never intend to write to it.

Do you know that you have actually rendered something to the source texture?

Share this post


Link to post
Share on other sites

I would like to access pixel data from Dirty Rects after calling AcquireNextFrame.

I have this function from the windows project sample "Desktop Duplication API" :

DUPL_RETURN DISPLAYMANAGER::ProcessFrame(_In_ FRAME_DATA* Data, _Inout_ ID3D11Texture2D* SharedSurf, INT OffsetX, INT OffsetY, _In_ DXGI_OUTPUT_DESC* DeskDesc)
{
    DUPL_RETURN Ret = DUPL_RETURN_SUCCESS;
	
    // Process dirties and moves
    if (Data->FrameInfo.TotalMetadataBufferSize)
    {
        D3D11_TEXTURE2D_DESC Desc;
        Data->Frame->GetDesc(&Desc);

        if (Data->MoveCount)
        {
			Ret = CopyMove(SharedSurf, reinterpret_cast<DXGI_OUTDUPL_MOVE_RECT*>(Data->MetaData), Data->MoveCount, OffsetX, OffsetY, DeskDesc, Desc.Width, Desc.Height);
            if (Ret != DUPL_RETURN_SUCCESS)
            {
                return Ret;
            }
        }

        if (Data->DirtyCount)
        {
			Ret = CopyDirty(Data->Frame, SharedSurf, reinterpret_cast<RECT*>(Data->MetaData + (Data->MoveCount * sizeof(DXGI_OUTDUPL_MOVE_RECT))), Data->DirtyCount, OffsetX, OffsetY, DeskDesc, Desc);
			GetImageData(Data->Frame, Desc); //Here I call the function to get DirtyRect pixel buffer data
        }
    }

    return Ret;
}

I would like to access pixel buffer data from Dirty Rect after calling AcquireNextFrame from DXGI.

Here is the function from the Windows Sample Project "Desktop Duplication API":

//
// Process a given frame and its metadata
//
DUPL_RETURN DISPLAYMANAGER::ProcessFrame(_In_ FRAME_DATA* Data, _Inout_ ID3D11Texture2D* SharedSurf, INT OffsetX, INT OffsetY, _In_ DXGI_OUTPUT_DESC* DeskDesc)
{
    DUPL_RETURN Ret = DUPL_RETURN_SUCCESS;
	
    // Process dirties and moves
    if (Data->FrameInfo.TotalMetadataBufferSize)
    {
        D3D11_TEXTURE2D_DESC Desc;
        Data->Frame->GetDesc(&Desc);

        if (Data->MoveCount)
        {
			Ret = CopyMove(SharedSurf, reinterpret_cast<DXGI_OUTDUPL_MOVE_RECT*>(Data->MetaData), Data->MoveCount, OffsetX, OffsetY, DeskDesc, Desc.Width, Desc.Height);
            if (Ret != DUPL_RETURN_SUCCESS)
            {
                return Ret;
            }
        }

        if (Data->DirtyCount)
        {
			Ret = CopyDirty(Data->Frame, SharedSurf, reinterpret_cast<RECT*>(Data->MetaData + (Data->MoveCount * sizeof(DXGI_OUTDUPL_MOVE_RECT))), Data->DirtyCount, OffsetX, OffsetY, DeskDesc, Desc);
			GetImageData(Data->Frame, Desc); //Here I want to get pixel dirty rect buffer data
        }
    }

    return Ret;
}

Can you take a look at : https://www.google.fr/?gws_rd=ssl#q=desktop+duplication+api+windows+10&spf=389

Maybe there is an other method to get the dirty rect pixel buffer data and my method is completely wrong..

Thank you

Edited by DevAndroid

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

Sign in to follow this  

  • Advertisement