Map [duplicate] kullanarak backbuffer D3D11'den veri alma

Nov 13 2020

ID3D11DeviceContext :: Map () doğru şekilde kullanırken bazı sorunlar yaşıyorum.

İşte şimdiye kadarki tüm işlev:

 Color Graphics::GetPixel(int x, int y) const
{
    //Temp frame buffer descriptor;
    CD3D11_TEXTURE2D_DESC pFrameDesc;
    pFrameDesc.Width = WindowWidth;
    pFrameDesc.Height = WindowHeight;
    pFrameDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
    pFrameDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
    pFrameDesc.MipLevels = 1u;
    pFrameDesc.ArraySize = 1u;
    pFrameDesc.SampleDesc.Count = 1u;
    pFrameDesc.SampleDesc.Quality = 0u;
    pFrameDesc.Usage = D3D11_USAGE_DEFAULT;
    pFrameDesc.BindFlags = D3D11_BIND_UNORDERED_ACCESS;
    pFrameDesc.MiscFlags = 0u;

    // Create temp frame buffer(2d texture)
    wrl::ComPtr<ID3D11Texture2D>pFrame = nullptr;
    HRESULT hr = pDevice->CreateTexture2D(&pFrameDesc, nullptr, &pFrame);
    GFX_THROW_INFO(hr);

    hr = pSwapChain->GetBuffer(0, __uuidof(pFrame), &pFrame);
    GFX_THROW_INFO(hr);
    
    
    D3D11_MAPPED_SUBRESOURCE map;
    map.RowPitch = WindowWidth * 4;
    map.DepthPitch = WindowHeight * 4;
    
    //Throwing here, CPU access flags problem. 
    hr = pContext->Map(pFrame.Get(), 0u, D3D11_MAP_READ, 0u, &map);
    GFX_THROW_INFO(hr);

    pContext->Unmap(pFrame.Get(), 0u);
    


    return { 0.0f, 0.4f, 0.0f, 0.0f };
}

PContext-> Map () 'de bir istisna atıyorum

"Harita, MAP_READ erişimiyle çağrılamaz, çünkü Kaynak D3D11_CPU_ACCESS_READ bayrağıyla oluşturulmadı."

Yeni oluşturulan kaynak D3D11_CPU_ACCESS_READ bayrağına sahip olduğu için bu kafa karıştırıcı. Bu aynı zamanda MAP_WRITE'ı denersem ve erişim bayraklarını eşleşecek şekilde değiştirirsem de geçerlidir. Ayrıca bayrakları ORing'i şanssız bir şekilde denedim.

Bu işlevin nihai kullanımı, daha sonra NewTek NDI sdk'ye gönderebileceğim Color nesnelerinin bir CPU tarafı dizisi oluşturabilmektir (Renkler şu anda dört kayan bir yapıdır). Ancak ekran görüntülerini kaydetmek, renk seçiciler oluşturmak vb. İçin de faydalı olacağını düşünüyorum.

Herhangi bir fikir hoş geldiniz!

Yanıtlar

5 ChuckWalbourn Nov 14 2020 at 08:33

Kodunuz, oluşturduğunuz dokuyu ile kullanmıyor D3D11_CPU_ACCESS_READ.

Bir doku oluşturur:

// Create temp frame buffer(2d texture)
wrl::ComPtr<ID3D11Texture2D>pFrame = nullptr;
HRESULT hr = pDevice->CreateTexture2D(&pFrameDesc, nullptr, &pFrame);
GFX_THROW_INFO(hr);

Ardından bu dokuyu serbest bırakır ve takas zincirinden birine referans alır (ki bu asla CPU READ için ayarlanmamıştır):

hr = pSwapChain->GetBuffer(0, __uuidof(pFrame), &pFrame);
GFX_THROW_INFO(hr);

Sonra takas zinciri arabelleğini eşlemeye çalışırsınız:

hr = pContext->Map(pFrame.Get(), 0u, D3D11_MAP_READ, 0u, &map);
GFX_THROW_INFO(hr);
1 PandaPajama Nov 14 2020 at 15:08

Chuck Walbourn'un cevabı doğru, ancak size biraz daha fazla bağlam sağlamak için, yarattığınız doku ile elde ettiğiniz doku pDevice->CreateTexture2Daynı değil pSwapChain->GetBuffer.

İle pDevice->CreateTexture2D, yepyeni bir bellek içi doku oluşturuyorsunuz (gerçekten CPU'dan okunmak üzere ayarlanmış). Yeni doku, içinde saklanır pFrame.

Ardından, ile pSwapChain->GetBuffer, takas zincirinden (CPU okuma erişimiyle yaratılmamış olan) mevcut arka arabelleği alıyorsunuz . Bu, pFrameönceki değeri etkin bir şekilde atarak içinde saklanır .

Burada iki seçeneğiniz var (D3D11'e yıllardır dokunmadım. Bu benim D3D12, Vulkan ve mevcut konsol bilgilerime dayanıyor)

Seçenek A , yeni bellek üzerindeki dokuyu ortadan kaldırmak ve bunun yerine CPU erişimi için takas zincirindeki geri arabelleği ayarlamaktır. Bunu yapmanızı tavsiye etmiyorum, çünkü CPU'nun yeniden okunması için tasarlanan dokular, CPU erişimi olmadan oluşturulanlar kadar iyi performans göstermeyecektir.

Ayrıca, belleği eşlerken, GPU'nun doku içeriğini temizlemesi ve siz bellek eşlemesini kaldırana kadar üzerindeki tüm GPU işlemlerini durdurması gerekecektir. Bunu çizim sürecinizin neresinde yaptığınıza bağlı olarak, bu işlem büyük bir darboğaz olacaktır.

Bu arada, Direct3D'nin CPU okuma erişimine sahip bir takas zinciri oluşturmanıza izin verip vermeyeceğinden bile emin değilim, bu yüzden muhtemelen deneyemezsiniz bile.

Seçenek B , dokunun arka arabellek olduğu ve işleme hedefinin CPU tarafından okunabilir dokunuz olduğu tam ekran dokulu dörtlü bir çizim işlemi yapmaktır. Bu genellikle, bir tam ekran çizim işlemi pahasına Seçenek A'nın sahip olduğu tüm sorunları çözdüğü için tercih edilen yöntemdir.

Arka arabelleği çizim için bir doku olarak ayarlayamayacağınızın mümkün olduğunu (D3D11 ile hatırlamıyorum) unutmayın. Bu durumda, oyununuzun beraberlik çağrılarını doğrudan arka arabelleğe yapmak yerine, başka bir oluşturma hedefi oluşturmanız, her şeyi ona çekmeniz ve ardından iki kopya almanız gerekebilir: biri takas zinciri geri arabelleğine ve diğeri CPU tarafından okunabilir dokunuz.

Bu yöntem biraz daha karmaşıktır, ancak büyük olasılıkla diğer tüm modern platformların yanı sıra D3D12'de de çalışması garanti edilir.