Skip to content
This repository has been archived by the owner. It is now read-only.
Permalink
Tree: a98025d2bd
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
428 lines (351 sloc) 18.4 KB
use winit;
use error::Error;
use backend;
use logs::LogManager;
use app::app::App;
use std;
use gfx_hal;
use gfx_hal::{ Instance, QueueFamily, Capability, PhysicalDevice, Surface, Device };
type ACommandPool = gfx_hal::pool::CommandPool<backend::Backend, gfx_hal::Graphics>;
type ACommandSubmit = gfx_hal::command::Submit<backend::Backend, gfx_hal::Graphics, gfx_hal::command::MultiShot, gfx_hal::command::Primary>;
type ASemaphore = <backend::Backend as gfx_hal::Backend>::Semaphore;
type AFence = <backend::Backend as gfx_hal::Backend>::Fence;type VecCommandQueue = Vec<gfx_hal::queue::CommandQueue<backend::Backend, gfx_hal::Graphics>>;
type ADevice = <backend::Backend as gfx_hal::Backend>::Device;
type ASurface = <backend::Backend as gfx_hal::Backend>::Surface;
type ASwapchain = <backend::Backend as gfx_hal::Backend>::Swapchain;
type ImageThinger = Vec<(<backend::Backend as gfx_hal::Backend>::Image,<backend::Backend as gfx_hal::Backend>::ImageView)>;
type APipelineLayout = <backend::Backend as gfx_hal::Backend>::PipelineLayout;
type ADescriptorSetLayout = <backend::Backend as gfx_hal::Backend>::DescriptorSetLayout;
type ARenderPass = <backend::Backend as gfx_hal::Backend>::RenderPass;
type AGraphicsPipeline = <backend::Backend as gfx_hal::Backend>::GraphicsPipeline;
type AShaderModule = <backend::Backend as gfx_hal::Backend>::ShaderModule;
type AFrameBuffer = <backend::Backend as gfx_hal::Backend>::Framebuffer;
pub struct AppBuilder {
width : f64,
height : f64,
title : String,
logger : Option<LogManager>,
clear_color : [f32; 4],
}
impl Default for AppBuilder {
fn default() -> AppBuilder {
AppBuilder {
width : 600.0,
height : 600.0,
title : "new app".to_string(),
logger : None,
clear_color : [0.0; 4],
}
}
}
impl AppBuilder {
pub fn new() -> AppBuilder {
AppBuilder::default()
}
pub fn with_title<'a>(&'a mut self,title : &str) -> &'a mut AppBuilder {
self.title = title.to_string(); self
}
pub fn with_size<'a>(&'a mut self, width : f64, height: f64) -> &'a mut AppBuilder {
self.height = height; self.width = width; self
}
pub fn with_logger<'a>(&'a mut self, logger : Option<LogManager>) -> &'a mut Self {
self.logger = logger; self
}
pub fn with_clear_color<'a>(&'a mut self, color : [f32; 4]) -> &'a mut Self {
self.clear_color = color; self
}
pub fn create(&self) -> Result<App, Error> {
debug!("Creating app '{}'",self.title);
// creates the window.
let events_loop = winit::EventsLoop::new();
let window = self.create_window_builder(&events_loop)?;
// creates the canvas
let instance = self.create_instance()?;
let mut surface = self.create_surface(&instance, &window)?;
let mut adapter = self.choose_adapter(&instance)?;
let (device, command_queue, queue_type, qf_id) = self.create_device(&mut adapter, &surface)?;
let (swapchain, extent, backbuffer, format) = self.create_swapchain(&adapter, &device, &mut surface, None)?;
let frame_images = self.create_frame_images(backbuffer,format, &device)?;
let render_pass = self.create_render_pass(&device,Some(format))?;
let (ds_layouts, pipeline_layout, graphics_pipeline) = self.create_graphics_pipeline(&device, extent, &render_pass)?;
let framebuffers = self.create_framebuffers(&device, &render_pass, &frame_images, extent)?;
let mut command_pool = self.create_command_pool(&device, queue_type, qf_id)?;
let submission_command_buffers = self.create_command_buffers(&mut command_pool, &render_pass, &framebuffers, extent, &graphics_pipeline)?;
let (image_available_semaphores, render_finished_semaphores, in_flight_fences) = self.create_sync_objects(&device)?;
Ok(App {
window: window,
events_loop: events_loop,
instance: instance,
device: device,
swapchain: swapchain,
surface: surface,
frame_images: frame_images,
descriptor_set_layout: ds_layouts,
pipeline_layout: pipeline_layout,
render_pass: render_pass,
graphics_pipeline: graphics_pipeline,
framebuffers: framebuffers,
submission_command_buffers: submission_command_buffers,
image_available_semaphores : image_available_semaphores,
render_finished_semaphores : render_finished_semaphores,
in_flight_fences : in_flight_fences,
command_queues : command_queue,
})
}
///////////////////////////////////////////////////////////////////////////////////////
// BUILDER FUNCTIONS
// creates the window
fn create_window_builder(&self, events_loop : &winit::EventsLoop) -> Result<winit::Window,Error> {
let window_builder = winit::WindowBuilder::new()
.with_dimensions(winit::dpi::LogicalSize::new(self.width,self.height))
.with_title(self.title.clone());
match window_builder.build(&events_loop) {
Err(error) => { Err(Error::Generic(format!("{}",error))) },
Ok(window) => { Ok(window) },
}
}
// creates an instance of the backend
fn create_instance(&self) -> Result<backend::Instance,Error> {
Ok(backend::Instance::create(&self.title,1))
}
// picks what video card to use as the main video card.
fn choose_adapter(&self, instance : &backend::Instance) -> Result<gfx_hal::Adapter<backend::Backend>,Error> {
let mut adapters = instance.enumerate_adapters();
let mut chosen_adapter : Option<usize> = None;
for ai in 0 .. adapters.len() {
// check if the adapter is valid?
for family in &adapters[ai].queue_families {
if family.max_queues() > 0 && family.supports_graphics() {
chosen_adapter = Some(ai);
}
}
}
match chosen_adapter {
None => Err(Error::Graphics("No valid adapter found.".to_string())),
Some(ai) => Ok(adapters.remove(ai)),
}
}
// just did this to make the signature easier to read.
fn create_device(&self, adapter : &mut gfx_hal::Adapter<backend::Backend>, surface : &<backend::Backend as gfx_hal::Backend>::Surface) ->
Result<(ADevice,VecCommandQueue,gfx_hal::queue::QueueType,gfx_hal::queue::family::QueueFamilyId),Error> {
match adapter.queue_families.iter().find(|fam| {
gfx_hal::Graphics::supported_by(fam.queue_type()) &&
fam.max_queues() > 0 &&
surface.supports_queue_family(fam)
})
{
None => { return Err(Error::Graphics("No supported graphics modes found.".to_string())); },
Some(fam) => {
let priorities = vec![1.0, 1.0];
let fams = [(fam,priorities.as_slice())];
match adapter.physical_device.open(&fams) {
Err(error) => { return Err(Error::Graphics(format!("{}",error))); },
Ok(gfx_hal::Gpu {device, mut queues}) => {
match queues.take::<gfx_hal::Graphics>(fam.id()) {
None => { return Err(Error::Graphics("Error".to_string())); },
Some(mut queue_group) => {
let command_queues : VecCommandQueue = queue_group.queues.drain(..1).collect();
return Ok((device,command_queues,fam.queue_type(),fam.id()));
}
}
}
}
}
}
}
fn create_surface(&self, instance : &backend::Instance, window : &winit::Window) -> Result<ASurface,Error> {
Ok(instance.create_surface(window))
}
fn create_swapchain(&self, adapter : &gfx_hal::Adapter<backend::Backend>, device : &ADevice, surface : &mut ASurface, old_swapchain : Option<ASwapchain>) ->
Result<(ASwapchain, gfx_hal::window::Extent2D, gfx_hal::Backbuffer<backend::Backend>, gfx_hal::format::Format),Error> {
let (caps, formats, present_modes) = surface.compatibility(&adapter.physical_device);
let format = formats.map_or(gfx_hal::format::Format::Rgba8Srgb, |fs| {
fs.iter().find(|f| f.base_format().1 == gfx_hal::format::ChannelType::Srgb).map(|f| *f).unwrap_or(fs[0])
});
let swap_config = gfx_hal::SwapchainConfig::from_caps(&caps, format);
let extent = swap_config.extent.clone();
let(swapchain, backbuffer) = device.create_swapchain(surface, swap_config, old_swapchain);
Ok((swapchain, extent, backbuffer, format))
}
fn create_frame_images(&self, backbuffer : gfx_hal::Backbuffer<backend::Backend>, format : gfx_hal::format::Format, device : &ADevice) ->
Result<ImageThinger,Error> {
match backbuffer {
gfx_hal::window::Backbuffer::Images(i) => i.into_iter().map(|i|{
let iv = match device.create_image_view(&i,
gfx_hal::image::ViewKind::D2,
format,
gfx_hal::format::Swizzle::NO,
gfx_hal::image::SubresourceRange {
aspects : gfx_hal::format::Aspects::COLOR,
levels: 0 .. 1,
layers: 0 .. 1,
}) {
Ok(iv) => iv,
Err(_) => panic!("Error creating image view for an image!"),
};
Ok((i,iv))
}).collect(),
_ => Err(Error::Graphics("Error creating image".to_string())),
}
}
fn create_graphics_pipeline(&self, device : &ADevice, extent : gfx_hal::window::Extent2D, render_pass: &ARenderPass) ->
Result<(Vec<ADescriptorSetLayout>, APipelineLayout, AGraphicsPipeline),Error> {
use gfx_hal::pso;
let shader_vert = self.create_shader_module(&device,"target/shaders/basic.glslv.spv")?;
let shader_frag = self.create_shader_module(&device,"target/shaders/basic.glslf.spv")?;
let (vse,fse) = (
pso::EntryPoint::<backend::Backend> { entry: "main", module: &shader_vert, specialization: Default::default(), },
pso::EntryPoint::<backend::Backend> { entry: "main", module: &shader_frag, specialization: Default::default(), },
);
// removed the fragment shader because it wasn't compiling properly.
let shaders = pso::GraphicsShaderSet { vertex: vse, hull : None, domain: None, geometry: None, fragment: None, };
let rasterizer = pso::Rasterizer {
depth_clamping: false,
polygon_mode: pso::PolygonMode::Fill,
cull_face: <pso::Face>::BACK,
front_face: pso::FrontFace::Clockwise,
depth_bias: None,
conservative: false,
};
let vertex_buffers: Vec<pso::VertexBufferDesc> = Vec::new();
let attributes: Vec<pso::AttributeDesc> = Vec::new();
let input_assembler = pso::InputAssemblerDesc::new(gfx_hal::Primitive::TriangleList);
let blender = {
let blend_state = pso::BlendState::On {
color: pso::BlendOp::Add { src: pso::Factor::One, dst: pso::Factor::Zero, },
alpha: pso::BlendOp::Add { src: pso::Factor::One, dst: pso::Factor::Zero, },
};
pso::BlendDesc {
logic_op : Some(pso::LogicOp::Copy),
targets: vec![pso::ColorBlendDesc(
pso::ColorMask::ALL,blend_state,
)],
}
};
let depth_stencil = pso::DepthStencilDesc {
depth: pso::DepthTest::Off,
depth_bounds: false,
stencil: pso::StencilTest::Off,
};
let multisampling : Option<pso::Multisampling> = None;
let baked_states = pso::BakedStates {
viewport: Some(pso::Viewport {
rect: pso::Rect { x: 0, y: 0, w: extent.width as i16, h: extent.height as i16 },
depth: (0.0 .. 1.0),
}),
scissor: Some(pso::Rect { x: 0, y: 0, w: extent.width as i16, h: extent.height as i16 }),
blend_color: None,
depth_bounds: None,
};
let bindings = Vec::<gfx_hal::pso::DescriptorSetLayoutBinding>::new();
let immutable_samplers = Vec::<<backend::Backend as gfx_hal::Backend>::Sampler>::new();
let ds_layouts: Vec<<backend::Backend as gfx_hal::Backend>::DescriptorSetLayout> =
vec![device.create_descriptor_set_layout(bindings, immutable_samplers)];
let push_constants = Vec::<(gfx_hal::pso::ShaderStageFlags, std::ops::Range<u32>)>::new();
let layout = device.create_pipeline_layout(&ds_layouts, push_constants);
let subpass = gfx_hal::pass::Subpass {
index: 0, main_pass: render_pass,
};
let flags = pso::PipelineCreationFlags::empty();
let parent = pso::BasePipeline::None;
let graphics_pipeline = {
let desc = gfx_hal::pso::GraphicsPipelineDesc {
shaders,
rasterizer,
vertex_buffers,
attributes,
input_assembler,
blender,
depth_stencil,
multisampling,
baked_states,
layout: &layout,
subpass,
flags,
parent,
};
device.create_graphics_pipeline(&desc, None)
};
match graphics_pipeline {
Err(error) => { Err(Error::Graphics(format!("Error creating graphics pipeline: {}",error))) },
Ok(pipeline) => { Ok((ds_layouts, layout, pipeline)) }
}
}
fn create_shader_module(&self, device : &ADevice, path : &str) -> Result<AShaderModule,Error> {
let raw_shader = include_bytes!("../../target/shaders/basic.glslv.spv");
match device.create_shader_module(raw_shader) {
Err(_error) => Err(Error::Graphics(format!("Failed to created shader module for '{}'",path))),
Ok(module) => Ok(module),
}
}
fn create_render_pass(&self, device : &ADevice, format: Option<gfx_hal::format::Format>) ->
Result<ARenderPass,Error> {
use gfx_hal::pass;
let samples : u8 = 1;
let ops = pass::AttachmentOps {
load: pass::AttachmentLoadOp::Clear,
store: pass::AttachmentStoreOp::Store,
};
let stencil_ops = pass::AttachmentOps::DONT_CARE;
let layouts = gfx_hal::image::Layout::Undefined .. gfx_hal::image::Layout::Present;
let color_attachment = pass::Attachment { format, samples, ops, stencil_ops, layouts, };
let color_attachment_ref : pass::AttachmentRef = (0, gfx_hal::image::Layout::ColorAttachmentOptimal);
let subpass = pass::SubpassDesc { colors : &[color_attachment_ref], depth_stencil: None, inputs: &[], resolves: &[], preserves: &[], };
Ok(device.create_render_pass(&[color_attachment], &[subpass], &[]))
}
fn create_framebuffers(&self, device : &ADevice, render_pass : &ARenderPass, frame_images: &ImageThinger, extent : gfx_hal::window::Extent2D) ->
Result<Vec<AFrameBuffer>,Error>{
let mut swapchain_framebuffers : Vec<AFrameBuffer> = Vec::new();
for (_, iv) in frame_images.iter() {
swapchain_framebuffers.push(
match device.create_framebuffer(render_pass, vec![iv], gfx_hal::image::Extent { width: extent.width, height: extent.height, depth: 1}) {
Ok(fb) => { fb },
Err(error) => { return Err(Error::Graphics(format!("Cannot create framebuffer: {}",error))); },
}
);
}
Ok(swapchain_framebuffers)
}
fn create_command_buffers<'a>(&self, command_pool : &'a mut ACommandPool, render_pass : &ARenderPass, framebuffers : &Vec<AFrameBuffer>, extent : gfx_hal::window::Extent2D, pipeline : &AGraphicsPipeline) ->
Result<Vec<ACommandSubmit>,Error> {
command_pool.reserve(framebuffers.iter().count());
let mut submission_command_buffers : Vec<ACommandSubmit> = Vec::new();
for fb in framebuffers.iter() {
let mut command_buffer : gfx_hal::command::CommandBuffer<backend::Backend, gfx_hal::Graphics, gfx_hal::command::MultiShot, gfx_hal::command::Primary> = command_pool.acquire_command_buffer(true);
command_buffer.bind_graphics_pipeline(pipeline);
{
let render_area = gfx_hal::pso::Rect { x: 0, y: 0, w: extent.width as i16, h: extent.height as i16 };
let clear_values = vec![gfx_hal::command::ClearValue::Color(
gfx_hal::command::ClearColor::Float(self.clear_color)
)];
let mut render_pass_inline_encoder = command_buffer.begin_render_pass_inline(
render_pass, fb, render_area, clear_values.iter()
);
render_pass_inline_encoder.draw(0 .. 3, 0 .. 1);
}
let submission_command_buffer = command_buffer.finish();
submission_command_buffers.push(submission_command_buffer);
}
Ok(submission_command_buffers)
}
fn create_command_pool(&self, device : &ADevice, queue_type : gfx_hal::queue::QueueType, qf_id : gfx_hal::queue::family::QueueFamilyId) ->
Result<gfx_hal::pool::CommandPool<backend::Backend, gfx_hal::Graphics>,Error> {
let raw_command_pool = device.create_command_pool(qf_id, gfx_hal::pool::CommandPoolCreateFlags::empty());
match gfx_hal::Graphics::supported_by(queue_type) {
false => Err(Error::Graphics("Cannot make command pool, not supported by queue type".to_string())),
true => unsafe { Ok(gfx_hal::pool::CommandPool::new(raw_command_pool)) }
}
}
fn create_sync_objects(&self, device : &ADevice) -> Result<(Vec<ASemaphore>,Vec<ASemaphore>,Vec<AFence>),Error> {
let mut image_available_semaphores : Vec<ASemaphore> = Vec::new();
let mut render_finished_semaphores : Vec<ASemaphore> = Vec::new();
let mut in_flight_fences : Vec<AFence> = Vec::new();
// MAX FRAMES IN FLIGHT
for _ in 0 .. 2 {
image_available_semaphores.push(device.create_semaphore());
render_finished_semaphores.push(device.create_semaphore());
in_flight_fences.push(device.create_fence(true));
}
Ok((image_available_semaphores, render_finished_semaphores, in_flight_fences))
}
}
You can’t perform that action at this time.