Skip to content

Instantly share code, notes, and snippets.

@Dooskington
Created October 9, 2018 14:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Dooskington/0d708c48af131bb06d50dd65042fc84e to your computer and use it in GitHub Desktop.
Save Dooskington/0d708c48af131bb06d50dd65042fc84e to your computer and use it in GitHub Desktop.
#[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