diff --git a/src/backend/gl/Cargo.toml b/src/backend/gl/Cargo.toml index 80509e01d2c..536b7854a6b 100644 --- a/src/backend/gl/Cargo.toml +++ b/src/backend/gl/Cargo.toml @@ -24,4 +24,4 @@ gfx_gl = "0.5" gfx-hal = { path = "../../hal", version = "0.1" } smallvec = "0.6" glutin = { version = "0.16", optional = true } -spirv_cross = "0.8" +spirv_cross = "0.9.2" diff --git a/src/backend/gl/src/command.rs b/src/backend/gl/src/command.rs index ab28143b41f..7a3b30fd40f 100644 --- a/src/backend/gl/src/command.rs +++ b/src/backend/gl/src/command.rs @@ -108,6 +108,10 @@ pub enum Command { CopySurfaceToBuffer(n::Surface, n::RawBuffer, command::BufferImageCopy), CopyImageToTexture(n::ImageKind, n::Texture, command::ImageCopy), CopyImageToSurface(n::ImageKind, n::Surface, command::ImageCopy), + + BindBufferRange(gl::types::GLenum, gl::types::GLuint, n::RawBuffer, gl::types::GLintptr, gl::types::GLsizeiptr), + BindTexture(gl::types::GLenum, n::Texture), + BindSampler(gl::types::GLuint, n::Texture), } pub type FrameBufferTarget = gl::types::GLenum; @@ -862,17 +866,56 @@ impl command::RawCommandBuffer for RawCommandBuffer { fn bind_graphics_descriptor_sets( &mut self, - _layout: &n::PipelineLayout, - _first_set: usize, - _sets: I, - _offsets: J, + layout: &n::PipelineLayout, + first_set: usize, + sets: I, + offsets: J, ) where I: IntoIterator, I::Item: Borrow, J: IntoIterator, J::Item: Borrow, { - // TODO + assert!(offsets.into_iter().next().is_none()); // TODO: offsets unsupported + + let mut set = first_set as _; + let drd = &*layout.desc_remap_data.read().unwrap(); + + for desc_set in sets { + let desc_set = desc_set.borrow(); + for new_binding in &*desc_set.bindings.lock().unwrap() { + match new_binding { + n::DescSetBindings::Buffer {ty: btype, binding, buffer, offset, size} => { + for binding in drd.get_binding(n::BindingTypes::UniformBuffers, set, *binding).unwrap() { + self.push_cmd(Command::BindBufferRange( + gl::UNIFORM_BUFFER, + *binding, + *buffer, + *offset, + *size, + )) + } + } + n::DescSetBindings::Texture(binding, texture) => { + for binding in drd.get_binding(n::BindingTypes::Images, set, *binding).unwrap() { + self.push_cmd(Command::BindTexture( + *binding, + *texture, + )) + } + } + n::DescSetBindings::Sampler(binding, sampler) => { + for binding in drd.get_binding(n::BindingTypes::Images, set, *binding).unwrap() { + self.push_cmd(Command::BindSampler( + *binding, + *sampler, + )) + } + } + } + } + set += 1; + } } fn bind_compute_pipeline(&mut self, pipeline: &n::ComputePipeline) { diff --git a/src/backend/gl/src/device.rs b/src/backend/gl/src/device.rs index e5873f41f77..5f663fdc70e 100644 --- a/src/backend/gl/src/device.rs +++ b/src/backend/gl/src/device.rs @@ -3,7 +3,7 @@ use std::cell::Cell; use std::iter::repeat; use std::ops::Range; use std::{ptr, mem, slice}; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Mutex, RwLock}; use gl; use gl::types::{GLint, GLenum, GLfloat}; @@ -284,21 +284,143 @@ impl Device { }) } + fn remap_bindings( + &self, + ast: &mut spirv::Ast, + desc_remap_data: &mut n::DescRemapData, + nb_map: &mut FastHashMap, + ) { + let res = ast.get_shader_resources().unwrap(); + self.remap_binding(ast, desc_remap_data, nb_map, &res.sampled_images, n::BindingTypes::Images); + self.remap_binding(ast, desc_remap_data, nb_map, &res.uniform_buffers, n::BindingTypes::UniformBuffers); + } + + fn remap_binding( + &self, + ast: &mut spirv::Ast, + desc_remap_data: &mut n::DescRemapData, + nb_map: &mut FastHashMap, + all_res: &[spirv::Resource], + btype: n::BindingTypes, + ) { + for res in all_res { + let set = ast.get_decoration(res.id, spirv::Decoration::DescriptorSet).unwrap(); + let binding = ast.get_decoration(res.id, spirv::Decoration::Binding).unwrap(); + let nbs = desc_remap_data.get_binding(btype, set as _, binding).unwrap(); + + for nb in nbs { + ast.set_decoration(res.id, spirv::Decoration::DescriptorSet, 0).unwrap(); + if self.share.legacy_features.contains(LegacyFeatures::EXPLICIT_LAYOUTS_IN_SHADER) { + ast.set_decoration(res.id, spirv::Decoration::Binding, *nb).unwrap() + } else { + ast.set_decoration(res.id, spirv::Decoration::Binding, 0).unwrap(); + assert!(nb_map.insert(res.name.clone(), *nb).is_none()) + } + } + } + } + + fn combine_seperate_images_and_samplers( + &self, + ast: &mut spirv::Ast, + desc_remap_data: &mut n::DescRemapData, + nb_map: &mut FastHashMap, + ) { + let mut id_map = FastHashMap::::default(); + let res = ast.get_shader_resources().unwrap(); + self.populate_id_map(ast, &mut id_map, &res.separate_images); + self.populate_id_map(ast, &mut id_map, &res.separate_samplers); + + let comb_res = ast.get_shader_resources().unwrap().sampled_images; + + for cis in ast.get_combined_image_samplers().unwrap() { + let (set, binding) = id_map.get(&cis.image_id).unwrap(); + let nb = desc_remap_data.reserve_binding(n::BindingTypes::Images); + desc_remap_data.insert_missing_binding( + nb, + n::BindingTypes::Images, + *set, + *binding, + ); + let (set, binding) = id_map.get(&cis.sampler_id).unwrap(); + desc_remap_data.insert_missing_binding( + nb, + n::BindingTypes::Images, + *set, + *binding, + ); + + ast.set_decoration(cis.combined_id, spirv::Decoration::DescriptorSet, 0).unwrap(); + if self.share.legacy_features.contains(LegacyFeatures::EXPLICIT_LAYOUTS_IN_SHADER) { + ast.set_decoration(cis.combined_id, spirv::Decoration::Binding, nb).unwrap() + } else { + ast.set_decoration(cis.combined_id, spirv::Decoration::Binding, 0).unwrap(); + let name = comb_res + .iter() + .filter_map(|t| + if t.id == cis.combined_id { + Some(t.name.clone()) + } else { + None + } + ) + .next() + .unwrap(); + + assert!(nb_map.insert(name, nb).is_none()) + } + } + } + + fn populate_id_map( + &self, + ast: &mut spirv::Ast, + id_map: &mut FastHashMap, + all_res: &[spirv::Resource], + ) { + for res in all_res { + let set = ast.get_decoration(res.id, spirv::Decoration::DescriptorSet).unwrap(); + let binding = ast.get_decoration(res.id, spirv::Decoration::Binding).unwrap(); + assert!(id_map.insert(res.id, (set as _, binding)).is_none()) + } + } + fn compile_shader( - &self, point: &pso::EntryPoint, stage: pso::Stage + &self, point: &pso::EntryPoint, stage: pso::Stage, desc_remap_data: &mut n::DescRemapData ) -> n::Shader { assert_eq!(point.entry, "main"); match *point.module { - n::ShaderModule::Raw(raw) => raw, + n::ShaderModule::Raw(raw) => { + debug!("Can't remap bindings for raw shaders. Assuming they are already rebound."); + raw + } n::ShaderModule::Spirv(ref spirv) => { let mut ast = self.parse_spirv(spirv).unwrap(); + + let mut name_binding_map = FastHashMap::::default(); + self.specialize_ast(&mut ast, point.specialization).unwrap(); + self.remap_bindings(&mut ast, desc_remap_data, &mut name_binding_map); + self.combine_seperate_images_and_samplers(&mut ast, desc_remap_data, &mut name_binding_map); + let glsl = self.translate_spirv(&mut ast).unwrap(); info!("Generated:\n{:?}", glsl); - match self.create_shader_module_from_source(glsl.as_bytes(), stage).unwrap() { + let program = match self.create_shader_module_from_source(glsl.as_bytes(), stage).unwrap() { n::ShaderModule::Raw(raw) => raw, _ => panic!("Unhandled") + }; + + if !self.share.legacy_features.contains(LegacyFeatures::EXPLICIT_LAYOUTS_IN_SHADER) { + let gl = &self.share.context; + for (name, binding) in name_binding_map.iter() { + unsafe { + let index = gl.GetUniformBlockIndex(program, name.as_ptr() as _); + gl.UniformBlockBinding(program, index, *binding) + } + } } + + program } } } @@ -383,14 +505,57 @@ impl d::Device for Device { } } - fn create_pipeline_layout(&self, _: IS, _: IR) -> n::PipelineLayout + fn create_pipeline_layout(&self, layouts: IS, _: IR) -> n::PipelineLayout where IS: IntoIterator, IS::Item: Borrow, IR: IntoIterator, IR::Item: Borrow<(pso::ShaderStageFlags, Range)>, { - n::PipelineLayout + let mut drd = n::DescRemapData::new(); + + layouts + .into_iter() + .enumerate() + .for_each(|(set, layout)| { + layout.borrow().iter().for_each(|binding| { + // DescriptorType -> Descriptor + // + // Sampler -> Sampler + // Image -> SampledImage, StorageImage, InputAttachment + // CombinedImageSampler -> CombinedImageSampler + // Buffer -> UniformBuffer, StorageBuffer + // UniformTexel -> UniformTexel + // StorageTexel -> StorageTexel + + assert!(!binding.immutable_samplers); //TODO: Implement immutable_samplers + use pso::DescriptorType::*; + match binding.ty { + CombinedImageSampler => { + drd.insert_missing_binding_into_spare(n::BindingTypes::Images, set as _, binding.binding); + } + Sampler | SampledImage => { + // We need to figure out combos once we get the shaders, until then we + // do nothing + } + UniformBuffer => { + drd.insert_missing_binding_into_spare(n::BindingTypes::UniformBuffers, set as _, binding.binding); + } + StorageImage + | UniformTexelBuffer + | UniformBufferDynamic + | StorageTexelBuffer + | StorageBufferDynamic + | StorageBuffer + + | InputAttachment => unimplemented!(), // 6 + } + }) + }); + + n::PipelineLayout { + desc_remap_data: Arc::new(RwLock::new(drd)), + } } fn create_graphics_pipeline<'a>( @@ -423,7 +588,7 @@ impl d::Device for Device { .iter() .filter_map(|&(stage, point_maybe)| { point_maybe.map(|point| { - let shader_name = self.compile_shader(point, stage); + let shader_name = self.compile_shader(point, stage, &mut desc.layout.desc_remap_data.write().unwrap()); unsafe { gl.AttachShader(name, shader_name); } shader_name }) @@ -507,10 +672,11 @@ impl d::Device for Device { ) -> Result { let gl = &self.share.context; let share = &self.share; + let program = { let name = unsafe { gl.CreateProgram() }; - let shader = self.compile_shader(&desc.shader, pso::Stage::Compute); + let shader = self.compile_shader(&desc.shader, pso::Stage::Compute, &mut desc.layout.desc_remap_data.write().unwrap()); unsafe { gl.AttachShader(name, shader) }; unsafe { gl.LinkProgram(name) }; @@ -588,7 +754,6 @@ impl d::Device for Device { assert!(pass.attachments.len() <= att_points.len()); gl.DrawBuffers(attachments_len as _, att_points.as_ptr()); let status = gl.CheckFramebufferStatus(target); - assert_eq!(status, gl::FRAMEBUFFER_COMPLETE); gl.BindFramebuffer(target, 0); } if let Err(err) = self.share.check() { @@ -764,6 +929,7 @@ impl d::Device for Device { Ok(n::Buffer { raw: unbound.name, target, + size: unbound.requirements.size, }) } @@ -973,14 +1139,15 @@ impl d::Device for Device { n::DescriptorPool { } } - fn create_descriptor_set_layout(&self, _: I, _: J) -> n::DescriptorSetLayout + fn create_descriptor_set_layout(&self, layout: I, _: J) -> n::DescriptorSetLayout where I: IntoIterator, I::Item: Borrow, J: IntoIterator, J::Item: Borrow, { - n::DescriptorSetLayout + // Just return it + layout.into_iter().map(|l| l.borrow().clone()).collect() } fn write_descriptor_sets<'a, I, J>(&self, writes: I) @@ -989,9 +1156,66 @@ impl d::Device for Device { J: IntoIterator, J::Item: Borrow>, { - for _write in writes { - //unimplemented!() // not panicing because of Warden - warn!("TODO: implement `write_descriptor_sets`"); + for mut write in writes { + let set = &mut write.set; + let mut bindings = set.bindings.lock().unwrap(); + let binding = write.binding; + let mut offset = write.array_offset as _; + + for descriptor in write.descriptors { + match descriptor.borrow() { + pso::Descriptor::Buffer(buffer, ref range) => { + let start = range.start.unwrap_or(0); + let end = range.end.unwrap_or(buffer.size); + let size = (end - start) as _; + + bindings + .push(n::DescSetBindings::Buffer { + ty: n::BindingTypes::UniformBuffers, + binding, + buffer: buffer.raw, + offset, + size, + }); + + offset += size; + }, + pso::Descriptor::CombinedImageSampler(view, _layout, sampler) => { + match view { + n::ImageView::Texture(tex, _) + | n::ImageView::TextureLayer(tex, _, _) => + bindings + .push(n::DescSetBindings::Texture(binding, *tex)), + n::ImageView::Surface(_) => unimplemented!(), + } + match sampler { + n::FatSampler::Sampler(sampler) => + bindings + .push(n::DescSetBindings::Sampler(binding, *sampler)), + n::FatSampler::Info(_) => unimplemented!(), + } + } + pso::Descriptor::Image(view, _layout) => { + match view { + n::ImageView::Texture(tex, _) + | n::ImageView::TextureLayer(tex, _, _) => + bindings + .push(n::DescSetBindings::Texture(binding, *tex)), + n::ImageView::Surface(_) => unimplemented!(), + } + } + pso::Descriptor::Sampler(sampler) => { + match sampler { + n::FatSampler::Sampler(sampler) => + bindings + .push(n::DescSetBindings::Sampler(binding, *sampler)), + n::FatSampler::Info(_) => unimplemented!(), + } + } + pso::Descriptor::UniformTexelBuffer(_view) => unimplemented!(), + pso::Descriptor::StorageTexelBuffer(_view) => unimplemented!(), + } + } } } diff --git a/src/backend/gl/src/info.rs b/src/backend/gl/src/info.rs index fcf61338de0..8195a934c53 100644 --- a/src/backend/gl/src/info.rs +++ b/src/backend/gl/src/info.rs @@ -226,6 +226,8 @@ bitflags! { const SAMPLER_LOD_BIAS = 0x2000; /// Support setting border texel colors. const SAMPLER_BORDER_COLOR = 0x4000; + /// No explicit layouts in shader support + const EXPLICIT_LAYOUTS_IN_SHADER = 0x8000; } } @@ -347,6 +349,11 @@ pub fn query_all(gl: &gl::Gl) -> (Info, Features, LegacyFeatures, Limits, Privat ]) { features |= Features::SAMPLER_ANISOTROPY; } + if info.is_supported(&[ + Core(4, 2), + ]) { + legacy |= LegacyFeatures::EXPLICIT_LAYOUTS_IN_SHADER; + } if info.is_supported(&[ Core(3, 3), Es(3, 0), diff --git a/src/backend/gl/src/lib.rs b/src/backend/gl/src/lib.rs index 15208afc2a6..acc99fb4098 100644 --- a/src/backend/gl/src/lib.rs +++ b/src/backend/gl/src/lib.rs @@ -20,7 +20,7 @@ use std::sync::Arc; use std::ops::Deref; use std::thread::{self, ThreadId}; -use hal::{error, image}; +use hal::{error, image, pso}; use hal::queue::{Queues, QueueFamilyId}; pub use self::device::Device; diff --git a/src/backend/gl/src/native.rs b/src/backend/gl/src/native.rs index 3f60af4100f..c483d96bba7 100644 --- a/src/backend/gl/src/native.rs +++ b/src/backend/gl/src/native.rs @@ -1,13 +1,14 @@ use std::cell::Cell; +use std::sync::{Arc, Mutex, RwLock}; use hal::{format, image as i, pass, pso}; use hal::memory::Properties; +use hal::backend::FastHashMap; use gl; use Backend; use std::borrow::Borrow; - pub type RawBuffer = gl::types::GLuint; pub type Shader = gl::types::GLuint; pub type Program = gl::types::GLuint; @@ -16,12 +17,15 @@ pub type Surface = gl::types::GLuint; pub type Texture = gl::types::GLuint; pub type Sampler = gl::types::GLuint; +pub type DescriptorSetLayout = Vec; + pub const DEFAULT_FRAMEBUFFER: FrameBuffer = 0; #[derive(Debug)] pub struct Buffer { pub(crate) raw: RawBuffer, pub(crate) target: gl::types::GLenum, + pub(crate) size: u64, } #[derive(Debug)] @@ -38,6 +42,72 @@ impl Fence { } } +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] +pub enum BindingTypes { + Images, + UniformBuffers, +} + +#[derive(Clone, Debug)] +pub struct DescRemapData { + bindings: FastHashMap<(BindingTypes, pso::DescriptorSetIndex, pso::DescriptorBinding), Vec>, + names: FastHashMap, + next_binding: FastHashMap, +} + +/// Stores where the descriptor bindings have been remaped too. +/// +/// OpenGL doesn't support sets, so we have to flatten out the bindings. +impl DescRemapData { + pub fn new() -> Self { + DescRemapData { + bindings: FastHashMap::default(), + names: FastHashMap::default(), + next_binding: FastHashMap::default(), + } + } + + pub fn insert_missing_binding_into_spare( + &mut self, + btype: BindingTypes, + set: pso::DescriptorSetIndex, + binding: pso::DescriptorBinding, + ) -> &[pso::DescriptorBinding] { + let nb = self.next_binding.entry(btype).or_insert(0); + let val = self.bindings.entry((btype, set, binding)).or_insert(Vec::new()); + val.push(*nb); + *nb += 1; + &*val + } + + pub fn reserve_binding(&mut self, btype: BindingTypes) -> pso::DescriptorBinding { + let nb = self.next_binding.entry(btype).or_insert(0); + *nb += 1; + *nb - 1 + } + + pub fn insert_missing_binding( + &mut self, + nb: pso::DescriptorBinding, + btype: BindingTypes, + set: pso::DescriptorSetIndex, + binding: pso::DescriptorBinding, + ) -> &[pso::DescriptorBinding] { + let val = self.bindings.entry((btype, set, binding)).or_insert(Vec::new()); + val.push(nb); + &*val + } + + pub fn get_binding( + &self, + btype: BindingTypes, + set: pso::DescriptorSetIndex, + binding: pso::DescriptorBinding, + ) -> Option<&[pso::DescriptorBinding]> { + self.bindings.get(&(btype, set, binding)).map(AsRef::as_ref) + } +} + #[derive(Clone, Debug)] pub struct GraphicsPipeline { pub(crate) program: Program, @@ -48,7 +118,7 @@ pub struct GraphicsPipeline { pub(crate) vertex_buffers: Vec>, } -#[derive(Clone, Debug, Copy)] +#[derive(Clone, Debug)] pub struct ComputePipeline { pub(crate) program: Program, } @@ -82,10 +152,23 @@ pub enum ImageView { } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub struct DescriptorSetLayout; +pub(crate) enum DescSetBindings { + Buffer { + ty: BindingTypes, + binding: pso::DescriptorBinding, + buffer: RawBuffer, + offset: gl::types::GLintptr, + size: gl::types::GLsizeiptr + }, + Texture(pso::DescriptorBinding, Texture), + Sampler(pso::DescriptorBinding, Sampler), +} -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub struct DescriptorSet; +#[derive(Clone, Debug)] +pub struct DescriptorSet { + layout: DescriptorSetLayout, + pub(crate) bindings: Arc>>, +} #[derive(Debug)] pub struct DescriptorPool {} @@ -96,7 +179,10 @@ impl pso::DescriptorPool for DescriptorPool { I: IntoIterator, I::Item: Borrow, { - layouts.into_iter().map(|_| Ok(DescriptorSet)).collect() + layouts.into_iter().map(|layout| Ok(DescriptorSet { + layout: layout.borrow().clone(), + bindings: Arc::new(Mutex::new(Vec::new())), + })).collect() } fn free_sets(&mut self, _descriptor_sets: &[DescriptorSet]) { @@ -166,7 +252,9 @@ impl SubpassDesc { } #[derive(Debug)] -pub struct PipelineLayout; +pub struct PipelineLayout { + pub(crate) desc_remap_data: Arc>, +} #[derive(Debug)] // No inter-queue synchronization required for GL. diff --git a/src/backend/gl/src/queue.rs b/src/backend/gl/src/queue.rs index 53c0e5d43bd..0b1c265169d 100644 --- a/src/backend/gl/src/queue.rs +++ b/src/backend/gl/src/queue.rs @@ -551,6 +551,19 @@ impl CommandQueue { com::Command::CopyImageToSurface(..) => { unimplemented!() //TODO: use FBO } + com::Command::BindBufferRange(target, index, buffer, offset, size) => unsafe { + let gl = &self.share.context; + gl.BindBufferRange(target, index, buffer, offset, size); + } + com::Command::BindTexture(index, texture) => unsafe { + let gl = &self.share.context; + gl.ActiveTexture(gl::TEXTURE0 + index); + gl.BindTexture(gl::TEXTURE_2D, texture); + } + com::Command::BindSampler(index, sampler) => unsafe { + let gl = &self.share.context; + gl.BindSampler(index, sampler); + } /* com::Command::BindConstantBuffer(pso::ConstantBufferParam(buffer, _, slot)) => unsafe { self.share.context.BindBufferBase(gl::UNIFORM_BUFFER, slot as gl::types::GLuint, buffer);