การรับข้อมูลจาก backbuffer D3D11 โดยใช้แผนที่ [ซ้ำ]
ฉันมีปัญหาในการใช้ ID3D11DeviceContext :: Map () อย่างถูกต้อง
นี่คือฟังก์ชั่นทั้งหมดจนถึงตอนนี้:
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 ()
"ไม่สามารถเรียกแผนที่ด้วยการเข้าถึง MAP_READ เนื่องจากทรัพยากรไม่ได้สร้างขึ้นด้วยแฟล็ก D3D11_CPU_ACCESS_READ"
ซึ่งสร้างความสับสนเนื่องจากทรัพยากรที่สร้างขึ้นใหม่มีการเปิดใช้งานแฟล็ก D3D11_CPU_ACCESS_READ นี่ก็เป็นจริงเช่นกันถ้าฉันลอง MAP_WRITE และเปลี่ยนแฟล็กการเข้าถึงให้ตรงกัน ฉันได้ลองหรือแฟล็กด้วยกันโดยไม่มีโชค
การใช้ฟังก์ชันนี้ขั้นสูงสุดคือสามารถสร้างอาร์เรย์ด้าน CPU ของวัตถุสีได้ (ปัจจุบันสีเป็นโครงสร้างของโฟลตสี่ตัว) ซึ่งฉันสามารถส่งไปยัง NewTek NDI sdk ได้ แต่ฉันรู้สึกว่ามันจะมีประโยชน์สำหรับการบันทึกภาพหน้าจอการสร้างตัวเลือกสีเป็นต้น
ยินดีต้อนรับความคิดใด ๆ !
คำตอบ
D3D11_CPU_ACCESS_READรหัสของคุณไม่ได้ใช้เนื้อสร้างของคุณด้วย
มันสร้างพื้นผิว:
// Create temp frame buffer(2d texture)
wrl::ComPtr<ID3D11Texture2D>pFrame = nullptr;
HRESULT hr = pDevice->CreateTexture2D(&pFrameDesc, nullptr, &pFrame);
GFX_THROW_INFO(hr);
จากนั้นจะปล่อยพื้นผิวนั้นและใช้การอ้างอิงจาก swapchain (ซึ่งไม่เคยตั้งค่าสำหรับ CPU READ):
hr = pSwapChain->GetBuffer(0, __uuidof(pFrame), &pFrame);
GFX_THROW_INFO(hr);
จากนั้นคุณลองแมปบัฟเฟอร์ swapchain:
hr = pContext->Map(pFrame.Get(), 0u, D3D11_MAP_READ, 0u, &map);
GFX_THROW_INFO(hr);
คำตอบชัค Walbourn เป็นที่ถูกต้อง แต่เพื่อให้คุณบริบทอีกเล็กน้อยเนื้อคุณกำลังสร้างด้วยไม่ได้เป็นเช่นเดียวกับสิ่งที่คุณได้รับด้วยpDevice->CreateTexture2DpSwapChain->GetBuffer
ด้วยpDevice->CreateTexture2Dคุณกำลังสร้างพื้นผิวบนหน่วยความจำใหม่ล่าสุด (ซึ่งแน่นอนว่าตั้งค่าให้อ่านจาก CPU) pFrameพื้นผิวใหม่จะถูกเก็บไว้ใน
จากนั้นpSwapChain->GetBufferคุณจะได้รับบัฟเฟอร์ย้อนกลับที่มีอยู่จาก swap chain (ซึ่งไม่ได้สร้างด้วยการเข้าถึงการอ่าน CPU) สิ่งนี้ถูกเก็บไว้ในการpFrameทิ้งค่าก่อนหน้านี้อย่างมีประสิทธิภาพ
คุณมีทางเลือกสองทางที่นี่ (ฉันไม่ได้แตะ D3D11 มาหลายปีแล้วนี่เป็นไปตามความรู้คอนโซล D3D12, Vulkan และรุ่นปัจจุบันของฉัน)
ตัวเลือก Aคือการทิ้งพื้นผิวบนหน่วยความจำใหม่และตั้งค่าบัฟเฟอร์ด้านหลังในห่วงโซ่การแลกเปลี่ยนสำหรับการเข้าถึง CPU แทน ฉันไม่แนะนำให้คุณทำเช่นนี้เนื่องจากพื้นผิวที่มีไว้สำหรับการอ่านกลับของ CPU ไม่จำเป็นต้องทำงานได้ดีเท่ากับที่สร้างขึ้นโดยไม่ต้องเข้าถึง CPU
นอกจากนี้ในขณะที่คุณทำการแมปหน่วยความจำ GPU จะต้องล้างเนื้อหาของพื้นผิวและหยุดการทำงานของ GPU ทั้งหมดจนกว่าคุณจะยกเลิกการแมปหน่วยความจำ ขึ้นอยู่กับว่าคุณกำลังทำสิ่งนี้อยู่ในขั้นตอนไหนการดำเนินการนี้จะเป็นคอขวดใหญ่
อย่างไรก็ตามฉันไม่แน่ใจด้วยซ้ำว่า Direct3D จะอนุญาตให้คุณสร้าง swap chain ที่มีการเข้าถึงการอ่านของ CPU หรือไม่ดังนั้นคุณอาจไม่สามารถลองใช้งานได้
ตัวเลือก Bคือการดำเนินการวาดด้วยรูปสี่เหลี่ยมที่มีพื้นผิวแบบเต็มหน้าจอโดยที่พื้นผิวเป็นบัฟเฟอร์ด้านหลังและเป้าหมายการเรนเดอร์คือพื้นผิวที่ CPU อ่านได้ โดยทั่วไปเป็นวิธีการที่ต้องการเนื่องจากสามารถแก้ไขปัญหาทั้งหมดที่ตัวเลือก A มีโดยเสียค่าใช้จ่ายในการวาดแบบเต็มหน้าจอหนึ่งครั้ง
โปรดทราบว่าเป็นไปได้ (ฉันจำไม่ได้กับ D3D11) ที่คุณจะไม่สามารถตั้งค่าบัฟเฟอร์ด้านหลังเป็นพื้นผิวสำหรับการวาดภาพได้ ในกรณีนั้นแทนที่จะทำการเรียกเกมของคุณไปยังบัฟเฟอร์ด้านหลังโดยตรงคุณอาจต้องสร้างเป้าหมายการเรนเดอร์อื่นดึงทุกอย่างไปที่มันจากนั้นทำสำเนาสองชุด: หนึ่งชุดไปที่บัฟเฟอร์แบ็คของโซ่แลกเปลี่ยนและอีกอันหนึ่งเป็น พื้นผิวที่อ่านได้ของ CPU
วิธีนี้ซับซ้อนกว่าเล็กน้อย แต่รับประกันว่าจะใช้ได้กับ D3D12 รวมถึงแพลตฟอร์มอื่น ๆ ที่ทันสมัยที่สุด