Vendor things

This commit is contained in:
John Doty 2024-03-08 11:03:01 -08:00
parent 5deceec006
commit 977e3c17e5
19434 changed files with 10682014 additions and 0 deletions

View file

@ -0,0 +1,237 @@
//! Example showcasing [`gpu-allocator`] with types and functions from the [`windows`] crate.
use gpu_allocator::d3d12::{
AllocationCreateDesc, Allocator, AllocatorCreateDesc, ResourceCategory,
};
use gpu_allocator::MemoryLocation;
use log::*;
use windows::core::{Interface, Result};
use windows::Win32::{
Foundation::E_NOINTERFACE,
Graphics::{
Direct3D::{D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_12_0},
Direct3D12::{
D3D12CreateDevice, ID3D12Device, ID3D12Resource,
D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT, D3D12_RESOURCE_DESC,
D3D12_RESOURCE_DIMENSION_BUFFER, D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_COMMON,
D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
},
Dxgi::Common::{DXGI_FORMAT_UNKNOWN, DXGI_SAMPLE_DESC},
Dxgi::{
CreateDXGIFactory2, IDXGIAdapter4, IDXGIFactory6, DXGI_ADAPTER_FLAG3_SOFTWARE,
DXGI_ERROR_NOT_FOUND,
},
},
};
fn create_d3d12_device(dxgi_factory: &IDXGIFactory6) -> Option<ID3D12Device> {
for idx in 0.. {
// TODO: Might as well return Result<> from this function
let adapter1 = match unsafe { dxgi_factory.EnumAdapters1(idx) } {
Ok(a) => a,
Err(e) if e.code() == DXGI_ERROR_NOT_FOUND => break,
Err(e) => panic!("{:?}", e),
};
let adapter4: IDXGIAdapter4 = adapter1.cast().unwrap();
let mut desc = Default::default();
unsafe { adapter4.GetDesc3(&mut desc) }.unwrap();
// Skip software adapters
// Vote for https://github.com/microsoft/windows-rs/issues/793!
if (desc.Flags & DXGI_ADAPTER_FLAG3_SOFTWARE) == DXGI_ADAPTER_FLAG3_SOFTWARE {
continue;
}
let feature_levels = [
(D3D_FEATURE_LEVEL_11_0, "D3D_FEATURE_LEVEL_11_0"),
(D3D_FEATURE_LEVEL_11_1, "D3D_FEATURE_LEVEL_11_1"),
(D3D_FEATURE_LEVEL_12_0, "D3D_FEATURE_LEVEL_12_0"),
];
let device =
feature_levels
.iter()
.rev()
.find_map(|&(feature_level, feature_level_name)| {
let mut device = None;
match unsafe { D3D12CreateDevice(&adapter4, feature_level, &mut device) } {
Ok(()) => {
info!("Using D3D12 feature level: {}", feature_level_name);
Some(device.unwrap())
}
Err(e) if e.code() == E_NOINTERFACE => {
error!("ID3D12Device interface not supported");
None
}
Err(e) => {
info!(
"D3D12 feature level {} not supported: {}",
feature_level_name, e
);
None
}
}
});
if device.is_some() {
return device;
}
}
None
}
fn main() -> Result<()> {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("trace")).init();
let dxgi_factory = unsafe { CreateDXGIFactory2(0) }?;
let device = create_d3d12_device(&dxgi_factory).expect("Failed to create D3D12 device.");
// Setting up the allocator
let mut allocator = Allocator::new(&AllocatorCreateDesc {
device: device.clone(),
debug_settings: Default::default(),
})
.unwrap();
// Test allocating Gpu Only memory
{
let test_buffer_desc = D3D12_RESOURCE_DESC {
Dimension: D3D12_RESOURCE_DIMENSION_BUFFER,
Alignment: D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT as u64,
Width: 512,
Height: 1,
DepthOrArraySize: 1,
MipLevels: 1,
Format: DXGI_FORMAT_UNKNOWN,
SampleDesc: DXGI_SAMPLE_DESC {
Count: 1,
Quality: 0,
},
Layout: D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
Flags: D3D12_RESOURCE_FLAG_NONE,
};
let allocation_desc = AllocationCreateDesc::from_d3d12_resource_desc(
allocator.device(),
&test_buffer_desc,
"Test allocation (Gpu only)",
MemoryLocation::GpuOnly,
);
let allocation = allocator.allocate(&allocation_desc).unwrap();
let mut resource: Option<ID3D12Resource> = None;
unsafe {
device.CreatePlacedResource(
allocation.heap(),
allocation.offset(),
&test_buffer_desc,
D3D12_RESOURCE_STATE_COMMON,
None,
&mut resource,
)
}?;
drop(resource);
allocator.free(allocation).unwrap();
info!("Allocation and deallocation of GpuOnly memory was successful.");
}
// Test allocating Cpu to Gpu memory
{
let test_buffer_desc = D3D12_RESOURCE_DESC {
Dimension: D3D12_RESOURCE_DIMENSION_BUFFER,
Alignment: D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT as u64,
Width: 512,
Height: 1,
DepthOrArraySize: 1,
MipLevels: 1,
Format: DXGI_FORMAT_UNKNOWN,
SampleDesc: DXGI_SAMPLE_DESC {
Count: 1,
Quality: 0,
},
Layout: D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
Flags: D3D12_RESOURCE_FLAG_NONE,
};
let alloc_info = unsafe { device.GetResourceAllocationInfo(0, &[test_buffer_desc]) };
let allocation = allocator
.allocate(&AllocationCreateDesc {
name: "Test allocation (Cpu To Gpu)",
location: MemoryLocation::CpuToGpu,
size: alloc_info.SizeInBytes,
alignment: alloc_info.Alignment,
resource_category: ResourceCategory::Buffer,
})
.unwrap();
let mut resource: Option<ID3D12Resource> = None;
unsafe {
device.CreatePlacedResource(
allocation.heap(),
allocation.offset(),
&test_buffer_desc,
D3D12_RESOURCE_STATE_COMMON,
None,
&mut resource,
)
}?;
drop(resource);
allocator.free(allocation).unwrap();
info!("Allocation and deallocation of CpuToGpu memory was successful.");
}
// Test allocating Gpu to Cpu memory
{
let test_buffer_desc = D3D12_RESOURCE_DESC {
Dimension: D3D12_RESOURCE_DIMENSION_BUFFER,
Alignment: D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT as u64,
Width: 512,
Height: 1,
DepthOrArraySize: 1,
MipLevels: 1,
Format: DXGI_FORMAT_UNKNOWN,
SampleDesc: DXGI_SAMPLE_DESC {
Count: 1,
Quality: 0,
},
Layout: D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
Flags: D3D12_RESOURCE_FLAG_NONE,
};
let alloc_info = unsafe { device.GetResourceAllocationInfo(0, &[test_buffer_desc]) };
let allocation = allocator
.allocate(&AllocationCreateDesc {
name: "Test allocation (Gpu to Cpu)",
location: MemoryLocation::GpuToCpu,
size: alloc_info.SizeInBytes,
alignment: alloc_info.Alignment,
resource_category: ResourceCategory::Buffer,
})
.unwrap();
let mut resource: Option<ID3D12Resource> = None;
unsafe {
device.CreatePlacedResource(
allocation.heap(),
allocation.offset(),
&test_buffer_desc,
D3D12_RESOURCE_STATE_COMMON,
None,
&mut resource,
)
}?;
drop(resource);
allocator.free(allocation).unwrap();
info!("Allocation and deallocation of CpuToGpu memory was successful.");
}
Ok(())
}

View file

@ -0,0 +1,282 @@
//! Example showcasing [`winapi`] interop with [`gpu-allocator`] which is driven by the [`windows`] crate.
use winapi::shared::{dxgiformat, winerror};
use winapi::um::{d3d12, d3dcommon};
use winapi::Interface;
mod all_dxgi {
pub use winapi::shared::{dxgi1_3::*, dxgi1_6::*, dxgitype::*};
}
use log::*;
use gpu_allocator::d3d12::{
AllocationCreateDesc, Allocator, AllocatorCreateDesc, ResourceCategory, ToWinapi, ToWindows,
};
use gpu_allocator::MemoryLocation;
fn create_d3d12_device(
dxgi_factory: *mut all_dxgi::IDXGIFactory6,
) -> Option<*mut d3d12::ID3D12Device> {
for idx in 0.. {
let mut adapter4: *mut all_dxgi::IDXGIAdapter4 = std::ptr::null_mut();
let hr = unsafe {
dxgi_factory.as_ref().unwrap().EnumAdapters1(
idx,
<*mut *mut all_dxgi::IDXGIAdapter4>::cast(&mut adapter4),
)
};
if hr == winerror::DXGI_ERROR_NOT_FOUND {
break;
}
assert_eq!(hr, winerror::S_OK);
let mut desc = all_dxgi::DXGI_ADAPTER_DESC3::default();
let hr = unsafe { adapter4.as_ref().unwrap().GetDesc3(&mut desc) };
if hr != winerror::S_OK {
error!("Failed to get adapter description for adapter");
continue;
}
// Skip software adapters
if (desc.Flags & all_dxgi::DXGI_ADAPTER_FLAG3_SOFTWARE)
== all_dxgi::DXGI_ADAPTER_FLAG3_SOFTWARE
{
continue;
}
let feature_levels = [
(d3dcommon::D3D_FEATURE_LEVEL_11_0, "D3D_FEATURE_LEVEL_11_0"),
(d3dcommon::D3D_FEATURE_LEVEL_11_1, "D3D_FEATURE_LEVEL_11_1"),
(d3dcommon::D3D_FEATURE_LEVEL_12_0, "D3D_FEATURE_LEVEL_12_0"),
];
let device =
feature_levels
.iter()
.rev()
.find_map(|&(feature_level, feature_level_name)| {
let mut device: *mut d3d12::ID3D12Device = std::ptr::null_mut();
let hr = unsafe {
d3d12::D3D12CreateDevice(
adapter4.cast(),
feature_level,
&d3d12::ID3D12Device::uuidof(),
<*mut *mut d3d12::ID3D12Device>::cast(&mut device),
)
};
match hr {
winapi::shared::winerror::S_OK => {
info!("Using D3D12 feature level: {}.", feature_level_name);
Some(device)
}
winapi::shared::winerror::E_NOINTERFACE => {
error!("ID3D12Device interface not supported.");
None
}
_ => {
info!(
"D3D12 feature level: {} not supported: {:x}",
feature_level_name, hr
);
None
}
}
});
if device.is_some() {
return device;
}
}
None
}
fn main() {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("trace")).init();
let dxgi_factory = {
let mut dxgi_factory: *mut all_dxgi::IDXGIFactory6 = std::ptr::null_mut();
let hr = unsafe {
all_dxgi::CreateDXGIFactory2(
0,
&all_dxgi::IID_IDXGIFactory6,
<*mut *mut all_dxgi::IDXGIFactory6>::cast(&mut dxgi_factory),
)
};
assert_eq!(
hr,
winapi::shared::winerror::S_OK,
"Failed to create DXGI factory",
);
dxgi_factory
};
let device = create_d3d12_device(dxgi_factory).expect("Failed to create D3D12 device.");
// Setting up the allocator
let mut allocator = Allocator::new(&AllocatorCreateDesc {
device: device.as_windows().clone(),
debug_settings: Default::default(),
})
.unwrap();
let device = unsafe { device.as_ref() }.unwrap();
// Test allocating Gpu Only memory
{
let test_buffer_desc = d3d12::D3D12_RESOURCE_DESC {
Dimension: d3d12::D3D12_RESOURCE_DIMENSION_BUFFER,
Alignment: d3d12::D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT as u64,
Width: 512,
Height: 1,
DepthOrArraySize: 1,
MipLevels: 1,
Format: dxgiformat::DXGI_FORMAT_UNKNOWN,
SampleDesc: all_dxgi::DXGI_SAMPLE_DESC {
Count: 1,
Quality: 0,
},
Layout: d3d12::D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
Flags: d3d12::D3D12_RESOURCE_FLAG_NONE,
};
let allocation_desc = AllocationCreateDesc::from_winapi_d3d12_resource_desc(
device,
&test_buffer_desc,
"Test allocation (Gpu Only)",
MemoryLocation::GpuOnly,
);
let allocation = allocator.allocate(&allocation_desc).unwrap();
let mut resource: *mut d3d12::ID3D12Resource = std::ptr::null_mut();
let hr = unsafe {
device.CreatePlacedResource(
allocation.heap().as_winapi() as *mut _,
allocation.offset(),
&test_buffer_desc,
d3d12::D3D12_RESOURCE_STATE_COMMON,
std::ptr::null(),
&d3d12::IID_ID3D12Resource,
<*mut *mut d3d12::ID3D12Resource>::cast(&mut resource),
)
};
if hr != winerror::S_OK {
panic!("Failed to create placed resource.");
}
unsafe { resource.as_ref().unwrap().Release() };
allocator.free(allocation).unwrap();
info!("Allocation and deallocation of GpuOnly memory was successful.");
}
// Test allocating Cpu to Gpu memory
{
let test_buffer_desc = d3d12::D3D12_RESOURCE_DESC {
Dimension: d3d12::D3D12_RESOURCE_DIMENSION_BUFFER,
Alignment: d3d12::D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT as u64,
Width: 512,
Height: 1,
DepthOrArraySize: 1,
MipLevels: 1,
Format: dxgiformat::DXGI_FORMAT_UNKNOWN,
SampleDesc: all_dxgi::DXGI_SAMPLE_DESC {
Count: 1,
Quality: 0,
},
Layout: d3d12::D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
Flags: d3d12::D3D12_RESOURCE_FLAG_NONE,
};
let alloc_info = unsafe { device.GetResourceAllocationInfo(0, 1, &test_buffer_desc) };
let allocation = allocator
.allocate(&AllocationCreateDesc {
name: "Test allocation (Cpu to Gpu)",
location: MemoryLocation::CpuToGpu,
size: alloc_info.SizeInBytes,
alignment: alloc_info.Alignment,
resource_category: ResourceCategory::Buffer,
})
.unwrap();
let mut resource: *mut d3d12::ID3D12Resource = std::ptr::null_mut();
let hr = unsafe {
device.CreatePlacedResource(
allocation.heap().as_winapi() as *mut _,
allocation.offset(),
&test_buffer_desc,
d3d12::D3D12_RESOURCE_STATE_COMMON,
std::ptr::null(),
&d3d12::IID_ID3D12Resource,
<*mut *mut d3d12::ID3D12Resource>::cast(&mut resource),
)
};
if hr != winerror::S_OK {
panic!("Failed to create placed resource.");
}
unsafe { resource.as_ref().unwrap().Release() };
allocator.free(allocation).unwrap();
info!("Allocation and deallocation of CpuToGpu memory was successful.");
}
// Test allocating Gpu to Cpu memory
{
let test_buffer_desc = d3d12::D3D12_RESOURCE_DESC {
Dimension: d3d12::D3D12_RESOURCE_DIMENSION_BUFFER,
Alignment: d3d12::D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT as u64,
Width: 512,
Height: 1,
DepthOrArraySize: 1,
MipLevels: 1,
Format: dxgiformat::DXGI_FORMAT_UNKNOWN,
SampleDesc: all_dxgi::DXGI_SAMPLE_DESC {
Count: 1,
Quality: 0,
},
Layout: d3d12::D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
Flags: d3d12::D3D12_RESOURCE_FLAG_NONE,
};
let alloc_info = unsafe { device.GetResourceAllocationInfo(0, 1, &test_buffer_desc) };
let allocation = allocator
.allocate(&AllocationCreateDesc {
name: "Test allocation (Gpu to Cpu)",
location: MemoryLocation::GpuToCpu,
size: alloc_info.SizeInBytes,
alignment: alloc_info.Alignment,
resource_category: ResourceCategory::Buffer,
})
.unwrap();
let mut resource: *mut d3d12::ID3D12Resource = std::ptr::null_mut();
let hr = unsafe {
device.CreatePlacedResource(
allocation.heap().as_winapi() as *mut _,
allocation.offset(),
&test_buffer_desc,
d3d12::D3D12_RESOURCE_STATE_COMMON,
std::ptr::null(),
&d3d12::IID_ID3D12Resource,
<*mut *mut d3d12::ID3D12Resource>::cast(&mut resource),
)
};
if hr != winerror::S_OK {
panic!("Failed to create placed resource.");
}
unsafe { resource.as_ref().unwrap().Release() };
allocator.free(allocation).unwrap();
info!("Allocation and deallocation of CpuToGpu memory was successful.");
}
drop(allocator); // Explicitly drop before destruction of device.
unsafe { device.Release() };
unsafe { dxgi_factory.as_ref().unwrap().Release() };
}

