ডাইরেক্টএক্স 11 পিক্সেল শেডার ব্যবহার করে জিপিইউতে DXGI_FORMAT_B8G8R8A8_UNORM থেকে NV12 এ রঙ রূপান্তর


9

আমি ডেস্কটপ ডুপ্লিকেশন ব্যবহার করে ডেস্কটপ ক্যাপচার করতে একটি কোডে কাজ করছি এবং ইন্টেল হার্ডওয়্যার এমএফটি ব্যবহার করে এটিকে h264 এ এনকোড করব। এনকোডার কেবল এনভি 12 ফর্ম্যাটটিকে ইনপুট হিসাবে গ্রহণ করে। আমার কাছে একটি DXGI_FORMAT_B8G8R8A8_UNORM পেয়েছে NV12 রূপান্তরকারী ( https://github.com/NVIDIA/video-sdk-sams/blob/master/nvEncDXGIOutput ডুপ্লিকেশনসাম্পল / প্রিপ্রোকসিপি ) জরিমানা কাজ করে, এবং ডাইরেক্ট ভিডিও ভিত্তিতে ভিত্তি করে।

সমস্যাটি হ'ল কিছু নির্দিষ্ট ইন্টেল গ্রাফিক্স হার্ডওয়্যারে ভিডিও প্রসেসর কেবলমাত্র DXGI_FORMAT_B8G8R8A8_UNORM থেকে YUY2 তে রূপান্তরকে সমর্থন করে তবে এনভি 12 নয়, আমি গেটভিডিওপ্রসেসরআউটপুট ফর্ম্যাটগুলির মাধ্যমে সমর্থিত ফর্ম্যাটগুলি গণনা করে এটির নিশ্চিত করেছি। যদিও ভিডিওপ্রসেসর ব্ল্ট কোনও ত্রুটি ছাড়াই সফল হয়েছিল এবং আমি দেখতে পেলাম যে আউটপুট ভিডিওতে ফ্রেমগুলি কিছুটা পিক্সেলাইটেড রয়েছে, আমি যদি এটি ঘনিষ্ঠভাবে দেখি তবে আমি এটি লক্ষ্য করতে পারি।

আমার ধারণা, ভিডিওপ্রসেসর কেবল পরবর্তী সমর্থিত আউটপুট ফর্ম্যাট (YUY2) এ ব্যর্থ হয়েছে এবং আমি অজান্তে এটিকে এনকোডারকে খাওয়াই যা মনে করে যে ইনপুটটি এনভি 12 তে কনফিগার করা আছে as বায়ার অর্ডার এবং এনভি 12 এবং ওয়াইওয়াই 2 এর মধ্যে সাব্যাম্পলিংয়ের মতো সামান্য পার্থক্য থাকার কারণে ফ্রেমের কোনও ব্যর্থতা বা বড় দুর্নীতি নেই। এছাড়াও, আমার কাছে হার্ডওয়্যারে পিক্সেলটিং সমস্যা নেই যা এনভি 12 রূপান্তরকে সমর্থন করে।

তাই আমি পিক্সেল শেডার ব্যবহার করে রঙ রূপান্তর করার সিদ্ধান্ত নিয়েছি যা এই কোডের উপর ভিত্তি করে রয়েছে ( https://github.com/bavulapati/DXGICaptureDXColorSpaceConversionIntelEncode/blob/master/DXGICaptureDXororSpaceConversionIntelEncode/ ডুপ্লিকেশন ম্যানেজার ।) আমি পিক্সেল শেডারগুলিকে কাজ করতে সক্ষম হয়েছি, আমি রেফারেন্সের জন্য আমার কোডও এখানে ( https://codeshare.io/5PJjxP ) আপলোড করেছি (এটি যথাসম্ভব সহজতর করা)।

এখন, আমি যথাক্রমে দুটি চ্যানেল, ক্রোমা এবং লুমা রেখেছি (ID3D11 টেক্সচার 2 ডি টেক্সচার)। এবং আমি দুটি আইডি 3 ডি 11 টেক্সচার 2 ডি টেক্সচারে দক্ষতার সাথে দুটি পৃথক চ্যানেল দক্ষতার সাথে প্যাক করার বিষয়ে সত্যিই বিভ্রান্ত হয়েছি যাতে আমি একইটিকে এনকোডারে ফিড করতে পারি। জিপিইউতে একক আইডি 3 ডি 11 টেক্সচার 2 ডি-তে Y এবং UV চ্যানেলগুলি দক্ষতার সাথে প্যাক করার কোনও উপায় আছে কি? এটি ব্যয়বহুল হওয়ার কারণে আমি সিপিইউ ভিত্তিক পদ্ধতির দ্বারা সত্যই ক্লান্ত হয়ে পড়েছি এবং সর্বোত্তম সম্ভাব্য ফ্রেম রেট দিচ্ছি না। আসলে, আমি এমনকি টেক্সচারগুলি সিপিইউতে অনুলিপি করতে নারাজ। আমি সিপিইউ এবং জিপিইউর মধ্যে কোনও পেছনের অনুলিপি ছাড়াই জিপিইউতে এটি করার একটি উপায় নিয়ে ভাবছি।

আমি বেশ কিছুদিন ধরে এই গবেষণা করে চলেছি কোনও অগ্রগতি ছাড়াই, কোনও সাহায্যের প্রশংসা হবে।

/**
* 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;
}

এনভি 12 আঁকুন:

 //
// Draw frame for NV12 texture
//
HRESULT DrawNV12Frame(ID3D11Texture2D* inputTexture)
{
    HRESULT hr;

    // If window was resized, resize swapchain
    if (!m_bIntialized)
    {
        HRESULT Ret = InitializeNV12Surfaces(inputTexture);
        if (!SUCCEEDED(Ret))
        {
            return Ret;
        }

        m_bIntialized = true;
    }

    m_pD3D11DeviceContext->CopyResource(m_ShaderResourceSurf, inputTexture);

    D3D11_TEXTURE2D_DESC FrameDesc;
    m_ShaderResourceSurf->GetDesc(&FrameDesc);

    D3D11_SHADER_RESOURCE_VIEW_DESC ShaderDesc;
    ShaderDesc.Format = FrameDesc.Format;
    ShaderDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
    ShaderDesc.Texture2D.MostDetailedMip = FrameDesc.MipLevels - 1;
    ShaderDesc.Texture2D.MipLevels = FrameDesc.MipLevels;

    // Create new shader resource view
    ID3D11ShaderResourceView* ShaderResource = nullptr;
    hr = m_pD3D11Device->CreateShaderResourceView(m_ShaderResourceSurf, &ShaderDesc, &ShaderResource);

    IF_FAILED_THROW(hr);

    m_pD3D11DeviceContext->PSSetShaderResources(0, 1, &ShaderResource);

    // Set resources
    m_pD3D11DeviceContext->OMSetRenderTargets(1, &m_pLumaRT, nullptr);
    m_pD3D11DeviceContext->PSSetShader(m_pPixelShaderLuma, nullptr, 0);
    m_pD3D11DeviceContext->RSSetViewports(1, &m_VPLuminance);

    // Draw textured quad onto render target
    m_pD3D11DeviceContext->Draw(NUMVERTICES, 0);

    m_pD3D11DeviceContext->OMSetRenderTargets(1, &m_pChromaRT, nullptr);
    m_pD3D11DeviceContext->PSSetShader(m_pPixelShaderChroma, nullptr, 0);
    m_pD3D11DeviceContext->RSSetViewports(1, &m_VPChrominance);

    // Draw textured quad onto render target
    m_pD3D11DeviceContext->Draw(NUMVERTICES, 0);

    // Release shader resource
    ShaderResource->Release();
    ShaderResource = nullptr;

    return S_OK;
}

ইনিশ শেডারগুলি:

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;
}

এটি পরীক্ষা করা উচিত, তবে আমি মনে করি যে হার্ডওয়্যারে যেখানে ভিডিওপ্রসেসর কেবল YUY2 এ আউটপুট দিতে পারে, সেখানে হার্ডওয়্যার এনকোডারও YUY2 গ্রহণ করবে। সুতরাং আপনি এটি যাচাই করতে পারেন এবং সরাসরি এ ক্ষেত্রে এনকোডারটিতে ভিডিওপ্রসেসর আউটপুট খাওয়াতে পারেন।
VuVirt

@ ভার্ভিট, আমিও এটি একই রকম মনে করি, কিন্তু যখন আমি ইনপুট প্রকার হিসাবে YUY2 সহ হার্ডওয়্যার এনকোডারটি গণনার চেষ্টা করি তখন আমি কোনও এনকোডার ফিরে পাইনি।
রাম

আপনি কি দ্বৈত জিপিইউ পিসিতে চেষ্টা করেছেন তা সম্ভব?
VuVirt

আমি নিশ্চিত, আমি একাধিক গ্রাফিক্স কার্ড সহ একটি মেশিনে এটি চালাচ্ছি না। আমি এখনও ভাবছি যে এই ধরণের অসম্পূর্ণতা কীভাবে ঘটতে পারে। আমি এই থ্রেডে আরও বিশদ আপডেট করার চেষ্টা করব।
রাম

উত্তর:


5

আমি ডাইরেক্টএক্স 11 ব্যবহার করে কেবল জিপিইউতে এই আরজিবিএ রূপান্তরটি এনভি 12 এ পরীক্ষা করছি।

এটি একটি ভাল চ্যালেঞ্জ। আমি ডাইরেক্টএক্স 11 এর সাথে পরিচিত নই, সুতরাং এটি আমার প্রথম পরীক্ষা।

আপডেটগুলির জন্য এই প্রকল্পটি দেখুন: D3D11ShaderNV12

আমার বর্তমান বাস্তবায়নে (শেষ নাও হতে পারে), আমি যা করছি তা এখানে:

  • পদক্ষেপ 1: ইনপুট টেক্সচার হিসাবে একটি DXGI_FORMAT_B8G8R8A8_UNORM ব্যবহার করুন
  • পদক্ষেপ 2: 3 টি টেক্সচার পেতে 1 ম পাসের শেডার তৈরি করুন (ওয়াই: লুমা, ইউ: ক্রোমাসিবি এবং ভি: ক্রোমাসিআর): YCbCrPS2.hlsl দেখুন
  • পদক্ষেপ 3: ওয়াই DXGI_FORMAT_R8_UNORM, এবং চূড়ান্ত এনভি 12 টেক্সচারের জন্য প্রস্তুত
  • পদক্ষেপ ৪: ইউভিকে ২ য় পাসের শেডারে ডাউনস্যাম্পল করা দরকার: স্ক্রিনএসপিএস ২ এসএলএসএল দেখুন (লিনিয়ার ফিল্টারিং ব্যবহার করে)
  • পদক্ষেপ 5: ওয়াই জমিনের নমুনার জন্য তৃতীয় পাসের শেডার
  • পদক্ষেপ:: শিফট টেক্সচার ব্যবহার করে ইউভি টেক্সচারের নমুনা করার জন্য চতুর্থ পাসের শেডার (আমি মনে করি অন্যান্য কৌশল ব্যবহার করা যেতে পারে)

ShaderNV12

আমার চূড়ান্ত টেক্সচারটি DXGI_FORMAT_NV12 নয়, তবে অনুরূপ DXGI_FORMAT_R8_UNORM টেক্সচার। আমার কম্পিউটারটি উইন্ডোজ 7, ​​সুতরাং DXGI_FORMAT_NV12 পরিচালনা করা হয় না। আমি পরে অন্য কম্পিউটারে চেষ্টা করব।

ছবি সহ প্রক্রিয়া:

RenderTarget


গ্রেট। আমি ঠিক এটিই খুঁজছিলাম। ধন্যবাদ।
রাম

আপনি আইডি 3 ডি 11 ডিভাইস কনটেক্সট :: জেনারেটমপস কল দিয়ে আপনার দ্বিতীয় রেন্ডার পাসটি প্রতিস্থাপনের চেষ্টা করতে পারেন। এটি জিপিইউ ড্রাইভারের অভ্যন্তরে গভীরভাবে প্রয়োগ করা হয়েছে, আপনার কোডে অতিরিক্ত রেন্ডার পাসের চেয়ে দ্রুত হতে পারে।
শীঘ্রই 16

আমি জানি না এটি দ্রুত বা না, তবে আমি শেডারের পরিবর্তে জেনারেটমিপস ব্যবহার করতে একটি বৈকল্পিক যুক্ত করেছি। এটি একটি আকর্ষণীয় কৌশল। টিপস জন্য ধন্যবাদ।
mofo77
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.