use windows::Win32::{
Foundation::HWND, Graphics::Direct3D::*, Graphics::Direct3D12::*, Graphics::Dxgi::Common::*,
Graphics::Dxgi::*,
};
use windows::core::Interface;
fn main() -> anyhow::Result<()> {
pnte::co_initialize(pnte::CoInit::ApartmentThreaded)?;
unsafe {
let debug = {
let mut p: Option<ID3D12Debug> = None;
D3D12GetDebugInterface(&mut p).map(|_| p.unwrap())
};
if let Ok(debug) = debug {
debug.EnableDebugLayer();
println!("enabled d3d12 debug layer");
}
}
let mut event_rx = wiard::EventReceiver::new();
let window = wiard::Window::builder(&event_rx)
.title("pnte d3d12")
.build()?;
let size = window.inner_size().unwrap();
let dpi = window.dpi().unwrap();
let device: ID3D12Device = unsafe {
let mut p = None;
D3D12CreateDevice(None, D3D_FEATURE_LEVEL_12_0, &mut p).map(|_| p.unwrap())?
};
let cmd_queue: ID3D12CommandQueue = unsafe {
device.CreateCommandQueue(&D3D12_COMMAND_QUEUE_DESC {
Type: D3D12_COMMAND_LIST_TYPE_DIRECT,
..Default::default()
})?
};
let cmd_allocator: ID3D12CommandAllocator =
unsafe { device.CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT)? };
let cmd_list: ID3D12GraphicsCommandList = unsafe {
device.CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, &cmd_allocator, None)?
};
unsafe {
cmd_list.Close()?;
}
let dxgi_factory: IDXGIFactory6 = unsafe { CreateDXGIFactory1()? };
let swap_chain: IDXGISwapChain4 = unsafe {
dxgi_factory
.CreateSwapChainForHwnd(
&cmd_queue,
HWND(window.raw_handle()),
&DXGI_SWAP_CHAIN_DESC1 {
Width: size.width,
Height: size.height,
Format: DXGI_FORMAT_B8G8R8A8_UNORM,
BufferCount: 2,
BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
SampleDesc: DXGI_SAMPLE_DESC {
Count: 1,
Quality: 0,
},
..Default::default()
},
None,
None,
)?
.cast()?
};
let rtv_heap: ID3D12DescriptorHeap = unsafe {
device.CreateDescriptorHeap(&D3D12_DESCRIPTOR_HEAP_DESC {
Type: D3D12_DESCRIPTOR_HEAP_TYPE_RTV,
NumDescriptors: 2,
..Default::default()
})?
};
let inc = unsafe { device.GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV) };
let buffers = unsafe {
(0..2)
.map(|i| -> anyhow::Result<ID3D12Resource> {
let mut handle = rtv_heap.GetCPUDescriptorHandleForHeapStart();
handle.ptr += inc as usize * i;
let buffer: ID3D12Resource = swap_chain.GetBuffer(i as u32)?;
device.CreateRenderTargetView(&buffer, None, handle);
Ok(buffer)
})
.collect::<anyhow::Result<Vec<ID3D12Resource>>>()?
};
let mut ctx = pnte::Context::new(pnte::Direct3D12::new(&device, &cmd_queue, 0)?)?;
ctx.set_dpi(dpi as f32, dpi as f32);
let targets = buffers
.iter()
.map(|buffer| -> anyhow::Result<pnte::d3d12::RenderTarget> {
let target = ctx.create_render_target(buffer)?;
Ok(target)
})
.collect::<anyhow::Result<Vec<_>>>()?;
let fence: ID3D12Fence = unsafe { device.CreateFence(0, D3D12_FENCE_FLAG_NONE)? };
let mut next_frame = 1u64;
loop {
let event = event_rx.try_recv();
match event {
Ok((event, _)) => match event {
_ => {}
},
Err(wiard::TryRecvError::Empty) => unsafe {
if window.is_closed() {
continue;
}
let index = swap_chain.GetCurrentBackBufferIndex() as usize;
let mut handle = rtv_heap.GetCPUDescriptorHandleForHeapStart();
handle.ptr += inc as usize * index;
let buffer = &buffers[index];
let target = &targets[index];
cmd_allocator.Reset()?;
cmd_list.Reset(&cmd_allocator, None)?;
let barriers = [D3D12_RESOURCE_BARRIER {
Type: D3D12_RESOURCE_BARRIER_TYPE_TRANSITION,
Flags: D3D12_RESOURCE_BARRIER_FLAG_NONE,
Anonymous: D3D12_RESOURCE_BARRIER_0 {
Transition: std::mem::ManuallyDrop::new(
D3D12_RESOURCE_TRANSITION_BARRIER {
pResource: std::mem::ManuallyDrop::new(Some(buffer.clone())),
Subresource: 0,
StateBefore: D3D12_RESOURCE_STATE_PRESENT,
StateAfter: D3D12_RESOURCE_STATE_RENDER_TARGET,
},
),
},
}];
cmd_list.ResourceBarrier(&barriers);
barriers.into_iter().for_each(|barrier| {
let t = std::mem::ManuallyDrop::into_inner(barrier.Anonymous.Transition);
std::mem::ManuallyDrop::into_inner(t.pResource);
});
cmd_list.ClearRenderTargetView(handle, &[0.0, 0.0, 0.3, 0.0], None);
let barriers = [D3D12_RESOURCE_BARRIER {
Type: D3D12_RESOURCE_BARRIER_TYPE_TRANSITION,
Flags: D3D12_RESOURCE_BARRIER_FLAG_NONE,
Anonymous: D3D12_RESOURCE_BARRIER_0 {
Transition: std::mem::ManuallyDrop::new(
D3D12_RESOURCE_TRANSITION_BARRIER {
pResource: std::mem::ManuallyDrop::new(Some(buffer.clone())),
Subresource: 0,
StateBefore: D3D12_RESOURCE_STATE_RENDER_TARGET,
StateAfter: D3D12_RESOURCE_STATE_PRESENT,
},
),
},
}];
cmd_list.ResourceBarrier(&barriers);
barriers.into_iter().for_each(|barrier| {
let t = std::mem::ManuallyDrop::into_inner(barrier.Anonymous.Transition);
std::mem::ManuallyDrop::into_inner(t.pResource);
});
cmd_list.Close()?;
cmd_queue.ExecuteCommandLists(&[Some(cmd_list.cast().unwrap())]);
ctx.draw(target, |cmd| {
let yellow_green =
pnte::SolidColorBrush::new(&ctx, (0.5, 0.8, 0.0, 1.0)).unwrap();
let white = pnte::SolidColorBrush::new(&ctx, (1.0, 1.8, 1.0, 1.0)).unwrap();
cmd.fill(
&pnte::Rect::from_point_size((50.0, 50.0), (100.0, 100.0)),
&yellow_green,
);
cmd.draw_text("hello!!!", (200.0, 50.0), &white).ok();
})?;
swap_chain.Present(0, DXGI_PRESENT(0)).ok()?;
let frame = next_frame;
next_frame += 1;
cmd_queue.Signal(&fence, frame)?;
if fence.GetCompletedValue() < frame {
fence.SetEventOnCompletion(
frame,
windows::Win32::Foundation::HANDLE::default(),
)?;
}
},
Err(wiard::TryRecvError::Disconnected) => break,
}
}
Ok(())
}