View file

@ -0,0 +1,990 @@
mod all_dxgi {
pub use winapi::shared::{
dxgi::*, dxgi1_2::*, dxgi1_3::*, dxgi1_4::*, dxgi1_6::*, dxgiformat::*, dxgitype::*,
};
}
use gpu_allocator::d3d12::ToWinapi;
use winapi::um::d3d12::*;
use winapi::um::d3dcommon::*;
use winapi::Interface;
use winapi::shared::winerror::FAILED;
use gpu_allocator::d3d12::{Allocation, AllocationCreateDesc, Allocator};
use gpu_allocator::MemoryLocation;
use super::transition_resource;
#[repr(C)]
#[derive(Clone, Copy)]
struct ImGuiCBuffer {
scale: [f32; 2],
translation: [f32; 2],
}
pub struct ImGuiRenderer {
root_signature: *mut ID3D12RootSignature,
pipeline: *mut ID3D12PipelineState,
vb_capacity: u64,
ib_capacity: u64,
vb_allocation: Allocation,
ib_allocation: Allocation,
vb_pointer: *mut u8,
ib_pointer: *mut u8,
vertex_buffer: *mut ID3D12Resource,
index_buffer: *mut ID3D12Resource,
cb_allocation: Allocation,
cb_pointer: *mut u8,
constant_buffer: *mut ID3D12Resource,
font_image: *mut ID3D12Resource,
font_image_memory: Allocation,
font_image_srv_index: usize,
font_image_upload_buffer: *mut ID3D12Resource,
font_image_upload_buffer_memory: Allocation,
}
impl ImGuiRenderer {
pub(crate) fn new(
imgui: &mut imgui::Context,
device: &mut ID3D12Device,
allocator: &mut Allocator,
descriptor_heap: &mut ID3D12DescriptorHeap,
descriptor_heap_counter: &mut usize,
cmd: &mut ID3D12GraphicsCommandList,
) -> Self {
let root_signature = {
let mut root_parameters = [
D3D12_ROOT_PARAMETER {
ParameterType: D3D12_ROOT_PARAMETER_TYPE_CBV,
ShaderVisibility: D3D12_SHADER_VISIBILITY_ALL,
..Default::default()
},
D3D12_ROOT_PARAMETER {
ParameterType: D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE,
ShaderVisibility: D3D12_SHADER_VISIBILITY_ALL,
..Default::default()
},
];
let ranges = [D3D12_DESCRIPTOR_RANGE {
RangeType: D3D12_DESCRIPTOR_RANGE_TYPE_SRV,
NumDescriptors: 1,
BaseShaderRegister: 2,
RegisterSpace: 0,
OffsetInDescriptorsFromTableStart: 0,
}];
unsafe {
root_parameters[0].u.Descriptor_mut().ShaderRegister = 0;
root_parameters[0].u.Descriptor_mut().RegisterSpace = 0;
root_parameters[1]
.u
.DescriptorTable_mut()
.NumDescriptorRanges = ranges.len() as u32;
root_parameters[1].u.DescriptorTable_mut().pDescriptorRanges = ranges.as_ptr();
}
let static_samplers = [D3D12_STATIC_SAMPLER_DESC {
Filter: D3D12_FILTER_MIN_MAG_MIP_LINEAR,
AddressU: D3D12_TEXTURE_ADDRESS_MODE_CLAMP,
AddressV: D3D12_TEXTURE_ADDRESS_MODE_CLAMP,
AddressW: D3D12_TEXTURE_ADDRESS_MODE_CLAMP,
MipLODBias: 0.0,
MaxAnisotropy: 0,
ComparisonFunc: D3D12_COMPARISON_FUNC_ALWAYS,
BorderColor: D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK,
MinLOD: 0.0,
MaxLOD: 0.0,
ShaderRegister: 1,
RegisterSpace: 0,
ShaderVisibility: D3D12_SHADER_VISIBILITY_ALL,
}];
let rsig_desc = D3D12_ROOT_SIGNATURE_DESC {
NumParameters: root_parameters.len() as u32,
pParameters: root_parameters.as_ptr(),
NumStaticSamplers: static_samplers.len() as u32,
pStaticSamplers: static_samplers.as_ptr(),
Flags: D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT,
};
let mut blob = std::ptr::null_mut();
let mut error_blob = std::ptr::null_mut();
let hr = unsafe {
D3D12SerializeRootSignature(
&rsig_desc,
D3D_ROOT_SIGNATURE_VERSION_1,
&mut blob,
&mut error_blob,
)
};
if FAILED(hr) {
let error = unsafe {
let error_blob = error_blob.as_ref().unwrap();
std::slice::from_raw_parts(
error_blob.GetBufferPointer() as *const u8,
error_blob.GetBufferSize(),
)
};
panic!(
"Failed to serialize root signature: '{}'. hr: {:#x}",
std::str::from_utf8(error).unwrap(),
hr
);
}
let blob = unsafe { blob.as_ref() }.unwrap();
let mut rsig = std::ptr::null_mut();
let hr = unsafe {
device.CreateRootSignature(
0,
blob.GetBufferPointer(),
blob.GetBufferSize(),
&ID3D12RootSignature::uuidof(),
<*mut *mut ID3D12RootSignature>::cast(&mut rsig),
)
};
if FAILED(hr) {
panic!("Failed to create root signature. hr: {:#x}", hr);
}
unsafe { rsig.as_mut() }.unwrap()
};
let pipeline = unsafe {
let vs = include_bytes!("./dxil/imgui.vs.dxil");
let ps = include_bytes!("./dxil/imgui.ps.dxil");
let input_elements = [
D3D12_INPUT_ELEMENT_DESC {
SemanticName: b"POSITION\0".as_ptr().cast(),
SemanticIndex: 0,
Format: all_dxgi::DXGI_FORMAT_R32G32_FLOAT,
InputSlot: 0,
AlignedByteOffset: 0,
InputSlotClass: D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,
InstanceDataStepRate: 0,
},
D3D12_INPUT_ELEMENT_DESC {
SemanticName: b"TEXCOORD\0".as_ptr().cast(),
SemanticIndex: 0,
Format: all_dxgi::DXGI_FORMAT_R32G32_FLOAT,
InputSlot: 0,
AlignedByteOffset: 8,
InputSlotClass: D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,
InstanceDataStepRate: 0,
},
D3D12_INPUT_ELEMENT_DESC {
SemanticName: b"COLOR\0".as_ptr().cast(),
SemanticIndex: 0,
Format: all_dxgi::DXGI_FORMAT_R8G8B8A8_UNORM,
InputSlot: 0,
AlignedByteOffset: 16,
InputSlotClass: D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,
InstanceDataStepRate: 0,
},
];
let desc = D3D12_GRAPHICS_PIPELINE_STATE_DESC {
pRootSignature: root_signature,
VS: D3D12_SHADER_BYTECODE {
pShaderBytecode: vs.as_ptr().cast(),
BytecodeLength: vs.len(),
},
PS: D3D12_SHADER_BYTECODE {
pShaderBytecode: ps.as_ptr().cast(),
BytecodeLength: ps.len(),
},
BlendState: D3D12_BLEND_DESC {
AlphaToCoverageEnable: 0,
IndependentBlendEnable: 0,
RenderTarget: [
D3D12_RENDER_TARGET_BLEND_DESC {
BlendEnable: 1,
LogicOpEnable: 0,
SrcBlend: D3D12_BLEND_SRC_ALPHA,
DestBlend: D3D12_BLEND_INV_SRC_ALPHA,
BlendOp: D3D12_BLEND_OP_ADD,
SrcBlendAlpha: D3D12_BLEND_ONE,
DestBlendAlpha: D3D12_BLEND_ZERO,
BlendOpAlpha: D3D12_BLEND_OP_ADD,
LogicOp: D3D12_LOGIC_OP_NOOP,
RenderTargetWriteMask: D3D12_COLOR_WRITE_ENABLE_ALL as u8,
},
D3D12_RENDER_TARGET_BLEND_DESC::default(),
D3D12_RENDER_TARGET_BLEND_DESC::default(),
D3D12_RENDER_TARGET_BLEND_DESC::default(),
D3D12_RENDER_TARGET_BLEND_DESC::default(),
D3D12_RENDER_TARGET_BLEND_DESC::default(),
D3D12_RENDER_TARGET_BLEND_DESC::default(),
D3D12_RENDER_TARGET_BLEND_DESC::default(),
],
},
SampleMask: !0u32,
RasterizerState: D3D12_RASTERIZER_DESC {
FillMode: D3D12_FILL_MODE_SOLID,
CullMode: D3D12_CULL_MODE_NONE,
FrontCounterClockwise: 0,
DepthBias: 0,
DepthBiasClamp: 0.0,
SlopeScaledDepthBias: 0.0,
DepthClipEnable: 0,
MultisampleEnable: 0,
AntialiasedLineEnable: 0,
ForcedSampleCount: 1,
ConservativeRaster: D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF,
},
DepthStencilState: D3D12_DEPTH_STENCIL_DESC {
DepthEnable: 0,
DepthWriteMask: D3D12_DEPTH_WRITE_MASK_ZERO,
DepthFunc: D3D12_COMPARISON_FUNC_ALWAYS,
StencilEnable: 0,
StencilReadMask: 0,
StencilWriteMask: 0,
FrontFace: D3D12_DEPTH_STENCILOP_DESC::default(),
BackFace: D3D12_DEPTH_STENCILOP_DESC::default(),
},
InputLayout: D3D12_INPUT_LAYOUT_DESC {
pInputElementDescs: input_elements.as_ptr(),
NumElements: input_elements.len() as u32,
},
PrimitiveTopologyType: D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE,
NumRenderTargets: 1,
RTVFormats: [
all_dxgi::DXGI_FORMAT_R8G8B8A8_UNORM,
all_dxgi::DXGI_FORMAT_UNKNOWN,
all_dxgi::DXGI_FORMAT_UNKNOWN,
all_dxgi::DXGI_FORMAT_UNKNOWN,
all_dxgi::DXGI_FORMAT_UNKNOWN,
all_dxgi::DXGI_FORMAT_UNKNOWN,
all_dxgi::DXGI_FORMAT_UNKNOWN,
all_dxgi::DXGI_FORMAT_UNKNOWN,
],
SampleDesc: all_dxgi::DXGI_SAMPLE_DESC {
Quality: 0,
Count: 1,
},
..Default::default()
};
let mut pipeline: *mut ID3D12PipelineState = std::ptr::null_mut();
let hr = device.CreateGraphicsPipelineState(
&desc,
&ID3D12PipelineState::uuidof(),
<*mut *mut ID3D12PipelineState>::cast(&mut pipeline),
);
if FAILED(hr) {
panic!("Failed to create imgui pipeline: {:#x}", hr);
}
pipeline.as_mut().unwrap()
};
let (
font_image,
font_image_memory,
font_image_srv_index,
font_image_upload_buffer,
font_image_upload_buffer_memory,
) = {
let fonts = imgui.fonts();
let font_atlas = fonts.build_rgba32_texture();
let desc = D3D12_RESOURCE_DESC {
Dimension: D3D12_RESOURCE_DIMENSION_TEXTURE2D,
Alignment: 0,
Width: font_atlas.width as u64,
Height: font_atlas.height,
DepthOrArraySize: 1,
MipLevels: 1,
Format: all_dxgi::DXGI_FORMAT_R8G8B8A8_UNORM,
SampleDesc: all_dxgi::DXGI_SAMPLE_DESC {
Count: 1,
Quality: 0,
},
Layout: D3D12_TEXTURE_LAYOUT_UNKNOWN,
Flags: D3D12_RESOURCE_FLAG_NONE,
};
let font_image_memory = allocator
.allocate(&AllocationCreateDesc::from_winapi_d3d12_resource_desc(
device,
&desc,
"font_image",
MemoryLocation::GpuOnly,
))
.unwrap();
let font_image = unsafe {
let mut font_image: *mut ID3D12Resource = std::ptr::null_mut();
let hr = device.CreatePlacedResource(
font_image_memory.heap().as_winapi() as *mut _,
font_image_memory.offset(),
&desc,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
std::ptr::null(),
&ID3D12Resource::uuidof(),
<*mut *mut ID3D12Resource>::cast(&mut font_image),
);
if FAILED(hr) {
panic!("Failed to create font image. hr: {:#x}", hr);
}
font_image
};
//Create SRV
let srv_index = unsafe {
let srv_index = *descriptor_heap_counter;
*descriptor_heap_counter += 1;
let srv_size = device
.GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV)
as usize;
let desc_heap_handle = descriptor_heap.GetCPUDescriptorHandleForHeapStart();
let desc_heap_handle = D3D12_CPU_DESCRIPTOR_HANDLE {
ptr: desc_heap_handle.ptr + srv_index * srv_size,
};
const fn d3d12_encode_shader_4_component_mapping(
src0: u32,
src1: u32,
src2: u32,
src3: u32,
) -> u32 {
(src0 & D3D12_SHADER_COMPONENT_MAPPING_MASK)
| ((src1 & D3D12_SHADER_COMPONENT_MAPPING_MASK)
<< (D3D12_SHADER_COMPONENT_MAPPING_SHIFT))
| ((src2 & D3D12_SHADER_COMPONENT_MAPPING_MASK)
<< (D3D12_SHADER_COMPONENT_MAPPING_SHIFT * 2))
| ((src3 & D3D12_SHADER_COMPONENT_MAPPING_MASK)
<< (D3D12_SHADER_COMPONENT_MAPPING_SHIFT * 3))
| D3D12_SHADER_COMPONENT_MAPPING_ALWAYS_SET_BIT_AVOIDING_ZEROMEM_MISTAKES
}
const fn d3d12_default_shader_4_component_mapping() -> u32 {
d3d12_encode_shader_4_component_mapping(0, 1, 2, 3)
}
let mut srv_desc = D3D12_SHADER_RESOURCE_VIEW_DESC {
Format: all_dxgi::DXGI_FORMAT_R8G8B8A8_UNORM,
ViewDimension: D3D12_SRV_DIMENSION_TEXTURE2D,
Shader4ComponentMapping: d3d12_default_shader_4_component_mapping(),
..Default::default()
};
srv_desc.u.Texture2D_mut().MostDetailedMip = 0;
srv_desc.u.Texture2D_mut().MipLevels = 1;
srv_desc.u.Texture2D_mut().PlaneSlice = 0;
srv_desc.u.Texture2D_mut().ResourceMinLODClamp = 0.0;
device.CreateShaderResourceView(font_image, &srv_desc, desc_heap_handle);
srv_index
};
let mut layouts = [D3D12_PLACED_SUBRESOURCE_FOOTPRINT::default()];
let mut num_rows: u32 = 0;
let mut row_size_in_bytes: u64 = 0;
let mut total_bytes: u64 = 0;
unsafe {
device.GetCopyableFootprints(
&font_image.as_ref().unwrap().GetDesc(),
0, // first sub
layouts.len() as u32, // num sub
0, // intermediate offset
layouts.as_mut_ptr(),
&mut num_rows,
&mut row_size_in_bytes,
&mut total_bytes,
)
};
// Create upload buffer
let (upload_buffer, upload_buffer_memory) = {
let desc = D3D12_RESOURCE_DESC {
Dimension: D3D12_RESOURCE_DIMENSION_BUFFER,
Alignment: 0,
Width: total_bytes,
Height: 1,
DepthOrArraySize: 1,
MipLevels: 1,
Format: all_dxgi::DXGI_FORMAT_UNKNOWN,
SampleDesc: all_dxgi::DXGI_SAMPLE_DESC {
Count: 1,
Quality: 0,
},
Layout: D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
Flags: D3D12_RESOURCE_FLAG_NONE,
};
let upload_buffer_memory = allocator
.allocate(&AllocationCreateDesc::from_winapi_d3d12_resource_desc(
device,
&desc,
"font_image upload buffer",
MemoryLocation::CpuToGpu,
))
.unwrap();
let mut upload_buffer: *mut ID3D12Resource = std::ptr::null_mut();
let hr = unsafe {
device.CreatePlacedResource(
upload_buffer_memory.heap().as_winapi() as *mut _,
upload_buffer_memory.offset(),
&desc,
D3D12_RESOURCE_STATE_GENERIC_READ,
std::ptr::null(),
&ID3D12Resource::uuidof(),
<*mut *mut ID3D12Resource>::cast(&mut upload_buffer),
)
};
if FAILED(hr) {
panic!("Failed to create font image upload buffer. hr: {:x}.", hr);
}
(upload_buffer, upload_buffer_memory)
};
unsafe {
let mut mapped_ptr = std::ptr::null_mut();
upload_buffer
.as_ref()
.unwrap()
.Map(0, std::ptr::null(), &mut mapped_ptr);
let mapped_ptr = mapped_ptr.cast::<u8>();
let layout = &layouts[0];
let mapped_ptr = mapped_ptr.add(layout.Offset as usize);
let source_ptr = font_atlas.data.as_ptr();
for y in 0..layout.Footprint.Height {
let mapped_ptr = mapped_ptr.add((y * layout.Footprint.RowPitch) as usize);
let source_ptr = source_ptr.add((y * font_atlas.width * 4) as usize);
std::ptr::copy_nonoverlapping(
source_ptr,
mapped_ptr,
(layout.Footprint.Width * 4) as usize,
);
}
upload_buffer.as_ref().unwrap().Unmap(0, std::ptr::null())
};
let mut dst = D3D12_TEXTURE_COPY_LOCATION {
pResource: font_image,
Type: D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX,
..Default::default()
};
unsafe { *dst.u.SubresourceIndex_mut() = 0 };
let mut src = D3D12_TEXTURE_COPY_LOCATION {
pResource: upload_buffer,
Type: D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT,
..Default::default()
};
unsafe { *src.u.PlacedFootprint_mut() = layouts[0] };
unsafe {
let barriers = [transition_resource(
font_image,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
D3D12_RESOURCE_STATE_COPY_DEST,
)];
cmd.ResourceBarrier(barriers.len() as u32, barriers.as_ptr());
cmd.CopyTextureRegion(&dst, 0, 0, 0, &src, std::ptr::null())
};
(
font_image,
font_image_memory,
srv_index,
upload_buffer,
upload_buffer_memory,
)
};
let (constant_buffer, cb_allocation, cb_pointer) = {
let desc = D3D12_RESOURCE_DESC {
Dimension: D3D12_RESOURCE_DIMENSION_BUFFER,
Alignment: 0,
Width: std::mem::size_of::<ImGuiCBuffer>() as u64,
Height: 1,
DepthOrArraySize: 1,
MipLevels: 1,
Format: all_dxgi::DXGI_FORMAT_UNKNOWN,
SampleDesc: all_dxgi::DXGI_SAMPLE_DESC {
Count: 1,
Quality: 0,
},
Layout: D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
Flags: D3D12_RESOURCE_FLAG_NONE,
};
let allocation = allocator
.allocate(&AllocationCreateDesc::from_winapi_d3d12_resource_desc(
device,
&desc,
"ImGui Constant buffer",
MemoryLocation::CpuToGpu,
))
.unwrap();
let mut buffer: *mut ID3D12Resource = std::ptr::null_mut();
let hr = unsafe {
device.CreatePlacedResource(
allocation.heap().as_winapi() as *mut _,
allocation.offset(),
&desc,
D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER,
std::ptr::null(),
&ID3D12Resource::uuidof(),
<*mut *mut ID3D12Resource>::cast(&mut buffer),
)
};
if FAILED(hr) {
panic!("Failed to create constant buffer. hr: {:x}.", hr);
}
let mut mapped_ptr: *mut u8 = std::ptr::null_mut();
unsafe {
buffer.as_ref().unwrap().Map(
0,
std::ptr::null(),
<*mut *mut u8>::cast(&mut mapped_ptr),
)
};
(buffer, allocation, mapped_ptr)
};
let vb_capacity = 1024 * 1024;
let (vertex_buffer, vb_allocation, vb_pointer) = {
let desc = D3D12_RESOURCE_DESC {
Dimension: D3D12_RESOURCE_DIMENSION_BUFFER,
Alignment: 0,
Width: vb_capacity,
Height: 1,
DepthOrArraySize: 1,
MipLevels: 1,
Format: all_dxgi::DXGI_FORMAT_UNKNOWN,
SampleDesc: all_dxgi::DXGI_SAMPLE_DESC {
Count: 1,
Quality: 0,
},
Layout: D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
Flags: D3D12_RESOURCE_FLAG_NONE,
};
let allocation = allocator
.allocate(&AllocationCreateDesc::from_winapi_d3d12_resource_desc(
device,
&desc,
"ImGui Vertex buffer",
MemoryLocation::CpuToGpu,
))
.unwrap();
let mut buffer: *mut ID3D12Resource = std::ptr::null_mut();
let hr = unsafe {
device.CreatePlacedResource(
allocation.heap().as_winapi() as *mut _,
allocation.offset(),
&desc,
D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER,
std::ptr::null(),
&ID3D12Resource::uuidof(),
<*mut *mut ID3D12Resource>::cast(&mut buffer),
)
};
if FAILED(hr) {
panic!("Failed to create vertex buffer. hr: {:x}.", hr);
}
let mut mapped_ptr: *mut u8 = std::ptr::null_mut();
unsafe {
buffer.as_ref().unwrap().Map(
0,
std::ptr::null(),
<*mut *mut u8>::cast(&mut mapped_ptr),
)
};
(buffer, allocation, mapped_ptr)
};
let ib_capacity = 1024 * 1024;
let (index_buffer, ib_allocation, ib_pointer) = {
let desc = D3D12_RESOURCE_DESC {
Dimension: D3D12_RESOURCE_DIMENSION_BUFFER,
Alignment: 0,
Width: ib_capacity,
Height: 1,
DepthOrArraySize: 1,
MipLevels: 1,
Format: all_dxgi::DXGI_FORMAT_UNKNOWN,
SampleDesc: all_dxgi::DXGI_SAMPLE_DESC {
Count: 1,
Quality: 0,
},
Layout: D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
Flags: D3D12_RESOURCE_FLAG_NONE,
};
let allocation = allocator
.allocate(&AllocationCreateDesc::from_winapi_d3d12_resource_desc(
device,
&desc,
"ImGui Vertex buffer",
MemoryLocation::CpuToGpu,
))
.unwrap();
let mut buffer: *mut ID3D12Resource = std::ptr::null_mut();
let hr = unsafe {
device.CreatePlacedResource(
allocation.heap().as_winapi() as *mut _,
allocation.offset(),
&desc,
D3D12_RESOURCE_STATE_INDEX_BUFFER,
std::ptr::null(),
&ID3D12Resource::uuidof(),
<*mut *mut ID3D12Resource>::cast(&mut buffer),
)
};
if FAILED(hr) {
panic!("Failed to create vertex buffer. hr: {:x}.", hr);
}
let mut mapped_ptr: *mut u8 = std::ptr::null_mut();
unsafe {
buffer.as_ref().unwrap().Map(
0,
std::ptr::null(),
<*mut *mut u8>::cast(&mut mapped_ptr),
)
};
(buffer, allocation, mapped_ptr)
};
Self {
root_signature,
pipeline,
vb_capacity,
ib_capacity,
vb_allocation,
ib_allocation,
vb_pointer,
ib_pointer,
vertex_buffer,
index_buffer,
cb_allocation,
cb_pointer,
constant_buffer,
font_image,
font_image_memory,
font_image_srv_index,
font_image_upload_buffer,
font_image_upload_buffer_memory,
}
}
pub(crate) fn render(
&mut self,
imgui_draw_data: &imgui::DrawData,
device: &mut ID3D12Device,
window_width: u32,
window_height: u32,
descriptor_heap: &mut ID3D12DescriptorHeap,
cmd: &mut ID3D12GraphicsCommandList,
) {
// Update constant buffer
{
let left = imgui_draw_data.display_pos[0];
let right = imgui_draw_data.display_pos[0] + imgui_draw_data.display_size[0];
let top = imgui_draw_data.display_pos[1] + imgui_draw_data.display_size[1];
let bottom = imgui_draw_data.display_pos[1];
let cbuffer_data = ImGuiCBuffer {
scale: [(2.0 / (right - left)), (2.0 / (bottom - top))],
translation: [
(right + left) / (left - right),
(top + bottom) / (top - bottom),
],
};
unsafe { std::ptr::copy_nonoverlapping(&cbuffer_data, self.cb_pointer.cast(), 1) };
}
let (vtx_count, idx_count) =
imgui_draw_data
.draw_lists()
.fold((0, 0), |(vtx_count, idx_count), draw_list| {
(
vtx_count + draw_list.vtx_buffer().len(),
idx_count + draw_list.idx_buffer().len(),
)
});
let vtx_size = (vtx_count * std::mem::size_of::<imgui::DrawVert>()) as u64;
if vtx_size > self.vb_capacity {
// reallocate vertex buffer
todo!();
}
let idx_size = (idx_count * std::mem::size_of::<imgui::DrawIdx>()) as u64;
if idx_size > self.ib_capacity {
// reallocate index buffer
todo!();
}
let mut vb_offset = 0;
let mut ib_offset = 0;
unsafe {
let viewports = [D3D12_VIEWPORT {
TopLeftX: 0.0,
TopLeftY: 0.0,
Width: window_width as f32,
Height: window_height as f32,
MinDepth: 0.0,
MaxDepth: 1.0,
}];
cmd.RSSetViewports(viewports.len() as u32, viewports.as_ptr());
cmd.SetPipelineState(self.pipeline);
cmd.SetGraphicsRootSignature(self.root_signature);
cmd.IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
{
let constant_buffer = self.constant_buffer.as_mut().unwrap();
let addr = constant_buffer.GetGPUVirtualAddress();
cmd.SetGraphicsRootConstantBufferView(0, addr);
}
{
let srv_stride =
device.GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
let heap_handle = descriptor_heap.GetGPUDescriptorHandleForHeapStart();
let heap_handle = D3D12_GPU_DESCRIPTOR_HANDLE {
ptr: heap_handle.ptr + srv_stride as u64 * self.font_image_srv_index as u64,
};
cmd.SetGraphicsRootDescriptorTable(1, heap_handle);
}
}
for draw_list in imgui_draw_data.draw_lists() {
let vertices = draw_list.vtx_buffer();
let indices = draw_list.idx_buffer();
let vbv = {
let vertex_buffer = unsafe { self.vertex_buffer.as_mut().unwrap() };
let stride = std::mem::size_of::<imgui::DrawVert>();
let address =
unsafe { vertex_buffer.GetGPUVirtualAddress() } + (vb_offset * stride) as u64;
D3D12_VERTEX_BUFFER_VIEW {
BufferLocation: address,
SizeInBytes: (vertices.len() * stride) as u32,
StrideInBytes: stride as u32,
}
};
let ibv = {
let index_buffer = unsafe { self.index_buffer.as_mut().unwrap() };
let stride = std::mem::size_of::<u16>();
let address =
unsafe { index_buffer.GetGPUVirtualAddress() } + (ib_offset * stride) as u64;
D3D12_INDEX_BUFFER_VIEW {
BufferLocation: address,
SizeInBytes: (indices.len() * stride) as u32,
Format: all_dxgi::DXGI_FORMAT_R16_UINT,
}
};
// Upload vertices
unsafe {
let stride = std::mem::size_of::<imgui::DrawVert>();
let dst_ptr = self
.vb_pointer
.add(vb_offset * stride)
.cast::<imgui::DrawVert>();
std::ptr::copy_nonoverlapping(vertices.as_ptr(), dst_ptr, vertices.len());
}
vb_offset += vertices.len();
// Upload indices
unsafe {
let stride = std::mem::size_of::<u16>();
let dst_ptr = self.ib_pointer.add(ib_offset * stride).cast();
std::ptr::copy_nonoverlapping(indices.as_ptr(), dst_ptr, indices.len());
}
ib_offset += indices.len();
unsafe {
cmd.IASetVertexBuffers(0, 1, &vbv);
cmd.IASetIndexBuffer(&ibv);
};
for command in draw_list.commands() {
match command {
imgui::DrawCmd::Elements { count, cmd_params } => {
let scissor_rect = D3D12_RECT {
left: cmd_params.clip_rect[0] as i32,
top: cmd_params.clip_rect[1] as i32,
right: cmd_params.clip_rect[2] as i32,
bottom: cmd_params.clip_rect[3] as i32,
};
unsafe {
cmd.RSSetScissorRects(1, &scissor_rect);
cmd.DrawIndexedInstanced(
count as u32,
1,
cmd_params.idx_offset as u32,
cmd_params.vtx_offset as i32,
0,
);
};
}
_ => todo!(),
}
}
}
}
pub(crate) fn destroy(self, allocator: &mut Allocator) {
unsafe { self.pipeline.as_ref().unwrap().Release() };
unsafe { self.font_image_upload_buffer.as_ref().unwrap().Release() };
unsafe { self.vertex_buffer.as_ref().unwrap().Release() };
unsafe { self.index_buffer.as_ref().unwrap().Release() };
unsafe { self.constant_buffer.as_ref().unwrap().Release() };
unsafe { self.font_image.as_ref().unwrap().Release() };
allocator
.free(self.font_image_upload_buffer_memory)
.unwrap();
allocator.free(self.vb_allocation).unwrap();
allocator.free(self.ib_allocation).unwrap();
allocator.free(self.cb_allocation).unwrap();
allocator.free(self.font_image_memory).unwrap();
}
}
pub(crate) fn handle_imgui_event(
io: &mut imgui::Io,
window: &winit::window::Window,
event: &winit::event::Event<'_, ()>,
) -> bool {
use winit::event::{
DeviceEvent, ElementState, Event, KeyboardInput, MouseButton, MouseScrollDelta, TouchPhase,
VirtualKeyCode, WindowEvent,
};
match event {
Event::WindowEvent { event, window_id } if *window_id == window.id() => match *event {
WindowEvent::Resized(physical_size) => {
io.display_size = [physical_size.width as f32, physical_size.height as f32];
false
}
WindowEvent::KeyboardInput {
input:
KeyboardInput {
virtual_keycode: Some(key),
state,
..
},
..
} => {
let pressed = state == ElementState::Pressed;
io.keys_down[key as usize] = pressed;
match key {
VirtualKeyCode::LShift | VirtualKeyCode::RShift => io.key_shift = pressed,
VirtualKeyCode::LControl | VirtualKeyCode::RControl => io.key_ctrl = pressed,
VirtualKeyCode::LAlt | VirtualKeyCode::RAlt => io.key_alt = pressed,
VirtualKeyCode::LWin | VirtualKeyCode::RWin => io.key_super = pressed,
_ => (),
}
io.want_capture_keyboard
}
WindowEvent::ReceivedCharacter(ch) => {
io.add_input_character(ch);
io.want_capture_keyboard
}
WindowEvent::CursorMoved { position, .. } => {
io.mouse_pos = [position.x as f32, position.y as f32];
io.want_capture_mouse
}
WindowEvent::MouseWheel {
delta,
phase: TouchPhase::Moved,
..
} => {
match delta {
MouseScrollDelta::LineDelta(h, v) => {
io.mouse_wheel_h = h;
io.mouse_wheel = v;
}
MouseScrollDelta::PixelDelta(pos) => {
match pos.x.partial_cmp(&0.0) {
Some(std::cmp::Ordering::Greater) => io.mouse_wheel_h += 1.0,
Some(std::cmp::Ordering::Less) => io.mouse_wheel_h -= 1.0,
_ => (),
}
match pos.y.partial_cmp(&0.0) {
Some(std::cmp::Ordering::Greater) => io.mouse_wheel += 1.0,
Some(std::cmp::Ordering::Less) => io.mouse_wheel -= 1.0,
_ => (),
}
}
}
io.want_capture_mouse
}
WindowEvent::MouseInput { state, button, .. } => {
let pressed = state == ElementState::Pressed;
match button {
MouseButton::Left => io.mouse_down[0] = pressed,
MouseButton::Right => io.mouse_down[1] = pressed,
MouseButton::Middle => io.mouse_down[2] = pressed,
MouseButton::Other(idx @ 0..=4) => io.mouse_down[idx as usize] = pressed,
MouseButton::Other(_) => (),
}
io.want_capture_mouse
}
_ => false,
},
// Track key release events outside our window. If we don't do this,
// we might never see the release event if some other window gets focus.
Event::DeviceEvent {
event:
DeviceEvent::Key(KeyboardInput {
state: ElementState::Released,
virtual_keycode: Some(key),
..
}),
..
} => {
io.keys_down[*key as usize] = false;
match *key {
VirtualKeyCode::LShift | VirtualKeyCode::RShift => io.key_shift = false,
VirtualKeyCode::LControl | VirtualKeyCode::RControl => io.key_ctrl = false,
VirtualKeyCode::LAlt | VirtualKeyCode::RAlt => io.key_alt = false,
VirtualKeyCode::LWin | VirtualKeyCode::RWin => io.key_super = false,
_ => (),
}
io.want_capture_keyboard
}
_ => false,
}
}

