hal/gles: stencil and vertex state

This commit is contained in:
Dzmitry Malyshau 2021-06-24 17:19:26 -04:00 committed by Dzmitry Malyshau
parent 205327d3aa
commit 13b0a61dc8
6 changed files with 377 additions and 41 deletions

View File

@ -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,
};

View File

@ -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<super::Api> 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<super::Api> 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<super::Api> 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<super::Api> 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<super::Api> 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<super::Api> 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<super::Api> 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<f32>, depth: Range<f32>) {
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<u32>) {
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<f32>, depth_range: Range<f32>) {}
unsafe fn set_scissor_rect(&mut self, rect: &crate::Rect<u32>) {}
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<super::Api> 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<super::Api> 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<super::Api> 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::<wgt::DrawIndirectArgs>() as wgt::BufferAddress;
@ -419,6 +574,7 @@ impl crate::CommandEncoder<super::Api> 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<super::Api> 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) {}

View File

@ -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),
},
}
}

View File

@ -518,7 +518,7 @@ impl crate::Device<super::Api> for super::Device {
) -> Result<super::CommandEncoder, crate::DeviceError> {
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<super::Api> 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<super::Api> 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) {

View File

@ -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<pso::ColorBlendDesc>,
attributes: Box<[AttributeDesc]>,
//vertex_buffers: Box<[wgt::VertexBufferLayout]>,
vertex_buffers: Box<[VertexBufferDesc]>,
vertex_attributes: Box<[AttributeDesc]>,
primitive: wgt::PrimitiveState,
depth: Option<wgt::DepthStencilState>,
depth: Option<DepthState>,
depth_bias: Option<wgt::DepthBiasState>,
stencil: Option<StencilState>,
}
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<i32>,
depth: Range<f32>,
},
SetScissor(crate::Rect<i32>),
SetStencilFunc {
face: u32,
function: u32,
reference: u32,
read_mask: u32,
},
SetStencilOps {
face: u32,
write_mask: u32,
ops: StencilOps,
},
SetVertexAttribute(AttributeDesc, VertexBufferDesc),
InsertDebugMarker(Range<u32>),
PushDebugGroup(Range<u32>),
PopDebugGroup,
@ -489,20 +574,12 @@ pub struct CommandBuffer {
data_words: Vec<u32>,
}
#[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<typed_arena::Arena>`
// 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,
}

View File

@ -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(