diff --git a/wgpu-hal/src/gles/adapter.rs b/wgpu-hal/src/gles/adapter.rs index 0216e19f5..1c96a9376 100644 --- a/wgpu-hal/src/gles/adapter.rs +++ b/wgpu-hal/src/gles/adapter.rs @@ -232,7 +232,8 @@ impl super::Adapter { 0 }, max_vertex_buffers: gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_BINDINGS) as u32, - max_vertex_attributes: gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIBS) as u32, + max_vertex_attributes: (gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIBS) as u32) + .min(super::MAX_VERTEX_ATTRIBUTES as u32), max_vertex_buffer_array_stride: 2048, max_push_constant_size: 0, }; diff --git a/wgpu-hal/src/gles/command.rs b/wgpu-hal/src/gles/command.rs index d821f1318..24806fb21 100644 --- a/wgpu-hal/src/gles/command.rs +++ b/wgpu-hal/src/gles/command.rs @@ -1,6 +1,26 @@ use super::{conv, Command as C}; +use arrayvec::ArrayVec; use std::{mem, ops::Range}; +bitflags::bitflags! { + #[derive(Default)] + struct Dirty: u32 { + const VERTEX_BUFFERS = 0x0001; + } +} + +#[derive(Default)] +pub(super) struct State { + topology: u32, + index_format: wgt::IndexFormat, + index_offset: wgt::BufferAddress, + vertex_buffers: [super::VertexBufferDesc; crate::MAX_VERTEX_BUFFERS], + vertex_attributes: ArrayVec<[super::AttributeDesc; super::MAX_VERTEX_ATTRIBUTES]>, + stencil: super::StencilState, + has_pass_label: bool, + dirty: Dirty, +} + impl super::CommandBuffer { fn clear(&mut self) { self.label = None; @@ -16,9 +36,62 @@ impl super::CommandBuffer { } } +impl super::CommandEncoder { + fn rebind_stencil_func(&mut self) { + fn make(s: &super::StencilSide, face: u32) -> C { + C::SetStencilFunc { + face, + function: s.function, + reference: s.reference, + read_mask: s.mask_read, + } + } + + let s = &self.state.stencil; + if s.front.function == s.back.function + && s.front.mask_read == s.back.mask_read + && s.front.reference == s.back.reference + { + self.cmd_buffer + .commands + .push(make(&s.front, glow::FRONT_AND_BACK)); + } else { + self.cmd_buffer.commands.push(make(&s.front, glow::FRONT)); + self.cmd_buffer.commands.push(make(&s.back, glow::BACK)); + } + } + + fn rebind_vertex_attributes(&mut self, first_instance: u32) { + for attribute in self.state.vertex_attributes.iter() { + let vb = self.state.vertex_buffers[attribute.buffer_index as usize].clone(); + + let mut vat = attribute.clone(); + vat.offset += vb.offset as u32; + + if vb.step == wgt::InputStepMode::Instance { + vat.offset += vb.stride * first_instance; + } + + self.cmd_buffer + .commands + .push(C::SetVertexAttribute(vat, vb)); + } + } + + fn prepare_draw(&mut self, first_instance: u32) { + if first_instance != 0 { + self.rebind_vertex_attributes(first_instance); + self.state.dirty.set(Dirty::VERTEX_BUFFERS, true); + } else if self.state.dirty.contains(Dirty::VERTEX_BUFFERS) { + self.rebind_vertex_attributes(0); + self.state.dirty.set(Dirty::VERTEX_BUFFERS, false); + } + } +} + impl crate::CommandEncoder for super::CommandEncoder { unsafe fn begin_encoding(&mut self, label: crate::Label) -> Result<(), crate::DeviceError> { - self.state = super::CommandState::default(); + self.state = State::default(); self.cmd_buffer.label = label.map(str::to_string); Ok(()) } @@ -63,18 +136,22 @@ impl crate::CommandEncoder for super::CommandEncoder { { return; } + + let mut combined_usage = crate::TextureUse::empty(); for bar in barriers { // GLES only synchronizes storage -> anything explicitly if !bar.usage.start.contains(crate::TextureUse::STORAGE_STORE) { continue; } - let raw = match bar.texture.inner { - super::TextureInner::Texture { raw, .. } => raw, - super::TextureInner::Renderbuffer { .. } => continue, - }; + // unlike buffers, there is no need for a concrete texture + // object to be bound anywhere for a barrier + combined_usage |= bar.usage.end; + } + + if !combined_usage.is_empty() { self.cmd_buffer .commands - .push(C::TextureBarrier(raw, bar.usage.end)); + .push(C::TextureBarrier(combined_usage)); } } @@ -235,9 +312,7 @@ impl crate::CommandEncoder for super::CommandEncoder { } // set the framebuffer - self.cmd_buffer - .commands - .push(C::ResetFramebuffer(desc.extent)); + self.cmd_buffer.commands.push(C::ResetFramebuffer); for (i, cat) in desc.color_attachments.iter().enumerate() { let attachment = glow::COLOR_ATTACHMENT0 + i as u32; self.cmd_buffer.commands.push(C::SetFramebufferAttachment { @@ -257,9 +332,22 @@ impl crate::CommandEncoder for super::CommandEncoder { }); } + // set the draw buffers and states self.cmd_buffer .commands .push(C::SetDrawColorBuffers(desc.color_attachments.len() as u8)); + let rect = crate::Rect { + x: 0, + y: 0, + w: desc.extent.width as i32, + h: desc.extent.height as i32, + }; + self.cmd_buffer.commands.push(C::SetScissor(rect.clone())); + self.cmd_buffer.commands.push(C::SetViewport { + rect, + depth: 0.0..1.0, + }); + // issue the clears for (i, cat) in desc.color_attachments.iter().enumerate() { if !cat.ops.contains(crate::AttachmentOp::LOAD) { @@ -302,6 +390,7 @@ impl crate::CommandEncoder for super::CommandEncoder { self.cmd_buffer.commands.push(C::PopDebugGroup); self.state.has_pass_label = false; } + self.state.dirty = Dirty::empty(); } unsafe fn set_bind_group( @@ -335,6 +424,46 @@ impl crate::CommandEncoder for super::CommandEncoder { unsafe fn set_render_pipeline(&mut self, pipeline: &super::RenderPipeline) { self.state.topology = conv::map_primitive_topology(pipeline.primitive.topology); + self.state.dirty |= Dirty::VERTEX_BUFFERS; + + self.state.vertex_attributes.clear(); + for vat in pipeline.vertex_attributes.iter() { + self.state.vertex_attributes.push(vat.clone()); + } + for (state_desc, pipe_desc) in self + .state + .vertex_buffers + .iter_mut() + .zip(pipeline.vertex_buffers.iter()) + { + state_desc.step = pipe_desc.step; + state_desc.stride = pipe_desc.stride; + } + + if let Some(ref stencil) = pipeline.stencil { + self.state.stencil = stencil.clone(); + self.rebind_stencil_func(); + if stencil.front.ops == stencil.back.ops + && stencil.front.mask_write == stencil.back.mask_write + { + self.cmd_buffer.commands.push(C::SetStencilOps { + face: glow::FRONT_AND_BACK, + write_mask: stencil.front.mask_write, + ops: stencil.front.ops.clone(), + }); + } else { + self.cmd_buffer.commands.push(C::SetStencilOps { + face: glow::FRONT, + write_mask: stencil.front.mask_write, + ops: stencil.front.ops.clone(), + }); + self.cmd_buffer.commands.push(C::SetStencilOps { + face: glow::BACK, + write_mask: stencil.back.mask_write, + ops: stencil.back.ops.clone(), + }); + } + } } unsafe fn set_index_buffer<'a>( @@ -353,10 +482,35 @@ impl crate::CommandEncoder for super::CommandEncoder { index: u32, binding: crate::BufferBinding<'a, super::Api>, ) { + self.state.dirty |= Dirty::VERTEX_BUFFERS; + let vb = &mut self.state.vertex_buffers[index as usize]; + vb.raw = binding.buffer.raw; + vb.offset = binding.offset; + } + unsafe fn set_viewport(&mut self, rect: &crate::Rect, depth: Range) { + self.cmd_buffer.commands.push(C::SetViewport { + rect: crate::Rect { + x: rect.x as i32, + y: rect.y as i32, + w: rect.w as i32, + h: rect.h as i32, + }, + depth, + }); + } + unsafe fn set_scissor_rect(&mut self, rect: &crate::Rect) { + self.cmd_buffer.commands.push(C::SetScissor(crate::Rect { + x: rect.x as i32, + y: rect.y as i32, + w: rect.w as i32, + h: rect.h as i32, + })); + } + unsafe fn set_stencil_reference(&mut self, value: u32) { + self.state.stencil.front.reference = value; + self.state.stencil.back.reference = value; + self.rebind_stencil_func(); } - unsafe fn set_viewport(&mut self, rect: &crate::Rect, depth_range: Range) {} - unsafe fn set_scissor_rect(&mut self, rect: &crate::Rect) {} - unsafe fn set_stencil_reference(&mut self, value: u32) {} unsafe fn set_blend_constants(&mut self, color: &wgt::Color) {} unsafe fn draw( @@ -366,7 +520,7 @@ impl crate::CommandEncoder for super::CommandEncoder { start_instance: u32, instance_count: u32, ) { - debug_assert_eq!(start_instance, 0); + self.prepare_draw(start_instance); self.cmd_buffer.commands.push(C::Draw { topology: self.state.topology, start_vertex, @@ -382,7 +536,7 @@ impl crate::CommandEncoder for super::CommandEncoder { start_instance: u32, instance_count: u32, ) { - debug_assert_eq!(start_instance, 0); + self.prepare_draw(start_instance); let (index_size, index_type) = match self.state.index_format { wgt::IndexFormat::Uint16 => (2, glow::UNSIGNED_SHORT), wgt::IndexFormat::Uint32 => (4, glow::UNSIGNED_INT), @@ -403,6 +557,7 @@ impl crate::CommandEncoder for super::CommandEncoder { offset: wgt::BufferAddress, draw_count: u32, ) { + self.prepare_draw(0); for draw in 0..draw_count as wgt::BufferAddress { let indirect_offset = offset + draw * mem::size_of::() as wgt::BufferAddress; @@ -419,6 +574,7 @@ impl crate::CommandEncoder for super::CommandEncoder { offset: wgt::BufferAddress, draw_count: u32, ) { + self.prepare_draw(0); let index_type = match self.state.index_format { wgt::IndexFormat::Uint16 => glow::UNSIGNED_SHORT, wgt::IndexFormat::Uint32 => glow::UNSIGNED_INT, @@ -469,6 +625,7 @@ impl crate::CommandEncoder for super::CommandEncoder { self.cmd_buffer.commands.push(C::PopDebugGroup); self.state.has_pass_label = false; } + self.state.dirty = Dirty::empty(); } unsafe fn set_compute_pipeline(&mut self, pipeline: &super::ComputePipeline) {} diff --git a/wgpu-hal/src/gles/conv.rs b/wgpu-hal/src/gles/conv.rs index 3162e84e6..a415fb5b3 100644 --- a/wgpu-hal/src/gles/conv.rs +++ b/wgpu-hal/src/gles/conv.rs @@ -232,3 +232,44 @@ pub fn map_view_dimension(dim: wgt::TextureViewDimension) -> u32 { Tvd::D3 => glow::TEXTURE_3D, } } + +fn map_stencil_op(operation: wgt::StencilOperation) -> u32 { + use wgt::StencilOperation as So; + match operation { + So::Keep => glow::KEEP, + So::Zero => glow::ZERO, + So::Replace => glow::REPLACE, + So::Invert => glow::INVERT, + So::IncrementClamp => glow::INCR, + So::DecrementClamp => glow::DECR, + So::IncrementWrap => glow::INCR_WRAP, + So::DecrementWrap => glow::DECR_WRAP, + } +} + +fn map_stencil_ops(face: &wgt::StencilFaceState) -> super::StencilOps { + super::StencilOps { + pass: map_stencil_op(face.pass_op), + fail: map_stencil_op(face.fail_op), + depth_fail: map_stencil_op(face.depth_fail_op), + } +} + +pub(super) fn map_stencil(state: &wgt::StencilState) -> super::StencilState { + super::StencilState { + front: super::StencilSide { + function: map_compare_func(state.front.compare), + mask_read: state.read_mask, + mask_write: state.write_mask, + reference: 0, + ops: map_stencil_ops(&state.front), + }, + back: super::StencilSide { + function: map_compare_func(state.back.compare), + mask_read: state.read_mask, + mask_write: state.write_mask, + reference: 0, + ops: map_stencil_ops(&state.back), + }, + } +} diff --git a/wgpu-hal/src/gles/device.rs b/wgpu-hal/src/gles/device.rs index ef1e8ea2d..a3042a809 100644 --- a/wgpu-hal/src/gles/device.rs +++ b/wgpu-hal/src/gles/device.rs @@ -518,7 +518,7 @@ impl crate::Device for super::Device { ) -> Result { Ok(super::CommandEncoder { cmd_buffer: super::CommandBuffer::default(), - state: super::CommandState::default(), + state: Default::default(), private_caps: self.shared.private_caps, }) } @@ -665,11 +665,16 @@ impl crate::Device for super::Device { ); let inner = self.create_pipeline(shaders, desc.layout)?; - let attributes = { - let gl = &self.shared.context; + let (vertex_buffers, vertex_attributes) = { + let mut buffers = Vec::new(); let mut attributes = Vec::new(); - for (index, vb_layout) in desc.vertex_buffers.iter().enumerate() { + buffers.push(super::VertexBufferDesc { + raw: 0, + offset: 0, + step: vb_layout.step_mode, + stride: vb_layout.array_stride as u32, + }); for vat in vb_layout.attributes.iter() { let format_desc = conv::describe_vertex_format(vat.format); attributes.push(super::AttributeDesc { @@ -680,15 +685,23 @@ impl crate::Device for super::Device { }); } } - - attributes.into_boxed_slice() + (buffers.into_boxed_slice(), attributes.into_boxed_slice()) }; Ok(super::RenderPipeline { inner, primitive: desc.primitive, - attributes, - depth: desc.depth_stencil.clone(), + vertex_buffers, + vertex_attributes, + depth: desc.depth_stencil.as_ref().map(|ds| super::DepthState { + function: conv::map_compare_func(ds.depth_compare), + mask: ds.depth_write_enabled, + }), + depth_bias: desc.depth_stencil.as_ref().map(|ds| ds.bias), + stencil: desc + .depth_stencil + .as_ref() + .map(|ds| conv::map_stencil(&ds.stencil)), }) } unsafe fn destroy_render_pipeline(&self, pipeline: super::RenderPipeline) { diff --git a/wgpu-hal/src/gles/mod.rs b/wgpu-hal/src/gles/mod.rs index 7d7d9a9a0..31ae5368d 100644 --- a/wgpu-hal/src/gles/mod.rs +++ b/wgpu-hal/src/gles/mod.rs @@ -22,6 +22,7 @@ pub struct Api; //Note: we can support more samplers if not every one of them is used at a time, // but it probably doesn't worth it. const MAX_TEXTURE_SLOTS: usize = 16; +const MAX_VERTEX_ATTRIBUTES: usize = 16; impl crate::Api for Api { type Instance = Instance; @@ -159,6 +160,12 @@ enum VertexAttribKind { //Double, // glVertexAttribLPointer } +impl Default for VertexAttribKind { + fn default() -> Self { + Self::Float + } +} + #[derive(Debug)] struct TextureFormatDesc { internal: u32, @@ -296,12 +303,14 @@ pub struct ShaderModule { naga: crate::NagaShader, } +#[derive(Clone, Debug, Default)] struct VertexFormatDesc { element_count: i32, element_format: u32, attrib_kind: VertexAttribKind, } +#[derive(Clone, Debug, Default)] struct AttributeDesc { location: u32, offset: u32, @@ -309,6 +318,14 @@ struct AttributeDesc { format_desc: VertexFormatDesc, } +#[derive(Clone, Debug, Default)] +struct VertexBufferDesc { + raw: glow::Buffer, + offset: wgt::BufferAddress, + step: wgt::InputStepMode, + stride: u32, +} + #[derive(Clone)] struct UniformDesc { location: glow::UniformLocation, @@ -326,13 +343,20 @@ struct PipelineInner { uniforms: Box<[UniformDesc]>, } +struct DepthState { + function: u32, + mask: bool, +} + pub struct RenderPipeline { inner: PipelineInner, //blend_targets: Vec, - attributes: Box<[AttributeDesc]>, - //vertex_buffers: Box<[wgt::VertexBufferLayout]>, + vertex_buffers: Box<[VertexBufferDesc]>, + vertex_attributes: Box<[AttributeDesc]>, primitive: wgt::PrimitiveState, - depth: Option, + depth: Option, + depth_bias: Option, + stencil: Option, } pub struct ComputePipeline { @@ -387,6 +411,50 @@ struct TextureCopyInfo { texel_size: u8, } +#[derive(Clone, Debug, PartialEq)] +struct StencilOps { + pass: u32, + fail: u32, + depth_fail: u32, +} + +impl Default for StencilOps { + fn default() -> Self { + Self { + pass: glow::KEEP, + fail: glow::KEEP, + depth_fail: glow::KEEP, + } + } +} + +#[derive(Clone, Debug, PartialEq)] +struct StencilSide { + function: u32, + mask_read: u32, + mask_write: u32, + reference: u32, + ops: StencilOps, +} + +impl Default for StencilSide { + fn default() -> Self { + Self { + function: glow::ALWAYS, + mask_read: 0xFF, + mask_write: 0xFF, + reference: 0, + ops: StencilOps::default(), + } + } +} + +#[derive(Clone, Default)] +struct StencilState { + front: StencilSide, + back: StencilSide, +} + #[derive(Debug)] enum Command { Draw { @@ -463,7 +531,7 @@ enum Command { dst_target: BindTarget, dst_offset: wgt::BufferAddress, }, - ResetFramebuffer(wgt::Extent3d), + ResetFramebuffer, SetFramebufferAttachment { attachment: u32, view: TextureView, @@ -475,7 +543,24 @@ enum Command { ClearDepth(f32), ClearStencil(u32), BufferBarrier(glow::Buffer, crate::BufferUse), - TextureBarrier(glow::Texture, crate::TextureUse), + TextureBarrier(crate::TextureUse), + SetViewport { + rect: crate::Rect, + depth: Range, + }, + SetScissor(crate::Rect), + SetStencilFunc { + face: u32, + function: u32, + reference: u32, + read_mask: u32, + }, + SetStencilOps { + face: u32, + write_mask: u32, + ops: StencilOps, + }, + SetVertexAttribute(AttributeDesc, VertexBufferDesc), InsertDebugMarker(Range), PushDebugGroup(Range), PopDebugGroup, @@ -489,20 +574,12 @@ pub struct CommandBuffer { data_words: Vec, } -#[derive(Default)] -struct CommandState { - topology: u32, - index_format: wgt::IndexFormat, - index_offset: wgt::BufferAddress, - has_pass_label: bool, -} - //TODO: we would have something like `Arc` // here and in the command buffers. So that everything grows // inside the encoder and stays there until `reset_all`. pub struct CommandEncoder { cmd_buffer: CommandBuffer, - state: CommandState, + state: command::State, private_caps: PrivateCapability, } diff --git a/wgpu-hal/src/gles/queue.rs b/wgpu-hal/src/gles/queue.rs index 0692d86af..3f914b266 100644 --- a/wgpu-hal/src/gles/queue.rs +++ b/wgpu-hal/src/gles/queue.rs @@ -292,7 +292,7 @@ impl super::Queue { gl.bind_buffer(dst_target, Some(dst)); gl.buffer_sub_data_u8_slice(dst_target, dst_offset as i32, query_data); } - C::ResetFramebuffer(extent) => { + C::ResetFramebuffer => { gl.bind_framebuffer(glow::DRAW_FRAMEBUFFER, Some(self.draw_fbo)); gl.framebuffer_texture_2d( glow::DRAW_FRAMEBUFFER, @@ -317,8 +317,6 @@ impl super::Queue { gl.disable(glow::DEPTH_TEST); gl.disable(glow::STENCIL_TEST); gl.disable(glow::SCISSOR_TEST); - gl.scissor(0, 0, extent.width as i32, extent.height as i32); - gl.viewport(0, 0, extent.width as i32, extent.height as i32); } C::SetFramebufferAttachment { attachment, @@ -409,7 +407,7 @@ impl super::Queue { } gl.memory_barrier(flags); } - C::TextureBarrier(_raw, usage) => { + C::TextureBarrier(usage) => { let mut flags = 0; if usage.contains(crate::TextureUse::SAMPLED) { flags |= glow::TEXTURE_FETCH_BARRIER_BIT; @@ -431,6 +429,55 @@ impl super::Queue { } gl.memory_barrier(flags); } + C::SetViewport { + ref rect, + ref depth, + } => { + gl.viewport(rect.x, rect.y, rect.w, rect.h); + gl.depth_range_f32(depth.start, depth.end); + } + C::SetScissor(ref rect) => { + gl.scissor(rect.x, rect.y, rect.w, rect.h); + } + C::SetStencilFunc { + face, + function, + reference, + read_mask, + } => { + gl.stencil_func_separate(face, function, reference as i32, read_mask); + } + C::SetStencilOps { + face, + write_mask, + ref ops, + } => { + gl.stencil_mask_separate(face, write_mask); + gl.stencil_op_separate(face, ops.fail, ops.depth_fail, ops.pass); + } + C::SetVertexAttribute(ref vat, ref vb) => { + gl.bind_buffer(glow::ARRAY_BUFFER, Some(vb.raw)); + let offset = vat.offset as i32 + vb.offset as i32; + match vat.format_desc.attrib_kind { + super::VertexAttribKind::Float => gl.vertex_attrib_pointer_f32( + vat.location, + vat.format_desc.element_count, + vat.format_desc.element_format, + true, // always normalized + vb.stride as i32, + offset, + ), + super::VertexAttribKind::Integer => gl.vertex_attrib_pointer_i32( + vat.location, + vat.format_desc.element_count, + vat.format_desc.element_format, + vb.stride as i32, + offset, + ), + } + gl.vertex_attrib_divisor(vat.location, vb.step as u32); + gl.enable_vertex_attrib_array(vat.location); + } C::InsertDebugMarker(ref range) => { let marker = extract_marker(data_bytes, range); gl.debug_message_insert(