View file

@ -0,0 +1,556 @@
#![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::<Vec<_>>()
};
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;
}
});
}

View file

@ -0,0 +1,14 @@
SamplerState g_sampler : register(s1, space0);
Texture2D g_texture : register(t2, space0);
struct VertexInput
{
float4 position : SV_POSITION;
float2 texCoord: TEXCOORD0;
float4 color: COLOR;
};
float4 main(VertexInput input) : SV_Target0
{
return input.color * g_texture.Sample(g_sampler, input.texCoord);
}

View file

@ -0,0 +1,30 @@
struct VertexInput
{
float2 pos : POSITION;
float2 texCoord : TEXCOORD0;
float4 color: COLOR;
};
struct VertexOutput
{
float4 position : SV_POSITION;
float2 texCoord: TEXCOORD0;
float4 color: COLOR;
};
struct Constants
{
float2 scale;
float2 translation;
};
ConstantBuffer<Constants> g_constants : register(b0, space0);
VertexOutput main(VertexInput vertex)
{
VertexOutput o;
o.position = float4(vertex.pos * g_constants.scale + g_constants.translation, 0.0, 1.0);
o.texCoord = vertex.texCoord;
o.color = vertex.color;
return o;
}

View file

@ -0,0 +1,206 @@
use ash::vk;
use log::info;
use std::default::Default;
use std::ffi::CString;
use gpu_allocator::vulkan::{
AllocationCreateDesc, AllocationScheme, Allocator, AllocatorCreateDesc,
};
use gpu_allocator::MemoryLocation;
fn main() {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("trace")).init();
let entry = unsafe { ash::Entry::load() }.unwrap();
// Create Vulkan instance
let instance = {
let app_name = CString::new("Vulkan gpu-allocator test").unwrap();
let appinfo = vk::ApplicationInfo::builder()
.application_name(&app_name)
.application_version(0)
.engine_name(&app_name)
.engine_version(0)
.api_version(vk::make_api_version(0, 1, 0, 0));
let layer_names = [CString::new("VK_LAYER_KHRONOS_validation").unwrap()];
let layers_names_raw: Vec<*const i8> = layer_names
.iter()
.map(|raw_name| raw_name.as_ptr())
.collect();
let extensions_names_raw = vec![];
let create_info = vk::InstanceCreateInfo::builder()
.application_info(&appinfo)
.enabled_layer_names(&layers_names_raw)
.enabled_extension_names(&extensions_names_raw);
unsafe {
entry
.create_instance(&create_info, None)
.expect("Instance creation error")
}
};
// Look for vulkan physical device
let (pdevice, queue_family_index) = {
let pdevices = unsafe {
instance
.enumerate_physical_devices()
.expect("Physical device error")
};
pdevices
.iter()
.find_map(|pdevice| {
unsafe { instance.get_physical_device_queue_family_properties(*pdevice) }
.iter()
.enumerate()
.find_map(|(index, &info)| {
let supports_graphics = info.queue_flags.contains(vk::QueueFlags::GRAPHICS);
if supports_graphics {
Some((*pdevice, index))
} else {
None
}
})
})
.expect("Couldn't find suitable device.")
};
// Create vulkan device
let device = {
let device_extension_names_raw = vec![];
let features = vk::PhysicalDeviceFeatures {
shader_clip_distance: 1,
..Default::default()
};
let priorities = [1.0];
let queue_info = vk::DeviceQueueCreateInfo::builder()
.queue_family_index(queue_family_index as u32)
.queue_priorities(&priorities);
let create_info = vk::DeviceCreateInfo::builder()
.queue_create_infos(std::slice::from_ref(&queue_info))
.enabled_extension_names(&device_extension_names_raw)
.enabled_features(&features);
unsafe { instance.create_device(pdevice, &create_info, None).unwrap() }
};
// Setting up the allocator
let mut allocator = Allocator::new(&AllocatorCreateDesc {
instance: instance.clone(),
device: device.clone(),
physical_device: pdevice,
debug_settings: Default::default(),
buffer_device_address: false,
})
.unwrap();
// Test allocating Gpu Only memory
{
let test_buffer_info = vk::BufferCreateInfo::builder()
.size(512)
.usage(vk::BufferUsageFlags::STORAGE_BUFFER)
.sharing_mode(vk::SharingMode::EXCLUSIVE);
let test_buffer = unsafe { device.create_buffer(&test_buffer_info, None) }.unwrap();
let requirements = unsafe { device.get_buffer_memory_requirements(test_buffer) };
let location = MemoryLocation::GpuOnly;
let allocation = allocator
.allocate(&AllocationCreateDesc {
requirements,
location,
linear: true,
allocation_scheme: AllocationScheme::GpuAllocatorManaged,
name: "Test allocation (Gpu Only)",
})
.unwrap();
unsafe {
device
.bind_buffer_memory(test_buffer, allocation.memory(), allocation.offset())
.unwrap()
};
allocator.free(allocation).unwrap();
unsafe { device.destroy_buffer(test_buffer, None) };
info!("Allocation and deallocation of GpuOnly memory was successful.");
}
// Test allocating Cpu to Gpu memory
{
let test_buffer_info = vk::BufferCreateInfo::builder()
.size(512)
.usage(vk::BufferUsageFlags::STORAGE_BUFFER)
.sharing_mode(vk::SharingMode::EXCLUSIVE);
let test_buffer = unsafe { device.create_buffer(&test_buffer_info, None) }.unwrap();
let requirements = unsafe { device.get_buffer_memory_requirements(test_buffer) };
let location = MemoryLocation::CpuToGpu;
let allocation = allocator
.allocate(&AllocationCreateDesc {
requirements,
location,
linear: true,
allocation_scheme: AllocationScheme::GpuAllocatorManaged,
name: "Test allocation (Cpu to Gpu)",
})
.unwrap();
unsafe {
device
.bind_buffer_memory(test_buffer, allocation.memory(), allocation.offset())
.unwrap()
};
allocator.free(allocation).unwrap();
unsafe { device.destroy_buffer(test_buffer, None) };
info!("Allocation and deallocation of CpuToGpu memory was successful.");
}
// Test allocating Gpu to Cpu memory
{
let test_buffer_info = vk::BufferCreateInfo::builder()
.size(512)
.usage(vk::BufferUsageFlags::STORAGE_BUFFER)
.sharing_mode(vk::SharingMode::EXCLUSIVE);
let test_buffer = unsafe { device.create_buffer(&test_buffer_info, None) }.unwrap();
let requirements = unsafe { device.get_buffer_memory_requirements(test_buffer) };
let location = MemoryLocation::GpuToCpu;
let allocation = allocator
.allocate(&AllocationCreateDesc {
requirements,
location,
linear: true,
allocation_scheme: AllocationScheme::GpuAllocatorManaged,
name: "Test allocation (Gpu to Cpu)",
})
.unwrap();
unsafe {
device
.bind_buffer_memory(test_buffer, allocation.memory(), allocation.offset())
.unwrap()
};
allocator.free(allocation).unwrap();
unsafe { device.destroy_buffer(test_buffer, None) };
info!("Allocation and deallocation of GpuToCpu memory was successful.");
}
drop(allocator); // Explicitly drop before destruction of device and instance.
unsafe { device.destroy_device(None) };
unsafe { instance.destroy_instance(None) };
}

