#![windows_subsystem = "windows"] //! Example showcasing [`winapi`] interop with [`gpu-allocator`] which is driven by the [`windows`] crate. use log::info; use raw_window_handle::HasRawWindowHandle; use gpu_allocator::d3d12::{Allocator, AllocatorCreateDesc, ToWindows}; mod all_dxgi { pub use winapi::shared::{ dxgi::*, dxgi1_2::*, dxgi1_3::*, dxgi1_4::*, dxgi1_6::*, dxgiformat::*, dxgitype::*, }; } use winapi::um::d3d12::*; use winapi::um::d3dcommon::*; use winapi::um::winuser; use winapi::shared::minwindef::UINT; use winapi::shared::winerror; use winapi::shared::winerror::{FAILED, SUCCEEDED}; use winapi::Interface; mod imgui_renderer; use imgui_renderer::{handle_imgui_event, ImGuiRenderer}; const ENABLE_DEBUG_LAYER: bool = true; const FRAMES_IN_FLIGHT: usize = 2; struct BackBuffer { resource: *mut ID3D12Resource, rtv_handle: D3D12_CPU_DESCRIPTOR_HANDLE, } fn find_hardware_adapter( dxgi_factory: &all_dxgi::IDXGIFactory6, ) -> Option<*mut all_dxgi::IDXGIAdapter4> { let mut adapter: *mut all_dxgi::IDXGIAdapter4 = std::ptr::null_mut(); for adapter_index in 0.. { let hr = unsafe { dxgi_factory.EnumAdapters1( adapter_index, <*mut *mut all_dxgi::IDXGIAdapter4>::cast(&mut adapter), ) }; if hr == winerror::DXGI_ERROR_NOT_FOUND { break; } let mut desc = Default::default(); unsafe { adapter.as_ref().unwrap().GetDesc3(&mut desc) }; if (desc.Flags & all_dxgi::DXGI_ADAPTER_FLAG_SOFTWARE) != 0 { continue; } let hr = unsafe { D3D12CreateDevice( adapter.cast(), D3D_FEATURE_LEVEL_12_0, &IID_ID3D12Device, std::ptr::null_mut(), ) }; if SUCCEEDED(hr) { return Some(adapter); } } None } fn enable_d3d12_debug_layer() -> bool { use winapi::um::d3d12sdklayers::ID3D12Debug; let mut debug: *mut ID3D12Debug = std::ptr::null_mut(); let hr = unsafe { D3D12GetDebugInterface( &ID3D12Debug::uuidof(), <*mut *mut ID3D12Debug>::cast(&mut debug), ) }; if FAILED(hr) { return false; } let debug = unsafe { debug.as_mut().unwrap() }; unsafe { debug.EnableDebugLayer() }; unsafe { debug.Release() }; true } fn create_d3d12_device(adapter: &mut all_dxgi::IDXGIAdapter4) -> *mut ID3D12Device { unsafe { let mut device: *mut ID3D12Device = std::ptr::null_mut(); let hr = D3D12CreateDevice( <*mut all_dxgi::IDXGIAdapter4>::cast(adapter), D3D_FEATURE_LEVEL_12_0, &ID3D12Device::uuidof(), <*mut *mut ID3D12Device>::cast(&mut device), ); if FAILED(hr) { panic!("Failed to create ID3D12Device."); } device } } #[must_use] fn transition_resource( resource: *mut ID3D12Resource, before: D3D12_RESOURCE_STATES, after: D3D12_RESOURCE_STATES, ) -> D3D12_RESOURCE_BARRIER { let mut barrier = D3D12_RESOURCE_BARRIER { Type: D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, Flags: D3D12_RESOURCE_BARRIER_FLAG_NONE, ..D3D12_RESOURCE_BARRIER::default() }; unsafe { barrier.u.Transition_mut().pResource = resource; barrier.u.Transition_mut().StateBefore = before; barrier.u.Transition_mut().StateAfter = after; barrier.u.Transition_mut().Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; } barrier } fn main() { env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("trace")).init(); // Disable automatic DPI scaling by windows unsafe { winuser::SetProcessDPIAware() }; let event_loop = winit::event_loop::EventLoop::new(); let window_width = 1920; let window_height = 1080; let window = winit::window::WindowBuilder::new() .with_title("gpu-allocator d3d12 visualization") .with_inner_size(winit::dpi::PhysicalSize::new( window_width as f64, window_height as f64, )) .with_resizable(false) .build(&event_loop) .unwrap(); let (event_send, event_recv) = std::sync::mpsc::sync_channel(1); let quit_send = event_loop.create_proxy(); std::thread::spawn(move || { let mut dxgi_factory_flags = 0; if ENABLE_DEBUG_LAYER && enable_d3d12_debug_layer() { info!("Enabled D3D12 debug layer"); dxgi_factory_flags |= all_dxgi::DXGI_CREATE_FACTORY_DEBUG; } let dxgi_factory = unsafe { let mut factory: *mut all_dxgi::IDXGIFactory6 = std::ptr::null_mut(); let hr = all_dxgi::CreateDXGIFactory2( dxgi_factory_flags, &all_dxgi::IID_IDXGIFactory6, <*mut *mut all_dxgi::IDXGIFactory6>::cast(&mut factory), ); if FAILED(hr) { panic!("Failed to create dxgi factory"); } factory.as_mut().unwrap() }; let adapter = find_hardware_adapter(dxgi_factory).unwrap(); let adapter = unsafe { adapter.as_mut().unwrap() }; let device = create_d3d12_device(adapter); let device = unsafe { device.as_mut().unwrap() }; let queue = unsafe { let desc = D3D12_COMMAND_QUEUE_DESC { Type: D3D12_COMMAND_LIST_TYPE_DIRECT, Priority: 0, // ? Flags: D3D12_COMMAND_QUEUE_FLAG_NONE, NodeMask: 0, }; let mut queue: *mut ID3D12CommandQueue = std::ptr::null_mut(); let hr = device.CreateCommandQueue( &desc, &ID3D12CommandQueue::uuidof(), <*mut *mut ID3D12CommandQueue>::cast(&mut queue), ); if FAILED(hr) { panic!("Failed to create command queue."); } queue.as_mut().unwrap() }; let swapchain = unsafe { let mut swapchain: *mut all_dxgi::IDXGISwapChain3 = std::ptr::null_mut(); let swap_chain_desc = all_dxgi::DXGI_SWAP_CHAIN_DESC1 { BufferCount: FRAMES_IN_FLIGHT as UINT, Width: window_width, Height: window_height, Format: all_dxgi::DXGI_FORMAT_R8G8B8A8_UNORM, BufferUsage: all_dxgi::DXGI_USAGE_RENDER_TARGET_OUTPUT, SwapEffect: all_dxgi::DXGI_SWAP_EFFECT_FLIP_DISCARD, SampleDesc: all_dxgi::DXGI_SAMPLE_DESC { Count: 1, Quality: 0, }, ..all_dxgi::DXGI_SWAP_CHAIN_DESC1::default() }; let raw_window_haver: &dyn HasRawWindowHandle = &window; let hwnd = if let raw_window_handle::RawWindowHandle::Win32(handle) = raw_window_haver.raw_window_handle() { handle.hwnd } else { panic!("Failed to get HWND.") }; let hr = dxgi_factory.CreateSwapChainForHwnd( <*mut ID3D12CommandQueue>::cast(queue), hwnd.cast(), &swap_chain_desc, std::ptr::null(), std::ptr::null_mut(), <*mut *mut all_dxgi::IDXGISwapChain3>::cast(&mut swapchain), ); if FAILED(hr) { panic!("Failed to create swapchain. hr: {:#x}", hr); } swapchain.as_mut().unwrap() }; let rtv_heap = unsafe { let desc = D3D12_DESCRIPTOR_HEAP_DESC { Type: D3D12_DESCRIPTOR_HEAP_TYPE_RTV, NumDescriptors: 2, Flags: D3D12_DESCRIPTOR_HEAP_FLAG_NONE, NodeMask: 0, }; let mut heap: *mut ID3D12DescriptorHeap = std::ptr::null_mut(); let hr = device.CreateDescriptorHeap( &desc, &IID_ID3D12DescriptorHeap, <*mut *mut ID3D12DescriptorHeap>::cast(&mut heap), ); if FAILED(hr) { panic!("Failed to create RTV Descriptor heap"); } heap.as_mut().unwrap() }; let backbuffers = unsafe { (0..FRAMES_IN_FLIGHT) .map(|i| { let mut resource: *mut ID3D12Resource = std::ptr::null_mut(); let hr = swapchain.GetBuffer( i as u32, &ID3D12Resource::uuidof(), <*mut *mut ID3D12Resource>::cast(&mut resource), ); if FAILED(hr) { panic!("Failed to access swapchain buffer {}", i); } let mut u = D3D12_RENDER_TARGET_VIEW_DESC_u::default(); let t2d = u.Texture2D_mut(); t2d.MipSlice = 0; t2d.PlaneSlice = 0; let rtv_stride = device .GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV) as usize; let rtv_handle = D3D12_CPU_DESCRIPTOR_HANDLE { ptr: rtv_heap.GetCPUDescriptorHandleForHeapStart().ptr + i * rtv_stride, }; let mut rtv_desc = D3D12_RENDER_TARGET_VIEW_DESC { Format: all_dxgi::DXGI_FORMAT_R8G8B8A8_UNORM, ViewDimension: D3D12_RTV_DIMENSION_TEXTURE2D, ..Default::default() }; rtv_desc.u.Texture2D_mut().MipSlice = 0; rtv_desc.u.Texture2D_mut().PlaneSlice = 0; device.CreateRenderTargetView(resource, &rtv_desc, rtv_handle); BackBuffer { resource, rtv_handle, } }) .collect::>() }; let command_allocator = unsafe { let mut command_allocator: *mut ID3D12CommandAllocator = std::ptr::null_mut(); let hr = device.CreateCommandAllocator( D3D12_COMMAND_LIST_TYPE_DIRECT, &ID3D12CommandAllocator::uuidof(), <*mut *mut ID3D12CommandAllocator>::cast(&mut command_allocator), ); if FAILED(hr) { panic!("Failed to create command allocator"); } command_allocator.as_mut().unwrap() }; let descriptor_heap = unsafe { let desc = D3D12_DESCRIPTOR_HEAP_DESC { Type: D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, NumDescriptors: 4096, Flags: D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, NodeMask: 0, }; let mut heap: *mut ID3D12DescriptorHeap = std::ptr::null_mut(); let hr = device.CreateDescriptorHeap( &desc, &IID_ID3D12DescriptorHeap, <*mut *mut ID3D12DescriptorHeap>::cast(&mut heap), ); if FAILED(hr) { panic!("Failed to create descriptor heap."); } heap.as_mut().unwrap() }; let command_list = unsafe { let mut command_list: *mut ID3D12GraphicsCommandList = std::ptr::null_mut(); let hr = device.CreateCommandList( 0, D3D12_COMMAND_LIST_TYPE_DIRECT, command_allocator, std::ptr::null_mut(), &ID3D12GraphicsCommandList::uuidof(), <*mut *mut ID3D12GraphicsCommandList>::cast(&mut command_list), ); if FAILED(hr) { panic!("Failed to create command list."); } command_list.as_mut().unwrap() }; let mut allocator = Allocator::new(&AllocatorCreateDesc { device: device.as_windows().clone(), debug_settings: Default::default(), }) .unwrap(); let mut descriptor_heap_counter = 0; let mut imgui = imgui::Context::create(); imgui.io_mut().display_size = [window_width as f32, window_height as f32]; let mut imgui_renderer = ImGuiRenderer::new( &mut imgui, device, &mut allocator, descriptor_heap, &mut descriptor_heap_counter, command_list, ); let fence = unsafe { let mut fence: *mut ID3D12Fence = std::ptr::null_mut(); let hr = device.CreateFence( 0, D3D12_FENCE_FLAG_NONE, &ID3D12Fence::uuidof(), <*mut *mut ID3D12Fence>::cast(&mut fence), ); if FAILED(hr) { panic!("Failed to create fence"); } fence.as_mut().unwrap() }; let mut fence_value = 0_u64; unsafe { command_list.Close() }; // Submit and wait idle unsafe { let lists = [<*mut ID3D12GraphicsCommandList>::cast(command_list)]; queue.ExecuteCommandLists(lists.len() as u32, lists.as_ptr()); fence_value += 1; queue.Signal(fence, fence_value); while fence.GetCompletedValue() < fence_value {} }; let mut visualizer = gpu_allocator::d3d12::AllocatorVisualizer::new(); loop { let event = event_recv.recv().unwrap(); handle_imgui_event(imgui.io_mut(), &window, &event); let mut should_quit = false; if let winit::event::Event::WindowEvent { event, .. } = event { match event { winit::event::WindowEvent::KeyboardInput { input, .. } => { if let Some(winit::event::VirtualKeyCode::Escape) = input.virtual_keycode { should_quit = true; } } winit::event::WindowEvent::CloseRequested => { should_quit = true; } _ => {} } } if should_quit { quit_send.send_event(()).unwrap(); break; } let buffer_index = unsafe { swapchain.GetCurrentBackBufferIndex() }; let current_backbuffer = &backbuffers[buffer_index as usize]; let ui = imgui.frame(); visualizer.render(&allocator, ui, None); let imgui_draw_data = imgui.render(); unsafe { command_allocator.Reset(); command_list.Reset(command_allocator, std::ptr::null_mut()); { let barriers = [transition_resource( current_backbuffer.resource, D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET, )]; command_list.ResourceBarrier(barriers.len() as u32, barriers.as_ptr()); } command_list.ClearRenderTargetView( current_backbuffer.rtv_handle, &[1.0, 1.0, 0.0, 0.0], 0, std::ptr::null_mut(), ); let rtv_handles = [current_backbuffer.rtv_handle]; command_list.OMSetRenderTargets( rtv_handles.len() as u32, rtv_handles.as_ptr(), 0, std::ptr::null_mut(), ); { let scissor_rects = [D3D12_RECT { left: 0, top: 0, right: window_width as i32, bottom: window_height as i32, }]; command_list .RSSetScissorRects(scissor_rects.len() as u32, scissor_rects.as_ptr()); } let mut heaps: [*mut _; 1] = [descriptor_heap]; command_list.SetDescriptorHeaps(heaps.len() as u32, heaps.as_mut_ptr()); imgui_renderer.render( imgui_draw_data, device, window_width, window_height, descriptor_heap, command_list, ); { let barriers = [transition_resource( current_backbuffer.resource, D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT, )]; command_list.ResourceBarrier(barriers.len() as u32, barriers.as_ptr()); } command_list.Close(); let lists = [<*mut ID3D12GraphicsCommandList>::cast(command_list)]; queue.ExecuteCommandLists(lists.len() as u32, lists.as_ptr()); } unsafe { swapchain.Present(0, 0) }; unsafe { fence_value += 1; queue.Signal(fence, fence_value); loop { if fence_value == fence.GetCompletedValue() { break; } } } } unsafe { fence_value += 1; queue.Signal(fence, fence_value); while fence.GetCompletedValue() < fence_value {} } imgui_renderer.destroy(&mut allocator); unsafe { for b in backbuffers { b.resource.as_ref().unwrap().Release(); } fence.Release(); command_list.Release(); command_allocator.Release(); swapchain.Release(); queue.Release(); device.Release(); adapter.Release(); dxgi_factory.Release(); } }); event_loop.run(move |event, _, control_flow| { *control_flow = winit::event_loop::ControlFlow::Wait; if event == winit::event::Event::UserEvent(()) { *control_flow = winit::event_loop::ControlFlow::Exit; } else if let Some(event) = event.to_static() { let _ = event_send.send(event); } else { *control_flow = winit::event_loop::ControlFlow::Exit; } }); }