This repository has been archived by the owner. It is now read-only.
Permalink
Cannot retrieve contributors at this time
Join GitHub today
GitHub is home to over 40 million developers working together to host and review code, manage projects, and build software together.
Sign up
Fetching contributors…
| 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)) | |
| } | |
| } |