View file

@ -0,0 +1,47 @@
use ash::vk;
#[allow(clippy::too_many_arguments)]
pub(crate) fn record_and_submit_command_buffer<F: FnOnce(&ash::Device, vk::CommandBuffer)>(
device: &ash::Device,
command_buffer: vk::CommandBuffer,
command_buffer_reuse_fence: vk::Fence,
submit_queue: vk::Queue,
wait_mask: &[vk::PipelineStageFlags],
wait_semaphores: &[vk::Semaphore],
signal_semaphores: &[vk::Semaphore],
f: F,
) {
unsafe { device.wait_for_fences(&[command_buffer_reuse_fence], true, u64::MAX) }.unwrap();
unsafe { device.reset_fences(&[command_buffer_reuse_fence]) }.unwrap();
unsafe {
device.reset_command_buffer(
command_buffer,
vk::CommandBufferResetFlags::RELEASE_RESOURCES,
)
}
.unwrap();
let command_buffer_begin_info =
vk::CommandBufferBeginInfo::builder().flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT);
unsafe { device.begin_command_buffer(command_buffer, &command_buffer_begin_info) }.unwrap();
f(device, command_buffer);
unsafe { device.end_command_buffer(command_buffer) }.unwrap();
let command_buffers = [command_buffer];
let submit_info = vk::SubmitInfo::builder()
.wait_semaphores(wait_semaphores)
.wait_dst_stage_mask(wait_mask)
.command_buffers(&command_buffers)
.signal_semaphores(signal_semaphores);
unsafe {
device.queue_submit(
submit_queue,
std::slice::from_ref(&submit_info),
command_buffer_reuse_fence,
)
}
.unwrap();
}

