diff --git a/blade-graphics/src/gles/command.rs b/blade-graphics/src/gles/command.rs index 1a7cf648..2159057e 100644 --- a/blade-graphics/src/gles/command.rs +++ b/blade-graphics/src/gles/command.rs @@ -227,6 +227,17 @@ impl super::PassEncoder<'_, super::RenderPipeline> { ) -> super::PipelineEncoder<'b> { self.commands .push(super::Command::SetProgram(pipeline.inner.program)); + + match &pipeline.inner.color_targets[..] { + &[(blend_state, write_masks)] => self + .commands + .push(super::Command::SetAllColorTargets(blend_state, write_masks)), + separate => self.commands.extend(separate.iter().zip(0..).map( + |(&(blend_state, write_masks), i)| { + super::Command::SetSingleColorTarget(i, blend_state, write_masks) + }, + )), + } super::PipelineEncoder { commands: self.commands, plain_data: self.plain_data, @@ -458,6 +469,38 @@ impl crate::VertexFormat { } } +impl crate::BlendFactor { + fn to_gles(self) -> u32 { + match self { + Self::Zero => glow::ZERO, + Self::One => glow::ONE, + Self::Src => glow::SRC_COLOR, + Self::OneMinusSrc => glow::ONE_MINUS_SRC_COLOR, + Self::SrcAlpha => glow::SRC_ALPHA, + Self::OneMinusSrcAlpha => glow::ONE_MINUS_SRC_ALPHA, + Self::Dst => glow::DST_COLOR, + Self::OneMinusDst => glow::ONE_MINUS_DST_COLOR, + Self::DstAlpha => glow::DST_ALPHA, + Self::OneMinusDstAlpha => glow::ONE_MINUS_DST_ALPHA, + Self::SrcAlphaSaturated => glow::SRC_ALPHA_SATURATE, + Self::Constant => glow::CONSTANT_ALPHA, + Self::OneMinusConstant => glow::ONE_MINUS_CONSTANT_ALPHA, + } + } +} + +impl crate::BlendOperation { + fn to_gles(self) -> u32 { + match self { + Self::Add => glow::FUNC_ADD, + Self::Subtract => glow::FUNC_SUBTRACT, + Self::ReverseSubtract => glow::FUNC_REVERSE_SUBTRACT, + Self::Min => glow::MIN, + Self::Max => glow::MAX, + } + } +} + const CUBEMAP_FACES: [u32; 6] = [ glow::TEXTURE_CUBE_MAP_POSITIVE_X, glow::TEXTURE_CUBE_MAP_NEGATIVE_X, @@ -737,6 +780,48 @@ impl super::Command { Self::SetDrawColorBuffers(count) => { gl.draw_buffers(&COLOR_ATTACHMENTS[..count as usize]); } + Self::SetAllColorTargets(blend, write_mask) => { + if let Some(blend_state) = blend { + gl.enable(glow::BLEND); + gl.blend_func_separate( + blend_state.color.src_factor.to_gles(), + blend_state.color.dst_factor.to_gles(), + blend_state.alpha.src_factor.to_gles(), + blend_state.alpha.dst_factor.to_gles(), + ); + gl.blend_equation(blend_state.color.operation.to_gles()); + } else { + gl.disable(glow::BLEND); + } + gl.color_mask( + write_mask.contains(crate::ColorWrites::RED), + write_mask.contains(crate::ColorWrites::GREEN), + write_mask.contains(crate::ColorWrites::BLUE), + write_mask.contains(crate::ColorWrites::ALPHA), + ); + } + Self::SetSingleColorTarget(i, blend, write_mask) => { + if let Some(blend_state) = blend { + gl.enable_draw_buffer(glow::BLEND, i); + gl.blend_func_separate_draw_buffer( + i, + blend_state.color.src_factor.to_gles(), + blend_state.color.dst_factor.to_gles(), + blend_state.alpha.src_factor.to_gles(), + blend_state.alpha.dst_factor.to_gles(), + ); + gl.blend_equation_draw_buffer(i, blend_state.color.operation.to_gles()); + } else { + gl.disable_draw_buffer(glow::BLEND, i); + } + gl.color_mask_draw_buffer( + i, + write_mask.contains(crate::ColorWrites::RED), + write_mask.contains(crate::ColorWrites::GREEN), + write_mask.contains(crate::ColorWrites::BLUE), + write_mask.contains(crate::ColorWrites::ALPHA), + ); + } Self::ClearColor { draw_buffer, color, @@ -813,7 +898,7 @@ impl super::Command { gl.use_program(None); } //SetPrimitive(PrimitiveState), - Self::SetBlendConstant(constant) => unimplemented!(), + Self::SetBlendConstant([r, g, b, a]) => gl.blend_color(r, g, b, a), Self::SetColorTarget { draw_buffer_index, //desc: ColorTargetDesc, diff --git a/blade-graphics/src/gles/egl.rs b/blade-graphics/src/gles/egl.rs index 64bb45ae..0a923f64 100644 --- a/blade-graphics/src/gles/egl.rs +++ b/blade-graphics/src/gles/egl.rs @@ -810,6 +810,16 @@ impl EglContext { super::Capabilities::BUFFER_STORAGE, extensions.contains("GL_EXT_buffer_storage"), ); + capabilities.set( + super::Capabilities::DRAW_BUFFERS_INDEXED, + if gl.version().is_embedded { + (gl.version().major, gl.version().minor) >= (3, 2) + } else { + (gl.version().major, gl.version().minor) >= (3, 0) + }, + // glow uses unsuffixed functions like glEnablei instead of glEnableiEXT. + // Therefore, GL_EXT_draw_buffers_indexed is not sufficient. + ); let limits = super::Limits { uniform_buffer_alignment: gl.get_parameter_i32(glow::UNIFORM_BUFFER_OFFSET_ALIGNMENT) diff --git a/blade-graphics/src/gles/mod.rs b/blade-graphics/src/gles/mod.rs index 40ff7eff..213f1b1d 100644 --- a/blade-graphics/src/gles/mod.rs +++ b/blade-graphics/src/gles/mod.rs @@ -15,6 +15,7 @@ const DEBUG_ID: u32 = 0; bitflags::bitflags! { struct Capabilities: u32 { const BUFFER_STORAGE = 1 << 0; + const DRAW_BUFFERS_INDEXED = 1 << 1; } } @@ -100,6 +101,7 @@ struct PipelineInner { program: glow::Program, group_mappings: Box<[ShaderDataMapping]>, vertex_attribute_infos: Box<[VertexAttributeInfo]>, + color_targets: Box<[(Option, crate::ColorWrites)]>, } pub struct ComputePipeline { @@ -253,6 +255,8 @@ enum Command { }, InvalidateAttachment(u32), SetDrawColorBuffers(u8), + SetAllColorTargets(Option, crate::ColorWrites), + SetSingleColorTarget(u32, Option, crate::ColorWrites), ClearColor { draw_buffer: u32, color: crate::TextureColor, diff --git a/blade-graphics/src/gles/pipeline.rs b/blade-graphics/src/gles/pipeline.rs index 4531ad4c..9ae3cfb7 100644 --- a/blade-graphics/src/gles/pipeline.rs +++ b/blade-graphics/src/gles/pipeline.rs @@ -1,6 +1,22 @@ use glow::HasContext as _; use naga::back::glsl; +fn separate(mut iter: impl Iterator) -> bool { + if let Some(first) = iter.next() { + iter.all(|el| el == first) + } else { + false + } +} + +fn conflate(iter: impl Iterator + Clone) -> Box<[T]> { + if separate(iter.clone()) { + iter.collect() + } else { + iter.take(1).collect() + } +} + impl super::Context { unsafe fn create_pipeline( &self, @@ -276,6 +292,7 @@ impl super::Context { program, group_mappings, vertex_attribute_infos: attributes.into_boxed_slice(), + color_targets: Box::new([]), } } @@ -303,7 +320,7 @@ impl super::Context { } else { glsl::WriterFlags::empty() }; - let inner = unsafe { + let mut inner = unsafe { self.create_pipeline( &[desc.vertex, desc.fragment], desc.data_layouts, @@ -312,6 +329,19 @@ impl super::Context { extra_flags, ) }; + + inner.color_targets = conflate(desc.color_targets.iter().map(|t| (t.blend, t.write_mask))); + + if !self + .capabilities + .contains(super::Capabilities::DRAW_BUFFERS_INDEXED) + { + assert!( + inner.color_targets.len() <= 1, + "separate blend states or write masks of multiple color targets are not supported" + ); + } + super::RenderPipeline { inner, topology: desc.primitive.topology,