use crate::pipeline::RenderPipeline;
use std::rc::Rc;
use glow::{Context, HasContext};
use crate::buffer::{VertexBuffer, IndexBuffer};
use std::ops::Range;
use crate::binding::UniformGroup;
use crate::access::AccessLock;
use crate::framebuffer::Framebuffer;
use std::convert::TryFrom;
use crate::{Information, Color, Buffer};
pub struct RenderPass<'a> {
pub(crate) context: Rc<Context>,
pub(crate) information: Rc<Information>,
pub(crate) _lock: std::cell::RefMut<'a, ()>,
pub(crate) general_setup: bool,
pub(crate) draw_buffers_setup: bool,
pub(crate) stencil_setup: bool,
pub(crate) blending_setup: bool,
pub(crate) framebuffer_loaded: bool,
pub(crate) pipeline: &'a RenderPipeline,
pub(crate) vertex: Option<&'a VertexBuffer>,
pub(crate) index: Option<&'a IndexBuffer>,
pub(crate) bind: Option<&'a UniformGroup>,
pub(crate) framebuffer: &'a Framebuffer,
pub(crate) stencil_reference: u8,
pub(crate) color_blend_constant: Color,
}
impl<'a> RenderPass<'a> {
pub fn set_vertex_buffer(&mut self, buffer: &'a VertexBuffer) {
let old = self.vertex.replace(buffer);
let updated = match old {
Some(old) if !Rc::ptr_eq(&buffer.inner, &old.inner) => true,
Some(_) => false,
None => true,
};
self.draw_buffers_setup = !updated;
}
pub fn set_index_buffer(&mut self, buffer: &'a IndexBuffer) {
let old = self.index.replace(buffer);
let updated = match old {
Some(old) if !Rc::ptr_eq(&buffer.inner, &old.inner) => true,
Some(_) => false,
None => true,
};
self.draw_buffers_setup = !updated;
}
pub fn set_bind_group(&mut self, group: &'a UniformGroup) {
let old = self.bind.replace(group);
let updated = match old {
Some(old) if !std::ptr::eq(old as *const _, group as *const _) =>
true,
Some(_) => false,
None => true,
};
self.general_setup = !updated;
}
pub fn set_viewport(&mut self, viewport: Viewport) {
let mut width = viewport.width;
if let Some(max_width) = self.information.limits.max_viewport_width {
if viewport.width > max_width {
warn!("Clamping requested viewport width ({}) to the maximum ({})",
viewport.width,
max_width);
width = max_width
}
}
let mut height = viewport.height;
if let Some(max_height) = self.information.limits.max_viewport_height {
if viewport.height > max_height {
warn!("Clamping requested viewport height ({}) to the maximum ({})",
viewport.height,
max_height);
height = max_height
}
}
let gl = self.context.as_ref();
unsafe {
gl.viewport(
viewport.x,
viewport.y,
i32::try_from(width)
.expect("the viewport width must fit in an i32"),
i32::try_from(height)
.expect("the viewport height must fit in an i32"))
}
}
pub fn set_blend_color(&mut self, color: Color) {
self.color_blend_constant = color;
self.blending_setup = false;
}
pub fn set_stencil_reference(&mut self, reference: u8) {
self.stencil_reference = reference;
self.stencil_setup = false;
}
pub fn set_pipeline(&mut self, pipeline: &'a RenderPipeline) {
self.pipeline = pipeline;
self.general_setup = false;
}
unsafe fn ensure_setup(&mut self) {
let gl = self.context.as_ref();
if !self.framebuffer_loaded {
self.framebuffer.bind_and_load(gl);
self.framebuffer_loaded = true;
}
if !self.general_setup {
self.framebuffer.bind(gl);
self.pipeline.bind(gl);
let vertex = self.vertex.map(|vertex| vertex.as_raw_handle());
let index = self.index.map(|index| index.as_raw_handle());
if let Some(binder) = &self.bind {
binder.bind(
gl,
&self.information.features,
&self.pipeline.inner.program)
}
gl.bind_buffer(glow::ARRAY_BUFFER, vertex);
gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, index);
self.general_setup = true;
}
if !self.draw_buffers_setup {
self.pipeline.vertex_array_setup(
gl,
self.vertex,
self.index);
self.draw_buffers_setup = true;
}
if !self.stencil_setup {
self.pipeline.stencil_setup(gl, self.stencil_reference);
self.stencil_setup = true;
}
if !self.blending_setup {
self.pipeline.blending_setup(gl, self.color_blend_constant);
self.blending_setup = true;
}
}
pub fn draw_indexed(
&mut self,
indices: Range<u32>,
instances: u32) {
let _atoms = (
self.pipeline.acquire_read_guarded(),
self.vertex.as_ref().map(|buffer| buffer.acquire_read_guarded()),
self.index.as_ref().map(|buffer| buffer.acquire_read_guarded()),
self.bind.as_ref().map(|bind| bind.acquire_read_guarded()));
self.pipeline.framebuffer_acquire_write(
&self.framebuffer,
!self.information.features.readonly_framebuffer_feedback);
let check_i32 = |val|
i32::try_from(val).expect("value does not fit in an i32, as is \
required by the opengl interface");
unsafe {
self.ensure_setup();
}
let gl = self.context.as_ref();
unsafe {
gl.draw_elements_instanced(
self.pipeline.drawing_mode(),
check_i32(indices.end) - check_i32(indices.start),
self.pipeline.index_type(),
check_i32(indices.start * self.pipeline.index_len()),
check_i32(instances))
}
self.pipeline.framebuffer_release_write(
&self.framebuffer,
!self.information.features.readonly_framebuffer_feedback);
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Viewport {
pub x: i32,
pub y: i32,
pub width: u32,
pub height: u32,
}
pub struct RenderPassDescriptor<'a> {
pub pipeline: &'a RenderPipeline,
pub framebuffer: &'a Framebuffer,
}