DirectX 11 渲染 BGRA32 帧

DirectX 11 render BGRA32 Frame

第一次尝试渲染某些东西时遇到了很大的麻烦...我正在使用 KlearTouch.MediaPlayer 中的 DirectN library and SwapChainSurface class。我正在尝试使用 D3D11Device 渲染 BGRA32 帧。

为此我稍微修改了 OnNewSurfaceAvailable:

public void OnNewSurfaceAvailable2(Action<ID3D11Device, ID3D11DeviceContext> updateSurface)
{
    if (rendering)
    {
        return;
    }

    try
    {
        if (this.swapChain is null || swapChainComObject is null)
        {
            return;
        }

        swapChainComObject.GetDesc(out var swapChainDesc).ThrowOnError();

        if (swapChainDesc.BufferDesc.Width != PanelWidth || swapChainDesc.BufferDesc.Height != PanelHeight)
        {
            swapChainComObject.ResizeBuffers(2, PanelWidth, PanelHeight, DXGI_FORMAT.DXGI_FORMAT_UNKNOWN, 0).ThrowOnError();
        }

        var device = swapChain.Object.GetDevice1().Object.As<ID3D11Device>();

        device.GetImmediateContext(out var context);

        // context.ClearRenderTargetView(renderTargetView.Object, new []{0f, 1f, 1f, 1f});

        updateSurface(device, context);

        swapChainComObject.Present(1, 0).ThrowOnError();
    }
    catch (ObjectDisposedException)
    {
        Reinitialize();
    }
    catch (Exception ex)
    {
        System.Diagnostics.Debug.WriteLine("\nException: " + ex, nameof(SwapChainSurface) + '.' + nameof(OnNewSurfaceAvailable));
    }

    rendering = false;
}

OnSurfaceAvailable2 调用自:

void VideoFrameArrived(Bgra32VideoFrame frame)
    {
        DispatcherQueue.TryEnqueue(() =>
        {
            previewSurface.OnNewSurfaceAvailable2((device, context) =>
            {
                var size = frame.m_height * frame.m_height * 4;

                D3D11_TEXTURE2D_DESC td;
                td.ArraySize = 1;
                td.BindFlags = (uint) D3D11_BIND_FLAG.D3D11_BIND_SHADER_RESOURCE;
                td.Usage = D3D11_USAGE.D3D11_USAGE_DYNAMIC;
                td.CPUAccessFlags = (uint) D3D11_CPU_ACCESS_FLAG.D3D11_CPU_ACCESS_WRITE;
                td.Format = DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM;
                td.Height = (uint) frame.m_height;
                td.Width = (uint) frame.m_width;
                td.MipLevels = 1;
                td.MiscFlags = 0;
                td.SampleDesc.Count = 1;
                td.SampleDesc.Quality = 0;

                D3D11_SUBRESOURCE_DATA srd;
                srd.pSysMem = frame.m_pixelBuffer;
                srd.SysMemPitch = (uint) frame.m_height;
                srd.SysMemSlicePitch = 0;

                var texture = device.CreateTexture2D<ID3D11Texture2D>(td, new []{srd});

                var mappedResource = context.Map(texture.Object, 0, D3D11_MAP.D3D11_MAP_WRITE_DISCARD);

                var mappedData = mappedResource.pData;
                

                unsafe
                {
                    Buffer.MemoryCopy(frame.m_pixelBuffer.ToPointer(), mappedData.ToPointer(), size, size);
                }

                // Just for debug
                var pixelsInFrame = new byte[size];
                var pixelsInResource = new byte[size];

                Marshal.Copy(frame.m_pixelBuffer, pixelsInFrame, 0, size);
                Marshal.Copy(mappedResource.pData, pixelsInResource, 0, size);
                
                context.Unmap(texture.Object, 0);
            });
        });
    }

问题是我看不到任何渲染的东西并且表面保持黑色,我认为它不应该是。

更新:Project repository

更新 2:

我解决了我的问题。我对 DX11 的了解太少,所以我不得不更多地研究那里的工作原理。有了这些知识,我更新了可以显示黑魔法设计卡预览的存储库。这只是许多问题的示例,因此请小心并随时在那里寻找或启发。

这里有各种各样的问题。

第一帧到达,你有

var texture = device.CreateTexture2D<ID3D11Texture2D>(td, new []{srd});

所以你创建了一个纹理,但你没有在任何地方使用它,它需要被 blit 到交换链(可以在设备上下文上做一个 CopyResource 或绘制一个全屏 triangle/quad)。

请注意,只有当您的交换链与传入纹理的大小相同时,CopyResource 才会起作用,这是不太可能的,因此您很可能必须使用着色器绘制 blit。

而且你实际上复制了两次纹理中的数据:

 var texture = device.CreateTexture2D<ID3D11Texture2D>(td, new []{srd});

由于您提供了初始数据,因此内容已经存在。

此外,音调不正确: srd.SysMemPitch = (uint) frame.m_height;

pitch 是一行的长度(以字节为单位),所以它应该是:

srd.SysMemPitch = frame.GetRowBytes();

另请注意,如果是未转换的 Decklink 框架, GetRowBytes 可以与 width*4 不同(它们可以将行大小对齐为 16/32 的倍数或其他值)。

接下来,在资源映射的情况下,以下也是不正确的:

unsafe
{
    Buffer.MemoryCopy(frame.m_pixelBuffer.ToPointer(), mappedData.ToPointer(), size, size);
}

您没有检查纹理的 pitch/stride 要求(也可能不同),

所以你需要做:

if (mappedResource.RowPitch == frame.GetRowBytes())
{
    //here you can use a direct copy as above
}
else
{
   //here you need to copy data line per line
}