Created
October 9, 2018 14:49
-
-
Save Dooskington/0d708c48af131bb06d50dd65042fc84e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#[cfg(windows)] | |
extern crate gfx_backend_dx12 as backend; | |
#[cfg(target_os = "macos")] | |
extern crate gfx_backend_metal as backend; | |
#[cfg(all(unix, not(target_os = "macos")))] | |
extern crate gfx_backend_vulkan as backend; | |
extern crate gfx_hal; | |
extern crate nalgebra_glm as glm; | |
extern crate winit; | |
extern crate image; | |
use winit::{Event, EventsLoop, KeyboardInput, VirtualKeyCode, WindowBuilder, WindowEvent}; | |
use gfx_hal::{ | |
buffer, | |
command::{ClearColor, ClearValue, BufferImageCopy}, | |
image::{ | |
self as img, Access, Extent, Filter, Layout, Offset, SubresourceLayers, SubresourceRange, | |
ViewCapabilities, ViewKind, WrapMode, | |
}, | |
format::{Aspects, ChannelType, Format, Swizzle}, | |
memory::{Properties, Barrier, Dependencies}, | |
pass::{ | |
Attachment, AttachmentLoadOp, AttachmentOps, AttachmentStoreOp, Subpass, SubpassDependency, | |
SubpassDesc, SubpassRef, | |
}, | |
pool::CommandPoolCreateFlags, | |
pso::{ | |
AttributeDesc, BlendState, ColorBlendDesc, ColorMask, Descriptor, DescriptorRangeDesc, | |
DescriptorSetLayoutBinding, DescriptorSetWrite, DescriptorType, Element, EntryPoint, Face, | |
FrontFace, GraphicsPipelineDesc, GraphicsShaderSet, PipelineStage, Rasterizer, Rect, | |
ShaderStageFlags, Specialization, VertexBufferDesc, Viewport, | |
}, | |
queue::Submission, | |
Backbuffer, Backend, DescriptorPool, Device, FrameSync, Graphics, IndexType, Instance, | |
MemoryTypeId, PhysicalDevice, Primitive, Surface, SwapImageIndex, Swapchain, SwapchainConfig, | |
}; | |
type GfxBuffer = <backend::Backend as Backend>::Buffer; | |
type GfxMemory = <backend::Backend as Backend>::Memory; | |
type GfxImage = <backend::Backend as Backend>::Image; | |
type GfxImageView = <backend::Backend as Backend>::ImageView; | |
#[derive(Debug, Clone, Copy)] | |
#[repr(C)] | |
struct Vertex { | |
position: [f32; 3], | |
color: [f32; 4], | |
uv: [f32; 2], | |
} | |
#[derive(Debug, Clone, Copy)] | |
struct UniformBufferObject { | |
model: [[f32; 4]; 4], | |
view: [[f32; 4]; 4], | |
projection: [[f32; 4]; 4], | |
} | |
const MESH: &[Vertex] = &[ | |
// Top left | |
Vertex { | |
position: [-0.5, -0.5, 0.0], | |
color: [1.0, 0.0, 0.0, 1.0], | |
uv: [0.0, 0.0], | |
}, | |
// Top right | |
Vertex { | |
position: [0.5, -0.5, 0.0], | |
color: [0.0, 1.0, 0.0, 1.0], | |
uv: [1.0, 0.0], | |
}, | |
// Bottom right | |
Vertex { | |
position: [0.5, 0.5, 0.0], | |
color: [0.0, 1.0, 0.0, 1.0], | |
uv: [1.0, 1.0], | |
}, | |
// Bottom left | |
Vertex { | |
position: [-0.5, 0.5, 0.0], | |
color: [1.0, 0.0, 0.0, 1.0], | |
uv: [0.0, 1.0], | |
}, | |
]; | |
const MESH_INDICES: &[u32] = &[0, 1, 2, 2, 3, 0]; | |
fn main() { | |
// TODO | |
// use logical size for window dimensions to avoid DPI issues | |
// Create events loop and window. | |
let mut events_loop = EventsLoop::new(); | |
let window = WindowBuilder::new() | |
.with_title("gfx-rs testing") | |
.with_dimensions((512, 512).into()) | |
.build(&events_loop) | |
.unwrap(); | |
// Create an instance, which is the entry point to the graphics API. | |
let instance = backend::Instance::create("gfx-rs", 1); | |
// Create a surface, which is an abstraction over the OS's native window. | |
let mut surface = instance.create_surface(&window); | |
// Grab the first available adapter. | |
// An adapter represents a physical device, like a GPU. | |
let mut adapter = instance.enumerate_adapters().remove(0); | |
// The device is a logical device that allows us to perform GPU operations. | |
// The queue group contains a set of command queues which we can submit drawing commands to. | |
// Here we are requesting a queue group with the graphics capability. | |
let num_queues = 1; | |
let (device, mut queue_group) = adapter | |
.open_with::<_, Graphics>(num_queues, |family| surface.supports_queue_family(family)) | |
.unwrap(); | |
// A command pool is used to acquire command buffers, which are used to | |
// send drawing instructions to the GPU. | |
let num_buffers = 16; | |
let mut command_pool = device.create_command_pool_typed( | |
&queue_group, | |
CommandPoolCreateFlags::empty(), | |
num_buffers, | |
); | |
let physical_device = &adapter.physical_device; | |
// Grab the supported image formats for our surface. | |
let (_capabilities, formats, _) = surface.compatibility(physical_device); | |
let surface_color_format = match formats { | |
Some(choices) => choices | |
.into_iter() | |
.find(|format| format.base_format().1 == ChannelType::Srgb) | |
.unwrap(), | |
None => Format::Rgba8Srgb, | |
}; | |
let render_pass = { | |
let color_attachment = Attachment { | |
format: Some(surface_color_format), | |
samples: 1, | |
ops: AttachmentOps::new(AttachmentLoadOp::Clear, AttachmentStoreOp::Store), | |
stencil_ops: AttachmentOps::DONT_CARE, | |
layouts: Layout::Undefined..Layout::Present, | |
}; | |
let subpass = SubpassDesc { | |
colors: &[(0, Layout::ColorAttachmentOptimal)], | |
depth_stencil: None, | |
inputs: &[], | |
resolves: &[], | |
preserves: &[], | |
}; | |
let dependency = SubpassDependency { | |
passes: SubpassRef::External..SubpassRef::Pass(0), | |
stages: PipelineStage::COLOR_ATTACHMENT_OUTPUT..PipelineStage::COLOR_ATTACHMENT_OUTPUT, | |
accesses: Access::empty() | |
..(Access::COLOR_ATTACHMENT_READ | Access::COLOR_ATTACHMENT_WRITE), | |
}; | |
device.create_render_pass(&[color_attachment], &[subpass], &[dependency]) | |
}; | |
let frame_semaphore = device.create_semaphore(); | |
let frame_fence = device.create_fence(false); | |
// We need to describe the uniform buffer object binding through a descriptor set layout. | |
let set_layout = device.create_descriptor_set_layout( | |
&[DescriptorSetLayoutBinding { | |
binding: 0, | |
ty: DescriptorType::UniformBuffer, | |
count: 1, | |
stage_flags: ShaderStageFlags::VERTEX, | |
immutable_samplers: false, | |
}, | |
DescriptorSetLayoutBinding { | |
binding: 1, | |
ty: DescriptorType::SampledImage, | |
count: 1, | |
stage_flags: ShaderStageFlags::FRAGMENT, | |
immutable_samplers: false, | |
}, | |
DescriptorSetLayoutBinding { | |
binding: 2, | |
ty: DescriptorType::Sampler, | |
count: 1, | |
stage_flags: ShaderStageFlags::FRAGMENT, | |
immutable_samplers: false, | |
}], | |
&[], | |
); | |
// Create a descriptor pool so we can allocate descriptor sets | |
let mut descriptor_pool = device.create_descriptor_pool( | |
1, | |
&[DescriptorRangeDesc { | |
ty: DescriptorType::UniformBuffer, | |
count: 1, | |
}, DescriptorRangeDesc { | |
ty: DescriptorType::SampledImage, | |
count: 1, | |
}, DescriptorRangeDesc { | |
ty: DescriptorType::Sampler, | |
count: 1, | |
}], | |
); | |
// Now create a descriptor set | |
let descriptor_set = descriptor_pool.allocate_set(&set_layout).unwrap(); | |
let pipeline_layout = device.create_pipeline_layout(vec![&set_layout], &[]); | |
let vertex_shader_module = { | |
let spirv = include_bytes!("../res/shaders/bin/simple.glslv.spv"); | |
device.create_shader_module(spirv).unwrap() | |
}; | |
let fragment_shader_module = { | |
let spirv = include_bytes!("../res/shaders/bin/simple.glslf.spv"); | |
device.create_shader_module(spirv).unwrap() | |
}; | |
let pipeline = { | |
let vs_entry = EntryPoint::<backend::Backend> { | |
entry: "main", | |
module: &vertex_shader_module, | |
specialization: Specialization::default(), | |
}; | |
let fs_entry = EntryPoint::<backend::Backend> { | |
entry: "main", | |
module: &fragment_shader_module, | |
specialization: Specialization::default(), | |
}; | |
let shader_entries = GraphicsShaderSet { | |
vertex: vs_entry, | |
hull: None, | |
domain: None, | |
geometry: None, | |
fragment: Some(fs_entry), | |
}; | |
let subpass = Subpass { | |
index: 0, | |
main_pass: &render_pass, | |
}; | |
let mut rasterizer: Rasterizer = Rasterizer::FILL; | |
//rasterizer.front_face = FrontFace::CounterClockwise; | |
//rasterizer.cull_face = Face::BACK; | |
let mut pipeline_desc = GraphicsPipelineDesc::new( | |
shader_entries, | |
Primitive::TriangleList, | |
rasterizer, | |
&pipeline_layout, | |
subpass, | |
); | |
pipeline_desc | |
.blender | |
.targets | |
.push(ColorBlendDesc(ColorMask::ALL, BlendState::ALPHA)); | |
// Let our pipeline know about the vertex buffers we are going to use | |
pipeline_desc.vertex_buffers.push(VertexBufferDesc { | |
binding: 0, | |
stride: std::mem::size_of::<Vertex>() as u32, | |
rate: 0, | |
}); | |
// Let our pipeline know about our vertex attributes | |
// Position | |
pipeline_desc.attributes.push(AttributeDesc { | |
location: 0, | |
binding: 0, | |
element: Element { | |
format: Format::Rgb32Float, | |
offset: 0, | |
}, | |
}); | |
// Color | |
pipeline_desc.attributes.push(AttributeDesc { | |
location: 1, | |
binding: 0, | |
element: Element { | |
format: Format::Rgb32Float, | |
offset: 12, | |
}, | |
}); | |
// UV | |
pipeline_desc.attributes.push(AttributeDesc { | |
location: 2, | |
binding: 0, | |
element: Element { | |
format: Format::Rgb32Float, | |
offset: 28, | |
}, | |
}); | |
device | |
.create_graphics_pipeline(&pipeline_desc, None) | |
.unwrap() | |
}; | |
// Create texture | |
let (texture_image, texture_memory, texture_view, texture_sampler) = { | |
let img = image::open("res/textures/costanza.png") | |
.expect("Failed to load image!") | |
.to_rgba(); | |
let (width, height) = img.dimensions(); | |
let (texture_image, texture_memory, texture_view) = create_image( | |
&device, | |
physical_device, | |
width, | |
height, | |
Format::Rgba8Srgb, | |
img::Usage::TRANSFER_DST | img::Usage::SAMPLED, | |
Aspects::COLOR); | |
let texture_sampler = device.create_sampler(img::SamplerInfo::new(Filter::Linear, WrapMode::Tile)); | |
// Write data into texture | |
{ | |
let row_alignment_mask = | |
physical_device.limits().min_buffer_copy_pitch_alignment as u32 - 1; | |
let image_stride: usize = 4; | |
let row_pitch = | |
(width * image_stride as u32 + row_alignment_mask) & !row_alignment_mask; | |
let upload_size: u64 = (height * row_pitch).into(); | |
let (image_upload_buffer, mut image_upload_memory) = create_buffer( | |
&device, | |
physical_device, | |
buffer::Usage::TRANSFER_SRC, | |
Properties::CPU_VISIBLE, | |
upload_size, | |
); | |
{ | |
let mut data = device | |
.acquire_mapping_writer::<u8>(&image_upload_memory, 0..upload_size) | |
.unwrap(); | |
for y in 0..height as usize { | |
let row = &(*img)[y * (width as usize) * image_stride | |
..(y + 1) * (width as usize) * image_stride]; | |
let dest_base = y * row_pitch as usize; | |
data[dest_base..dest_base + row.len()].copy_from_slice(row); | |
} | |
device.release_mapping_writer(data); | |
} | |
// Submit commands to transfer data | |
let submit = { | |
let mut cmd_buffer = command_pool.acquire_command_buffer(false); | |
let image_barrier = Barrier::Image { | |
states: (Access::empty(), Layout::Undefined) | |
..(Access::TRANSFER_WRITE, Layout::TransferDstOptimal), | |
target: &texture_image, | |
range: SubresourceRange { | |
aspects: Aspects::COLOR, | |
levels: 0..1, | |
layers: 0..1, | |
}, | |
}; | |
cmd_buffer.pipeline_barrier( | |
PipelineStage::TOP_OF_PIPE..PipelineStage::TRANSFER, | |
Dependencies::empty(), | |
&[image_barrier] | |
); | |
cmd_buffer.copy_buffer_to_image( | |
&image_upload_buffer, | |
&texture_image, | |
Layout::TransferDstOptimal, | |
&[BufferImageCopy { | |
buffer_offset: 0, | |
buffer_width: row_pitch / (image_stride as u32), | |
buffer_height: height as u32, | |
image_layers: SubresourceLayers { | |
aspects: Aspects::COLOR, | |
level: 0, | |
layers: 0..1, | |
}, | |
image_offset: Offset { x: 0, y: 0, z: 0 }, | |
image_extent: Extent { width, height, depth: 1 }, | |
}], | |
); | |
let image_barrier = Barrier::Image { | |
states: (Access::TRANSFER_WRITE, Layout::TransferDstOptimal) | |
..(Access::SHADER_READ, Layout::ShaderReadOnlyOptimal), | |
target: &texture_image, | |
range: SubresourceRange { | |
aspects: Aspects::COLOR, | |
levels: 0..1, | |
layers: 0..1, | |
}, | |
}; | |
cmd_buffer.pipeline_barrier( | |
PipelineStage::TRANSFER..PipelineStage::FRAGMENT_SHADER, | |
Dependencies::empty(), | |
&[image_barrier], | |
); | |
cmd_buffer.finish() | |
}; | |
let submission = Submission::new().submit(Some(submit)); | |
queue_group.queues[0].submit(submission, Some(&frame_fence)); | |
device.wait_for_fence(&frame_fence, !0); | |
// Destroy our staging resources | |
device.destroy_buffer(image_upload_buffer); | |
device.free_memory(image_upload_memory); | |
} | |
(texture_image, texture_memory, texture_view, texture_sampler) | |
}; | |
let vertices = MESH; | |
let indices = MESH_INDICES; | |
// Create the vertex buffer | |
let (vertex_buffer, vertex_buffer_memory) = | |
create_vertex_buffer(&device, physical_device, vertices); | |
// Create the index buffer | |
let (index_buffer, index_buffer_memory) = | |
create_index_buffer(&device, physical_device, indices); | |
let model = glm::Mat4::identity(); | |
let view = glm::Mat4::identity(); | |
let projection = glm::Mat4::identity(); | |
/* | |
let view = glm::look_at( | |
&glm::Vec3::new(2.0, 2.0, 2.0), | |
&glm::Vec3::new(0.0, 0.0, 0.0), | |
&glm::Vec3::new(0.0, 0.0, 1.0), | |
); | |
let projection = glm::perspective(f32::to_radians(45.0), 512.0 / 512.0, 0.1, 1000.0); | |
*/ | |
// GLM was originally designed for OpenGL, where the Y coordinate of the clip coordinates | |
// is inverted. The easiest way to compensate for that is to flip the sign on the scaling | |
// factor of the Y axis in the projection matrix. If you don't do this, then the image will | |
// be rendered upside down. | |
// | |
// UPDATE: | |
// I don't think I actually need to do this. Seems to be causing more problems than it solves. | |
// projection.row_mut(1)[1] *= -1.0; | |
// Create the uniform buffer | |
let (uniform_buffer, uniform_buffer_memory) = create_uniform_buffer( | |
&device, | |
physical_device, | |
UniformBufferObject { | |
model: model.into(), | |
view: view.into(), | |
projection: projection.into(), | |
}, | |
); | |
// Configure our descriptor set to refer to our uniform buffer | |
device.write_descriptor_sets(vec![ | |
DescriptorSetWrite { | |
set: &descriptor_set, | |
binding: 0, | |
array_offset: 0, | |
descriptors: Some(Descriptor::Buffer(&uniform_buffer, None..None)), | |
}, | |
DescriptorSetWrite { | |
set: &descriptor_set, | |
binding: 1, | |
array_offset: 0, | |
descriptors: Some(Descriptor::Image(&texture_view, Layout::Undefined)), | |
}, | |
DescriptorSetWrite { | |
set: &descriptor_set, | |
binding: 2, | |
array_offset: 0, | |
descriptors: Some(Descriptor::Sampler(&texture_sampler)), | |
}, | |
]); | |
// This tuple holds our swapchain, extent, image views, and framebuffers | |
let mut swapchain_data: Option<(_, _, _, _)> = None; | |
// Main loop | |
let mut is_resizing = false; | |
let mut is_quitting = false; | |
loop { | |
events_loop.poll_events(|event| { | |
if let Event::WindowEvent { event, .. } = event { | |
match event { | |
WindowEvent::Resized(_) => is_resizing = true, | |
WindowEvent::CloseRequested => is_quitting = true, | |
WindowEvent::KeyboardInput { | |
input: | |
KeyboardInput { | |
virtual_keycode: Some(VirtualKeyCode::Escape), | |
.. | |
}, | |
.. | |
} => is_quitting = true, | |
_ => {} | |
} | |
} | |
}); | |
// If we are resizing or quitting, we need to destroy our swapchain | |
if (is_resizing || is_quitting) && swapchain_data.is_some() { | |
let (swapchain, _extent, frame_views, framebuffers) = swapchain_data.take().unwrap(); | |
device.wait_idle().unwrap(); | |
command_pool.reset(); | |
for framebuffer in framebuffers { | |
device.destroy_framebuffer(framebuffer); | |
} | |
for image_view in frame_views { | |
device.destroy_image_view(image_view); | |
} | |
device.destroy_swapchain(swapchain); | |
} | |
if is_quitting { | |
break; | |
} | |
// If we have no swapchain at this point, it needs to be created | |
if swapchain_data.is_none() { | |
is_resizing = false; | |
let (capabilities, _, _) = surface.compatibility(physical_device); | |
let swap_config = SwapchainConfig::from_caps(&capabilities, surface_color_format); | |
let extent = swap_config.extent.to_extent(); | |
let (swapchain, backbuffer) = device.create_swapchain(&mut surface, swap_config, None); | |
let (frame_views, framebuffers) = match backbuffer { | |
Backbuffer::Images(images) => { | |
let color_range = SubresourceRange { | |
aspects: Aspects::COLOR, | |
levels: 0..1, | |
layers: 0..1, | |
}; | |
let image_views = images | |
.iter() | |
.map(|image| { | |
device | |
.create_image_view( | |
image, | |
ViewKind::D2, | |
surface_color_format, | |
Swizzle::NO, | |
color_range.clone(), | |
) | |
.unwrap() | |
}) | |
.collect::<Vec<_>>(); | |
let fbos = image_views | |
.iter() | |
.map(|image_view| { | |
device | |
.create_framebuffer(&render_pass, vec![image_view], extent) | |
.unwrap() | |
}) | |
.collect(); | |
(image_views, fbos) | |
} | |
Backbuffer::Framebuffer(fbo) => (vec![], vec![fbo]), | |
}; | |
swapchain_data = Some((swapchain, extent, frame_views, framebuffers)); | |
} | |
// Rendering | |
let (swapchain, extent, _frame_views, framebuffers) = swapchain_data.as_mut().unwrap(); | |
// TODO | |
// Update uniforms here | |
// glm::perspective(90.0, 16.0 / 9.0, 0.1, 1000.0); | |
device.reset_fence(&frame_fence); | |
command_pool.reset(); | |
let frame_index: SwapImageIndex = { | |
match swapchain.acquire_image(!0, FrameSync::Semaphore(&frame_semaphore)) { | |
Ok(i) => i, | |
Err(_) => { | |
is_resizing = true; | |
continue; | |
} | |
} | |
}; | |
let final_command_buffer = { | |
let mut command_buffer = command_pool.acquire_command_buffer(false); | |
let viewport = Viewport { | |
rect: Rect { | |
x: 0, | |
y: 0, | |
w: extent.width as i16, | |
h: extent.height as i16, | |
}, | |
depth: 0.0..1.0, | |
}; | |
command_buffer.set_viewports(0, &[viewport.clone()]); | |
command_buffer.set_scissors(0, &[viewport.rect]); | |
command_buffer.bind_graphics_pipeline(&pipeline); | |
// Tell our pipeline to use a specific vertex buffer | |
command_buffer.bind_vertex_buffers(0, vec![(&vertex_buffer, 0)]); | |
command_buffer.bind_index_buffer(buffer::IndexBufferView { | |
buffer: &index_buffer, | |
offset: 0, | |
index_type: IndexType::U32, | |
}); | |
command_buffer.bind_graphics_descriptor_sets( | |
&pipeline_layout, | |
0, | |
vec![&descriptor_set], | |
&[], | |
); | |
{ | |
let mut encoder = command_buffer.begin_render_pass_inline( | |
&render_pass, | |
&framebuffers[frame_index as usize], | |
viewport.rect, | |
&[ClearValue::Color(ClearColor::Float([0.0, 0.0, 0.0, 1.0]))], | |
); | |
let num_indices = indices.len() as u32; | |
encoder.draw_indexed(0..num_indices, 0, 0..1); | |
} | |
command_buffer.finish() | |
}; | |
let submission = Submission::new() | |
.wait_on(&[(&frame_semaphore, PipelineStage::BOTTOM_OF_PIPE)]) | |
.submit(vec![final_command_buffer]); | |
queue_group.queues[0].submit(submission, Some(&frame_fence)); | |
device.wait_for_fence(&frame_fence, !0); | |
let result = swapchain.present(&mut queue_group.queues[0], frame_index, &[]); | |
if result.is_err() { | |
is_resizing = true; | |
} | |
} | |
// Cleanup | |
device.destroy_graphics_pipeline(pipeline); | |
device.destroy_pipeline_layout(pipeline_layout); | |
device.destroy_render_pass(render_pass); | |
device.destroy_shader_module(vertex_shader_module); | |
device.destroy_shader_module(fragment_shader_module); | |
device.destroy_command_pool(command_pool.into_raw()); | |
device.destroy_descriptor_set_layout(set_layout); | |
device.destroy_descriptor_pool(descriptor_pool); | |
// Destroy index buffer | |
device.destroy_buffer(index_buffer); | |
device.free_memory(index_buffer_memory); | |
// Destroy vertex buffer | |
device.destroy_buffer(vertex_buffer); | |
device.free_memory(vertex_buffer_memory); | |
// Destroy uniform buffer | |
device.destroy_buffer(uniform_buffer); | |
device.free_memory(uniform_buffer_memory); | |
// Destroy texture stuff | |
device.destroy_image(texture_image); | |
device.destroy_image_view(texture_view); | |
device.destroy_sampler(texture_sampler); | |
device.free_memory(texture_memory); | |
device.destroy_fence(frame_fence); | |
device.destroy_semaphore(frame_semaphore); | |
} | |
fn create_buffer( | |
device: &impl Device<backend::Backend>, | |
physical_device: &PhysicalDevice<backend::Backend>, | |
usage: buffer::Usage, | |
properties: Properties, | |
buffer_len: u64, | |
) -> (GfxBuffer, GfxMemory) { | |
// Get a list of available memory types | |
let memory_types = physical_device.memory_properties().memory_types; | |
// First create an unbound buffer | |
let unbound_buffer = device.create_buffer(buffer_len, usage).unwrap(); | |
// Get the memory requirements for this buffer | |
let mem_requirements = device.get_buffer_requirements(&unbound_buffer); | |
// Filter through memory types to find one that is appropriate. | |
let upload_type = memory_types | |
.iter() | |
.enumerate() | |
.find(|(id, ty)| { | |
let type_supported = mem_requirements.type_mask & (1_u64 << id) != 0; | |
type_supported && ty.properties.contains(properties) | |
}) | |
.map(|(id, _ty)| MemoryTypeId(id)) | |
.expect("Could not find appropriate vertex buffer memory type!"); | |
// Now allocate the memory and bind our buffer to it. | |
let buffer_memory = device | |
.allocate_memory(upload_type, mem_requirements.size) | |
.unwrap(); | |
let buffer = device | |
.bind_buffer_memory(&buffer_memory, 0, unbound_buffer) | |
.unwrap(); | |
(buffer, buffer_memory) | |
} | |
fn create_vertex_buffer( | |
device: &impl Device<backend::Backend>, | |
physical_device: &PhysicalDevice<backend::Backend>, | |
mesh: &[Vertex], | |
) -> (GfxBuffer, GfxMemory) { | |
let item_count = mesh.len(); | |
let stride = std::mem::size_of::<Vertex>() as u64; | |
let buffer_len = item_count as u64 * stride; | |
let (vertex_buffer, vertex_buffer_memory) = create_buffer( | |
device, | |
physical_device, | |
buffer::Usage::VERTEX, | |
Properties::CPU_VISIBLE, | |
buffer_len, | |
); | |
{ | |
let mut dest = device | |
.acquire_mapping_writer::<Vertex>(&vertex_buffer_memory, 0..buffer_len) | |
.unwrap(); | |
dest.copy_from_slice(mesh); | |
device.release_mapping_writer(dest); | |
} | |
(vertex_buffer, vertex_buffer_memory) | |
} | |
fn create_index_buffer( | |
device: &impl Device<backend::Backend>, | |
physical_device: &PhysicalDevice<backend::Backend>, | |
indicies: &[u32], | |
) -> (GfxBuffer, GfxMemory) { | |
let item_count = indicies.len(); | |
let stride = std::mem::size_of::<u32>() as u64; | |
let buffer_len = item_count as u64 * stride; | |
let (index_buffer, index_buffer_memory) = create_buffer( | |
device, | |
physical_device, | |
buffer::Usage::INDEX, | |
Properties::CPU_VISIBLE, | |
buffer_len, | |
); | |
{ | |
let mut dest = device | |
.acquire_mapping_writer::<u32>(&index_buffer_memory, 0..buffer_len) | |
.unwrap(); | |
dest.copy_from_slice(indicies); | |
device.release_mapping_writer(dest); | |
} | |
(index_buffer, index_buffer_memory) | |
} | |
fn create_uniform_buffer( | |
device: &impl Device<backend::Backend>, | |
physical_device: &PhysicalDevice<backend::Backend>, | |
ubo: UniformBufferObject, | |
) -> (GfxBuffer, GfxMemory) { | |
let buffer_len = std::mem::size_of::<UniformBufferObject>() as u64; | |
let (buffer, buffer_memory) = create_buffer( | |
device, | |
physical_device, | |
buffer::Usage::UNIFORM, | |
Properties::CPU_VISIBLE, | |
buffer_len, | |
); | |
{ | |
let mut dest = device | |
.acquire_mapping_writer::<UniformBufferObject>(&buffer_memory, 0..buffer_len) | |
.unwrap(); | |
dest.copy_from_slice(&[ubo]); | |
device.release_mapping_writer(dest); | |
} | |
(buffer, buffer_memory) | |
} | |
fn create_image( | |
device: &impl Device<backend::Backend>, | |
physical_device: &PhysicalDevice<backend::Backend>, | |
width: u32, | |
height: u32, | |
format: Format, | |
usage: img::Usage, | |
aspects: Aspects, | |
) -> (GfxImage, GfxMemory, GfxImageView) { | |
// Get a list of available memory types | |
let memory_types = physical_device.memory_properties().memory_types; | |
let kind = img::Kind::D2(width, height, 1, 1); | |
let unbound_image = device | |
.create_image( | |
kind, | |
1, | |
format, | |
img::Tiling::Optimal, | |
usage, | |
ViewCapabilities::empty(), | |
).expect("Failed to create unbound image!"); | |
let requirements = device.get_image_requirements(&unbound_image); | |
let device_type = memory_types | |
.iter() | |
.enumerate() | |
.position(|(id, memory_type)| { | |
requirements.type_mask & (1_u64 << id) != 0 | |
&& memory_type.properties.contains(Properties::DEVICE_LOCAL) | |
}).unwrap() | |
.into(); | |
let image_memory = device | |
.allocate_memory(device_type, requirements.size) | |
.expect("Failed to allocate image memory!"); | |
let image = device | |
.bind_image_memory(&image_memory, 0, unbound_image) | |
.expect("Failed to bind image memory!"); | |
let image_view = device | |
.create_image_view( | |
&image, | |
img::ViewKind::D2, | |
format, | |
Swizzle::NO, | |
img::SubresourceRange { | |
aspects, | |
levels: 0..1, | |
layers: 0..1, | |
}, | |
).expect("Failed to create image view!"); | |
(image, image_memory, image_view) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment