use std::cmp::max;
use std::cmp::min;
use std::ffi::c_void;
use std::ffi::CStr;
use std::intrinsics::copy_nonoverlapping;
use std::mem::size_of;
use std::mem::MaybeUninit;
use std::os::raw::c_char;
use std::rc::Rc;
use std::slice;
use cgmath::vec3;
use cgmath::vec4;
use log::error;
use log::info;
use log::trace;
use log::warn;
use std::{ffi::CString, io::Read, slice::Windows};
use widestring::WideCStr;
use cgmath::{prelude::*, Vector2, Vector4};
use cgmath::{Matrix4, Vector3};
use memoffset::offset_of;
use rand::Rng;
use winit::{
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
platform::windows::WindowExtWindows,
window::WindowBuilder,
};
use static_assertions::assert_eq_size;
use rusty_d3d12::*;
fn wait_for_debugger() {
while unsafe { winapi::um::debugapi::IsDebuggerPresent() } == 0 {
std::thread::sleep_ms(1000);
}
}
#[no_mangle]
pub static D3D12SDKVersion: u32 = 600;
#[no_mangle]
pub static D3D12SDKPath: &[u8; 9] = b".\\D3D12\\\0";
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[no_mangle]
pub extern "C" fn debug_callback(
category: i32,
severity: i32,
id: i32,
description: *const c_char,
_context: *mut c_void,
) {
let severity: MessageSeverity = unsafe { std::mem::transmute(severity) };
let category: MessageCategory = unsafe { std::mem::transmute(category) };
let description = unsafe { CStr::from_ptr(description) };
match severity {
MessageSeverity::Message | MessageSeverity::Info => {
info!(
"[D3D12 Message][{}][{}][{:#x}] {}",
severity,
category,
id as i32,
description
.to_str()
.expect("Cannot make Rust string from D3D12 layer message")
);
}
MessageSeverity::Warning => {
warn!(
"[D3D12 Message][{}][{}][{:#x}] {}",
severity,
category,
id as i32,
description
.to_str()
.expect("Cannot make Rust string from D3D12 layer message")
);
}
_ => {
error!(
"[D3D12 Message][{}][{}][{:#x}] {}",
severity,
category,
id as i32,
description
.to_str()
.expect("Cannot make Rust string from D3D12 layer message")
);
}
}
}
const WINDOW_WIDTH: u32 = 640;
const WINDOW_HEIGHT: u32 = 480;
const FRAMES_IN_FLIGHT: usize = 3;
const USE_DEBUG: bool = true;
const USE_WARP_ADAPTER: bool = false;
const CLEAR_COLOR: [f32; 4] = [0.0, 0.2, 0.3, 1.0];
type Mat4 = Matrix4<f32>;
type Vec3 = Vector3<f32>;
type Vec4 = Vector4<f32>;
type Vec2 = Vector2<f32>;
#[repr(C)]
pub struct Vertex {
position: Vec4,
color: Vec4,
}
impl Vertex {
pub fn make_desc() -> Vec<InputElementDesc<'static>> {
vec![
InputElementDesc::default()
.with_semantic_name("POSITION")
.unwrap()
.with_format(Format::R32G32B32A32Float)
.with_input_slot(0)
.with_aligned_byte_offset(ByteCount::from(offset_of!(
Self, position
))),
InputElementDesc::default()
.with_semantic_name("COLOR")
.unwrap()
.with_format(Format::R32G32B32A32Float)
.with_input_slot(0)
.with_aligned_byte_offset(ByteCount::from(offset_of!(
Self, color
))),
]
}
}
#[repr(C)]
struct SceneConstantBuffer {
offset: Vec4,
color: Vec4,
}
struct InterprocessCommunicationSample {
pipeline: Pipeline,
}
impl InterprocessCommunicationSample {
fn new(hwnd: *mut std::ffi::c_void, is_producer_process: bool) -> Self {
let mut pipeline = Pipeline::new(hwnd, is_producer_process);
InterprocessCommunicationSample { pipeline }
}
fn draw(&mut self) {
self.pipeline.render();
}
}
impl Drop for InterprocessCommunicationSample {
fn drop(&mut self) {
if USE_DEBUG {
self.pipeline
.debug_device
.as_ref()
.expect("No debug devices created")
.report_live_device_objects()
.expect("Device cannot report live objects");
}
}
}
struct Pipeline {
is_producer_process: bool,
device: Device,
debug_device: Option<DebugDevice>,
info_queue: Option<Rc<InfoQueue>>,
direct_command_queue: CommandQueue,
swapchain: Swapchain,
frame_index: usize,
viewport: Viewport,
scissor_rect: Rect,
rtv_heap: DescriptorHeap,
rtv_descriptor_handle_size: ByteCount,
cbv_srv_heap: DescriptorHeap,
cbv_srv_descriptor_handle_size: ByteCount,
render_targets: Vec<Resource>,
direct_command_allocators: Vec<CommandAllocator>,
shared_heap: Heap,
cross_process_resource: Resource,
root_signature: RootSignature,
pipeline_state: PipelineState,
direct_command_list: CommandList,
vertex_buffer: Resource,
vertex_buffer_upload: Resource,
vertex_buffer_view: VertexBufferView,
triangle_constant_buffer: Resource,
frame_resource_fence: Fence,
frame_resource_fence_event: Win32Event,
shared_resource_fence: Fence,
shared_resource_fence_event: Win32Event,
current_frame_resource_fence_value: u64,
current_shared_fence_value: u64,
frame_resources_fence_values: Vec<u64>,
}
impl Pipeline {
fn new(hwnd: *mut c_void, is_producer_process: bool) -> Self {
let mut factory_flags = CreateFactoryFlags::None;
if USE_DEBUG {
let debug_controller =
Debug::new().expect("Cannot create debug controller");
debug_controller.enable_debug_layer();
debug_controller.enable_gpu_based_validation();
debug_controller.enable_object_auto_name();
factory_flags = CreateFactoryFlags::Debug;
}
let factory =
Factory::new(factory_flags).expect("Cannot create factory");
let (device, is_software_adapter) =
create_device(&factory, !is_producer_process);
let debug_device;
if USE_DEBUG {
debug_device = Some(
DebugDevice::new(&device).expect("Cannot create debug device"),
);
} else {
debug_device = None;
}
let info_queue;
if USE_DEBUG {
let temp_info_queue = Rc::from(
InfoQueue::new(
&device,
None,
)
.expect("Cannot create debug info queue"),
);
#[cfg(feature = "debug_callback")]
temp_info_queue
.register_callback(
debug_callback,
MessageCallbackFlags::FlagNone,
)
.expect("Cannot set debug callback on info queue");
info_queue = Some(temp_info_queue);
} else {
info_queue = None;
}
let direct_command_queue = device
.create_command_queue(
&CommandQueueDesc::default()
.with_queue_type(CommandListType::Direct),
)
.expect("Cannot create direct command queue");
let swapchain = create_swapchain(factory, &direct_command_queue, hwnd);
let frame_index = swapchain.get_current_back_buffer_index() as usize;
trace!("Swapchain returned frame index {}", frame_index);
let viewport = Viewport::default()
.with_top_left_x(0.)
.with_top_left_y(0.)
.with_width(WINDOW_WIDTH as f32)
.with_height(WINDOW_HEIGHT as f32);
let scissor_rect = Rect::default()
.with_left(0)
.with_top(0)
.with_right(WINDOW_WIDTH as i32)
.with_bottom(WINDOW_HEIGHT as i32);
let (rtv_heap, cbv_srv_heap) =
create_descriptor_heaps(&device, &swapchain);
let (render_targets, direct_command_allocators) =
create_frame_resources(
&device,
&rtv_heap,
&swapchain,
device.get_descriptor_handle_increment_size(
DescriptorHeapType::Rtv,
),
);
let (texture_size, cross_adapter_desc) =
create_shared_resource_desc(&device);
let shared_heap;
if is_producer_process {
shared_heap = device
.create_heap(
&HeapDesc::default()
.with_properties(
HeapProperties::default()
.with_heap_type(HeapType::Default),
)
.with_size_in_bytes(texture_size * FRAMES_IN_FLIGHT)
.with_flags(
HeapFlags::Shared | HeapFlags::SharedCrossAdapter,
),
)
.expect("Cannot create heap");
let heap_as_dc: DeviceChild = shared_heap.clone().into();
let heap_handle = device
.create_shared_handle(&heap_as_dc, "SharedHeapHandle")
.expect("Cannot create shared heap handle");
} else {
let shared_heap_handle = device
.open_shared_handle_by_name("SharedHeapHandle")
.expect("Cannot open SharedHeapHandle");
shared_heap = device
.open_shared_heap_handle(shared_heap_handle)
.expect("Cannot open shared heap");
shared_heap_handle.close();
}
trace!("Successfully created and opened heaps");
let mut cross_process_resource = device
.create_placed_resource(
&shared_heap,
texture_size,
&cross_adapter_desc,
ResourceStates::Common,
None,
)
.expect("Cannot create placed resource");
cross_process_resource
.set_name(&format!("shared texture"))
.expect("Cannot set resource name");
let root_signature = create_root_signature(&device);
trace!("Created root signature");
let pso = create_pso(&device, &root_signature);
let direct_command_list = create_command_list(
&device,
frame_index,
&direct_command_allocators,
&pso,
);
trace!("Created command list");
let (vertex_buffer, vertex_buffer_upload, vertex_buffer_view) =
create_vertex_buffer(&device, &direct_command_list);
let triangle_constant_buffer =
create_scene_constant_buffer(&device, is_producer_process);
trace!("Created triangle constant buffer");
let (
frame_resource_fence,
frame_resource_fence_event,
shared_resource_fence,
shared_resource_fence_event,
) = create_fences(&device, &direct_command_queue, is_producer_process);
trace!("Created fences");
{
direct_command_list
.close()
.expect("Cannot close command list");
direct_command_queue
.execute_command_lists(slice::from_ref(&direct_command_list));
direct_command_queue
.signal(&frame_resource_fence, 1)
.expect("Cannot signal fence");
frame_resource_fence
.set_event_on_completion(1, &frame_resource_fence_event)
.expect("Cannot set fence event");
frame_resource_fence_event.wait(None);
direct_command_queue
.signal(
&frame_resource_fence,
if is_producer_process { 0 } else { 1 },
)
.expect("Cannot signal fence");
}
trace!("Executed command lists");
let rtv_descriptor_handle_size = device
.get_descriptor_handle_increment_size(DescriptorHeapType::Rtv);
let cbv_srv_descriptor_handle_size = device
.get_descriptor_handle_increment_size(
DescriptorHeapType::CbvSrvUav,
);
Self {
is_producer_process,
device,
debug_device,
info_queue,
direct_command_queue,
swapchain,
frame_index,
viewport,
scissor_rect,
rtv_heap,
rtv_descriptor_handle_size,
cbv_srv_heap,
cbv_srv_descriptor_handle_size,
render_targets,
direct_command_allocators,
shared_heap,
cross_process_resource,
root_signature,
pipeline_state: pso,
direct_command_list,
vertex_buffer,
vertex_buffer_upload,
vertex_buffer_view,
triangle_constant_buffer,
frame_resource_fence,
frame_resource_fence_event,
shared_resource_fence,
shared_resource_fence_event,
current_frame_resource_fence_value: 0,
current_shared_fence_value: if is_producer_process { 0 } else { 1 },
frame_resources_fence_values: vec![0, 0, 0],
}
}
fn populate_producer_command_list(&mut self) {
self.direct_command_allocators[self.frame_index]
.reset()
.expect("Cannot reset direct command allocator");
self.direct_command_list
.reset(
&self.direct_command_allocators[self.frame_index],
Some(&self.pipeline_state),
)
.expect("Cannot reset direct command list");
self.direct_command_list
.set_graphics_root_signature(&self.root_signature);
self.direct_command_list
.set_viewports(slice::from_ref(&self.viewport));
self.direct_command_list
.set_scissor_rects(slice::from_ref(&self.scissor_rect));
self.direct_command_list.resource_barrier(slice::from_ref(
&ResourceBarrier::new_transition(
&ResourceTransitionBarrier::default()
.with_resource(&self.render_targets[self.frame_index])
.with_state_before(ResourceStates::Common)
.with_state_after(ResourceStates::RenderTarget),
),
));
let rtv_handle = self
.rtv_heap
.get_cpu_descriptor_handle_for_heap_start()
.advance(self.frame_index as u32, self.rtv_descriptor_handle_size);
self.direct_command_list.set_render_targets(
slice::from_ref(&rtv_handle),
false,
None,
);
self.direct_command_list.clear_render_target_view(
rtv_handle,
CLEAR_COLOR,
&[],
);
self.direct_command_list
.set_primitive_topology(PrimitiveTopology::TriangleList);
self.direct_command_list
.set_vertex_buffers(0, slice::from_ref(&self.vertex_buffer_view));
self.direct_command_list
.set_graphics_root_constant_buffer_view(
0,
GpuVirtualAddress(
self.triangle_constant_buffer.get_gpu_virtual_address().0,
),
);
self.direct_command_list.draw_instanced(3, 1, 0, 0);
self.direct_command_list.resource_barrier(&[
ResourceBarrier::new_transition(
&ResourceTransitionBarrier::default()
.with_resource(&self.render_targets[self.frame_index])
.with_state_before(ResourceStates::RenderTarget)
.with_state_after(ResourceStates::CopySource),
),
ResourceBarrier::new_transition(
&ResourceTransitionBarrier::default()
.with_resource(&self.cross_process_resource)
.with_state_before(ResourceStates::Common)
.with_state_after(ResourceStates::CopyDest),
),
]);
self.direct_command_list.copy_resource(
&self.cross_process_resource,
&self.render_targets[self.frame_index],
);
self.direct_command_list.resource_barrier(&[
ResourceBarrier::new_transition(
&ResourceTransitionBarrier::default()
.with_resource(&self.render_targets[self.frame_index])
.with_state_before(ResourceStates::CopySource)
.with_state_after(ResourceStates::Common),
),
ResourceBarrier::new_transition(
&ResourceTransitionBarrier::default()
.with_resource(&self.cross_process_resource)
.with_state_before(ResourceStates::CopyDest)
.with_state_after(ResourceStates::Common),
),
]);
self.direct_command_list
.close()
.expect("Cannot close command list");
}
fn populate_consumer_command_list(&mut self) {
self.direct_command_allocators[self.frame_index]
.reset()
.expect("Cannot reset direct command allocator");
self.direct_command_list
.reset(
&self.direct_command_allocators[self.frame_index],
Some(&self.pipeline_state),
)
.expect("Cannot reset direct command list");
self.direct_command_list
.set_graphics_root_signature(&self.root_signature);
self.direct_command_list
.set_viewports(slice::from_ref(&self.viewport));
self.direct_command_list
.set_scissor_rects(slice::from_ref(&self.scissor_rect));
self.direct_command_list.resource_barrier(&[
ResourceBarrier::new_transition(
&ResourceTransitionBarrier::default()
.with_resource(&self.render_targets[self.frame_index])
.with_state_before(ResourceStates::Common)
.with_state_after(ResourceStates::CopyDest),
),
ResourceBarrier::new_transition(
&ResourceTransitionBarrier::default()
.with_resource(&self.cross_process_resource)
.with_state_before(ResourceStates::Common)
.with_state_after(ResourceStates::CopySource),
),
]);
self.direct_command_list.copy_resource(
&self.render_targets[self.frame_index],
&self.cross_process_resource,
);
self.direct_command_list.resource_barrier(&[
ResourceBarrier::new_transition(
&ResourceTransitionBarrier::default()
.with_resource(&self.render_targets[self.frame_index])
.with_state_before(ResourceStates::CopyDest)
.with_state_after(ResourceStates::RenderTarget),
),
ResourceBarrier::new_transition(
&ResourceTransitionBarrier::default()
.with_resource(&self.cross_process_resource)
.with_state_before(ResourceStates::CopySource)
.with_state_after(ResourceStates::Common),
),
]);
let rtv_handle = self
.rtv_heap
.get_cpu_descriptor_handle_for_heap_start()
.advance(self.frame_index as u32, self.rtv_descriptor_handle_size);
self.direct_command_list.set_render_targets(
slice::from_ref(&rtv_handle),
false,
None,
);
self.direct_command_list
.set_primitive_topology(PrimitiveTopology::TriangleList);
self.direct_command_list
.set_vertex_buffers(0, slice::from_ref(&self.vertex_buffer_view));
self.direct_command_list
.set_graphics_root_constant_buffer_view(
0,
GpuVirtualAddress(
self.triangle_constant_buffer.get_gpu_virtual_address().0,
),
);
self.direct_command_list.draw_instanced(3, 1, 0, 0);
self.direct_command_list.resource_barrier(slice::from_ref(
&ResourceBarrier::new_transition(
&ResourceTransitionBarrier::default()
.with_resource(&self.render_targets[self.frame_index])
.with_state_before(ResourceStates::RenderTarget)
.with_state_after(ResourceStates::Common),
),
));
self.direct_command_list
.close()
.expect("Cannot close command list");
}
fn render(&mut self) {
trace!("Rendering frame, idx {}", self.frame_index);
let completed_shared_resource_fence_value =
self.shared_resource_fence.get_completed_value();
if completed_shared_resource_fence_value
< self.current_shared_fence_value
{
self.shared_resource_fence
.set_event_on_completion(
self.current_shared_fence_value,
&self.shared_resource_fence_event,
)
.expect("Cannot set fence event");
self.shared_resource_fence_event.wait(None);
}
if self.is_producer_process {
self.populate_producer_command_list();
} else {
self.populate_consumer_command_list();
}
self.direct_command_queue
.execute_command_lists(slice::from_ref(&self.direct_command_list));
self.current_frame_resource_fence_value += 1;
self.direct_command_queue
.signal(
&self.frame_resource_fence,
self.current_frame_resource_fence_value,
)
.expect("Cannot signal direct command queue");
self.frame_resources_fence_values[self.frame_index] =
self.current_frame_resource_fence_value;
self.current_shared_fence_value += 1;
self.direct_command_queue
.signal(
&self.shared_resource_fence,
self.current_shared_fence_value,
)
.expect("Cannot signal direct command queue");
self.current_shared_fence_value += 1;
self.swapchain
.present(1, PresentFlags::None)
.expect("Cannot present");
self.move_to_next_frame();
}
fn move_to_next_frame(&mut self) {
self.frame_index =
self.swapchain.get_current_back_buffer_index() as usize;
let completed_frame_fence_value =
self.frame_resource_fence.get_completed_value();
if completed_frame_fence_value
< self.frame_resources_fence_values[self.frame_index]
{
self.frame_resource_fence
.set_event_on_completion(
self.frame_resources_fence_values[self.frame_index],
&self.frame_resource_fence_event,
)
.expect("Cannot set fence event");
self.frame_resource_fence_event.wait(None);
}
}
}
fn create_fences(
device: &Device,
direct_command_queue: &CommandQueue,
is_producer_process: bool,
) -> (Fence, Win32Event, Fence, Win32Event) {
let frame_resource_fence = device
.create_fence(0, FenceFlags::None)
.expect("Cannot create frame_resource_fence");
let frame_resource_fence_event = Win32Event::default();
let shared_resource_fence;
let shared_resource_fence_event = Win32Event::default();
if is_producer_process {
shared_resource_fence = device
.create_fence(0, FenceFlags::Shared | FenceFlags::CrossAdapter)
.expect("Cannot create fence");
let shared_resource_fence_handle = device
.create_shared_handle(
&shared_resource_fence.clone().into(),
"CrossProcessResourceFence",
)
.expect(
"Cannot create shared handle for CrossProcessResourceFence",
);
} else {
let shared_resource_fence_handle = device
.open_shared_handle_by_name("CrossProcessResourceFence")
.expect("Cannot open CrossProcessConsumerFence handle");
shared_resource_fence = device
.open_shared_fence_handle(shared_resource_fence_handle)
.expect("Cannot open frame_resource_fence handle");
shared_resource_fence_handle.close();
}
(
frame_resource_fence,
frame_resource_fence_event,
shared_resource_fence,
shared_resource_fence_event,
)
}
fn create_scene_constant_buffer(
device: &Device,
is_producer_process: bool,
) -> Resource {
let constant_buffer_size =
ByteCount::from(size_of::<SceneConstantBuffer>() as u32);
let constant_buffer = device
.create_committed_resource(
&HeapProperties::default().with_heap_type(HeapType::Upload),
HeapFlags::None,
&ResourceDesc::default()
.with_dimension(ResourceDimension::Buffer)
.with_layout(TextureLayout::RowMajor)
.with_width(constant_buffer_size.0.into()),
ResourceStates::GenericRead,
None,
)
.expect("Cannot create constant buffer");
constant_buffer
.set_name("Constant buffer")
.expect("Cannot set name on resource");
let mut constant_buffer_data = SceneConstantBuffer {
offset: if is_producer_process {
vec4(0., 0., 0., 0.)
} else {
vec4(1., 0., 0., 0.)
},
color: vec4(0.8, 0.1, 0.2, 1.),
};
let mapped_data = constant_buffer
.map(0, None)
.expect("Cannot map constant buffer");
unsafe {
copy_nonoverlapping(
&constant_buffer_data,
mapped_data as *mut SceneConstantBuffer,
1,
);
}
constant_buffer
}
fn create_vertex_buffer(
device: &Device,
direct_command_list: &CommandList,
) -> (Resource, Resource, VertexBufferView) {
let triangle_vertices = [
Vertex {
position: vec4(-1., 0., 0., 1.),
color: vec4(0.8, 0.2, 0.1, 1.),
},
Vertex {
position: vec4(-0.5, 1., 0., 1.),
color: vec4(0.8, 0.2, 0.1, 1.),
},
Vertex {
position: vec4(0., 0., 0., 1.),
color: vec4(0.8, 0.2, 0.1, 1.),
},
];
let vertex_buffer_size =
ByteCount::from(triangle_vertices.len() * size_of::<Vertex>());
let vertex_buffer = device
.create_committed_resource(
&HeapProperties::default().with_heap_type(HeapType::Default),
HeapFlags::None,
&ResourceDesc::default()
.with_dimension(ResourceDimension::Buffer)
.with_layout(TextureLayout::RowMajor)
.with_width(vertex_buffer_size.0),
ResourceStates::CopyDest,
None,
)
.expect("Cannot create vertex_buffer");
vertex_buffer
.set_name("Vertex buffer")
.expect("Cannot set name on resource");
let vertex_buffer_upload = device
.create_committed_resource(
&HeapProperties::default().with_heap_type(HeapType::Upload),
HeapFlags::None,
&ResourceDesc::default()
.with_dimension(ResourceDimension::Buffer)
.with_layout(TextureLayout::RowMajor)
.with_width(vertex_buffer_size.0.into()),
ResourceStates::GenericRead,
None,
)
.expect("Cannot create vertex_buffer");
vertex_buffer_upload
.set_name("Vertex buffer upload")
.expect("Cannot set name on resource");
let vertex_data = SubresourceData::default()
.with_data(&triangle_vertices)
.with_row_pitch(vertex_buffer_size)
.with_slice_pitch(vertex_buffer_size);
direct_command_list
.update_subresources_heap_alloc(
&vertex_buffer,
&vertex_buffer_upload,
ByteCount(0),
0,
1,
slice::from_ref(&vertex_data),
)
.expect("Cannot upload vertex buffer");
trace!("Uploaded vertex buffer");
direct_command_list.resource_barrier(slice::from_ref(
&ResourceBarrier::new_transition(
&ResourceTransitionBarrier::default()
.with_resource(&vertex_buffer)
.with_state_before(ResourceStates::CopyDest)
.with_state_after(ResourceStates::VertexAndConstantBuffer),
),
));
let vertex_buffer_view = VertexBufferView::default()
.with_buffer_location(vertex_buffer.get_gpu_virtual_address())
.with_stride_in_bytes(ByteCount::from(std::mem::size_of::<Vertex>()))
.with_size_in_bytes(vertex_buffer_size);
trace!("Created primary adapter vertex buffer");
(vertex_buffer, vertex_buffer_upload, vertex_buffer_view)
}
fn create_command_list(
device: &Device,
frame_index: usize,
direct_command_allocators: &[CommandAllocator],
pso: &PipelineState,
) -> CommandList {
device
.create_command_list(
CommandListType::Direct,
&direct_command_allocators[frame_index],
Some(pso),
)
.expect("Cannot create direct command list")
}
fn create_pso(
device: &Device,
root_signature: &RootSignature,
) -> PipelineState {
let vertex_shader = compile_shader(
"VertexShader",
&std::fs::read_to_string("assets/ic_shaders.hlsl")
.expect("Cannot open vertex shader file"),
"VShader",
"vs_6_0",
&[],
&[],
)
.expect("Cannot compile vertex shader");
let pixel_shader = compile_shader(
"PixelShader",
&std::fs::read_to_string("assets/ic_shaders.hlsl")
.expect("Cannot open pixel shader file"),
"PShader",
"ps_6_0",
&[],
&[],
)
.expect("Cannot compile pixel shader");
let input_layout = Vertex::make_desc();
let vs_bytecode = ShaderBytecode::new(&vertex_shader);
let ps_bytecode = ShaderBytecode::new(&pixel_shader);
let input_layout =
InputLayoutDesc::default().with_input_elements(&input_layout);
let pso_desc = GraphicsPipelineStateDesc::default()
.with_input_layout(&input_layout)
.with_root_signature(root_signature)
.with_vs_bytecode(&vs_bytecode)
.with_ps_bytecode(&ps_bytecode)
.with_rasterizer_state(RasterizerDesc::default())
.with_blend_state(BlendDesc::default())
.with_depth_stencil_state(
DepthStencilDesc::default().with_depth_enable(false),
)
.with_primitive_topology_type(PrimitiveTopologyType::Triangle)
.with_rtv_formats(&[Format::R8G8B8A8Unorm])
.with_dsv_format(Format::D32Float);
let pso = device
.create_graphics_pipeline_state(&pso_desc)
.expect("Cannot create PSO");
pso.set_name("Main PSO").expect("Cannot set name on pso");
trace!("Created PSO");
pso
}
fn create_root_signature(device: &Device) -> RootSignature {
let root_signature = {
let root_parameters = [RootParameter::default()
.with_shader_visibility(ShaderVisibility::Vertex)
.new_descriptor(
&RootDescriptor::default().with_shader_register(0),
RootParameterType::Cbv,
)];
let root_signature_desc = VersionedRootSignatureDesc::default()
.with_desc_1_1(
&RootSignatureDesc::default()
.with_parameters(&root_parameters)
.with_flags(
RootSignatureFlags::AllowInputAssemblerInputLayout,
),
);
let (serialized_signature, serialization_result) =
RootSignature::serialize_versioned(&root_signature_desc);
assert!(
serialization_result.is_ok(),
"Result: {}",
&serialization_result.err().unwrap()
);
let root_signature = device
.create_root_signature(
0,
&ShaderBytecode::new(serialized_signature.get_buffer()),
)
.expect("Cannot create root signature on device 0");
root_signature
};
root_signature
}
fn create_shared_resource_desc(device: &Device) -> (ByteCount, ResourceDesc) {
let texture_size: ByteCount;
let cross_adapter_desc = ResourceDesc::default()
.with_dimension(ResourceDimension::Texture2D)
.with_layout(TextureLayout::RowMajor)
.with_format(Format::R8G8B8A8Unorm)
.with_width(WINDOW_WIDTH.into())
.with_height(WINDOW_HEIGHT.into())
.with_flags(ResourceFlags::AllowCrossAdapter);
let (layout, _, _, _) =
device.get_copyable_footprints(&cross_adapter_desc, 0, 1, 0.into());
texture_size = align_to_multiple(
(layout[0].footprint().row_pitch() * layout[0].footprint().height()).0,
DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT.0,
)
.into();
(texture_size, cross_adapter_desc)
}
fn create_frame_resources(
device: &Device,
rtv_heap: &DescriptorHeap,
swapchain: &Swapchain,
rtv_descriptor_handle_size: ByteCount,
) -> (Vec<Resource>, Vec<CommandAllocator>) {
let clear_value = ClearValue::default()
.with_format(Format::R8G8B8A8Unorm)
.with_color(CLEAR_COLOR);
let render_target_desc = ResourceDesc::default()
.with_dimension(ResourceDimension::Texture2D)
.with_format(Format::R8G8B8A8Unorm)
.with_width(WINDOW_WIDTH.into())
.with_height(WINDOW_HEIGHT.into())
.with_flags(ResourceFlags::AllowRenderTarget);
let mut render_targets = vec![];
for frame_idx in 0..FRAMES_IN_FLIGHT {
render_targets.push(
swapchain
.get_buffer(frame_idx as u32)
.expect("Cannot get buffer from swapchain"),
);
}
let mut direct_command_allocators = vec![];
let mut rtv_handle = rtv_heap.get_cpu_descriptor_handle_for_heap_start();
for frame_idx in 0..FRAMES_IN_FLIGHT {
device
.create_render_target_view(&render_targets[frame_idx], rtv_handle);
rtv_handle = rtv_handle.advance(1, rtv_descriptor_handle_size);
direct_command_allocators.push(
device
.create_command_allocator(CommandListType::Direct)
.expect("Cannot create command allocator"),
);
}
trace!("created command allocators");
(render_targets, direct_command_allocators)
}
fn create_descriptor_heaps(
device: &Device,
swapchain: &Swapchain,
) -> (DescriptorHeap, DescriptorHeap) {
let num_descriptors = FRAMES_IN_FLIGHT as u32;
let mut rtv_heap = device
.create_descriptor_heap(
&DescriptorHeapDesc::default()
.with_heap_type(DescriptorHeapType::Rtv)
.with_num_descriptors(num_descriptors),
)
.expect("Cannot create RTV heap");
rtv_heap
.set_name("RTV heap")
.expect("Cannot set RTV heap name");
let cbv_srv_heap = device
.create_descriptor_heap(
&DescriptorHeapDesc::default()
.with_heap_type(DescriptorHeapType::CbvSrvUav)
.with_num_descriptors(FRAMES_IN_FLIGHT as u32 + 1)
.with_flags(DescriptorHeapFlags::ShaderVisible),
)
.expect("Cannot create CBV_SRV heap");
cbv_srv_heap
.set_name("CBV_SRV heap")
.expect("Cannot set CBV_SRV heap name");
(rtv_heap, cbv_srv_heap)
}
fn create_swapchain(
factory: Factory,
command_queue: &CommandQueue,
hwnd: *mut std::ffi::c_void,
) -> Swapchain {
let swapchain_desc = SwapChainDesc::default()
.with_width(WINDOW_WIDTH)
.with_height(WINDOW_HEIGHT)
.with_buffer_count(FRAMES_IN_FLIGHT as u32);
let swapchain = factory
.create_swapchain(&command_queue, hwnd as *mut HWND__, &swapchain_desc)
.expect("Cannot create swapchain");
factory
.make_window_association(hwnd, MakeWindowAssociationFlags::NoAltEnter)
.expect("Cannot make window association");
swapchain
}
fn get_hardware_adapter(factory: &Factory) -> Adapter {
let mut adapters = factory
.enum_adapters_by_gpu_preference(GpuPreference::HighPerformance)
.expect("Cannot enumerate adapters");
for adapter in &adapters {
let desc = adapter.get_desc().expect("Cannot get adapter desc");
info!("found adapter: {}", desc);
}
adapters.remove(0)
}
fn create_device(factory: &Factory, use_warp: bool) -> (Device, bool) {
let adapter;
if use_warp {
adapter = factory
.enum_warp_adapter()
.expect("Cannot enum warp adapter");
} else {
adapter = get_hardware_adapter(factory);
}
let adapter_desc = adapter.get_desc().expect("Cannot get adapter desc");
info!("Enumerated adapter: \n\t{}", adapter_desc,);
(
Device::new(&adapter).unwrap_or_else(|_| {
panic!("Cannot create device on adapter {}", adapter_desc)
}),
adapter_desc.is_software(),
)
}
fn main() {
let command_args = clap::App::new("InterprocessCommunicationSample")
.arg(
clap::Arg::with_name("breakonerr")
.short("b")
.takes_value(false)
.help("Break on validation errors"),
)
.arg(
clap::Arg::with_name("v")
.short("v")
.multiple(true)
.help("Verbosity level"),
)
.arg(
clap::Arg::with_name("primary")
.short("p")
.takes_value(false)
.help("Defines if current instance is the primary one"),
)
.get_matches();
let log_level: log::Level;
match command_args.occurrences_of("v") {
0 => log_level = log::Level::Debug,
1 | _ => log_level = log::Level::Trace,
};
simple_logger::init_with_level(log_level).unwrap();
let event_loop = EventLoop::new();
let window = WindowBuilder::new()
.build(&event_loop)
.expect("Cannot create window");
window.set_inner_size(winit::dpi::LogicalSize::new(
WINDOW_WIDTH,
WINDOW_HEIGHT,
));
let mut sample = InterprocessCommunicationSample::new(
window.hwnd(),
command_args.is_present("primary"),
);
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Poll;
match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => *control_flow = ControlFlow::Exit,
Event::MainEventsCleared => {
window.request_redraw();
}
Event::RedrawRequested(_) => {
sample.draw();
}
_ => (),
}
});
}