#include "D3D12Manager.h" #include D3D12Manager::D3D12Manager() : m_device(nullptr) , m_command_queue(nullptr) , m_command_allocator(nullptr) , m_command_list(nullptr) , m_fence(nullptr) , m_fence_value(0) , m_fence_event(nullptr) { } D3D12Manager::~D3D12Manager() { Cleanup(); } bool D3D12Manager::Initialize() { if (!CreateDevice()) { printf("[D3D12Manager] Failed to create D3D12 device\n"); return false; } if (!CreateCommandObjects()) { printf("[D3D12Manager] Failed to create command objects\n"); Cleanup(); return false; } printf("[D3D12Manager] Initialized successfully\n"); return true; } void D3D12Manager::Cleanup() { WaitForGPU(); if (m_fence_event) { CloseHandle(m_fence_event); m_fence_event = nullptr; } if (m_fence) { m_fence->Release(); m_fence = nullptr; } if (m_command_list) { m_command_list->Release(); m_command_list = nullptr; } if (m_command_allocator) { m_command_allocator->Release(); m_command_allocator = nullptr; } if (m_command_queue) { m_command_queue->Release(); m_command_queue = nullptr; } if (m_device) { m_device->Release(); m_device = nullptr; } } bool D3D12Manager::CreateDevice() { // Create DXGI factory IDXGIFactory4* factory = nullptr; HRESULT hr = CreateDXGIFactory2(0, IID_PPV_ARGS(&factory)); if (FAILED(hr)) { printf("[D3D12Manager] Failed to create DXGI factory: 0x%08X\n", hr); return false; } // Enumerate adapters and find the first hardware adapter IDXGIAdapter1* adapter = nullptr; for (UINT i = 0; factory->EnumAdapters1(i, &adapter) != DXGI_ERROR_NOT_FOUND; ++i) { DXGI_ADAPTER_DESC1 desc; adapter->GetDesc1(&desc); // Skip software adapter if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) { adapter->Release(); continue; } // Try to create D3D12 device hr = D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device)); if (SUCCEEDED(hr)) { printf("[D3D12Manager] Created D3D12 device on adapter: %ls\n", desc.Description); adapter->Release(); factory->Release(); return true; } adapter->Release(); } factory->Release(); printf("[D3D12Manager] No suitable D3D12 adapter found\n"); return false; } bool D3D12Manager::CreateCommandObjects() { // Create command queue D3D12_COMMAND_QUEUE_DESC queue_desc = {}; queue_desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; queue_desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; HRESULT hr = m_device->CreateCommandQueue(&queue_desc, IID_PPV_ARGS(&m_command_queue)); if (FAILED(hr)) { printf("[D3D12Manager] Failed to create command queue: 0x%08X\n", hr); return false; } // Create command allocator hr = m_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&m_command_allocator)); if (FAILED(hr)) { printf("[D3D12Manager] Failed to create command allocator: 0x%08X\n", hr); return false; } // Create command list hr = m_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_command_allocator, nullptr, IID_PPV_ARGS(&m_command_list)); if (FAILED(hr)) { printf("[D3D12Manager] Failed to create command list: 0x%08X\n", hr); return false; } // Close command list (we'll reset it when needed) m_command_list->Close(); // Create fence hr = m_device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_fence)); if (FAILED(hr)) { printf("[D3D12Manager] Failed to create fence: 0x%08X\n", hr); return false; } m_fence_value = 1; // Create fence event m_fence_event = CreateEvent(nullptr, FALSE, FALSE, nullptr); if (m_fence_event == nullptr) { printf("[D3D12Manager] Failed to create fence event\n"); return false; } return true; } void D3D12Manager::WaitForGPU() { if (!m_command_queue || !m_fence || !m_fence_event) { return; } // Signal the fence const UINT64 fence = m_fence_value; m_command_queue->Signal(m_fence, fence); m_fence_value++; // Wait for fence if (m_fence->GetCompletedValue() < fence) { m_fence->SetEventOnCompletion(fence, m_fence_event); WaitForSingleObject(m_fence_event, INFINITE); } } ID3D12Resource* D3D12Manager::CreateNV12Texture(uint32_t width, uint32_t height) { D3D12_RESOURCE_DESC desc = {}; desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; desc.Width = width; desc.Height = height; desc.DepthOrArraySize = 1; desc.MipLevels = 1; desc.Format = DXGI_FORMAT_NV12; desc.SampleDesc.Count = 1; desc.SampleDesc.Quality = 0; desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; D3D12_HEAP_PROPERTIES heap_props = {}; heap_props.Type = D3D12_HEAP_TYPE_DEFAULT; ID3D12Resource* texture = nullptr; HRESULT hr = m_device->CreateCommittedResource( &heap_props, D3D12_HEAP_FLAG_SHARED, &desc, D3D12_RESOURCE_STATE_COMMON, nullptr, IID_PPV_ARGS(&texture)); if (FAILED(hr)) { printf("[D3D12Manager] Failed to create NV12 texture: 0x%08X\n", hr); return nullptr; } return texture; } uint8_t* D3D12Manager::ReadbackTexture(ID3D12Resource* texture, uint32_t width, uint32_t height) { // Get texture description D3D12_RESOURCE_DESC desc = texture->GetDesc(); // Calculate layout for both Y and UV planes D3D12_PLACED_SUBRESOURCE_FOOTPRINT layouts[2]; UINT num_rows[2] = { 0 }; UINT64 row_sizes[2] = { 0 }; UINT64 total_bytes = 0; m_device->GetCopyableFootprints(&desc, 0, 2, 0, layouts, num_rows, row_sizes, &total_bytes); // Create readback buffer D3D12_HEAP_PROPERTIES readback_heap_props = {}; readback_heap_props.Type = D3D12_HEAP_TYPE_READBACK; D3D12_RESOURCE_DESC readback_desc = {}; readback_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; readback_desc.Width = total_bytes; readback_desc.Height = 1; readback_desc.DepthOrArraySize = 1; readback_desc.MipLevels = 1; readback_desc.Format = DXGI_FORMAT_UNKNOWN; readback_desc.SampleDesc.Count = 1; readback_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; ID3D12Resource* readback_buffer = nullptr; HRESULT hr = m_device->CreateCommittedResource( &readback_heap_props, D3D12_HEAP_FLAG_NONE, &readback_desc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&readback_buffer)); if (FAILED(hr)) { printf("[D3D12Manager] Failed to create readback buffer: 0x%08X\n", hr); return nullptr; } // Reset command list m_command_allocator->Reset(); m_command_list->Reset(m_command_allocator, nullptr); // Transition texture to COPY_SOURCE D3D12_RESOURCE_BARRIER barrier = {}; barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; barrier.Transition.pResource = texture; barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COMMON; barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE; barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; m_command_list->ResourceBarrier(1, &barrier); // Copy Y plane (subresource 0) D3D12_TEXTURE_COPY_LOCATION src_y = {}; src_y.pResource = texture; src_y.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; src_y.SubresourceIndex = 0; D3D12_TEXTURE_COPY_LOCATION dst_y = {}; dst_y.pResource = readback_buffer; dst_y.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; dst_y.PlacedFootprint = layouts[0]; m_command_list->CopyTextureRegion(&dst_y, 0, 0, 0, &src_y, nullptr); // Copy UV plane (subresource 1) D3D12_TEXTURE_COPY_LOCATION src_uv = {}; src_uv.pResource = texture; src_uv.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; src_uv.SubresourceIndex = 1; D3D12_TEXTURE_COPY_LOCATION dst_uv = {}; dst_uv.pResource = readback_buffer; dst_uv.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; dst_uv.PlacedFootprint = layouts[1]; m_command_list->CopyTextureRegion(&dst_uv, 0, 0, 0, &src_uv, nullptr); // Transition texture back to COMMON barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE; barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COMMON; m_command_list->ResourceBarrier(1, &barrier); // Execute command list m_command_list->Close(); ID3D12CommandList* cmd_lists[] = { m_command_list }; m_command_queue->ExecuteCommandLists(1, cmd_lists); // Wait for GPU WaitForGPU(); // Map readback buffer void* mapped_data = nullptr; D3D12_RANGE read_range = { 0, static_cast(total_bytes) }; hr = readback_buffer->Map(0, &read_range, &mapped_data); if (FAILED(hr)) { printf("[D3D12Manager] Failed to map readback buffer: 0x%08X\n", hr); readback_buffer->Release(); return nullptr; } // Allocate CPU buffer (NV12 format: Y plane + UV plane) uint32_t y_size = width * height; uint32_t uv_size = width * (height / 2); uint8_t* cpu_buffer = new uint8_t[y_size + uv_size]; // Copy Y plane uint8_t* src_y_data = static_cast(mapped_data) + layouts[0].Offset; uint8_t* dst_y_data = cpu_buffer; for (UINT row = 0; row < height; ++row) { memcpy(dst_y_data + row * width, src_y_data + row * layouts[0].Footprint.RowPitch, width); } // Copy UV plane uint8_t* src_uv_data = static_cast(mapped_data) + layouts[1].Offset; uint8_t* dst_uv_data = cpu_buffer + y_size; for (UINT row = 0; row < height / 2; ++row) { memcpy(dst_uv_data + row * width, src_uv_data + row * layouts[1].Footprint.RowPitch, width); } // Unmap and release readback buffer readback_buffer->Unmap(0, nullptr); readback_buffer->Release(); return cpu_buffer; }