使用DirectX11像素着色器在GPU中从DXGI_格式_B8G8R8A8_UNORM到NV12的颜色转换

使用DirectX11像素着色器在GPU中从DXGI_格式_B8G8R8A8_UNORM到NV12的颜色转换,directx,directx-11,yuv,ms-media-foundation,pixel-shader,Directx,Directx 11,Yuv,Ms Media Foundation,Pixel Shader,我正在编写一段代码,使用桌面复制捕获桌面,并使用Intel hardwareMFT将其编码为h264。编码器仅接受NV12格式作为输入。我有一个DXGI_格式的B8G8R8A8_UNORM-to-NV12转换器(),它工作正常,基于DirectX视频处理器 问题是,某些英特尔图形硬件上的视频处理器仅支持从DXGI_格式_B8G8R8A8_UNORM到YUY2的转换,而不支持NV12。我通过GetVideoProcessor OutputFormats枚举支持的格式来确认这一点。虽然视频处理器Bl

我正在编写一段代码,使用桌面复制捕获桌面,并使用Intel hardwareMFT将其编码为h264。编码器仅接受NV12格式作为输入。我有一个DXGI_格式的B8G8R8A8_UNORM-to-NV12转换器(),它工作正常,基于DirectX视频处理器

问题是,某些英特尔图形硬件上的视频处理器仅支持从DXGI_格式_B8G8R8A8_UNORM到YUY2的转换,而不支持NV12。我通过GetVideoProcessor OutputFormats枚举支持的格式来确认这一点。虽然视频处理器Blt成功了,没有任何错误,而且我可以看到输出视频中的帧有点像素化,但如果我仔细观察,我会注意到它

我猜,视频处理器已经简单地故障切换到下一个支持的输出格式(YUY2),我正在不知不觉地将其输入到编码器,该编码器认为输入是在NV12中配置的。由于NV12和YUY2之间的字节顺序和子采样等差异很小,因此不会出现故障或严重的帧损坏。另外,我在支持NV12转换的硬件上没有像素化问题

因此,我决定使用基于此代码()的像素着色器进行颜色转换。我可以使像素着色器工作,我还上传了我的代码here(),以供参考(尽可能简化它)

现在,我剩下两个通道,分别是色度和亮度 (ID3D11Texture2D纹理)。我真的很困惑 将两个单独的通道打包为一个ID3D11Texture2D纹理,以便 我可以把它输入编码器。有没有一种有效的方法 将Y和UV通道打包到GPU中的单个ID3D11Texture2D中?我是 真的厌倦了基于CPU的方法,因为它的成本很高, 并且没有提供尽可能好的帧速率。事实上,我 甚至不愿意将纹理复制到CPU。我在想一个办法 在GPU中进行,CPU和GPU之间没有任何来回拷贝

我一直在研究这个很长一段时间没有任何进展,任何帮助将不胜感激

/**
* This method is incomplete. It's just a template of what I want to achieve.
*/

HRESULT CreateNV12TextureFromLumaAndChromaSurface(ID3D11Texture2D** pOutputTexture)
{
    HRESULT hr = S_OK;

    try
    {
        //Copying from GPU to CPU. Bad :(
        m_pD3D11DeviceContext->CopyResource(m_CPUAccessibleLuminanceSurf, m_LuminanceSurf);

        D3D11_MAPPED_SUBRESOURCE resource;
        UINT subresource = D3D11CalcSubresource(0, 0, 0);

        HRESULT hr = m_pD3D11DeviceContext->Map(m_CPUAccessibleLuminanceSurf, subresource, D3D11_MAP_READ, 0, &resource);

        BYTE* sptr = reinterpret_cast<BYTE*>(resource.pData);
        BYTE* dptrY = nullptr; // point to the address of Y channel in output surface

        //Store Image Pitch
        int m_ImagePitch = resource.RowPitch;

        int height = GetImageHeight();
        int width = GetImageWidth();

        for (int i = 0; i < height; i++)
        {
            memcpy_s(dptrY, m_ImagePitch, sptr, m_ImagePitch);

            sptr += m_ImagePitch;
            dptrY += m_ImagePitch;
        }

        m_pD3D11DeviceContext->Unmap(m_CPUAccessibleLuminanceSurf, subresource);

        //Copying from GPU to CPU. Bad :(
        m_pD3D11DeviceContext->CopyResource(m_CPUAccessibleChrominanceSurf, m_ChrominanceSurf);
        hr = m_pD3D11DeviceContext->Map(m_CPUAccessibleChrominanceSurf, subresource, D3D11_MAP_READ, 0, &resource);

        sptr = reinterpret_cast<BYTE*>(resource.pData);
        BYTE* dptrUV = nullptr; // point to the address of UV channel in output surface

        m_ImagePitch = resource.RowPitch;
        height /= 2;
        width /= 2;

        for (int i = 0; i < height; i++)
        {
            memcpy_s(dptrUV, m_ImagePitch, sptr, m_ImagePitch);

            sptr += m_ImagePitch;
            dptrUV += m_ImagePitch;
        }

        m_pD3D11DeviceContext->Unmap(m_CPUAccessibleChrominanceSurf, subresource);
    }
    catch(HRESULT){}

    return hr;
}
初始化着色器:

