上下文 我正在开发一个本机C ++ Unity 5插件,它读入DXT压缩纹理数据并将其上传到GPU以便在Unity中进一步使用。目的是创建一个快速的图像序列播放器,即时更新图像数据。使用脱机控制台应用程序压缩纹理。 Unity可以使用不同的图形引擎,我的目标是DirectX11和OpenGL 3.3+。
问题: DirectX运行时纹理更新代码通过映射的子资源在不同的图形驱动程序上提供不同的输出。通过这样的映射资源更新纹理意味着将指针映射到纹理数据并将数据从RAM缓冲区存储到映射的GPU缓冲区。这样做时,不同的驱动程序似乎期望复制字节时行间距值的参数不同。我在测试的几个Nvidia GPU上从来没有遇到任何问题,但是AMD和Intel GPU看起来行为不同,我得到了扭曲的输出,如下图所示。此外,我正在处理DXT1像素数据(0.5bpp)和DXT5数据(1bpp)。 我似乎无法为这些DXT纹理获得正确的音高参数。
代码: 下面的初始化代码用于生成d3d11纹理并用初始纹理数据填充它 - 例如图像序列的第一帧 - 适用于所有驱动程序。 播放器指针指向一个自定义类,它处理所有文件读取并包含当前加载的DXT压缩帧的getter,它的维度等...
if (s_DeviceType == kUnityGfxRendererD3D11)
{
HRESULT hr;
DXGI_FORMAT format = (compression_type == DxtCompressionType::DXT_TYPE_DXT1_NO_ALPHA) ? DXGI_FORMAT_BC1_UNORM : DXGI_FORMAT_BC3_UNORM;
// Create texture
D3D11_TEXTURE2D_DESC desc;
desc.Width = w;
desc.Height = h;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = format;
// no anti-aliasing
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.Usage = D3D11_USAGE_DYNAMIC;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
desc.MiscFlags = 0;
// Initial data: first frame
D3D11_SUBRESOURCE_DATA data;
data.pSysMem = player->getBufferPtr();
data.SysMemPitch = 16 * (player->getWidth() / 4);
data.SysMemSlicePitch = 0; // just a 2d texture, no depth
// Init with initial data
hr = g_D3D11Device->CreateTexture2D(&desc, &data, &dxt_d3d_tex);
if (SUCCEEDED(hr) && dxt_d3d_tex != 0)
{
DXT_VERBOSE("Succesfully created D3D Texture.");
DXT_VERBOSE("Creating D3D SRV.");
D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc;
memset(&SRVDesc, 0, sizeof(SRVDesc));
SRVDesc.Format = format;
SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
SRVDesc.Texture2D.MipLevels = 1;
hr = g_D3D11Device->CreateShaderResourceView(dxt_d3d_tex, &SRVDesc, &textureView);
if (FAILED(hr))
{
dxt_d3d_tex->Release();
return hr;
}
DXT_VERBOSE("Succesfully created D3D SRV.");
}
else
{
DXT_ERROR("Error creating D3D texture.")
}
}
为每个新帧运行的以下更新代码在某处出现错误。请注意包含方法1的注释行使用简单的memcpy而没有指定任何rowpitch,这对NVIDIA驱动程序很有效。
您可以在方法2中进一步查看我记录的不同行间距值。对于1920x960帧的实例,我得到缓冲区步幅为1920,运行时步长为2048。这个128像素的差异可能需要填充(如下面的示例图片中所示),但我无法弄清楚如何。当我只使用mappedResource.RowPitch而不将它除以4(由bitshift完成)时,Unity会崩溃。
ID3D11DeviceContext* ctx = NULL;
g_D3D11Device->GetImmediateContext(&ctx);
if (dxt_d3d_tex && bShouldUpload)
{
if (player->gather_stats) before_upload = ns();
D3D11_MAPPED_SUBRESOURCE mappedResource;
ctx->Map(dxt_d3d_tex, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
/* 1: THIS CODE WORKS ON ALL NVIDIA DRIVERS BUT GENERATES DISTORTED OR NO OUTPUT ON AMD/INTEL: */
//memcpy(mappedResource.pData, player->getBufferPtr(), player->getBytesPerFrame());
/* 2: THIS CODE GENERATES OUTPUT BUT SEEMS TO NEED PADDING? */
BYTE* mappedData = reinterpret_cast<BYTE*>(mappedResource.pData);
BYTE* buffer = player->getBufferPtr();
UINT height = player->getHeight();
UINT buffer_stride = player->getBytesPerFrame() / player->getHeight();
UINT runtime_stride = mappedResource.RowPitch >> 2;
DXT_VERBOSE("Buffer stride: %d", buffer_stride);
DXT_VERBOSE("Runtime stride: %d", runtime_stride);
for (UINT i = 0; i < height; ++i)
{
memcpy(mappedData, buffer, buffer_stride);
mappedData += runtime_stride;
buffer += buffer_stride;
}
ctx->Unmap(dxt_d3d_tex, 0);
}
示例图1 - 使用memcpy复制整个缓冲区而不使用AMD / INTEL上的单独行间距(方法1)时的失真输出
示例图2 - 在AMD / INTEL上使用带有mappedResource.RowPitch的上述代码时,输出更好但仍然是错误的(方法2)。蓝色条表示错误区域,需要消失,以便所有像素对齐良好并形成一个图像。
感谢您的任何指示! 最好, 文森特
答案 0 :(得分:0)
映射数据行间距以字节为单位,当除以4时,肯定是个问题。
UINT runtime_stride = mappedResource.RowPitch >> 2;
...
mappedData += runtime_stride; // here you are only jumping one quarter of a row
BC格式的高度计数除以4.
另外BC1格式是每4x4块8字节,所以下面的行应该是8 *
而不是16 *
,但只要你正确地处理行间距,d3d就会理解,你只是浪费了一半的记忆。
data.SysMemPitch = 16 * (player->getWidth() / 4);