View file

@ -0,0 +1,953 @@
use ash::vk;
use crate::helper::record_and_submit_command_buffer;
use gpu_allocator::vulkan::{Allocation, AllocationCreateDesc, AllocationScheme, Allocator};
use gpu_allocator::MemoryLocation;
#[repr(C)]
#[derive(Clone, Copy)]
struct ImGuiCBuffer {
scale: [f32; 2],
translation: [f32; 2],
}
pub struct ImGuiRenderer {
sampler: vk::Sampler,
vb_capacity: u64,
ib_capacity: u64,
vb_allocation: Allocation,
ib_allocation: Allocation,
vertex_buffer: vk::Buffer,
index_buffer: vk::Buffer,
cb_allocation: Allocation,
constant_buffer: vk::Buffer,
font_image: vk::Image,
font_image_memory: Allocation,
font_image_view: vk::ImageView,
descriptor_sets: Vec<vk::DescriptorSet>,
vs_module: vk::ShaderModule,
ps_module: vk::ShaderModule,
descriptor_set_layouts: Vec<vk::DescriptorSetLayout>,
pipeline_layout: vk::PipelineLayout,
pub(crate) render_pass: vk::RenderPass,
pipeline: vk::Pipeline,
}
impl ImGuiRenderer {
#[allow(clippy::too_many_arguments)]
pub(crate) fn new(
imgui: &mut imgui::Context,
device: &ash::Device,
descriptor_pool: vk::DescriptorPool,
render_target_format: vk::Format,
allocator: &mut Allocator,
cmd: vk::CommandBuffer,
cmd_reuse_fence: vk::Fence,
queue: vk::Queue,
) -> Result<Self, vk::Result> {
let (pipeline_layout, descriptor_set_layouts) = {
let bindings = [
vk::DescriptorSetLayoutBinding {
binding: 0,
descriptor_type: vk::DescriptorType::UNIFORM_BUFFER,
descriptor_count: 1,
stage_flags: vk::ShaderStageFlags::VERTEX,
p_immutable_samplers: std::ptr::null(),
},
vk::DescriptorSetLayoutBinding {
binding: 1,
descriptor_type: vk::DescriptorType::SAMPLER,
descriptor_count: 1,
stage_flags: vk::ShaderStageFlags::FRAGMENT,
p_immutable_samplers: std::ptr::null(),
},
vk::DescriptorSetLayoutBinding {
binding: 2,
descriptor_type: vk::DescriptorType::SAMPLED_IMAGE,
descriptor_count: 1,
stage_flags: vk::ShaderStageFlags::FRAGMENT,
p_immutable_samplers: std::ptr::null(),
},
];
let set_layout_infos =
[vk::DescriptorSetLayoutCreateInfo::builder().bindings(&bindings)];
let set_layouts = set_layout_infos
.iter()
.map(|info| unsafe { device.create_descriptor_set_layout(info, None) })
.collect::<Result<Vec<_>, vk::Result>>()?;
let layout_info = vk::PipelineLayoutCreateInfo::builder().set_layouts(&set_layouts);
let pipeline_layout = unsafe { device.create_pipeline_layout(&layout_info, None) }?;
(pipeline_layout, set_layouts)
};
let render_pass = {
let attachments = vk::AttachmentDescription::builder()
.format(render_target_format)
.samples(vk::SampleCountFlags::TYPE_1)
.load_op(vk::AttachmentLoadOp::CLEAR)
.store_op(vk::AttachmentStoreOp::STORE)
.final_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL);
let subpass_attachment = vk::AttachmentReference::builder()
.attachment(0)
.layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL);
let subpass_description = vk::SubpassDescription::builder()
.pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS)
.color_attachments(std::slice::from_ref(&subpass_attachment));
let dependencies = vk::SubpassDependency::builder()
.src_subpass(vk::SUBPASS_EXTERNAL)
.src_stage_mask(vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT)
.dst_access_mask(
vk::AccessFlags::COLOR_ATTACHMENT_READ
| vk::AccessFlags::COLOR_ATTACHMENT_WRITE,
)
.dst_stage_mask(vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT);
let render_pass_create_info = vk::RenderPassCreateInfo::builder()
.attachments(std::slice::from_ref(&attachments))
.subpasses(std::slice::from_ref(&subpass_description))
.dependencies(std::slice::from_ref(&dependencies));
unsafe { device.create_render_pass(&render_pass_create_info, None) }.unwrap()
};
let vs_module = {
let vs = include_bytes!("./spirv/imgui.vs.spv");
#[allow(clippy::cast_ptr_alignment)]
let shader_info = vk::ShaderModuleCreateInfo::builder().code(unsafe {
assert_eq!(vs.len() % 4, 0);
std::slice::from_raw_parts(vs.as_ptr().cast(), vs.len() / 4)
});
unsafe { device.create_shader_module(&shader_info, None) }?
};
let ps_module = {
let ps = include_bytes!("./spirv/imgui.ps.spv");
#[allow(clippy::cast_ptr_alignment)]
let shader_info = vk::ShaderModuleCreateInfo::builder().code(unsafe {
assert_eq!(ps.len() % 4, 0);
std::slice::from_raw_parts(ps.as_ptr().cast(), ps.len() / 4)
});
unsafe { device.create_shader_module(&shader_info, None) }?
};
let pipeline = {
let vertex_stage = vk::PipelineShaderStageCreateInfo::builder()
.stage(vk::ShaderStageFlags::VERTEX)
.module(vs_module)
.name(std::ffi::CStr::from_bytes_with_nul(b"main\0").unwrap());
let fragment_stage = vk::PipelineShaderStageCreateInfo::builder()
.stage(vk::ShaderStageFlags::FRAGMENT)
.module(ps_module)
.name(std::ffi::CStr::from_bytes_with_nul(b"main\0").unwrap());
let stages = [vertex_stage.build(), fragment_stage.build()];
let vertex_binding_descriptions = [vk::VertexInputBindingDescription {
binding: 0,
stride: std::mem::size_of::<imgui::DrawVert>() as u32,
input_rate: vk::VertexInputRate::VERTEX,
}];
let vertex_attribute_descriptions = [
vk::VertexInputAttributeDescription {
location: 0,
binding: 0,
format: vk::Format::R32G32_SFLOAT,
offset: 0,
},
vk::VertexInputAttributeDescription {
location: 1,
binding: 0,
format: vk::Format::R32G32_SFLOAT,
offset: 8,
},
vk::VertexInputAttributeDescription {
location: 2,
binding: 0,
format: vk::Format::R8G8B8A8_UNORM,
offset: 16,
},
];
let vertex_input_state = vk::PipelineVertexInputStateCreateInfo::builder()
.vertex_binding_descriptions(&vertex_binding_descriptions)
.vertex_attribute_descriptions(&vertex_attribute_descriptions);
let input_assembly_state = vk::PipelineInputAssemblyStateCreateInfo::builder()
.topology(vk::PrimitiveTopology::TRIANGLE_LIST);
let viewport_state = vk::PipelineViewportStateCreateInfo::builder()
.viewport_count(1)
.scissor_count(1);
let rasterization_state = vk::PipelineRasterizationStateCreateInfo::builder()
.polygon_mode(vk::PolygonMode::FILL)
.cull_mode(vk::CullModeFlags::NONE)
.front_face(vk::FrontFace::CLOCKWISE)
.depth_bias_enable(false)
.line_width(1.0);
let multisample_state = vk::PipelineMultisampleStateCreateInfo::builder()
.rasterization_samples(vk::SampleCountFlags::TYPE_1);
let noop_stencil_state = vk::StencilOpState {
fail_op: vk::StencilOp::KEEP,
pass_op: vk::StencilOp::KEEP,
depth_fail_op: vk::StencilOp::KEEP,
compare_op: vk::CompareOp::ALWAYS,
..Default::default()
};
let depth_stencil_state = vk::PipelineDepthStencilStateCreateInfo::builder()
.depth_test_enable(false)
.depth_write_enable(false)
.depth_compare_op(vk::CompareOp::ALWAYS)
.depth_bounds_test_enable(false)
.stencil_test_enable(false)
.front(noop_stencil_state)
.back(noop_stencil_state)
.max_depth_bounds(1.0);
let attachments = vk::PipelineColorBlendAttachmentState::builder()
.blend_enable(true)
.src_color_blend_factor(vk::BlendFactor::SRC_ALPHA)
.dst_color_blend_factor(vk::BlendFactor::ONE_MINUS_SRC_ALPHA)
.color_blend_op(vk::BlendOp::ADD)
.src_alpha_blend_factor(vk::BlendFactor::ZERO)
.dst_alpha_blend_factor(vk::BlendFactor::ZERO)
.alpha_blend_op(vk::BlendOp::ADD)
.color_write_mask({
vk::ColorComponentFlags::R
| vk::ColorComponentFlags::G
| vk::ColorComponentFlags::B
| vk::ColorComponentFlags::A
});
let color_blend_state = vk::PipelineColorBlendStateCreateInfo::builder()
.logic_op(vk::LogicOp::CLEAR)
.attachments(std::slice::from_ref(&attachments));
let dynamic_state = vk::PipelineDynamicStateCreateInfo::builder()
.dynamic_states(&[vk::DynamicState::VIEWPORT, vk::DynamicState::SCISSOR]);
let pipeline_create_info = vk::GraphicsPipelineCreateInfo::builder()
.stages(&stages)
.vertex_input_state(&vertex_input_state)
.input_assembly_state(&input_assembly_state)
.viewport_state(&viewport_state)
.rasterization_state(&rasterization_state)
.multisample_state(&multisample_state)
.depth_stencil_state(&depth_stencil_state)
.color_blend_state(&color_blend_state)
.dynamic_state(&dynamic_state)
.layout(pipeline_layout)
.render_pass(render_pass)
.subpass(0);
unsafe {
device.create_graphics_pipelines(
vk::PipelineCache::null(),
std::slice::from_ref(&pipeline_create_info),
None,
)
}
.unwrap()[0]
};
let (font_image, font_image_memory, font_image_view) = {
let fonts = imgui.fonts();
let font_atlas = fonts.build_rgba32_texture();
// Create image
let image_usage = vk::ImageUsageFlags::SAMPLED
| vk::ImageUsageFlags::TRANSFER_DST
| vk::ImageUsageFlags::TRANSFER_SRC;
let create_info = vk::ImageCreateInfo::builder()
.image_type(vk::ImageType::TYPE_2D)
.format(vk::Format::R8G8B8A8_UNORM)
.extent(vk::Extent3D {
width: font_atlas.width,
height: font_atlas.height,
depth: 1,
})
.mip_levels(1)
.array_layers(1)
.samples(vk::SampleCountFlags::TYPE_1)
.tiling(vk::ImageTiling::OPTIMAL)
.usage(image_usage)
.initial_layout(vk::ImageLayout::UNDEFINED);
let image = unsafe { device.create_image(&create_info, None) }?;
// Allocate and bind memory to image
let requirements = unsafe { device.get_image_memory_requirements(image) };
let allocation = allocator
.allocate(&AllocationCreateDesc {
name: "ImGui font image",
requirements,
location: MemoryLocation::GpuOnly,
linear: false,
allocation_scheme: AllocationScheme::GpuAllocatorManaged,
})
.unwrap();
unsafe { device.bind_image_memory(image, allocation.memory(), allocation.offset()) }
.unwrap();
// Create image view
let view_create_info = vk::ImageViewCreateInfo::builder()
.image(image)
.view_type(vk::ImageViewType::TYPE_2D)
.format(vk::Format::R8G8B8A8_UNORM)
.components(vk::ComponentMapping {
r: vk::ComponentSwizzle::R,
g: vk::ComponentSwizzle::G,
b: vk::ComponentSwizzle::B,
a: vk::ComponentSwizzle::A,
})
.subresource_range(vk::ImageSubresourceRange {
aspect_mask: vk::ImageAspectFlags::COLOR,
base_mip_level: 0,
level_count: 1,
base_array_layer: 0,
layer_count: 1,
});
let image_view = unsafe { device.create_image_view(&view_create_info, None) }?;
// Create upload buffer
let (upload_buffer, upload_buffer_memory) = {
let create_info = vk::BufferCreateInfo::builder()
.size((font_atlas.width * font_atlas.height * 4) as u64)
.usage(vk::BufferUsageFlags::TRANSFER_SRC);
let buffer = unsafe { device.create_buffer(&create_info, None) }?;
let requirements = unsafe { device.get_buffer_memory_requirements(buffer) };
let buffer_memory = allocator
.allocate(&AllocationCreateDesc {
name: "ImGui font image upload buffer",
requirements,
location: MemoryLocation::CpuToGpu,
linear: true,
allocation_scheme: AllocationScheme::GpuAllocatorManaged,
})
.unwrap();
unsafe {
device.bind_buffer_memory(
buffer,
buffer_memory.memory(),
buffer_memory.offset(),
)
}?;
(buffer, buffer_memory)
};
// Copy font data to upload buffer
let dst = upload_buffer_memory.mapped_ptr().unwrap().cast().as_ptr();
unsafe {
std::ptr::copy_nonoverlapping(
font_atlas.data.as_ptr(),
dst,
(font_atlas.width * font_atlas.height * 4) as usize,
);
}
// Copy upload buffer to image
record_and_submit_command_buffer(
device,
cmd,
cmd_reuse_fence,
queue,
&[],
&[],
&[],
|device, cmd| {
{
let layout_transition_barriers = vk::ImageMemoryBarrier::builder()
.image(image)
.dst_access_mask(vk::AccessFlags::TRANSFER_WRITE)
.new_layout(vk::ImageLayout::TRANSFER_DST_OPTIMAL)
.old_layout(vk::ImageLayout::UNDEFINED)
.subresource_range(vk::ImageSubresourceRange {
aspect_mask: vk::ImageAspectFlags::COLOR,
base_mip_level: 0,
level_count: vk::REMAINING_MIP_LEVELS,
base_array_layer: 0,
layer_count: vk::REMAINING_ARRAY_LAYERS,
});
unsafe {
device.cmd_pipeline_barrier(
cmd,
vk::PipelineStageFlags::BOTTOM_OF_PIPE,
vk::PipelineStageFlags::TRANSFER,
vk::DependencyFlags::empty(),
&[],
&[],
std::slice::from_ref(&layout_transition_barriers),
)
};
}
let regions = vk::BufferImageCopy::builder()
.buffer_offset(0)
.buffer_row_length(font_atlas.width)
.buffer_image_height(font_atlas.height)
.image_subresource(vk::ImageSubresourceLayers {
aspect_mask: vk::ImageAspectFlags::COLOR,
mip_level: 0,
base_array_layer: 0,
layer_count: 1,
})
.image_offset(vk::Offset3D { x: 0, y: 0, z: 0 })
.image_extent(vk::Extent3D {
width: font_atlas.width,
height: font_atlas.height,
depth: 1,
});
unsafe {
device.cmd_copy_buffer_to_image(
cmd,
upload_buffer,
image,
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
std::slice::from_ref(&regions),
)
};
{
let layout_transition_barriers = vk::ImageMemoryBarrier::builder()
.image(image)
.dst_access_mask(vk::AccessFlags::SHADER_READ)
.new_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL)
.old_layout(vk::ImageLayout::TRANSFER_DST_OPTIMAL)
.subresource_range(vk::ImageSubresourceRange {
aspect_mask: vk::ImageAspectFlags::COLOR,
base_mip_level: 0,
level_count: vk::REMAINING_MIP_LEVELS,
base_array_layer: 0,
layer_count: vk::REMAINING_ARRAY_LAYERS,
});
unsafe {
device.cmd_pipeline_barrier(
cmd,
vk::PipelineStageFlags::BOTTOM_OF_PIPE,
vk::PipelineStageFlags::FRAGMENT_SHADER,
vk::DependencyFlags::empty(),
&[],
&[],
std::slice::from_ref(&layout_transition_barriers),
)
};
}
},
);
unsafe { device.queue_wait_idle(queue) }?;
// Free upload buffer
unsafe { device.destroy_buffer(upload_buffer, None) };
allocator.free(upload_buffer_memory).unwrap();
(image, allocation, image_view)
};
let sampler = {
let create_info = vk::SamplerCreateInfo::builder()
.mag_filter(vk::Filter::NEAREST)
.min_filter(vk::Filter::NEAREST)
.mipmap_mode(vk::SamplerMipmapMode::NEAREST)
.address_mode_u(vk::SamplerAddressMode::REPEAT)
.address_mode_v(vk::SamplerAddressMode::REPEAT)
.address_mode_w(vk::SamplerAddressMode::REPEAT)
.mip_lod_bias(0.0)
.anisotropy_enable(false)
.compare_enable(false)
.unnormalized_coordinates(false);
unsafe { device.create_sampler(&create_info, None) }?
};
let (vertex_buffer, vb_allocation, vb_capacity) = {
let capacity = 1024 * 1024;
let create_info = vk::BufferCreateInfo::builder()
.size(capacity)
.usage(vk::BufferUsageFlags::VERTEX_BUFFER)
.sharing_mode(vk::SharingMode::EXCLUSIVE);
let buffer = unsafe { device.create_buffer(&create_info, None) }?;
let requirements = unsafe { device.get_buffer_memory_requirements(buffer) };
let allocation = allocator
.allocate(&AllocationCreateDesc {
name: "ImGui Vertex buffer",
requirements,
location: MemoryLocation::CpuToGpu,
linear: true,
allocation_scheme: AllocationScheme::GpuAllocatorManaged,
})
.unwrap();
unsafe { device.bind_buffer_memory(buffer, allocation.memory(), allocation.offset()) }?;
(buffer, allocation, capacity)
};
let (index_buffer, ib_allocation, ib_capacity) = {
let capacity = 1024 * 1024;
let create_info = vk::BufferCreateInfo::builder()
.size(capacity)
.usage(vk::BufferUsageFlags::INDEX_BUFFER)
.sharing_mode(vk::SharingMode::EXCLUSIVE);
let buffer = unsafe { device.create_buffer(&create_info, None) }?;
let requirements = unsafe { device.get_buffer_memory_requirements(buffer) };
let allocation = allocator
.allocate(&AllocationCreateDesc {
name: "ImGui Index buffer",
requirements,
location: MemoryLocation::CpuToGpu,
linear: true,
allocation_scheme: AllocationScheme::GpuAllocatorManaged,
})
.unwrap();
unsafe { device.bind_buffer_memory(buffer, allocation.memory(), allocation.offset()) }?;
(buffer, allocation, capacity)
};
let (constant_buffer, cb_allocation) = {
let create_info = vk::BufferCreateInfo::builder()
.size(std::mem::size_of::<ImGuiCBuffer>() as u64)
.usage(vk::BufferUsageFlags::UNIFORM_BUFFER)
.sharing_mode(vk::SharingMode::EXCLUSIVE);
let buffer = unsafe { device.create_buffer(&create_info, None) }?;
let requirements = unsafe { device.get_buffer_memory_requirements(buffer) };
let allocation = allocator
.allocate(&AllocationCreateDesc {
name: "ImGui Constant buffer",
requirements,
location: MemoryLocation::CpuToGpu,
linear: true,
allocation_scheme: AllocationScheme::GpuAllocatorManaged,
})
.unwrap();
unsafe { device.bind_buffer_memory(buffer, allocation.memory(), allocation.offset()) }?;
(buffer, allocation)
};
let descriptor_sets = {
let alloc_info = vk::DescriptorSetAllocateInfo::builder()
.descriptor_pool(descriptor_pool)
.set_layouts(&descriptor_set_layouts);
let descriptor_sets = unsafe { device.allocate_descriptor_sets(&alloc_info) }?;
let buffer_info = vk::DescriptorBufferInfo::builder()
.buffer(constant_buffer)
.offset(0)
.range(std::mem::size_of::<ImGuiCBuffer>() as u64);
let uniform_buffer = vk::WriteDescriptorSet::builder()
.dst_set(descriptor_sets[0])
.dst_binding(0)
.descriptor_type(vk::DescriptorType::UNIFORM_BUFFER)
.buffer_info(std::slice::from_ref(&buffer_info));
let image_info = vk::DescriptorImageInfo::builder().sampler(sampler);
let sampler = vk::WriteDescriptorSet::builder()
.dst_set(descriptor_sets[0])
.dst_binding(1)
.descriptor_type(vk::DescriptorType::SAMPLER)
.image_info(std::slice::from_ref(&image_info));
let image_info = vk::DescriptorImageInfo::builder()
.image_view(font_image_view)
.image_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL);
let sampled_image = vk::WriteDescriptorSet::builder()
.dst_set(descriptor_sets[0])
.dst_binding(2)
.descriptor_type(vk::DescriptorType::SAMPLED_IMAGE)
.image_info(std::slice::from_ref(&image_info));
unsafe {
device.update_descriptor_sets(
&[
uniform_buffer.build(),
sampler.build(),
sampled_image.build(),
],
&[],
)
};
descriptor_sets
};
Ok(Self {
sampler,
vb_capacity,
ib_capacity,
vb_allocation,
ib_allocation,
vertex_buffer,
index_buffer,
cb_allocation,
constant_buffer,
font_image,
font_image_memory,
font_image_view,
descriptor_sets,
vs_module,
ps_module,
descriptor_set_layouts,
pipeline_layout,
render_pass,
pipeline,
})
}
pub(crate) fn render(
&mut self,
imgui_draw_data: &imgui::DrawData,
device: &ash::Device,
window_width: u32,
window_height: u32,
framebuffer: vk::Framebuffer,
cmd: vk::CommandBuffer,
) {
// Update constant buffer
{
let left = imgui_draw_data.display_pos[0];
let right = imgui_draw_data.display_pos[0] + imgui_draw_data.display_size[0];
let top = imgui_draw_data.display_pos[1];
let bottom = imgui_draw_data.display_pos[1] + imgui_draw_data.display_size[1];
let cbuffer_data = ImGuiCBuffer {
scale: [(2.0 / (right - left)), (2.0 / (bottom - top))],
translation: [
(right + left) / (left - right),
(top + bottom) / (top - bottom),
],
};
unsafe {
std::ptr::copy_nonoverlapping(
&cbuffer_data,
self.cb_allocation.mapped_ptr().unwrap().cast().as_ptr(),
1,
)
};
}
let render_pass_begin_info = vk::RenderPassBeginInfo::builder()
.render_pass(self.render_pass)
.framebuffer(framebuffer)
.render_area(vk::Rect2D {
offset: vk::Offset2D { x: 0, y: 0 },
extent: vk::Extent2D {
width: window_width,
height: window_height,
},
})
.clear_values(&[vk::ClearValue {
color: vk::ClearColorValue {
float32: [1.0, 0.5, 1.0, 0.0],
},
}]);
unsafe {
device.cmd_begin_render_pass(cmd, &render_pass_begin_info, vk::SubpassContents::INLINE)
};
unsafe { device.cmd_bind_pipeline(cmd, vk::PipelineBindPoint::GRAPHICS, self.pipeline) };
let viewport = vk::Viewport::builder()
.x(0.0)
.y(0.0)
.width(window_width as f32)
.height(window_height as f32);
unsafe { device.cmd_set_viewport(cmd, 0, std::slice::from_ref(&viewport)) };
{
let scissor_rect = vk::Rect2D {
offset: vk::Offset2D { x: 0, y: 0 },
extent: vk::Extent2D {
width: window_width,
height: window_height,
},
};
unsafe { device.cmd_set_scissor(cmd, 0, &[scissor_rect]) };
}
unsafe {
device.cmd_bind_descriptor_sets(
cmd,
vk::PipelineBindPoint::GRAPHICS,
self.pipeline_layout,
0,
&self.descriptor_sets,
&[],
)
};
let (vtx_count, idx_count) =
imgui_draw_data
.draw_lists()
.fold((0, 0), |(vtx_count, idx_count), draw_list| {
(
vtx_count + draw_list.vtx_buffer().len(),
idx_count + draw_list.idx_buffer().len(),
)
});
let vtx_size = (vtx_count * std::mem::size_of::<imgui::DrawVert>()) as u64;
if vtx_size > self.vb_capacity {
// reallocate vertex buffer
todo!();
}
let idx_size = (idx_count * std::mem::size_of::<imgui::DrawIdx>()) as u64;
if idx_size > self.ib_capacity {
// reallocate index buffer
todo!();
}
let mut vb_offset = 0;
let mut ib_offset = 0;
for draw_list in imgui_draw_data.draw_lists() {
unsafe {
device.cmd_bind_vertex_buffers(
cmd,
0,
&[self.vertex_buffer],
&[vb_offset as u64 * std::mem::size_of::<imgui::DrawVert>() as u64],
)
};
unsafe {
device.cmd_bind_index_buffer(
cmd,
self.index_buffer,
ib_offset as u64 * std::mem::size_of::<imgui::DrawIdx>() as u64,
vk::IndexType::UINT16,
)
};
{
let vertices = draw_list.vtx_buffer();
let dst_ptr = self
.vb_allocation
.mapped_ptr()
.unwrap()
.cast::<imgui::DrawVert>()
.as_ptr();
let dst_ptr = unsafe { dst_ptr.offset(vb_offset) };
unsafe {
std::ptr::copy_nonoverlapping(vertices.as_ptr(), dst_ptr, vertices.len())
};
vb_offset += vertices.len() as isize;
}
{
let indices = draw_list.idx_buffer();
let dst_ptr = self
.ib_allocation
.mapped_ptr()
.unwrap()
.cast::<imgui::DrawIdx>()
.as_ptr();
let dst_ptr = unsafe { dst_ptr.offset(ib_offset) };
unsafe { std::ptr::copy_nonoverlapping(indices.as_ptr(), dst_ptr, indices.len()) };
ib_offset += indices.len() as isize;
}
for command in draw_list.commands() {
match command {
imgui::DrawCmd::Elements { count, cmd_params } => {
let scissor_rect = vk::Rect2D {
offset: vk::Offset2D {
x: cmd_params.clip_rect[0] as i32,
y: cmd_params.clip_rect[1] as i32,
},
extent: vk::Extent2D {
width: (cmd_params.clip_rect[2] - cmd_params.clip_rect[0]) as u32,
height: (cmd_params.clip_rect[3] - cmd_params.clip_rect[1]) as u32,
},
};
unsafe { device.cmd_set_scissor(cmd, 0, &[scissor_rect]) };
unsafe {
device.cmd_draw_indexed(
cmd,
count as u32,
1,
cmd_params.idx_offset as u32,
cmd_params.vtx_offset as i32,
0,
)
};
}
_ => todo!(),
}
}
}
unsafe { device.cmd_end_render_pass(cmd) };
}
pub(crate) fn destroy(self, device: &ash::Device, allocator: &mut Allocator) {
unsafe { device.destroy_buffer(self.constant_buffer, None) };
allocator.free(self.cb_allocation).unwrap();
unsafe { device.destroy_buffer(self.index_buffer, None) };
allocator.free(self.ib_allocation).unwrap();
unsafe { device.destroy_buffer(self.vertex_buffer, None) };
allocator.free(self.vb_allocation).unwrap();
unsafe {
device.destroy_sampler(self.sampler, None);
}
unsafe {
device.destroy_image_view(self.font_image_view, None);
}
unsafe {
device.destroy_image(self.font_image, None);
}
allocator.free(self.font_image_memory).unwrap();
unsafe { device.destroy_shader_module(self.ps_module, None) };
unsafe { device.destroy_shader_module(self.vs_module, None) };
unsafe { device.destroy_pipeline(self.pipeline, None) };
unsafe { device.destroy_render_pass(self.render_pass, None) };
unsafe {
device.destroy_pipeline_layout(self.pipeline_layout, None);
}
for &layout in self.descriptor_set_layouts.iter() {
unsafe { device.destroy_descriptor_set_layout(layout, None) };
}
}
}
pub(crate) fn handle_imgui_event(
io: &mut imgui::Io,
window: &winit::window::Window,
event: &winit::event::Event<'_, ()>,
) -> bool {
use winit::event::{
DeviceEvent, ElementState, Event, KeyboardInput, MouseButton, MouseScrollDelta, TouchPhase,
VirtualKeyCode, WindowEvent,
};
match event {
Event::WindowEvent { event, window_id } if *window_id == window.id() => match *event {
WindowEvent::Resized(physical_size) => {
io.display_size = [physical_size.width as f32, physical_size.height as f32];
false
}
WindowEvent::KeyboardInput {
input:
KeyboardInput {
virtual_keycode: Some(key),
state,
..
},
..
} => {
let pressed = state == ElementState::Pressed;
io.keys_down[key as usize] = pressed;
match key {
VirtualKeyCode::LShift | VirtualKeyCode::RShift => io.key_shift = pressed,
VirtualKeyCode::LControl | VirtualKeyCode::RControl => io.key_ctrl = pressed,
VirtualKeyCode::LAlt | VirtualKeyCode::RAlt => io.key_alt = pressed,
VirtualKeyCode::LWin | VirtualKeyCode::RWin => io.key_super = pressed,
_ => (),
}
io.want_capture_keyboard
}
WindowEvent::ReceivedCharacter(ch) => {
io.add_input_character(ch);
io.want_capture_keyboard
}
WindowEvent::CursorMoved { position, .. } => {
io.mouse_pos = [position.x as f32, position.y as f32];
io.want_capture_mouse
}
WindowEvent::MouseWheel {
delta,
phase: TouchPhase::Moved,
..
} => {
match delta {
MouseScrollDelta::LineDelta(h, v) => {
io.mouse_wheel_h = h;
io.mouse_wheel = v;
}
MouseScrollDelta::PixelDelta(pos) => {
match pos.x.partial_cmp(&0.0) {
Some(std::cmp::Ordering::Greater) => io.mouse_wheel_h += 1.0,
Some(std::cmp::Ordering::Less) => io.mouse_wheel_h -= 1.0,
_ => (),
}
match pos.y.partial_cmp(&0.0) {
Some(std::cmp::Ordering::Greater) => io.mouse_wheel += 1.0,
Some(std::cmp::Ordering::Less) => io.mouse_wheel -= 1.0,
_ => (),
}
}
}
io.want_capture_mouse
}
WindowEvent::MouseInput { state, button, .. } => {
let pressed = state == ElementState::Pressed;
match button {
MouseButton::Left => io.mouse_down[0] = pressed,
MouseButton::Right => io.mouse_down[1] = pressed,
MouseButton::Middle => io.mouse_down[2] = pressed,
MouseButton::Other(idx @ 0..=4) => io.mouse_down[idx as usize] = pressed,
MouseButton::Other(_) => (),
}
io.want_capture_mouse
}
_ => false,
},
// Track key release events outside our window. If we don't do this,
// we might never see the release event if some other window gets focus.
Event::DeviceEvent {
event:
DeviceEvent::Key(KeyboardInput {
state: ElementState::Released,
virtual_keycode: Some(key),
..
}),
..
} => {
io.keys_down[*key as usize] = false;
match *key {
VirtualKeyCode::LShift | VirtualKeyCode::RShift => io.key_shift = false,
VirtualKeyCode::LControl | VirtualKeyCode::RControl => io.key_ctrl = false,
VirtualKeyCode::LAlt | VirtualKeyCode::RAlt => io.key_alt = false,
VirtualKeyCode::LWin | VirtualKeyCode::RWin => io.key_super = false,
_ => (),
}
io.want_capture_keyboard
}
_ => false,
}
}