void SetViewPort(D3D11_VIEWPORT* VP, UINT Width, UINT Height)
{
    VP->Width = static_cast<FLOAT>(Width);
    VP->Height = static_cast<FLOAT>(Height);
    VP->MinDepth = 0.0f;
    VP->MaxDepth = 1.0f;
    VP->TopLeftX = 0;
    VP->TopLeftY = 0;
}

HRESULT MakeRTV(ID3D11RenderTargetView** pRTV, ID3D11Texture2D* pSurf)
{
    if (*pRTV)
    {
        (*pRTV)->Release();
        *pRTV = nullptr;
    }
    // Create a render target view
    HRESULT hr = m_pD3D11Device->CreateRenderTargetView(pSurf, nullptr, pRTV);

    IF_FAILED_THROW(hr);

    return S_OK;
}

HRESULT InitializeNV12Surfaces(ID3D11Texture2D* inputTexture)
{
    ReleaseSurfaces();

    D3D11_TEXTURE2D_DESC lOutputDuplDesc;
    inputTexture->GetDesc(&lOutputDuplDesc);


    // Create shared texture for all duplication threads to draw into
    D3D11_TEXTURE2D_DESC DeskTexD;
    RtlZeroMemory(&DeskTexD, sizeof(D3D11_TEXTURE2D_DESC));
    DeskTexD.Width = lOutputDuplDesc.Width;
    DeskTexD.Height = lOutputDuplDesc.Height;
    DeskTexD.MipLevels = 1;
    DeskTexD.ArraySize = 1;
    DeskTexD.Format = lOutputDuplDesc.Format;
    DeskTexD.SampleDesc.Count = 1;
    DeskTexD.Usage = D3D11_USAGE_DEFAULT;
    DeskTexD.BindFlags = D3D11_BIND_SHADER_RESOURCE;

    HRESULT hr = m_pD3D11Device->CreateTexture2D(&DeskTexD, nullptr, &m_ShaderResourceSurf);
    IF_FAILED_THROW(hr);

    DeskTexD.Format = DXGI_FORMAT_R8_UNORM;
    DeskTexD.BindFlags = D3D11_BIND_RENDER_TARGET;

    hr = m_pD3D11Device->CreateTexture2D(&DeskTexD, nullptr, &m_LuminanceSurf);
    IF_FAILED_THROW(hr);

    DeskTexD.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
    DeskTexD.Usage = D3D11_USAGE_STAGING;
    DeskTexD.BindFlags = 0;

    hr = m_pD3D11Device->CreateTexture2D(&DeskTexD, NULL, &m_CPUAccessibleLuminanceSurf);
    IF_FAILED_THROW(hr);

    SetViewPort(&m_VPLuminance, DeskTexD.Width, DeskTexD.Height);

    HRESULT Ret = MakeRTV(&m_pLumaRT, m_LuminanceSurf);
    if (!SUCCEEDED(Ret))
        return Ret;

    DeskTexD.Width = lOutputDuplDesc.Width / 2;
    DeskTexD.Height = lOutputDuplDesc.Height / 2;
    DeskTexD.Format = DXGI_FORMAT_R8G8_UNORM;

    DeskTexD.Usage = D3D11_USAGE_DEFAULT;
    DeskTexD.CPUAccessFlags = 0;
    DeskTexD.BindFlags = D3D11_BIND_RENDER_TARGET;

    hr = m_pD3D11Device->CreateTexture2D(&DeskTexD, nullptr, &m_ChrominanceSurf);
    IF_FAILED_THROW(hr);

    DeskTexD.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
    DeskTexD.Usage = D3D11_USAGE_STAGING;
    DeskTexD.BindFlags = 0;

    hr = m_pD3D11Device->CreateTexture2D(&DeskTexD, NULL, &m_CPUAccessibleChrominanceSurf);
    IF_FAILED_THROW(hr);

    SetViewPort(&m_VPChrominance, DeskTexD.Width, DeskTexD.Height);
    return MakeRTV(&m_pChromaRT, m_ChrominanceSurf);
}

