Copy 2D array into Texture2D?

Started by
5 comments, last by KieronH 9 years ago

So here's my problem, I have a 2D array filled with values that I want to be accessed on the GPU. Firstly I thought it was best to use a texture2d to achieve this. This needs to be updated when I need to pass new values over to the GPU.

Having looked around for a solution I came across this: http://www.gamedev.net/topic/606100-solved-dx11-updating-texture-data/

This gave me a further idea of how to achieve something like this but I am still having trouble actually doing it.

At the moment there's not much code to show other than creation of a texture and the array that I need to pass over. I've tried many different options with them all resulting in either completely wrong textures or crashes. I'll show what I have so far though:


	D3D11_TEXTURE2D_DESC desc2;
	ZeroMemory(&desc2, sizeof(desc2));
	desc2.Width = width;
	desc2.Height = height;
	desc2.ArraySize = 1;
	desc2.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
	desc2.Usage = D3D11_USAGE_DYNAMIC;
	desc2.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	desc2.BindFlags = D3D11_BIND_SHADER_RESOURCE;
	desc2.MipLevels = 1;
	desc2.SampleDesc.Count = 1;
	desc2.SampleDesc.Quality = 0;

	D3D11_SUBRESOURCE_DATA data;
	data.pSysMem = grid;
	data.SysMemPitch = desc2.Width * 16;
	data.SysMemSlicePitch = 0;

	device->CreateTexture2D(&desc2, NULL, &textureBuffer);

        D3D11_MAPPED_SUBRESOURCE mappedResource;
	deviceContext->Map(textureBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
	memcpy(&mappedResource.pData, &data, width);
	deviceContext->Unmap(textureBuffer, 0);

I know most of this is completely wrong. But hopefully it helps with visualising what I want. 'grid' is the 2D array data. Each value in the array is a float4 value.

Advertisement

For creating you can pass a pointer to your D3D11_SUBRESOURCE_DATA struct as the second parameter to your CreateTexture2D call and it will set your texture data without needing a subsequent Map call.

Your memcpy call is all wrong.

mappedResource.pData is already a pointer so you don't need the "&". This is probably where you are crashing.

You don't pass a pointer to a D3D11_SUBRESOURCE_DATA to the memcpy; pass the raw data instead.

Just using width as the size to copy is totally wrong; this should be "width * height * sizeof (float4)".

Watch the RowPitch member of your D3D11_MAPPED_SUBRESOURCE. For reasonable values of width this is normally equal to "width * sizeof (float4)" but on some hardware it's not. If you're using non-power-of-two sizes, or sizes that aren't multiples of 4, it's more likely to not be, so you need to copy in one row at a time, advancing by the difference, instead of just doing a straight memcpy.

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

First of all...are you looking to create a static texture that you initialize once and use for a long time? Or are you looking to create a dynamic texture, whose contents are updated frequently with new data from CPU memory? You're setting things up for the latter case, and I just want to make sure that this is what you intended.

Those last 4 lines, where you map the texture and update its contents, are definitely wrong. You're performing a memcpy using a pointer to your D3D11_SUBRESOURCE_DATA struct as your souce, and then copying "width" bytes. This isn't copying the right data, and will surely result in the memcpy reading garbage data off the stack. In fact you don't need that D3D11_SUBRESOURCE_DATA struct at all, that's only something that you use for initializing a texture with data. You're also using the address of mappedResource.pData as your destination, which means you're passing a pointer to a pointer to your mapped data, rather than passing a pointer to your mapped data. This will result in your memcpy stomping over your stack, which will result in very bad things happening.

You want something like this:

D3D11_TEXTURE2D_DESC desc2;
ZeroMemory(&desc2, sizeof(desc2));
desc2.Width = width;
desc2.Height = height;
desc2.ArraySize = 1;
desc2.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
desc2.Usage = D3D11_USAGE_DYNAMIC;
desc2.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
desc2.BindFlags = D3D11_BIND_SHADER_RESOURCE;
desc2.MipLevels = 1;
desc2.SampleDesc.Count = 1;
desc2.SampleDesc.Quality = 0;
 
device->CreateTexture2D(&desc2, NULL, &textureBuffer);
 
const uint32_t texelSize = 16;  // size of DXGI_FORMAT_R32G32B32A32_FLOAT
 
D3D11_MAPPED_SUBRESOURCE mappedResource;
deviceContext->Map(textureBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
uint8_t* dstData = reinterpret_cast<uint8_t*>(mappedResource.pData);
const uint8_t* srcData = reinterpret_cast<const uint8_t*>(grid);
for(uint32_t i = 0; i < height; ++i)
{
    memcpy(dstData, srcData, width * texelSize);
    dstData += mappedResource.RowPitch;
    srcData += width * texelSize;
}
deviceContext->Unmap(textureBuffer, 0);

Ugh, the stupid editor ate my text again. I had this following the code section:

For that code, I'm assuming that "grid" is a pointer to your texture data in CPU memory. The reason that I have that loop over the rows of the texture is that 2D texture resources will often have padding at the end of the rows in order to fulfill GPU alignment requirements. To handle that, you need to copy the data for each row individually, and use the "RowPitch" value from D3D11_MAPPED_SUBRESOURCE to ensure that you're skipping any padding.

First of all...are you looking to create a static texture that you initialize once and use for a long time? Or are you looking to create a dynamic texture, whose contents are updated frequently with new data from CPU memory? You're setting things up for the latter case, and I just want to make sure that this is what you intended.

Those last 4 lines, where you map the texture and update its contents, are definitely wrong. You're performing a memcpy using a pointer to your D3D11_SUBRESOURCE_DATA struct as your souce, and then copying "width" bytes. This isn't copying the right data, and will surely result in the memcpy reading garbage data off the stack. In fact you don't need that D3D11_SUBRESOURCE_DATA struct at all, that's only something that you use for initializing a texture with data. You're also using the address of mappedResource.pData as your destination, which means you're passing a pointer to a pointer to your mapped data, rather than passing a pointer to your mapped data. This will result in your memcpy stomping over your stack, which will result in very bad things happening.

You want something like this:


D3D11_TEXTURE2D_DESC desc2;
ZeroMemory(&desc2, sizeof(desc2));
desc2.Width = width;
desc2.Height = height;
desc2.ArraySize = 1;
desc2.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
desc2.Usage = D3D11_USAGE_DYNAMIC;
desc2.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
desc2.BindFlags = D3D11_BIND_SHADER_RESOURCE;
desc2.MipLevels = 1;
desc2.SampleDesc.Count = 1;
desc2.SampleDesc.Quality = 0;
 
device->CreateTexture2D(&desc2, NULL, &textureBuffer);
 
const uint32_t texelSize = 16;  // size of DXGI_FORMAT_R32G32B32A32_FLOAT
 
D3D11_MAPPED_SUBRESOURCE mappedResource;
deviceContext->Map(textureBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
uint8_t* dstData = reinterpret_cast<uint8_t*>(mappedResource.pData);
const uint8_t* srcData = reinterpret_cast<const uint8_t*>(grid);
for(uint32_t i = 0; i < height; ++i)
{
    memcpy(dstData, srcData, width * texelSize);
    dstData += mappedResource.RowPitch;
    srcData += width * texelSize;
}
deviceContext->Unmap(textureBuffer, 0);

I am indeed looking to create a dynamic texture and update it frequently.

First thing's first, yeah sorry I realised after that the memcpy was completely wrong when I pasted it. That's something I fixed before anyone replied, but thanks for pointing it out in any case.

Secondly, I've tried the solution you're suggesting here with similar results. It actually crashes with your solution as well, also I have been able to get something of the image output to the screen but it is messed up and you can make out only part of the image. It also crashes every now and then as well. The only other thing I can think of being the problem is the actual array that's being passed in. Looking further into it (I didn't write the array part) it seems it's a struct made up of 4 floats. The floats make up an RGBA value for each pixel of the texture.

How is it crashing, and where? When you get the crash while running in the debugger, you should be getting some information on the unhandled exception that lead to the crash. You should also be able to break into the debugger, and find out the callstack of the thread that encountered the exception. These two things can potentially give you valuable information for tracking down the crash.

How is it crashing, and where? When you get the crash while running in the debugger, you should be getting some information on the unhandled exception that lead to the crash. You should also be able to break into the debugger, and find out the callstack of the thread that encountered the exception. These two things can potentially give you valuable information for tracking down the crash.

I've fixed the crashes now and am on the way to solving this problem. However, using the code below the image appears sheared so there is still a problem with the numbers somewhere in there and I can't seem to work out what it is.


	const uint32_t texelSize = 16;  // size of DXGI_FORMAT_R32G32B32A32_FLOAT


	D3D11_MAPPED_SUBRESOURCE mappedResource;
	dc->Map(writeToTex, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
	uint8_t* dstData = reinterpret_cast<uint8_t*>(mappedResource.pData);
	const uint8_t* srcData = reinterpret_cast<const uint8_t*>(grid[0]);
	for (uint32_t i = 0; i < height; ++i)
	{
		memcpy(dstData, srcData, width * texelSize);
		dstData += mappedResource.RowPitch;
		srcData += width * texelSize;
	}
	dc->Unmap(writeToTex, 0);

This topic is closed to new replies.

Advertisement