View file

@ -0,0 +1,440 @@
use std::default::Default;
use std::ffi::CString;
use ash::vk;
use gpu_allocator::vulkan::{Allocator, AllocatorCreateDesc};
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
mod imgui_renderer;
use imgui_renderer::{handle_imgui_event, ImGuiRenderer};
mod helper;
use helper::record_and_submit_command_buffer;
fn main() -> ash::prelude::VkResult<()> {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("trace")).init();
let entry = unsafe { ash::Entry::load() }.unwrap();
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 Vulkan visualization")
.with_inner_size(winit::dpi::PhysicalSize::new(
window_width as f64,
window_height as f64,
))
.with_resizable(false)
.build(&event_loop)
.unwrap();
// Create Vulkan instance
let instance = {
let app_name = CString::new("gpu-allocator examples vulkan-visualization").unwrap();
let appinfo = vk::ApplicationInfo::builder()
.application_name(&app_name)
.application_version(0)
.engine_name(&app_name)
.engine_version(0)
.api_version(vk::make_api_version(0, 1, 0, 0));
let layer_names: &[CString] = &[CString::new("VK_LAYER_KHRONOS_validation").unwrap()];
let layers_names_raw: Vec<*const i8> = layer_names
.iter()
.map(|raw_name| raw_name.as_ptr())
.collect();
let surface_extensions =
ash_window::enumerate_required_extensions(event_loop.raw_display_handle()).unwrap();
let create_info = vk::InstanceCreateInfo::builder()
.application_info(&appinfo)
.enabled_layer_names(&layers_names_raw)
.enabled_extension_names(surface_extensions);
unsafe {
entry
.create_instance(&create_info, None)
.expect("Instance creation error")
}
};
let surface = unsafe {
ash_window::create_surface(
&entry,
&instance,
window.raw_display_handle(),
window.raw_window_handle(),
None,
)
}
.unwrap();
let surface_loader = ash::extensions::khr::Surface::new(&entry, &instance);
// Look for Vulkan physical device
let (pdevice, queue_family_index) = {
let pdevices = unsafe {
instance
.enumerate_physical_devices()
.expect("Physical device error")
};
pdevices
.iter()
.find_map(|pdevice| {
unsafe { instance.get_physical_device_queue_family_properties(*pdevice) }
.iter()
.enumerate()
.find_map(|(index, &info)| {
let supports_graphics = info.queue_flags.contains(vk::QueueFlags::GRAPHICS);
let supports_surface = unsafe {
surface_loader.get_physical_device_surface_support(
*pdevice,
index as u32,
surface,
)
}
.unwrap();
if supports_graphics && supports_surface {
Some((*pdevice, index))
} else {
None
}
})
})
.expect("Couldn't find suitable device.")
};
// Create Vulkan device
let device = {
let device_extension_names_raw = [ash::extensions::khr::Swapchain::name().as_ptr()];
let features = vk::PhysicalDeviceFeatures {
shader_clip_distance: 1,
..Default::default()
};
let priorities = [1.0];
let queue_info = vk::DeviceQueueCreateInfo::builder()
.queue_family_index(queue_family_index as u32)
.queue_priorities(&priorities);
let create_info = vk::DeviceCreateInfo::builder()
.queue_create_infos(std::slice::from_ref(&queue_info))
.enabled_extension_names(&device_extension_names_raw)
.enabled_features(&features);
unsafe { instance.create_device(pdevice, &create_info, None) }.unwrap()
};
let present_queue = unsafe { device.get_device_queue(queue_family_index as u32, 0) };
let surface_format =
unsafe { surface_loader.get_physical_device_surface_formats(pdevice, surface) }.unwrap()[0];
let surface_capabilities =
unsafe { surface_loader.get_physical_device_surface_capabilities(pdevice, surface) }
.unwrap();
let mut desired_image_count = surface_capabilities.min_image_count + 1;
if surface_capabilities.max_image_count > 0
&& desired_image_count > surface_capabilities.max_image_count
{
desired_image_count = surface_capabilities.max_image_count;
}
let surface_resolution = match surface_capabilities.current_extent.width {
u32::MAX => vk::Extent2D {
width: window_width,
height: window_height,
},
_ => surface_capabilities.current_extent,
};
let pre_transform = if surface_capabilities
.supported_transforms
.contains(vk::SurfaceTransformFlagsKHR::IDENTITY)
{
vk::SurfaceTransformFlagsKHR::IDENTITY
} else {
surface_capabilities.current_transform
};
let present_modes =
unsafe { surface_loader.get_physical_device_surface_present_modes(pdevice, surface) }
.unwrap();
let present_mode = present_modes
.iter()
.cloned()
.find(|&mode| mode == vk::PresentModeKHR::MAILBOX)
.unwrap_or(vk::PresentModeKHR::FIFO);
let swapchain_loader = ash::extensions::khr::Swapchain::new(&instance, &device);
let swapchain_create_info = vk::SwapchainCreateInfoKHR::builder()
.surface(surface)
.min_image_count(desired_image_count)
.image_color_space(surface_format.color_space)
.image_format(surface_format.format)
.image_extent(surface_resolution)
.image_usage(vk::ImageUsageFlags::COLOR_ATTACHMENT)
.image_sharing_mode(vk::SharingMode::EXCLUSIVE)
.pre_transform(pre_transform)
.composite_alpha(vk::CompositeAlphaFlagsKHR::OPAQUE)
.present_mode(present_mode)
.clipped(true)
.image_array_layers(1);
let swapchain =
unsafe { swapchain_loader.create_swapchain(&swapchain_create_info, None) }.unwrap();
let pool_create_info = vk::CommandPoolCreateInfo::builder()
.flags(vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER)
.queue_family_index(queue_family_index as u32);
let command_pool = unsafe { device.create_command_pool(&pool_create_info, None) }.unwrap();
let command_buffer_allocate_info = vk::CommandBufferAllocateInfo::builder()
.command_buffer_count(2)
.command_pool(command_pool)
.level(vk::CommandBufferLevel::PRIMARY);
let command_buffers =
unsafe { device.allocate_command_buffers(&command_buffer_allocate_info) }.unwrap();
let setup_command_buffer = command_buffers[0];
let draw_command_buffer = command_buffers[1];
let present_images = unsafe { swapchain_loader.get_swapchain_images(swapchain) }.unwrap();
let mut present_image_views = present_images
.iter()
.map(|&image| {
let create_view_info = vk::ImageViewCreateInfo::builder()
.view_type(vk::ImageViewType::TYPE_2D)
.format(surface_format.format)
.components(vk::ComponentMapping {
r: vk::ComponentSwizzle::R,
g: vk::ComponentSwizzle::G,
b: vk::ComponentSwizzle::B,
a: vk::ComponentSwizzle::A,
})
.subresource_range(vk::ImageSubresourceRange {
aspect_mask: vk::ImageAspectFlags::COLOR,
base_mip_level: 0,
level_count: 1,
base_array_layer: 0,
layer_count: 1,
})
.image(image);
unsafe { device.create_image_view(&create_view_info, None) }.unwrap()
})
.collect::<Vec<_>>();
// Setting up the allocator
let mut allocator = Some(
Allocator::new(&AllocatorCreateDesc {
instance: instance.clone(),
device: device.clone(),
physical_device: pdevice,
debug_settings: Default::default(),
buffer_device_address: false,
})
.unwrap(),
);
let fence_create_info = vk::FenceCreateInfo::builder().flags(vk::FenceCreateFlags::SIGNALED);
let draw_commands_reuse_fence =
unsafe { device.create_fence(&fence_create_info, None) }.unwrap();
let setup_commands_reuse_fence =
unsafe { device.create_fence(&fence_create_info, None) }.unwrap();
let semaphore_create_info = vk::SemaphoreCreateInfo::default();
let present_complete_semaphore =
unsafe { device.create_semaphore(&semaphore_create_info, None) }.unwrap();
let rendering_complete_semaphore =
unsafe { device.create_semaphore(&semaphore_create_info, None) }.unwrap();
let mut imgui = imgui::Context::create();
imgui.io_mut().display_size = [window_width as f32, window_height as f32];
let descriptor_pool = {
let pool_sizes = [
vk::DescriptorPoolSize {
ty: vk::DescriptorType::UNIFORM_BUFFER,
descriptor_count: 1,
},
vk::DescriptorPoolSize {
ty: vk::DescriptorType::SAMPLED_IMAGE,
descriptor_count: 1,
},
vk::DescriptorPoolSize {
ty: vk::DescriptorType::SAMPLER,
descriptor_count: 1,
},
];
let create_info = vk::DescriptorPoolCreateInfo::builder()
.max_sets(1)
.pool_sizes(&pool_sizes);
unsafe { device.create_descriptor_pool(&create_info, None) }?
};
let mut imgui_renderer = Some(ImGuiRenderer::new(
&mut imgui,
&device,
descriptor_pool,
surface_format.format,
allocator.as_mut().unwrap(),
setup_command_buffer,
setup_commands_reuse_fence,
present_queue,
)?);
let mut framebuffers = present_image_views
.iter()
.map(|&view| {
let create_info = vk::FramebufferCreateInfo::builder()
.render_pass(imgui_renderer.as_ref().unwrap().render_pass)
.attachments(std::slice::from_ref(&view))
.width(window_width)
.height(window_height)
.layers(1);
unsafe { device.create_framebuffer(&create_info, None) }.unwrap()
})
.collect::<Vec<_>>();
let mut visualizer = Some(gpu_allocator::vulkan::AllocatorVisualizer::new());
event_loop.run(move |event, _, control_flow| {
*control_flow = winit::event_loop::ControlFlow::Wait;
handle_imgui_event(imgui.io_mut(), &window, &event);
let mut ready_for_rendering = false;
match event {
winit::event::Event::WindowEvent { event, .. } => match event {
winit::event::WindowEvent::CloseRequested
| winit::event::WindowEvent::KeyboardInput {
input:
winit::event::KeyboardInput {
virtual_keycode: Some(winit::event::VirtualKeyCode::Escape),
..
},
..
} => {
*control_flow = winit::event_loop::ControlFlow::Exit;
}
_ => {}
},
winit::event::Event::MainEventsCleared => ready_for_rendering = true,
_ => {}
}
if ready_for_rendering {
let (present_index, _) = unsafe {
swapchain_loader.acquire_next_image(
swapchain,
u64::MAX,
present_complete_semaphore,
vk::Fence::null(),
)
}
.unwrap();
// Start ImGui frame
let ui = imgui.frame();
// Submit visualizer ImGui commands
visualizer
.as_mut()
.unwrap()
.render(allocator.as_ref().unwrap(), ui, None);
// Finish ImGui Frame
let imgui_draw_data = imgui.render();
record_and_submit_command_buffer(
&device,
draw_command_buffer,
draw_commands_reuse_fence,
present_queue,
&[vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT],
&[present_complete_semaphore],
&[rendering_complete_semaphore],
|device, cmd| {
// Render ImGui to swapchain image
imgui_renderer.as_mut().unwrap().render(
imgui_draw_data,
device,
window_width,
window_height,
framebuffers[present_index as usize],
cmd,
);
// Transition swapchain image to present state
let image_barriers = vk::ImageMemoryBarrier::builder()
.src_access_mask(
vk::AccessFlags::COLOR_ATTACHMENT_READ
| vk::AccessFlags::COLOR_ATTACHMENT_WRITE,
)
.old_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL)
.new_layout(vk::ImageLayout::PRESENT_SRC_KHR)
.image(present_images[present_index as usize])
.subresource_range(vk::ImageSubresourceRange {
aspect_mask: vk::ImageAspectFlags::COLOR,
base_mip_level: 0,
level_count: vk::REMAINING_MIP_LEVELS,
base_array_layer: 0,
layer_count: vk::REMAINING_ARRAY_LAYERS,
});
unsafe {
device.cmd_pipeline_barrier(
cmd,
vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT,
vk::PipelineStageFlags::BOTTOM_OF_PIPE,
vk::DependencyFlags::empty(),
&[],
&[],
std::slice::from_ref(&image_barriers),
)
};
},
);
let present_create_info = vk::PresentInfoKHR::builder()
.wait_semaphores(std::slice::from_ref(&rendering_complete_semaphore))
.swapchains(std::slice::from_ref(&swapchain))
.image_indices(std::slice::from_ref(&present_index));
unsafe { swapchain_loader.queue_present(present_queue, &present_create_info) }.unwrap();
} else if *control_flow == winit::event_loop::ControlFlow::Exit {
unsafe { device.queue_wait_idle(present_queue) }.unwrap();
visualizer.take();
for fb in framebuffers.drain(..) {
unsafe { device.destroy_framebuffer(fb, None) };
}
let mut allocator = allocator.take().unwrap();
imgui_renderer
.take()
.unwrap()
.destroy(&device, &mut allocator);
unsafe { device.destroy_descriptor_pool(descriptor_pool, None) };
unsafe { device.destroy_semaphore(rendering_complete_semaphore, None) };
unsafe { device.destroy_semaphore(present_complete_semaphore, None) };
unsafe { device.destroy_fence(setup_commands_reuse_fence, None) };
unsafe { device.destroy_fence(draw_commands_reuse_fence, None) };
drop(allocator);
for view in present_image_views.drain(..) {
unsafe { device.destroy_image_view(view, None) };
}
unsafe { device.free_command_buffers(command_pool, &command_buffers) };
unsafe { device.destroy_command_pool(command_pool, None) };
unsafe { swapchain_loader.destroy_swapchain(swapchain, None) };
unsafe { device.destroy_device(None) };
unsafe {
surface_loader.destroy_surface(surface, None);
}
unsafe { instance.destroy_instance(None) };
}
});
}

View file

@ -0,0 +1,14 @@
SamplerState g_sampler : register(s1, space0);
Texture2D g_texture : register(t2, space0);
struct VertexInput
{
float4 position : SV_POSITION;
float2 texCoord: TEXCOORD0;
float4 color: COLOR;
};
float4 main(VertexInput input) : SV_Target0
{
return input.color * g_texture.Sample(g_sampler, input.texCoord);
}

View file

@ -0,0 +1,30 @@
struct VertexInput
{
float2 pos : POSITION;
float2 texCoord : TEXCOORD0;
float4 color: COLOR;
};
struct VertexOutput
{
float4 position : SV_POSITION;
float2 texCoord: TEXCOORD0;
float4 color: COLOR;
};
struct Constants
{
float2 scale;
float2 translation;
};
ConstantBuffer<Constants> g_constants : register(b0, space0);
VertexOutput main(VertexInput vertex)
{
VertexOutput o;
o.position = float4(vertex.pos * g_constants.scale + g_constants.translation, 0.0, 1.0);
o.texCoord = vertex.texCoord;
o.color = vertex.color;
return o;
}