Ошибка обновления во время выполнения сжатых текстур DXT с Directx11

Контекст: я разрабатываю собственный плагин C++ Unity 5, который читает в DXT сжатые данные текстуры и загружает их в графический процессор для дальнейшего использования в Unity. Цель состоит в том, чтобы создать быстрый проигрыватель последовательностей изображений, обновляющий данные изображений на лету. Текстуры сжимаются с помощью автономного консольного приложения. Unity может работать с различными графическими движками, я стремлюсь к DirectX11 и OpenGL 3.3+.

Проблема: Код обновления текстуры среды выполнения DirectX через сопоставленный подресурс дает разные выходные данные для разных графических драйверов. Обновление текстуры с помощью такого сопоставленного ресурса означает отображение указателя на данные текстуры и запись данных из буфера RAM в буфер сопоставленного GPU. При этом разные драйверы ожидают разные параметры для значения шага строки при копировании байтов. У меня никогда не было проблем на нескольких графических процессорах Nvidia, на которых я тестировал, но AMD и Intel GPU, кажется, работают по-разному, и я получаю искаженный вывод, как показано ниже. Кроме того, я работаю с данными пикселей DXT1 (0,5 бит на пиксель) и данными DXT5 (1 бит на пиксел). Я не могу получить правильный параметр высоты тона для этих текстур 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.")
        }
    }

Следующий код обновления, который выполняется для каждого нового кадра, где-то содержит ошибку. Обратите внимание на закомментированную строку, содержащую метод 1, использующий простой memcpy без какого-либо заданного шага строки, который хорошо работает на драйверах NVIDIA.
Далее вы можете увидеть в методе 2, что я регистрирую различные значения шага строки. Для instace для кадра 1920x960 я получаю 1920 для шага буфера и 2048 для шага времени выполнения. Эта разница в 128 пикселей, вероятно, должна быть дополнена (как видно на приведенном ниже примере), но я не могу понять, как это сделать. Когда я просто использую mappedResource.RowPitch, не разделяя его на 4 (выполнено с помощью смещения по битам), происходит сбой 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 - лучший, но все же ошибочный вывод при использовании кода выше с mappedResource.RowPitch для AMD/INTEL (метод 2). Синие полосы обозначают зону ошибки и должны исчезнуть, чтобы все пиксели выровнялись и образовали одно изображение.

Спасибо за любые указатели! Лучший, Винсент

1 ответ

Решение

Шаг отображаемых строк данных в байтах, когда вы делите на четыре, это определенно проблема.

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

Это подсчет высоты в формате BC, который делится на 4.

Кроме того, формат BC1 составляет 8 байтов на блок 4x4, поэтому строка ниже 8 * и не 16 *, но пока вы правильно управляете ходом строк, d3d поймет, что вы просто тратите здесь половину памяти.

data.SysMemPitch = 16 * (player->getWidth() / 4);
Другие вопросы по тегам