无法从 Direct3D 11 中的内存正确初始化 2D 纹理
Failing to properly initialize a 2D texture from memory in Direct3D 11
我正在尝试在系统内存中生成一个表示 R8G8B8A8 纹理的简单数组,然后将该纹理传输到 GPU 内存。
首先,我分配一个数组并用所需的绿色数据填充它:
frame.width = 3;
frame.height = 1;
auto components = 4;
auto length = components * frame.width * frame.height;
frame.data = new uint8_t[length];
frame.data[0 + 0 * frame.width] = 0; frame.data[1 + 0 * frame.width] = 255; frame.data[2 + 0 * frame.width] = 0; frame.data[3 + 0 * frame.width] = 255;
frame.data[0 + 1 * frame.width] = 0; frame.data[1 + 1 * frame.width] = 255; frame.data[2 + 1 * frame.width] = 0; frame.data[3 + 1 * frame.width] = 255;
frame.data[0 + 2 * frame.width] = 0; frame.data[1 + 2 * frame.width] = 255; frame.data[2 + 2 * frame.width] = 0; frame.data[3 + 2 * frame.width] = 255;
然后,我创建纹理对象并将其设置为像素着色器资源:
D3D11_TEXTURE2D_DESC textureDescription;
textureDescription.Width = frame.width;
textureDescription.Height = frame.height;
textureDescription.MipLevels = textureDescription.ArraySize = 1;
textureDescription.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
textureDescription.SampleDesc.Count = 1;
textureDescription.SampleDesc.Quality = 0;
textureDescription.Usage = D3D11_USAGE_DYNAMIC;
textureDescription.BindFlags = D3D11_BIND_SHADER_RESOURCE;
textureDescription.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
textureDescription.MiscFlags = 0;
D3D11_SUBRESOURCE_DATA initialTextureData;
initialTextureData.pSysMem = frame.data;
initialTextureData.SysMemPitch = frame.width * components;
initialTextureData.SysMemSlicePitch = 0;
DX_CHECK(m_device->CreateTexture2D(&textureDescription, &initialTextureData, &m_texture));
DX_CHECK(m_device->CreateShaderResourceView(m_texture, NULL, &m_textureView));
m_context->PSSetShaderResources(0, 1, &m_textureView);
我的期望是 GPU 内存将包含 3x1 绿色纹理,并且每个纹素在 alpha 通道中将具有 1.0f。然而,情况并非如此,可以通过 Visual Studio 图形调试器检查加载的纹理对象来查看:
有人可以解释一下发生了什么吗?我该如何解决这个问题?
让我们看一下您的数组寻址方案(索引会随着您提供的维度而减少):
frame.data[0] = 0; frame.data[1] = 255; frame.data[2] = 0; frame.data[3] = 255;
frame.data[3] = 0; frame.data[4] = 255; frame.data[5] = 0; frame.data[6] = 255;
frame.data[6] = 0; frame.data[7] = 255; frame.data[8] = 0; frame.data[9] = 255;
重新排序,得到
data[ 0] = 0 B pixel 1
data[ 1] = 255 G pixel 1
data[ 2] = 0 R pixel 1
data[ 3] = 0 (overwritten) A pixel 1
data[ 4] = 255 pixel 2
data[ 5] = 0
data[ 6] = 0
data[ 7] = 255
data[ 8] = 0 pixel 3
data[ 9] = 255
data[10] = undefined
data[11] = undefined
如您所见,这正是您的调试器向您显示的数据。
所以你只需要修改你的寻址方案。正确的公式是:
index = component + x * components + y * pitch,
你用
定义了一个密集的包装
pitch = width * components
为了推导这个公式,你只需要考虑当你增加一个变量时你需要跳过多少个索引。例如。当您增加当前组件时,您只需要进一步进入一个条目(因为所有组件都彼此相邻)。另一方面,如果增加 y 坐标,则需要跳过与一行中一样多的条目(这称为间距,它是图像的宽度乘以密集包装的组件数).
我正在尝试在系统内存中生成一个表示 R8G8B8A8 纹理的简单数组,然后将该纹理传输到 GPU 内存。
首先,我分配一个数组并用所需的绿色数据填充它:
frame.width = 3;
frame.height = 1;
auto components = 4;
auto length = components * frame.width * frame.height;
frame.data = new uint8_t[length];
frame.data[0 + 0 * frame.width] = 0; frame.data[1 + 0 * frame.width] = 255; frame.data[2 + 0 * frame.width] = 0; frame.data[3 + 0 * frame.width] = 255;
frame.data[0 + 1 * frame.width] = 0; frame.data[1 + 1 * frame.width] = 255; frame.data[2 + 1 * frame.width] = 0; frame.data[3 + 1 * frame.width] = 255;
frame.data[0 + 2 * frame.width] = 0; frame.data[1 + 2 * frame.width] = 255; frame.data[2 + 2 * frame.width] = 0; frame.data[3 + 2 * frame.width] = 255;
然后,我创建纹理对象并将其设置为像素着色器资源:
D3D11_TEXTURE2D_DESC textureDescription;
textureDescription.Width = frame.width;
textureDescription.Height = frame.height;
textureDescription.MipLevels = textureDescription.ArraySize = 1;
textureDescription.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
textureDescription.SampleDesc.Count = 1;
textureDescription.SampleDesc.Quality = 0;
textureDescription.Usage = D3D11_USAGE_DYNAMIC;
textureDescription.BindFlags = D3D11_BIND_SHADER_RESOURCE;
textureDescription.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
textureDescription.MiscFlags = 0;
D3D11_SUBRESOURCE_DATA initialTextureData;
initialTextureData.pSysMem = frame.data;
initialTextureData.SysMemPitch = frame.width * components;
initialTextureData.SysMemSlicePitch = 0;
DX_CHECK(m_device->CreateTexture2D(&textureDescription, &initialTextureData, &m_texture));
DX_CHECK(m_device->CreateShaderResourceView(m_texture, NULL, &m_textureView));
m_context->PSSetShaderResources(0, 1, &m_textureView);
我的期望是 GPU 内存将包含 3x1 绿色纹理,并且每个纹素在 alpha 通道中将具有 1.0f。然而,情况并非如此,可以通过 Visual Studio 图形调试器检查加载的纹理对象来查看:
有人可以解释一下发生了什么吗?我该如何解决这个问题?
让我们看一下您的数组寻址方案(索引会随着您提供的维度而减少):
frame.data[0] = 0; frame.data[1] = 255; frame.data[2] = 0; frame.data[3] = 255;
frame.data[3] = 0; frame.data[4] = 255; frame.data[5] = 0; frame.data[6] = 255;
frame.data[6] = 0; frame.data[7] = 255; frame.data[8] = 0; frame.data[9] = 255;
重新排序,得到
data[ 0] = 0 B pixel 1
data[ 1] = 255 G pixel 1
data[ 2] = 0 R pixel 1
data[ 3] = 0 (overwritten) A pixel 1
data[ 4] = 255 pixel 2
data[ 5] = 0
data[ 6] = 0
data[ 7] = 255
data[ 8] = 0 pixel 3
data[ 9] = 255
data[10] = undefined
data[11] = undefined
如您所见,这正是您的调试器向您显示的数据。
所以你只需要修改你的寻址方案。正确的公式是:
index = component + x * components + y * pitch,
你用
定义了一个密集的包装pitch = width * components
为了推导这个公式,你只需要考虑当你增加一个变量时你需要跳过多少个索引。例如。当您增加当前组件时,您只需要进一步进入一个条目(因为所有组件都彼此相邻)。另一方面,如果增加 y 坐标,则需要跳过与一行中一样多的条目(这称为间距,它是图像的宽度乘以密集包装的组件数).