diff --git a/examples/hal/quad/data/frag.spv b/examples/hal/quad/data/frag.spv index 6296a203ffd..9a6a7b9d396 100644 Binary files a/examples/hal/quad/data/frag.spv and b/examples/hal/quad/data/frag.spv differ diff --git a/examples/hal/quad/main.rs b/examples/hal/quad/main.rs index fbe814a9d5f..c8863f3ca63 100644 --- a/examples/hal/quad/main.rs +++ b/examples/hal/quad/main.rs @@ -145,14 +145,7 @@ fn main() { &[ 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, + ty: pso::DescriptorType::CombinedImageSampler, count: 1, stage_flags: ShaderStageFlags::FRAGMENT, immutable_samplers: false, @@ -316,11 +309,7 @@ fn main() { 1, // sets &[ pso::DescriptorRangeDesc { - ty: pso::DescriptorType::SampledImage, - count: 1, - }, - pso::DescriptorRangeDesc { - ty: pso::DescriptorType::Sampler, + ty: pso::DescriptorType::CombinedImageSampler, count: 1, }, ], @@ -449,15 +438,7 @@ fn main() { 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::Descriptor::CombinedImageSampler(&image_srv, i::Layout::ShaderReadOnlyOptimal, &sampler) ), }, ]); diff --git a/examples/hal/quad/shader/quad.frag b/examples/hal/quad/shader/quad.frag index 689cc0caa2a..86452d14cb7 100644 --- a/examples/hal/quad/shader/quad.frag +++ b/examples/hal/quad/shader/quad.frag @@ -4,9 +4,8 @@ layout(location = 0) in vec2 v_Uv; layout(location = 0) out vec4 Target0; -layout(set = 0, binding = 0) uniform texture2D u_Texture; -layout(set = 0, binding = 1) uniform sampler u_Sampler; +layout(set = 0, binding = 0) uniform sampler2D u_Texture; void main() { - Target0 = texture(sampler2D(u_Texture, u_Sampler), v_Uv); + Target0 = texture(u_Texture, v_Uv); } diff --git a/src/backend/gl/src/command.rs b/src/backend/gl/src/command.rs index ccf0be8e742..520f7e35d70 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; @@ -836,17 +840,49 @@ 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 _; + + 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} => { + self.push_cmd(Command::BindBufferRange( + gl::UNIFORM_BUFFER, + *layout.desc_remap_data.read().unwrap().get_binding(*btype, set, *binding).unwrap(), + *buffer, + *offset, + *size, + )) + } + n::DescSetBindings::Texture(binding, texture) => { + self.push_cmd(Command::BindTexture( + *binding, + *texture, + )) + } + n::DescSetBindings::Sampler(binding, sampler) => { + 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 d8f06141709..48945870730 100644 --- a/src/backend/gl/src/device.rs +++ b/src/backend/gl/src/device.rs @@ -4,7 +4,7 @@ use std::collections::HashMap; 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,86 @@ impl Device { }) } + fn remap_desc_sets( + &self, + ast: &mut spirv::Ast, + desc_remap_data: &mut n::DescRemapData, + ) { + let res = ast.get_shader_resources().unwrap(); + + self.remap_desc_set(ast, desc_remap_data, &res.uniform_buffers, n::BindingTypes::UniformBuffers); + self.remap_desc_set(ast, desc_remap_data, &res.storage_buffers, n::BindingTypes::StorageBuffers); + self.remap_desc_set(ast, desc_remap_data, &res.storage_images, n::BindingTypes::StorageImages); + self.remap_desc_set(ast, desc_remap_data, &res.sampled_images, n::BindingTypes::SampledImages); + self.remap_desc_set(ast, desc_remap_data, &res.separate_images, n::BindingTypes::SeparateImages); + self.remap_desc_set(ast, desc_remap_data, &res.separate_samplers, n::BindingTypes::SeparateSamplers); + self.remap_desc_set(ast, desc_remap_data, &res.subpass_inputs, n::BindingTypes::SubpassInputs); + + // Everything but these four, I think + //all_res.append(&mut res.stage_inputs); + //all_res.append(&mut res.stage_outputs); + //all_res.append(&mut res.atomic_counters); + //all_res.append(&mut res.push_constants); + } + + fn remap_desc_set( + &self, + ast: &mut spirv::Ast, + desc_remap_data: &mut n::DescRemapData, + 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 mut new_binding = desc_remap_data.insert_missing_binding(btype, set as _, binding); + desc_remap_data.associate_name(res.name.clone(), btype, set as _, binding); + + if self.share.legacy_features.contains(LegacyFeatures::NO_EXPLICIT_LAYOUTS_IN_SHADER) { + debug!("Next remap is going to be to zero because explicit layouts in shaders aren't supported prior to OpenGL4.2"); + new_binding = 0 + } + + debug!("Uniform {} has been remaped from set {} binding {} to binding {}", res.name, set, binding, new_binding); + + ast.set_decoration(res.id, spirv::Decoration::DescriptorSet, 0).unwrap(); + ast.set_decoration(res.id, spirv::Decoration::Binding, new_binding).unwrap(); + } + } + 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(); + self.specialize_ast(&mut ast, point.specialization).unwrap(); + self.remap_desc_sets(&mut ast, desc_remap_data); + 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::NO_EXPLICIT_LAYOUTS_IN_SHADER) { + for (name, (btype, set, binding)) in desc_remap_data.get_names_and_data() { + let gl = &self.share.context; + unsafe { + let index = gl.GetUniformBlockIndex(program, name.as_ptr() as _); + gl.UniformBlockBinding(program, index, *desc_remap_data.get_binding(*btype, *set, *binding).unwrap()) + } + } } + + program } } } @@ -390,7 +455,9 @@ impl d::Device for Device { IR: IntoIterator, IR::Item: Borrow<(pso::ShaderStageFlags, Range)>, { - n::PipelineLayout + n::PipelineLayout { + desc_remap_data: Arc::new(RwLock::new(n::DescRemapData::new())), + } } fn create_graphics_pipeline<'a>( @@ -423,7 +490,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 +574,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) }; @@ -764,6 +832,7 @@ impl d::Device for Device { Ok(n::Buffer { raw: unbound.name, target, + size: unbound.requirements.size, }) } @@ -973,14 +1042,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 +1059,51 @@ 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) => unimplemented!(), + pso::Descriptor::Sampler(_sampler) => 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..c5908abdcba 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 NO_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::NO_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 8bf0467e7e4..9ab7f382b91 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..3cdf36ae4a3 100644 --- a/src/backend/gl/src/native.rs +++ b/src/backend/gl/src/native.rs @@ -1,4 +1,5 @@ use std::cell::Cell; +use std::sync::{Arc, Mutex, RwLock}; use hal::{format, image as i, pass, pso}; use hal::memory::Properties; @@ -7,6 +8,7 @@ use gl; use Backend; use std::borrow::Borrow; +use std::collections::{HashMap, hash_map}; pub type RawBuffer = gl::types::GLuint; pub type Shader = gl::types::GLuint; @@ -16,12 +18,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 +43,57 @@ impl Fence { } } +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] +pub enum BindingTypes { + UniformBuffers, + StorageBuffers, + StorageImages, + SampledImages, + SeparateImages, + SeparateSamplers, + SubpassInputs, +} + +#[derive(Clone, Debug)] +pub struct DescRemapData { + bindings: HashMap<(BindingTypes, pso::DescriptorSetIndex, pso::DescriptorBinding), pso::DescriptorBinding>, + names: HashMap, + next_binding: pso::DescriptorBinding, +} + +/// 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: HashMap::new(), + names: HashMap::new(), + next_binding: 0, + } + } + + pub fn insert_missing_binding(&mut self, btype: BindingTypes, set: pso::DescriptorSetIndex, binding: pso::DescriptorBinding) -> pso::DescriptorBinding { + let nb = &mut self.next_binding; + *self.bindings.entry((btype, set, binding)).or_insert_with(|| { + *nb += 1; + *nb - 1 + }) + } + + pub fn get_binding(&self, btype: BindingTypes, set: pso::DescriptorSetIndex, binding: pso::DescriptorBinding) -> Option<&pso::DescriptorBinding> { + self.bindings.get(&(btype, set, binding)) + } + + pub fn associate_name(&mut self, name: String, btype: BindingTypes, set: pso::DescriptorSetIndex, binding: pso::DescriptorBinding) { + self.names.insert(name, (btype, set, binding)); + } + + pub fn get_names_and_data(&self) -> hash_map::Iter { + self.names.iter() + } +} + #[derive(Clone, Debug)] pub struct GraphicsPipeline { pub(crate) program: Program, @@ -48,7 +104,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 +138,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 +165,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 +238,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 d025ece4353..bb2c6e56eeb 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);