0

コンテキスト: 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 つの画像を形成するように非表示にする必要があります。

ここに画像の説明を入力

ご指摘ありがとうございます。ベスト、ヴィンセント

4

1 に答える 1

0

マップされたデータ行のピッチはバイト単位です。4 で割ると、間違いなく問題になります。

UINT runtime_stride = mappedResource.RowPitch >> 2;
...
mappedData += runtime_stride; // here you are only jumping one quarter of a row

4 で割った BC 形式の高さカウントです。

また、BC1 フォーマットは 4x4 ブロックごとに 8 バイトであるため、以下の8 *16 *は .

data.SysMemPitch = 16 * (player->getWidth() / 4);
于 2016-06-19T04:32:19.827 に答える