use std::cell::Cell;
use windows::core::{Interface, PCWSTR};
use windows::Win32::{
Foundation::*,
Graphics::{Direct3D::*, Direct3D12::*, Dxgi::Common::*, Dxgi::*},
System::{Com::*, Threading::*, WindowsProgramming::*},
};
fn resource_barrier(
command_list: &ID3D12GraphicsCommandList,
resource: &ID3D12Resource,
before: D3D12_RESOURCE_STATES,
after: D3D12_RESOURCE_STATES,
) {
unsafe {
let mut barrier = [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: Some(resource.clone()),
Subresource: 0,
StateBefore: before,
StateAfter: after,
}),
},
}];
command_list.ResourceBarrier(&barrier);
std::mem::ManuallyDrop::drop(&mut barrier[0].Anonymous.Transition);
}
}
struct Application {
d3d12_device: ID3D12Device,
command_queue: ID3D12CommandQueue,
command_allocator: ID3D12CommandAllocator,
command_list: ID3D12GraphicsCommandList,
swap_chain: IDXGISwapChain4,
rtv_heap: ID3D12DescriptorHeap,
rtv_descriptor_size: usize,
render_targets: Vec<ID3D12Resource>,
fence: ID3D12Fence,
fence_value: Cell<u64>,
context: mltg::Context<mltg::Direct3D12>,
factory: mltg::Factory,
bitmaps: Vec<mltg::d3d12::RenderTarget>,
text: mltg::TextLayout,
white_brush: mltg::Brush,
grad: mltg::GradientStopCollection,
image: mltg::Image,
}
impl Application {
fn new() -> anyhow::Result<Self> {
unsafe {
CoInitialize(std::ptr::null_mut())?;
let window = wita::WindowBuilder::new().title("mltg d3d12").build()?;
let window_size = window.inner_size();
let d3d12_device = {
let mut device: Option<ID3D12Device> = None;
D3D12CreateDevice(None, D3D_FEATURE_LEVEL_12_0, &mut device)
.map(|_| device.unwrap())?
};
let command_queue: ID3D12CommandQueue =
d3d12_device.CreateCommandQueue(&D3D12_COMMAND_QUEUE_DESC {
Type: D3D12_COMMAND_LIST_TYPE_DIRECT,
..Default::default()
})?;
let command_allocator: ID3D12CommandAllocator =
d3d12_device.CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT)?;
let command_list: ID3D12GraphicsCommandList = d3d12_device.CreateCommandList(
0,
D3D12_COMMAND_LIST_TYPE_DIRECT,
&command_allocator,
None,
)?;
command_list.Close().unwrap();
let dxgi_factory: IDXGIFactory4 = CreateDXGIFactory1()?;
let swap_chain: IDXGISwapChain4 = {
dxgi_factory
.CreateSwapChainForHwnd(
&command_queue,
HWND(window.raw_handle() as _),
&DXGI_SWAP_CHAIN_DESC1 {
Width: window_size.width as _,
Height: window_size.height as _,
Format: DXGI_FORMAT_B8G8R8A8_UNORM,
BufferCount: 2,
SwapEffect: DXGI_SWAP_EFFECT_FLIP_DISCARD,
Scaling: DXGI_SCALING_NONE,
SampleDesc: DXGI_SAMPLE_DESC {
Count: 1,
Quality: 0,
},
..Default::default()
},
std::ptr::null(),
None,
)?
.cast()?
};
let rtv_heap: ID3D12DescriptorHeap =
d3d12_device.CreateDescriptorHeap(&D3D12_DESCRIPTOR_HEAP_DESC {
Type: D3D12_DESCRIPTOR_HEAP_TYPE_RTV,
NumDescriptors: 2,
..Default::default()
})?;
let rtv_descriptor_size = d3d12_device
.GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV)
as usize;
let render_targets = {
let mut handle = rtv_heap.GetCPUDescriptorHandleForHeapStart();
let mut buffers = vec![];
for i in 0..2 {
let buffer: ID3D12Resource = swap_chain.GetBuffer(i as _)?;
d3d12_device.CreateRenderTargetView(&buffer, std::ptr::null(), handle);
buffers.push(buffer);
handle.ptr += rtv_descriptor_size;
}
buffers
};
let fence = d3d12_device.CreateFence(0, D3D12_FENCE_FLAG_NONE)?;
let context =
mltg::Context::new(mltg::Direct3D12::new(&d3d12_device, &command_queue)?)?;
let factory = context.create_factory();
let bitmaps = context.create_back_buffers(&swap_chain)?;
let text_format = factory.create_text_format(
mltg::Font::System("Meiryo"),
mltg::font_point(14.0),
None,
)?;
let text = factory.create_text_layout(
"abcdefghijklmnopqrstuvwxyz",
&text_format,
mltg::TextAlignment::Leading,
None,
)?;
let white_brush = factory.create_solid_color_brush([1.0, 1.0, 1.0, 1.0])?;
let grad = factory.create_gradient_stop_collection(&[
(0.0, [1.0, 0.0, 0.0, 1.0]),
(1.0, [0.0, 1.0, 0.0, 1.0]),
])?;
let image = factory.create_image("examples/ferris.png")?;
context.set_dpi(window.dpi() as _);
Ok(Self {
d3d12_device,
command_queue,
command_allocator,
command_list,
swap_chain,
rtv_heap,
rtv_descriptor_size,
render_targets,
fence,
fence_value: Cell::new(1),
context,
factory,
bitmaps,
text,
white_brush,
grad,
image,
})
}
}
fn wait_gpu(&self) {
unsafe {
let fv = self.fence_value.get();
self.command_queue.Signal(&self.fence, fv).unwrap();
if self.fence.GetCompletedValue() < fv {
let event =
CreateEventW(std::ptr::null_mut(), false, false, PCWSTR::default()).unwrap();
self.fence.SetEventOnCompletion(fv, event).unwrap();
WaitForSingleObject(event, INFINITE);
CloseHandle(event);
}
self.fence_value.set(fv + 1);
}
}
}
impl wita::EventHandler for Application {
fn draw(&mut self, ev: wita::event::Draw) {
const CLEAR_COLOR: [f32; 4] = [0.0, 0.0, 0.3, 0.0];
let window_size = ev.window.inner_size().to_logical(ev.window.dpi());
let hw = window_size.width as f32 / 2.0;
let hh = window_size.height as f32 / 2.0;
let rect = {
let margin = 30.0;
let pos = mltg::point(margin, margin);
let pos2 = pos.x * 2.0;
mltg::rect(pos, (hw - pos2, hh - pos2))
};
let text_box = mltg::rect((hw, hh), self.text.size());
let path = self
.factory
.create_path()
.begin((30.0, hh + 30.0))
.cubic_bezier_to(
(hw / 2.0, hh + 30.0),
(hw / 2.0, window_size.height as f32 - 30.0),
(hw - 30.0, window_size.height as f32 - 30.0),
)
.end(mltg::FigureEnd::Open)
.build();
let linear_grad_brush = self
.factory
.create_linear_gradient_brush((30.0, 30.0), (hw - 30.0, hh - 30.0), &self.grad)
.unwrap();
let image_size = {
let size = self.image.size();
(size.width as f32 / 4.0, size.height as f32 / 4.0)
};
unsafe {
let index = self.swap_chain.GetCurrentBackBufferIndex() as usize;
let mut rtv_handle = self.rtv_heap.GetCPUDescriptorHandleForHeapStart();
rtv_handle.ptr += self.rtv_descriptor_size * index;
self.command_allocator.Reset().unwrap();
self.command_list
.Reset(&self.command_allocator, None)
.unwrap();
resource_barrier(
&self.command_list,
&self.render_targets[index],
D3D12_RESOURCE_STATE_PRESENT,
D3D12_RESOURCE_STATE_RENDER_TARGET,
);
let viewports = [D3D12_VIEWPORT {
Width: window_size.width as _,
Height: window_size.height as _,
MaxDepth: 1.0,
..Default::default()
}];
self.command_list.RSSetViewports(&viewports);
let scissor_rect = [RECT {
right: window_size.width as _,
bottom: window_size.height as _,
..Default::default()
}];
self.command_list.RSSetScissorRects(&scissor_rect);
self.command_list
.ClearRenderTargetView(rtv_handle, CLEAR_COLOR.as_ptr(), &[]);
self.command_list.Close().unwrap();
let command_lists = [Some(self.command_list.cast::<ID3D12CommandList>().unwrap())];
self.command_queue.ExecuteCommandLists(&command_lists);
let ret = self.context.draw(&self.bitmaps[index], |cmd| {
cmd.fill(&rect, &linear_grad_brush);
cmd.stroke(&text_box, &self.white_brush, 2.0, None);
cmd.draw_text_layout(&self.text, &self.white_brush, (hw, hh));
cmd.draw_image(
&self.image,
mltg::Rect::new((hw, 10.0), image_size),
None,
mltg::Interpolation::HighQualityCubic,
);
cmd.stroke(&path, &self.white_brush, 5.0, None);
});
match ret {
Ok(_) => {
self.swap_chain.Present(1, 0).unwrap();
}
Err(e) if e == mltg::ErrorKind::RecreateTarget => {
self.bitmaps.clear();
self.context.flush();
self.bitmaps = self.context.create_back_buffers(&self.swap_chain).unwrap();
ev.window.redraw();
}
Err(e) => panic!("{:?}", e),
}
}
self.wait_gpu();
}
fn dpi_changed(&mut self, ev: wita::event::DpiChanged) {
self.context.set_dpi(ev.new_dpi as _);
self.resizing(wita::event::Resizing {
window: ev.window,
size: &mut ev.window.inner_size(),
});
}
fn resizing(&mut self, ev: wita::event::Resizing) {
self.bitmaps.clear();
self.render_targets.clear();
self.context.flush();
unsafe {
self.swap_chain
.ResizeBuffers(0, ev.size.width, ev.size.height, DXGI_FORMAT_UNKNOWN, 0)
.unwrap();
}
self.render_targets = unsafe {
let mut handle = self.rtv_heap.GetCPUDescriptorHandleForHeapStart();
let mut buffers = vec![];
for i in 0..2 {
let buffer: ID3D12Resource = self.swap_chain.GetBuffer(i as _).unwrap();
self.d3d12_device
.CreateRenderTargetView(&buffer, std::ptr::null(), handle);
buffers.push(buffer);
handle.ptr += self.rtv_descriptor_size;
}
buffers
};
self.bitmaps = unsafe { self.context.create_back_buffers(&self.swap_chain).unwrap() };
ev.window.redraw();
}
}
fn main() {
wita::run(wita::RunType::Wait, Application::new).unwrap();
}