diff --git a/src/backend/gl/src/command.rs b/src/backend/gl/src/command.rs index e4ef4a75ff6..267b8caa3ee 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; @@ -832,14 +836,44 @@ impl command::RawCommandBuffer for RawCommandBuffer { fn bind_graphics_descriptor_sets( &mut self, - _layout: &n::PipelineLayout, - _first_set: usize, - _sets: T, + layout: &n::PipelineLayout, + first_set: usize, + sets: T, ) where T: IntoIterator, T::Item: Borrow, { - // TODO + let mut set = first_set as _; + + for desc_set in sets { + let desc_set = desc_set.borrow(); + for new_binding in desc_set.bindings.read().unwrap().iter() { + match new_binding { + n::DescSetBindings::Buffer(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 46f036e79df..0ea80158787 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.get_binding_insert(btype, set, binding); + desc_remap_data.associate_name(res.name.clone(), btype, set, 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 } } } @@ -386,11 +451,13 @@ impl d::Device for Device { fn create_pipeline_layout(&self, _: IS, _: IR) -> n::PipelineLayout where IS: IntoIterator, - IS::Item: Borrow, + IS::Item: Borrow>, 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,12 +1042,13 @@ impl d::Device for Device { n::DescriptorPool { } } - fn create_descriptor_set_layout(&self, _: I) -> n::DescriptorSetLayout + fn create_descriptor_set_layout(&self, layout: I) -> Vec where I: IntoIterator, I::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) @@ -987,9 +1057,55 @@ 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 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 _; + + set.bindings + .write() + .unwrap() + .push(n::DescSetBindings::Buffer( + n::BindingTypes::UniformBuffers, + binding, + buffer.raw, + offset, + size, + )); + + offset += size; + } + pso::Descriptor::CombinedImageSampler(view, layout, sampler) => { + match view { + n::ImageView::Texture(tex, _) + | n::ImageView::TextureLayer(tex, _, _) => set + .bindings + .write() + .unwrap() + .push(n::DescSetBindings::Texture(binding, *tex)), + n::ImageView::Surface(_) => unimplemented!(), + } + match sampler { + n::FatSampler::Sampler(sampler) => set.bindings + .write() + .unwrap() + .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!(), + } + } } } @@ -1133,7 +1249,7 @@ impl d::Device for Device { // Nothing to do } - fn destroy_descriptor_set_layout(&self, _: n::DescriptorSetLayout) { + fn destroy_descriptor_set_layout(&self, _: Vec) { // Nothing to do } 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..daa8559de72 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; @@ -70,7 +70,7 @@ impl hal::Backend for Backend { type ComputePipeline = native::ComputePipeline; type GraphicsPipeline = native::GraphicsPipeline; type PipelineLayout = native::PipelineLayout; - type DescriptorSetLayout = native::DescriptorSetLayout; + type DescriptorSetLayout = Vec; type DescriptorPool = native::DescriptorPool; type DescriptorSet = native::DescriptorSet; diff --git a/src/backend/gl/src/native.rs b/src/backend/gl/src/native.rs index d827d111d41..bc8b16debd8 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, 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; @@ -15,6 +17,8 @@ pub type FrameBuffer = gl::types::GLuint; pub type Surface = gl::types::GLuint; pub type Texture = gl::types::GLuint; pub type Sampler = gl::types::GLuint; +pub(crate) type SetID = gl::types::GLuint; +pub(crate) type BindingID = gl::types::GLuint; pub const DEFAULT_FRAMEBUFFER: FrameBuffer = 0; @@ -22,6 +26,7 @@ pub const DEFAULT_FRAMEBUFFER: FrameBuffer = 0; pub struct Buffer { pub(crate) raw: RawBuffer, pub(crate) target: gl::types::GLenum, + pub(crate) size: u64, } #[derive(Debug)] @@ -38,6 +43,79 @@ impl Fence { } } +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] +pub(crate) enum BindingTypes { + UniformBuffers, + StorageBuffers, + StorageImages, + SampledImages, + SeparateImages, + SeparateSamplers, + SubpassInputs, +} + +#[derive(Clone, Debug)] +pub(crate) struct DescRemapData { + bindings: HashMap>>, + names: HashMap, + next_binding: BindingID, +} + +impl DescRemapData { + pub(crate) fn new() -> Self { + DescRemapData { + bindings: HashMap::new(), + names: HashMap::new(), + next_binding: 0, + } + } + + pub(crate) fn get_binding_insert(&mut self, btype: BindingTypes, set: SetID, binding: BindingID) -> BindingID { + if !self.bindings.contains_key(&btype) { + self.bindings.insert(btype, HashMap::new()); + } + + let bindings = self.bindings.get_mut(&btype).unwrap(); + if !bindings.contains_key(&set) { + bindings.insert(set, HashMap::new()); + } + + let set_map = bindings.get_mut(&set).unwrap(); + if !set_map.contains_key(&binding) { + set_map.insert(binding, self.next_binding); + self.next_binding += 1; + } + + *set_map.get(&binding).unwrap() + } + + pub(crate) fn get_binding(&self, btype: BindingTypes, set: SetID, binding: BindingID) -> Option<&BindingID> { + if !self.bindings.contains_key(&btype) { + return None + } + + let bindings = self.bindings.get(&btype).unwrap(); + if !bindings.contains_key(&set) { + return None + } + + let set_map = bindings.get(&set).unwrap(); + if !set_map.contains_key(&binding) { + return None + } + + set_map.get(&binding) + } + + pub(crate) fn associate_name(&mut self, name: String, btype: BindingTypes, set: SetID, binding: BindingID) { + self.names.insert(name, (btype, set, binding)); + } + + pub(crate) fn get_names_and_data(&self) -> hash_map::Iter { + self.names.iter() + } +} + #[derive(Clone, Debug)] pub struct GraphicsPipeline { pub(crate) program: Program, @@ -48,7 +126,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 +160,17 @@ pub enum ImageView { } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub struct DescriptorSetLayout; +pub(crate) enum DescSetBindings { + Buffer(BindingTypes, BindingID, RawBuffer, gl::types::GLintptr, gl::types::GLsizeiptr), + Texture(BindingID, Texture), + Sampler(BindingID, Sampler), +} -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub struct DescriptorSet; +#[derive(Clone, Debug)] +pub struct DescriptorSet { + layout: Vec, + pub(crate) bindings: Arc>>, +} #[derive(Debug)] pub struct DescriptorPool {} @@ -94,9 +179,12 @@ impl pso::DescriptorPool for DescriptorPool { fn allocate_sets(&mut self, layouts: I) -> Vec> where I: IntoIterator, - I::Item: Borrow, + I::Item: Borrow>, { - layouts.into_iter().map(|_| Ok(DescriptorSet)).collect() + layouts.into_iter().map(|layout| Ok(DescriptorSet { + layout: layout.borrow().clone(), + bindings: Arc::new(RwLock::new(Vec::new())), + })).collect() } fn reset(&mut self) { @@ -162,7 +250,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);