Why is chrominance lost when i copy DXGI_FORMAT_NV12 ID3D11Texture from a d3d11device to a d3d11on12device?

316 views Asked by At
D3D11_TEXTURE2D_DESC texture_desc = {0};
texture_desc.Width = 640;
texture_desc.Height = 480;
texture_desc.MipLevels = 1;
texture_desc.Format = DXGI_FORMAT_NV12;
texture_desc.SampleDesc.Count = 1;
texture_desc.ArraySize = 1;
texture_desc.Usage = D3D11_USAGE_DEFAULT;
texture_desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;

Microsoft::WRL::ComPtr<ID3D11Texture2D> temp_texture_for_my_device{nullptr};
my_device->CreateTexture2D(&texture_desc, NULL, &temp_texture_for_my_device);

Microsoft::WRL::ComPtr<IDXGIResource> dxgi_resource{nullptr};
temp_texture_for_my_device.As(&dxgi_resource);
HANDLE shared_handle = NULL;
dxgi_resource->GetSharedHandle(&shared_handle);
dxgi_resource->Release();

Microsoft::WRL::ComPtr<ID3D11Texture2D> temp_texture_for_ffmpeg_device {nullptr};
ffmpeg_device->OpenSharedResource(shared_handle, __uuidof(ID3D11Texture2D), (void**)temp_texture_for_ffmpeg_device.GetAddressOf());
ffmpeg_device_context->CopySubresourceRegion(temp_texture_for_ffmpeg_device.Get(), 0, 0, 0, 0, (ID3D11Texture2D*)ffmpeg_avframe->data[0], (int)ffmpeg_avframe->data[1], NULL);
ffmpeg_device_context->Flush();

I copy temp_texture_for_ffmpeg_device to a D3D11_USAGE_STAGING, it's normal, but when i copy temp_texture_for_my_device to a D3D11_USAGE_STAGING, i lost the chrominance data.

When i map the texture to cpu via D3D11_USAGE_STAGING:

temp_texture_for_ffmpeg_device : RowPitch is 768, DepthPitch is 768 * 720; temp_texture_for_my_device : RowPitch is 1024, DepthPitch is 1024 * 480;

I think there are some different parameters between the two devices(or device context?), but I don't know what parameters would cause such a difference

my_device and my_device_context are created by D3D11On12CreateDevice

1

There are 1 answers

1
Chuck Walbourn On

The DirectX Video formats are planar, meaning that each component is contiguous in memory rather than being interleaved like most formats. For DirectX 12, this is explicitly exposed in the layout information which you can obtain via D3D12GetFormatPlaneCount.

Here's a template that works with D3D12_SUBRESOURCE_DATA and D3D12_MEMCPY_DEST. Here the SlicePitch is set to the size of an individual plane.

    template<typename T, typename PT> void AdjustPlaneResource(
        _In_ DXGI_FORMAT fmt,
        _In_ size_t height,
        _In_ size_t slicePlane,
        _Inout_ T& res) noexcept
    {
        switch (static_cast<int>(fmt))
        {
        case DXGI_FORMAT_NV12:
        case DXGI_FORMAT_P010:
        case DXGI_FORMAT_P016:
            if (!slicePlane)
            {
                // Plane 0
                res.SlicePitch = res.RowPitch * static_cast<PT>(height);
            }
            else
            {
                // Plane 1
                res.pData = const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(res.pData) + res.RowPitch * PT(height));
                res.SlicePitch = res.RowPitch * static_cast<PT>((height + 1) >> 1);
            }
            break;

        case DXGI_FORMAT_NV11:
            if (!slicePlane)
            {
                // Plane 0
                res.SlicePitch = res.RowPitch * static_cast<PT>(height);
            }
            else
            {
                // Plane 1
                res.pData = const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(res.pData) + res.RowPitch * PT(height));
                res.RowPitch = (res.RowPitch >> 1);
                res.SlicePitch = res.RowPitch * static_cast<PT>(height);
            }
            break;
        }
    }

For DirectX 11, the extra planar information has to be assumed as it's not directly exposed by the API as such. You have to compute the extra space required for the additional plane(s). Here's a snippet from DirectXTex. In this case slice is the total size of all the planes in one 'slice' of the resource.

    case DXGI_FORMAT_R8G8_B8G8_UNORM:
    case DXGI_FORMAT_G8R8_G8B8_UNORM:
    case DXGI_FORMAT_YUY2:
        assert(IsPacked(fmt));
        pitch = ((uint64_t(width) + 1u) >> 1) * 4u;
        slice = pitch * uint64_t(height);
        break;

    case DXGI_FORMAT_Y210:
    case DXGI_FORMAT_Y216:
        assert(IsPacked(fmt));
        pitch = ((uint64_t(width) + 1u) >> 1) * 8u;
        slice = pitch * uint64_t(height);
        break;

    case DXGI_FORMAT_NV12:
    case DXGI_FORMAT_420_OPAQUE:
        assert(IsPlanar(fmt));
        pitch = ((uint64_t(width) + 1u) >> 1) * 2u;
        slice = pitch * (uint64_t(height) + ((uint64_t(height) + 1u) >> 1));
        break;

    case DXGI_FORMAT_P010:
    case DXGI_FORMAT_P016:
        assert(IsPlanar(fmt));
        pitch = ((uint64_t(width) + 1u) >> 1) * 4u;
        slice = pitch * (uint64_t(height) + ((uint64_t(height) + 1u) >> 1));
        break;

    case DXGI_FORMAT_NV11:
        assert(IsPlanar(fmt));
        pitch = ((uint64_t(width) + 3u) >> 2) * 4u;
        slice = pitch * uint64_t(height) * 2u;
        break;

    case DXGI_FORMAT_P208:
        assert(IsPlanar(fmt));
        pitch = ((uint64_t(width) + 1u) >> 1) * 2u;
        slice = pitch * uint64_t(height) * 2u;
        break;

    case DXGI_FORMAT_V208:
        assert(IsPlanar(fmt));
        pitch = uint64_t(width);
        slice = pitch * (uint64_t(height) + (((uint64_t(height) + 1u) >> 1) * 2u));
        break;

    case DXGI_FORMAT_V408:
        assert(IsPlanar(fmt));
        pitch = uint64_t(width);
        slice = pitch * (uint64_t(height) + (uint64_t(height >> 1) * 4u));
        break;