コンテキスト: DXT 圧縮テクスチャ データを読み取り、Unity でさらに使用するために GPU にアップロードするネイティブ C++ Unity 5 プラグインを開発しています。目的は、オンザフライで画像データを更新する高速な画像シーケンス プレーヤーを作成することです。テクスチャは、オフライン コンソール アプリケーションで圧縮されます。Unity はさまざまなグラフィック エンジンで動作します。私は DirectX11 と OpenGL 3.3+ を目指しています。
問題: DirectX ランタイム テクスチャ更新コードは、マップされたサブリソースを介して、さまざまなグラフィックス ドライバーでさまざまな出力を提供します。このようなマップされたリソースを介してテクスチャを更新することは、ポインタをテクスチャ データにマップし、RAM バッファからマップされた GPU バッファにデータを memcpy することを意味します。そうすることで、ドライバーが異なれば、バイトをコピーするときに行ピッチ値に異なるパラメーターを期待しているように見えます。テストしたいくつかの Nvidia GPU で問題が発生したことはありませんが、AMD と Intel GPU は動作が異なるようで、下に示すように歪んだ出力が得られます。さらに、DXT1 ピクセル データ (0.5bpp) と DXT5 データ (1bpp) を使用しています。これらの DXT テクスチャの正しいピッチ パラメータを取得できないようです。
コード: d3d11 テクスチャを生成し、初期テクスチャ データ (イメージ シーケンスの最初のフレームなど) で埋めるための次の初期化コードは、すべてのドライバで完璧に機能します。プレーヤー ポインターは、すべてのファイルの読み取りを処理し、現在読み込まれている DXT 圧縮フレーム、そのサイズなどのゲッターを含むカスタム クラスを指します。
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.")
}
}
新しいフレームごとに実行される次の更新コードには、どこかにエラーがあります。NVIDIA ドライバーでうまく機能する行ピッチを指定せずに単純な memcpy を使用する方法 1 を含むコメント行に注意してください。
方法 2 で、さまざまな行ピッチ値をログに記録していることがわかります。たとえば、1920x960 フレームの場合、バッファ ストライドは 1920、ランタイム ストライドは 2048 になります。この 128 ピクセルの差はおそらくパディングする必要があります (下の例の写真に見られるように) が、その方法がわかりません。4 で割らずに(ビットシフトで)mappedResource.RowPitch を使用すると、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);
}
例 pic 1 - AMD/INTEL で個別の行ピッチを使用せずに memcpy を使用してバッファ全体をコピーすると、出力が歪む (方法 1)
例の写真 2 - 上記のコードを AMD/INTEL で mappingResource.RowPitch と一緒に使用した場合 (方法 2)、より良いが、依然として誤った出力。青いバーはエラーのゾーンを示しており、すべてのピクセルが適切に整列して 1 つの画像を形成するように非表示にする必要があります。
ご指摘ありがとうございます。ベスト、ヴィンセント