HRESULT InitVertexShader(ID3D11VertexShader** ppID3D11VertexShader)
{
    HRESULT hr = S_OK;
    UINT Size = ARRAYSIZE(g_VS);

    try
    {
        IF_FAILED_THROW(m_pD3D11Device->CreateVertexShader(g_VS, Size, NULL, ppID3D11VertexShader));;

        m_pD3D11DeviceContext->VSSetShader(m_pVertexShader, nullptr, 0);

        // Vertices for drawing whole texture
        VERTEX Vertices[NUMVERTICES] =
        {
            { XMFLOAT3(-1.0f, -1.0f, 0), XMFLOAT2(0.0f, 1.0f) },
            { XMFLOAT3(-1.0f, 1.0f, 0), XMFLOAT2(0.0f, 0.0f) },
            { XMFLOAT3(1.0f, -1.0f, 0), XMFLOAT2(1.0f, 1.0f) },
            { XMFLOAT3(1.0f, -1.0f, 0), XMFLOAT2(1.0f, 1.0f) },
            { XMFLOAT3(-1.0f, 1.0f, 0), XMFLOAT2(0.0f, 0.0f) },
            { XMFLOAT3(1.0f, 1.0f, 0), XMFLOAT2(1.0f, 0.0f) },
        };

        UINT Stride = sizeof(VERTEX);
        UINT Offset = 0;

        D3D11_BUFFER_DESC BufferDesc;
        RtlZeroMemory(&BufferDesc, sizeof(BufferDesc));
        BufferDesc.Usage = D3D11_USAGE_DEFAULT;
        BufferDesc.ByteWidth = sizeof(VERTEX) * NUMVERTICES;
        BufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
        BufferDesc.CPUAccessFlags = 0;
        D3D11_SUBRESOURCE_DATA InitData;
        RtlZeroMemory(&InitData, sizeof(InitData));
        InitData.pSysMem = Vertices;

        // Create vertex buffer
        IF_FAILED_THROW(m_pD3D11Device->CreateBuffer(&BufferDesc, &InitData, &m_VertexBuffer));

        m_pD3D11DeviceContext->IASetVertexBuffers(0, 1, &m_VertexBuffer, &Stride, &Offset);
        m_pD3D11DeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

        D3D11_INPUT_ELEMENT_DESC Layout[] =
        {
            { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
            { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }
        };

        UINT NumElements = ARRAYSIZE(Layout);
        hr = m_pD3D11Device->CreateInputLayout(Layout, NumElements, g_VS, Size, &m_pVertexLayout);

        m_pD3D11DeviceContext->IASetInputLayout(m_pVertexLayout);
    }
    catch (HRESULT) {}

    return hr;
}

HRESULT InitPixelShaders()
{
    HRESULT hr = S_OK;
    // Refer https://codeshare.io/5PJjxP for g_PS_Y & g_PS_UV blobs
    try
    {
        UINT Size = ARRAYSIZE(g_PS_Y);
        hr = m_pD3D11Device->CreatePixelShader(g_PS_Y, Size, nullptr, &m_pPixelShaderChroma);

        IF_FAILED_THROW(hr);

        Size = ARRAYSIZE(g_PS_UV);
        hr = m_pD3D11Device->CreatePixelShader(g_PS_UV, Size, nullptr, &m_pPixelShaderLuma);

        IF_FAILED_THROW(hr);
    }
    catch (HRESULT) {}

    return hr;
}
void SetViewPort(D3D11_VIEWPORT*VP、UINT宽度、UINT高度)
{
VP->宽度=静态施法(宽度);
VP->高度=静态施法(高度);
VP->MinDepth=0.0f;
VP->MaxDepth=1.0f;
VP->TopLeftX=0;
VP->TopLeftY=0;
}
HRESULT MakeRTV(ID3D11RenderTargetView**pRTV,ID3D11Texture2D*pSurf)
{
如果(*pRTV)
{
(*pRTV)->发布();
*pRTV=nullptr;
}
//创建渲染目标视图
HRESULT hr=m_pD3D11Device->CreateRenderTargetView(pSurf、nullptr、pRTV);
如果抛出失败(hr);
返回S_OK;
}
HRESULT初始化ENV12曲面(ID3D11Texture2D*inputTexture)
{
释放表面();
D3D11_纹理2 D_描述百叶窗出口;
inputTexture->GetDesc(&loutputduldesc);
//为所有要绘制的复制线程创建共享纹理
D3D11_TEXTURE2D_DESC DeskTexD;
RtlZeroMemory(&DeskTexD,sizeof(D3D11_TEXTURE2D_DESC));
DeskTexD.Width=loutputduldesc.Width;
DeskTexD.Height=lOutputDuplDesc.Height;
DeskTexD.MipLevels=1;
DeskTexD.ArraySize=1;
DeskTexD.Format=loutputduldesc.Format;
DeskTexD.SampleDesc.Count=1;
DeskTexD.Usage=D3D11\u Usage\u默认值;
DeskTexD.BindFlags=D3D11\u BIND\u SHADER\u资源;
HRESULT hr=m_pD3D11Device->CreateTexture2D(&DeskTexD、nullptr和m_ShaderResourceSurf);
如果抛出失败(hr);
DeskTexD.Format=DXGI\u Format\u R8\u UNORM;
DeskTexD.BindFlags=D3D11_BIND_RENDER_TARGET;
hr=m_pD3D11Device->CreateTexture2D(&DeskTexD、nullptr和m_LuminanceSurf);
如果抛出失败(hr);
DeskTexD.CPUAccessFlags=D3D11\u CPU\u访问\u读取;
DeskTexD.Usage=D3D11_Usage_STAGING;
DeskTexD.BindFlags=0;
hr=m_pD3D11Device->CreateTexture2D(&DeskTexD,NULL,&m_CPUAccessibleLuminanceSurf);
如果抛出失败(hr);
SetViewPort(亮度、DeskTexD.Width、DeskTexD.Height和亮度);
HRESULT Ret=MakeRTV(&m_pLumaRT,m_LuminanceSurf);
如果(!成功(Ret))
返回Ret;
DeskTexD.Width=lOutputDuplDesc.Width/2;
DeskTexD.Height=lOutputDuplDesc.Height/2;
DeskTexD.Format=DXGI_Format_R8G8_UNORM;
DeskTexD.Usage=D3D11\u Usage\u默认值;
DeskTexD.CPUAccessFlags=0;
DeskTexD.BindFlags=D3D11_BIND_RENDER_TARGET;
hr=m_pD3D11Device->CreateTexture2D(&DeskTexD、nullptr和m_ChrominanceSurf);
如果抛出失败(hr);
DeskTexD.CPUAccessFlags=D3D11\u CPU\u访问\u读取;
DeskTexD.Usage=D3D11_Usage_STAGING;
DeskTexD.BindFlags=0;
hr=m_pD3D11Device->CreateTexture2D(&DeskTexD,NULL,&m_CPUAccessibleChrominanceSurf);
如果抛出失败(hr);
SetViewPort(&m_VP色度、DeskTexD.宽度、DeskTexD.高度);
返回MakeRTV(&m_pchromeart,m_ChrominanceSurf);
}
HRESULT InitVertexShader(ID3D11VertexShader**ppID3D11VertexShader)
{
HRESULT hr=S_正常;
单元大小=阵列化(g\U VS);
尝试
{
如果抛出失败(m_pD3D11Device->CreateVertexShader(g_VS,Size,NULL,ppID3D11VertexShader));;
m_pD3D11DeviceContext->VSSetShader(m_pVertexShader,nullptr,0);
//用于绘制整个纹理的顶点
顶点[数值]=
{
{XMFLOAT3(-1.0f,-1.0f,0),XMFLOAT2(0.0f,1.0f)},
{XMFLOAT3(-1.0f,1.0f,0),XMFLOAT2(0.0f,0.0f)},
{XMFLOAT3(1.0f,-1.0f,0),XMFLOAT2(1.0f,1.0f)},
{XMFLOAT3(1.0f,-1.0f,0),XMFLOAT2(1.0f,1.0f)},
{XMFLOAT3(-1.0f,1.0f,0),XMFLOAT2(0.0f,0.0f)},
{XMFLOAT3(1.0f,1.0f,0),XMFLOAT2(1.0f,0.0f)},
};
UINT步幅=尺寸(顶点);
UINT偏移=0;
D3D11_BUFFER_DESC BufferDesc;
RtlZeroMemory(&BufferDesc,sizeof(BufferDesc));
BufferDesc.Usage=D3D11_Usage_默认值;
BufferDesc.ByteWidth=sizeof(顶点)*NUMVERTIC
void SetViewPort(D3D11_VIEWPORT* VP, UINT Width, UINT Height)
{
    VP->Width = static_cast<FLOAT>(Width);
    VP->Height = static_cast<FLOAT>(Height);
    VP->MinDepth = 0.0f;
    VP->MaxDepth = 1.0f;
    VP->TopLeftX = 0;
    VP->TopLeftY = 0;
}

HRESULT MakeRTV(ID3D11RenderTargetView** pRTV, ID3D11Texture2D* pSurf)
{
    if (*pRTV)
    {
        (*pRTV)->Release();
        *pRTV = nullptr;
    }
    // Create a render target view
    HRESULT hr = m_pD3D11Device->CreateRenderTargetView(pSurf, nullptr, pRTV);

    IF_FAILED_THROW(hr);

    return S_OK;
}

HRESULT InitializeNV12Surfaces(ID3D11Texture2D* inputTexture)
{
    ReleaseSurfaces();

    D3D11_TEXTURE2D_DESC lOutputDuplDesc;
    inputTexture->GetDesc(&lOutputDuplDesc);


    // Create shared texture for all duplication threads to draw into
    D3D11_TEXTURE2D_DESC DeskTexD;
    RtlZeroMemory(&DeskTexD, sizeof(D3D11_TEXTURE2D_DESC));
    DeskTexD.Width = lOutputDuplDesc.Width;
    DeskTexD.Height = lOutputDuplDesc.Height;
    DeskTexD.MipLevels = 1;
    DeskTexD.ArraySize = 1;
    DeskTexD.Format = lOutputDuplDesc.Format;
    DeskTexD.SampleDesc.Count = 1;
    DeskTexD.Usage = D3D11_USAGE_DEFAULT;
    DeskTexD.BindFlags = D3D11_BIND_SHADER_RESOURCE;

    HRESULT hr = m_pD3D11Device->CreateTexture2D(&DeskTexD, nullptr, &m_ShaderResourceSurf);
    IF_FAILED_THROW(hr);

    DeskTexD.Format = DXGI_FORMAT_R8_UNORM;
    DeskTexD.BindFlags = D3D11_BIND_RENDER_TARGET;

    hr = m_pD3D11Device->CreateTexture2D(&DeskTexD, nullptr, &m_LuminanceSurf);
    IF_FAILED_THROW(hr);

    DeskTexD.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
    DeskTexD.Usage = D3D11_USAGE_STAGING;
    DeskTexD.BindFlags = 0;

    hr = m_pD3D11Device->CreateTexture2D(&DeskTexD, NULL, &m_CPUAccessibleLuminanceSurf);
    IF_FAILED_THROW(hr);

    SetViewPort(&m_VPLuminance, DeskTexD.Width, DeskTexD.Height);

    HRESULT Ret = MakeRTV(&m_pLumaRT, m_LuminanceSurf);
    if (!SUCCEEDED(Ret))
        return Ret;

    DeskTexD.Width = lOutputDuplDesc.Width / 2;
    DeskTexD.Height = lOutputDuplDesc.Height / 2;
    DeskTexD.Format = DXGI_FORMAT_R8G8_UNORM;

    DeskTexD.Usage = D3D11_USAGE_DEFAULT;
    DeskTexD.CPUAccessFlags = 0;
    DeskTexD.BindFlags = D3D11_BIND_RENDER_TARGET;

    hr = m_pD3D11Device->CreateTexture2D(&DeskTexD, nullptr, &m_ChrominanceSurf);
    IF_FAILED_THROW(hr);

    DeskTexD.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
    DeskTexD.Usage = D3D11_USAGE_STAGING;
    DeskTexD.BindFlags = 0;

    hr = m_pD3D11Device->CreateTexture2D(&DeskTexD, NULL, &m_CPUAccessibleChrominanceSurf);
    IF_FAILED_THROW(hr);

    SetViewPort(&m_VPChrominance, DeskTexD.Width, DeskTexD.Height);
    return MakeRTV(&m_pChromaRT, m_ChrominanceSurf);
}

HRESULT InitVertexShader(ID3D11VertexShader** ppID3D11VertexShader)
{
    HRESULT hr = S_OK;
    UINT Size = ARRAYSIZE(g_VS);

    try
    {
        IF_FAILED_THROW(m_pD3D11Device->CreateVertexShader(g_VS, Size, NULL, ppID3D11VertexShader));;

        m_pD3D11DeviceContext->VSSetShader(m_pVertexShader, nullptr, 0);

        // Vertices for drawing whole texture
        VERTEX Vertices[NUMVERTICES] =
        {
            { XMFLOAT3(-1.0f, -1.0f, 0), XMFLOAT2(0.0f, 1.0f) },
            { XMFLOAT3(-1.0f, 1.0f, 0), XMFLOAT2(0.0f, 0.0f) },
            { XMFLOAT3(1.0f, -1.0f, 0), XMFLOAT2(1.0f, 1.0f) },
            { XMFLOAT3(1.0f, -1.0f, 0), XMFLOAT2(1.0f, 1.0f) },
            { XMFLOAT3(-1.0f, 1.0f, 0), XMFLOAT2(0.0f, 0.0f) },
            { XMFLOAT3(1.0f, 1.0f, 0), XMFLOAT2(1.0f, 0.0f) },
        };

        UINT Stride = sizeof(VERTEX);
        UINT Offset = 0;

        D3D11_BUFFER_DESC BufferDesc;
        RtlZeroMemory(&BufferDesc, sizeof(BufferDesc));
        BufferDesc.Usage = D3D11_USAGE_DEFAULT;
        BufferDesc.ByteWidth = sizeof(VERTEX) * NUMVERTICES;
        BufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
        BufferDesc.CPUAccessFlags = 0;
        D3D11_SUBRESOURCE_DATA InitData;
        RtlZeroMemory(&InitData, sizeof(InitData));
        InitData.pSysMem = Vertices;

        // Create vertex buffer
        IF_FAILED_THROW(m_pD3D11Device->CreateBuffer(&BufferDesc, &InitData, &m_VertexBuffer));

        m_pD3D11DeviceContext->IASetVertexBuffers(0, 1, &m_VertexBuffer, &Stride, &Offset);
        m_pD3D11DeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

        D3D11_INPUT_ELEMENT_DESC Layout[] =
        {
            { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
            { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }
        };

        UINT NumElements = ARRAYSIZE(Layout);
        hr = m_pD3D11Device->CreateInputLayout(Layout, NumElements, g_VS, Size, &m_pVertexLayout);

        m_pD3D11DeviceContext->IASetInputLayout(m_pVertexLayout);
    }
    catch (HRESULT) {}

    return hr;
}

HRESULT InitPixelShaders()
{
    HRESULT hr = S_OK;
    // Refer https://codeshare.io/5PJjxP for g_PS_Y & g_PS_UV blobs
    try
    {
        UINT Size = ARRAYSIZE(g_PS_Y);
        hr = m_pD3D11Device->CreatePixelShader(g_PS_Y, Size, nullptr, &m_pPixelShaderChroma);

        IF_FAILED_THROW(hr);

        Size = ARRAYSIZE(g_PS_UV);
        hr = m_pD3D11Device->CreatePixelShader(g_PS_UV, Size, nullptr, &m_pPixelShaderLuma);

        IF_FAILED_THROW(hr);
    }
    catch (HRESULT) {}

    return hr;
}