Skip to content

Instantly share code, notes, and snippets.

@LaylBongers
Created June 19, 2018 23:00
Show Gist options
  • Save LaylBongers/993244e65b4f5bc7ea3768c6b6051b25 to your computer and use it in GitHub Desktop.
Save LaylBongers/993244e65b4f5bc7ea3768c6b6051b25 to your computer and use it in GitHub Desktop.
extern crate cgmath;
extern crate gfx_backend_gl;
extern crate gfx_hal;
extern crate glsl_to_spirv;
extern crate image;
extern crate winit;
#[macro_use] extern crate slog;
extern crate sloggers;
use {
std::{
io::{Cursor, Read},
fs,
},
gfx_hal::{
format::{AsFormat, ChannelType, Rgba8Srgb as ColorFormat, Swizzle},
pass::{Subpass},
pso::{PipelineStage, ShaderStageFlags, Specialization},
queue::{Submission},
buffer, command, format as f, image as i, memory as m, pass, pso, pool, window::Extent2D,
Device, Instance, PhysicalDevice, Surface, Swapchain,
DescriptorPool, FrameSync, Primitive,
Backbuffer, SwapchainConfig,
},
slog::{Logger},
sloggers::{Build, terminal::{TerminalLoggerBuilder}, types::{Severity}},
};
const ENTRY_NAME: &str = "main";
#[derive(Debug, Clone, Copy)]
struct Vertex {
a_pos: [f32; 2],
a_uv: [f32; 2],
}
#[derive(Debug, Clone, Copy)]
struct MatrixData {
scale: f32,
}
const QUAD: [Vertex; 6] = [
Vertex { a_pos: [ -0.5, 0.33 ], a_uv: [0.0, 1.0] },
Vertex { a_pos: [ 0.5, 0.33 ], a_uv: [1.0, 1.0] },
Vertex { a_pos: [ 0.5,-0.33 ], a_uv: [1.0, 0.0] },
Vertex { a_pos: [ -0.5, 0.33 ], a_uv: [0.0, 1.0] },
Vertex { a_pos: [ 0.5,-0.33 ], a_uv: [1.0, 0.0] },
Vertex { a_pos: [ -0.5,-0.33 ], a_uv: [0.0, 0.0] },
];
const COLOR_RANGE: i::SubresourceRange = i::SubresourceRange {
aspects: f::Aspects::COLOR,
levels: 0..1,
layers: 0..1,
};
fn main() {
// Set up logging
let mut builder = TerminalLoggerBuilder::new();
builder.level(Severity::Debug);
let log = builder.build().unwrap();
let mut events_loop = winit::EventsLoop::new();
let wb = winit::WindowBuilder::new()
.with_dimensions(1024, 768)
.with_title("quad".to_string());
let (mut adapters, mut surface) = {
let window = {
let builder =
gfx_backend_gl::config_context(gfx_backend_gl::glutin::ContextBuilder::new(), ColorFormat::SELF, None)
.with_vsync(true);
gfx_backend_gl::glutin::GlWindow::new(wb, builder, &events_loop).unwrap()
};
let surface = gfx_backend_gl::Surface::from_window(window);
let adapters = surface.enumerate_adapters();
(adapters, surface)
};
// Log all available adapters, this lets us see some relevant hardware information when users
// submit bugs, and make sure the game is actually picking a good adapter.
info!(log, "Available Adapters:");
for (i, adapter) in adapters.iter().enumerate() {
info!(log, "{}: {}", i, adapter.info.name);
}
let mut adapter = adapters.remove(0);
let memory_types = adapter.physical_device.memory_properties().memory_types;
let limits = adapter.physical_device.limits();
// Build a new device and associated command queues
let (mut device, mut queue_group) = adapter
.open_with::<_,gfx_hal::Graphics>(1, |family| surface.supports_queue_family(family))
.unwrap();
let mut command_pool =
device.create_command_pool_typed(&queue_group, pool::CommandPoolCreateFlags::empty(), 16);
// Setup renderpass and pipeline
let set_layout = device.create_descriptor_set_layout(
&[
pso::DescriptorSetLayoutBinding {
binding: 0,
ty: pso::DescriptorType::SampledImage,
count: 1,
stage_flags: ShaderStageFlags::FRAGMENT,
immutable_samplers: false,
},
pso::DescriptorSetLayoutBinding {
binding: 1,
ty: pso::DescriptorType::Sampler,
count: 1,
stage_flags: ShaderStageFlags::FRAGMENT,
immutable_samplers: false,
},
pso::DescriptorSetLayoutBinding {
binding: 2,
ty: pso::DescriptorType::UniformBuffer,
count: 1,
stage_flags: ShaderStageFlags::FRAGMENT,
immutable_samplers: false,
},
],
&[],
);
// Descriptors
let mut desc_pool = device.create_descriptor_pool(
1, // sets
&[
pso::DescriptorRangeDesc {
ty: pso::DescriptorType::SampledImage,
count: 1,
},
pso::DescriptorRangeDesc {
ty: pso::DescriptorType::Sampler,
count: 1,
},
pso::DescriptorRangeDesc {
ty: pso::DescriptorType::UniformBuffer,
count: 1,
},
],
);
let desc_set = desc_pool.allocate_set(&set_layout).unwrap();
// Buffer allocations
info!(log, "Memory types: {:?}", memory_types);
let buffer_stride = std::mem::size_of::<Vertex>() as u64;
let buffer_len = QUAD.len() as u64 * buffer_stride;
let buffer_unbound = device
.create_buffer(buffer_len, buffer::Usage::VERTEX)
.unwrap();
let buffer_req = device.get_buffer_requirements(&buffer_unbound);
let upload_type = memory_types
.iter()
.enumerate()
.position(|(id, mem_type)| {
buffer_req.type_mask & (1 << id) != 0
&& mem_type.properties.contains(m::Properties::CPU_VISIBLE)
})
.unwrap()
.into();
let buffer_memory = device
.allocate_memory(upload_type, buffer_req.size)
.unwrap();
let vertex_buffer = device
.bind_buffer_memory(&buffer_memory, 0, buffer_unbound)
.unwrap();
{
let mut vertices = device
.acquire_mapping_writer::<Vertex>(&buffer_memory, 0..buffer_len)
.unwrap();
vertices.copy_from_slice(&QUAD);
device.release_mapping_writer(vertices);
}
// Image
let img_data = include_bytes!("../resources/placeholder.png");
let img = image::load(Cursor::new(&img_data[..]), image::PNG)
.unwrap()
.to_rgba();
let (width, height) = img.dimensions();
let kind = i::Kind::D2(width as i::Size, height as i::Size, 1, 1);
let row_alignment_mask = limits.min_buffer_copy_pitch_alignment as u32 - 1;
let image_stride = 4usize;
let row_pitch = (width * image_stride as u32 + row_alignment_mask) & !row_alignment_mask;
let upload_size = (height * row_pitch) as u64;
let image_buffer_unbound = device
.create_buffer(upload_size, buffer::Usage::TRANSFER_SRC)
.unwrap();
let image_mem_reqs = device.get_buffer_requirements(&image_buffer_unbound);
let image_upload_memory = device
.allocate_memory(upload_type, image_mem_reqs.size)
.unwrap();
let image_upload_buffer = device
.bind_buffer_memory(&image_upload_memory, 0, image_buffer_unbound)
.unwrap();
// copy image data into staging buffer
{
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);
}
let image_unbound = device
.create_image(
kind,
1,
ColorFormat::SELF,
i::Tiling::Optimal,
i::Usage::TRANSFER_DST | i::Usage::SAMPLED,
i::StorageFlags::empty(),
)
.unwrap(); // TODO: usage
let image_req = device.get_image_requirements(&image_unbound);
let device_type = memory_types
.iter()
.enumerate()
.position(|(id, memory_type)| {
image_req.type_mask & (1 << id) != 0
&& memory_type.properties.contains(m::Properties::DEVICE_LOCAL)
})
.unwrap()
.into();
let image_memory = device.allocate_memory(device_type, image_req.size).unwrap();
let image_logo = device
.bind_image_memory(&image_memory, 0, image_unbound)
.unwrap();
let image_srv = device
.create_image_view(
&image_logo,
i::ViewKind::D2,
ColorFormat::SELF,
Swizzle::NO,
COLOR_RANGE.clone(),
)
.unwrap();
let sampler = device.create_sampler(i::SamplerInfo::new(i::Filter::Linear, i::WrapMode::Clamp));
let matrices_len = std::mem::size_of::<MatrixData>() as u64;
let matrices_buffer_unbound = device.create_buffer(
matrices_len,
gfx_hal::buffer::Usage::UNIFORM,
).unwrap();
let matrices_buffer_req = device.get_buffer_requirements(&matrices_buffer_unbound);
let matrices_buffer_memory = device.allocate_memory(
upload_type, matrices_buffer_req.size,
).unwrap();
let matrices_buffer = device.bind_buffer_memory(
&matrices_buffer_memory,
0,
matrices_buffer_unbound,
).unwrap();
// TODO: add cleaing up
{
let matrix_data = MatrixData {
scale: 1.0,
};
let mut matrices = device
.acquire_mapping_writer::<MatrixData>(&matrices_buffer_memory, 0..matrices_len)
.unwrap();
matrices.copy_from_slice(&[matrix_data]);
device.release_mapping_writer(matrices);
}
device.write_descriptor_sets(vec![
pso::DescriptorSetWrite {
set: &desc_set,
binding: 0,
array_offset: 0,
descriptors: Some(pso::Descriptor::Image(&image_srv, i::Layout::Undefined)),
},
pso::DescriptorSetWrite {
set: &desc_set,
binding: 1,
array_offset: 0,
descriptors: Some(pso::Descriptor::Sampler(&sampler)),
},
pso::DescriptorSetWrite {
set: &desc_set,
binding: 2,
array_offset: 0,
descriptors: Some(pso::Descriptor::Buffer(
&matrices_buffer, Some(0)..Some(matrices_len)
)),
},
]);
let mut frame_semaphore = device.create_semaphore();
let mut frame_fence = device.create_fence(false);
// copy buffer to texture
{
let submit = {
let mut cmd_buffer = command_pool.acquire_command_buffer(false);
let image_barrier = m::Barrier::Image {
states: (i::Access::empty(), i::Layout::Undefined)
..(i::Access::TRANSFER_WRITE, i::Layout::TransferDstOptimal),
target: &image_logo,
range: COLOR_RANGE.clone(),
};
cmd_buffer.pipeline_barrier(
PipelineStage::TOP_OF_PIPE..PipelineStage::TRANSFER,
m::Dependencies::empty(),
&[image_barrier],
);
cmd_buffer.copy_buffer_to_image(
&image_upload_buffer,
&image_logo,
i::Layout::TransferDstOptimal,
&[command::BufferImageCopy {
buffer_offset: 0,
buffer_width: row_pitch / (image_stride as u32),
buffer_height: height as u32,
image_layers: i::SubresourceLayers {
aspects: f::Aspects::COLOR,
level: 0,
layers: 0..1,
},
image_offset: i::Offset { x: 0, y: 0, z: 0 },
image_extent: i::Extent {
width,
height,
depth: 1,
},
}],
);
let image_barrier = m::Barrier::Image {
states: (i::Access::TRANSFER_WRITE, i::Layout::TransferDstOptimal)
..(i::Access::SHADER_READ, i::Layout::ShaderReadOnlyOptimal),
target: &image_logo,
range: COLOR_RANGE.clone(),
};
cmd_buffer.pipeline_barrier(
PipelineStage::TRANSFER..PipelineStage::FRAGMENT_SHADER,
m::Dependencies::empty(),
&[image_barrier],
);
cmd_buffer.finish()
};
let submission = Submission::new().submit(Some(submit));
queue_group.queues[0].submit(submission, Some(&mut frame_fence));
device.wait_for_fence(&frame_fence, !0);
}
let (
mut swap_chain,
mut render_pass,
mut framebuffers,
mut frame_images,
mut pipeline,
mut pipeline_layout,
mut extent,
) = swapchain_stuff(
&log,
&mut surface,
&mut device,
&adapter.physical_device,
&set_layout,
);
// Rendering setup
let mut viewport = pso::Viewport {
rect: pso::Rect {
x: 0,
y: 0,
w: extent.width as _,
h: extent.height as _,
},
depth: 0.0..1.0,
};
//
let mut running = true;
let mut recreate_swapchain = false;
let mut first = true;
while running {
events_loop.poll_events(|event| {
if let winit::Event::WindowEvent { event, .. } = event {
match event {
winit::WindowEvent::KeyboardInput {
input:
winit::KeyboardInput {
virtual_keycode: Some(winit::VirtualKeyCode::Escape),
..
},
..
}
| winit::WindowEvent::CloseRequested => running = false,
winit::WindowEvent::Resized(_width, _height) => {
if !first {
recreate_swapchain = true;
}
}
_ => (),
}
}
});
first = false;
if recreate_swapchain {
device.wait_idle().unwrap();
command_pool.reset();
device.destroy_graphics_pipeline(pipeline);
device.destroy_pipeline_layout(pipeline_layout);
for framebuffer in framebuffers {
device.destroy_framebuffer(framebuffer);
}
for (_, rtv) in frame_images {
device.destroy_image_view(rtv);
}
device.destroy_render_pass(render_pass);
device.destroy_swapchain(swap_chain);
let (
new_swap_chain,
new_render_pass,
new_framebuffers,
new_frame_images,
new_pipeline,
new_pipeline_layout,
new_extent,
) = swapchain_stuff(
&log,
&mut surface,
&mut device,
&adapter.physical_device,
&set_layout,
);
swap_chain = new_swap_chain;
render_pass = new_render_pass;
framebuffers = new_framebuffers;
frame_images = new_frame_images;
pipeline = new_pipeline;
pipeline_layout = new_pipeline_layout;
extent = new_extent;
viewport.rect.w = extent.width as _;
viewport.rect.h = extent.height as _;
recreate_swapchain = false;
}
device.reset_fence(&frame_fence);
command_pool.reset();
let frame:gfx_hal::FrameImage = {
match swap_chain.acquire_frame(FrameSync::Semaphore(&mut frame_semaphore)) {
Ok(i) => i,
Err(_) => {
recreate_swapchain = true;
continue;
}
}
};
// Rendering
let submit = {
let mut cmd_buffer = command_pool.acquire_command_buffer(false);
cmd_buffer.set_viewports(0, &[viewport.clone()]);
cmd_buffer.set_scissors(0, &[viewport.rect]);
cmd_buffer.bind_graphics_pipeline(&pipeline);
cmd_buffer.bind_vertex_buffers(0, pso::VertexBufferSet(vec![(&vertex_buffer, 0)]));
cmd_buffer.bind_graphics_descriptor_sets(&pipeline_layout, 0, Some(&desc_set), &[]);
{
let mut encoder = cmd_buffer.begin_render_pass_inline(
&render_pass,
&framebuffers[frame as usize],
viewport.rect,
&[command::ClearValue::Color(command::ClearColor::Float([
0.8, 0.8, 0.8, 1.0,
]))],
);
encoder.draw(0..6, 0..1);
}
cmd_buffer.finish()
};
let submission = Submission::new()
.wait_on(&[(&frame_semaphore, PipelineStage::BOTTOM_OF_PIPE)])
.submit(Some(submit));
queue_group.queues[0].submit(submission, Some(&mut frame_fence));
// TODO: replace with semaphore
device.wait_for_fence(&frame_fence, !0);
// present frame
if let Err(_) = swap_chain.present(&mut queue_group.queues[0], frame, &[]) {
recreate_swapchain = true;
}
}
// cleanup!
device.destroy_command_pool(command_pool.into_raw());
device.destroy_descriptor_pool(desc_pool);
device.destroy_descriptor_set_layout(set_layout);
device.destroy_buffer(vertex_buffer);
device.destroy_buffer(image_upload_buffer);
device.destroy_image(image_logo);
device.destroy_image_view(image_srv);
device.destroy_sampler(sampler);
device.destroy_fence(frame_fence);
device.destroy_semaphore(frame_semaphore);
device.destroy_render_pass(render_pass);
device.free_memory(buffer_memory);
device.free_memory(image_memory);
device.free_memory(image_upload_memory);
device.destroy_graphics_pipeline(pipeline);
device.destroy_pipeline_layout(pipeline_layout);
for framebuffer in framebuffers {
device.destroy_framebuffer(framebuffer);
}
for (_, rtv) in frame_images {
device.destroy_image_view(rtv);
}
device.destroy_swapchain(swap_chain);
}
fn swapchain_stuff(
log: &Logger,
surface: &mut <gfx_backend_gl::Backend as gfx_hal::Backend>::Surface,
device: &mut gfx_backend_gl::Device,
physical_device: &gfx_backend_gl::PhysicalDevice,
set_layout: &<gfx_backend_gl::Backend as gfx_hal::Backend>::DescriptorSetLayout,
) -> (
<gfx_backend_gl::Backend as gfx_hal::Backend>::Swapchain,
<gfx_backend_gl::Backend as gfx_hal::Backend>::RenderPass,
std::vec::Vec<<gfx_backend_gl::Backend as gfx_hal::Backend>::Framebuffer>,
std::vec::Vec<(
<gfx_backend_gl::Backend as gfx_hal::Backend>::Image,
<gfx_backend_gl::Backend as gfx_hal::Backend>::ImageView,
)>,
<gfx_backend_gl::Backend as gfx_hal::Backend>::GraphicsPipeline,
<gfx_backend_gl::Backend as gfx_hal::Backend>::PipelineLayout,
Extent2D,
) {
let (caps, formats, _present_modes) = surface.compatibility(physical_device);
let format = formats.
map_or(f::Format::Rgba8Srgb, |formats| {
formats
.into_iter()
.find(|format| format.base_format().1 == ChannelType::Srgb)
.unwrap()
});
let extent = match caps.current_extent {
Some(e) => e,
None => {
let window = surface.get_window();
let window_size = window.get_inner_size().unwrap();
let mut extent =gfx_hal::window::Extent2D { width: window_size.0, height: window_size.1 };
extent.width = extent.width.max(caps.extents.start.width).min(caps.extents.end.width);
extent.height = extent.height.max(caps.extents.start.height).min(caps.extents.end.height);
extent
}
};
info!(log, "Surface format: {:?}", format);
let swap_config = SwapchainConfig::new()
.with_color(format)
.with_image_usage(i::Usage::COLOR_ATTACHMENT);
let (swap_chain, backbuffer) = device.create_swapchain(surface, swap_config, None, &extent);
let render_pass = {
let attachment = pass::Attachment {
format: Some(format),
samples: 1,
ops: pass::AttachmentOps::new(
pass::AttachmentLoadOp::Clear,
pass::AttachmentStoreOp::Store,
),
stencil_ops: pass::AttachmentOps::DONT_CARE,
layouts: i::Layout::Undefined..i::Layout::Present,
};
let subpass = pass::SubpassDesc {
colors: &[(0, i::Layout::ColorAttachmentOptimal)],
depth_stencil: None,
inputs: &[],
resolves: &[],
preserves: &[],
};
let dependency = pass::SubpassDependency {
passes: pass::SubpassRef::External..pass::SubpassRef::Pass(0),
stages: PipelineStage::COLOR_ATTACHMENT_OUTPUT..PipelineStage::COLOR_ATTACHMENT_OUTPUT,
accesses: i::Access::empty()
..(i::Access::COLOR_ATTACHMENT_READ | i::Access::COLOR_ATTACHMENT_WRITE),
};
device.create_render_pass(&[attachment], &[subpass], &[dependency])
};
let (frame_images, framebuffers) = match backbuffer {
Backbuffer::Images(images) => {
let extent = i::Extent {
width: extent.width as _,
height: extent.height as _,
depth: 1,
};
let pairs = images
.into_iter()
.map(|image| {
let rtv = device
.create_image_view(
&image,
i::ViewKind::D2,
format,
Swizzle::NO,
COLOR_RANGE.clone(),
)
.unwrap();
(image, rtv)
})
.collect::<Vec<_>>();
let fbos = pairs
.iter()
.map(|&(_, ref rtv)| {
device
.create_framebuffer(&render_pass, Some(rtv), extent)
.unwrap()
})
.collect();
(pairs, fbos)
}
Backbuffer::Framebuffer(fbo) => (Vec::new(), vec![fbo]),
};
let pipeline_layout =
device.create_pipeline_layout(Some(set_layout), &[(pso::ShaderStageFlags::VERTEX, 0..8)]);
let pipeline = {
let vs_module = {
let glsl = fs::read_to_string("resources/vert.glsl").unwrap();
let spirv: Vec<u8> = glsl_to_spirv::compile(&glsl, glsl_to_spirv::ShaderType::Vertex)
.unwrap()
.bytes()
.map(|b| b.unwrap())
.collect();
device.create_shader_module(&spirv).unwrap()
};
let fs_module = {
let glsl = fs::read_to_string("resources/frag.glsl").unwrap();
let spirv: Vec<u8> = glsl_to_spirv::compile(&glsl, glsl_to_spirv::ShaderType::Fragment)
.unwrap()
.bytes()
.map(|b| b.unwrap())
.collect();
device.create_shader_module(&spirv).unwrap()
};
let pipeline = {
let (vs_entry, fs_entry) = (
pso::EntryPoint::<gfx_backend_gl::Backend> {
entry: ENTRY_NAME,
module: &vs_module,
specialization: &[Specialization {
id: 0,
value: pso::Constant::F32(0.8),
}],
},
pso::EntryPoint::<gfx_backend_gl::Backend> {
entry: ENTRY_NAME,
module: &fs_module,
specialization: &[],
},
);
let shader_entries = pso::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 pipeline_desc = pso::GraphicsPipelineDesc::new(
shader_entries,
Primitive::TriangleList,
pso::Rasterizer::FILL,
&pipeline_layout,
subpass,
);
pipeline_desc.blender.targets.push(pso::ColorBlendDesc(
pso::ColorMask::ALL,
pso::BlendState::ALPHA,
));
pipeline_desc.vertex_buffers.push(pso::VertexBufferDesc {
binding: 0,
stride: std::mem::size_of::<Vertex>() as u32,
rate: 0,
});
pipeline_desc.attributes.push(pso::AttributeDesc {
location: 0,
binding: 0,
element: pso::Element {
format: f::Format::Rg32Float,
offset: 0,
},
});
pipeline_desc.attributes.push(pso::AttributeDesc {
location: 1,
binding: 0,
element: pso::Element {
format: f::Format::Rg32Float,
offset: 8,
},
});
device.create_graphics_pipeline(&pipeline_desc)
};
device.destroy_shader_module(vs_module);
device.destroy_shader_module(fs_module);
pipeline.unwrap()
};
(
swap_chain,
render_pass,
framebuffers,
frame_images,
pipeline,
pipeline_layout,
extent,
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment