Files
video-v1/vav2/platforms/windows/tests/large-resolution/src/D3D12Manager.cpp
2025-10-05 22:51:27 +09:00

342 lines
10 KiB
C++

#include "D3D12Manager.h"
#include <stdio.h>
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<SIZE_T>(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<uint8_t*>(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<uint8_t*>(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;
}