use smallvec::SmallVec;
use crate::texture::Texture;
use crate::access::{UnitAccessLock, AccessLock};
use glow::{HasContext, Context};
use std::rc::Rc;
#[derive(Debug)]
pub(crate) struct InnerFramebuffer {
pub(crate) context: Rc<Context>,
pub(crate) access: UnitAccessLock,
pub(crate) color_attachments: SmallVec<[Texture; 32]>,
pub(crate) depth_stencil: Option<Texture>,
pub(crate) framebuffer: <Context as HasContext>::Framebuffer,
pub(crate) color_load_op: LoadOp<Color>,
pub(crate) depth_load_op: LoadOp<f32>,
pub(crate) stencil_load_op: LoadOp<u8>,
}
impl AccessLock for InnerFramebuffer {
fn write_locks(&self) -> usize {
let a = self.access.write_locks();
for texture in &self.color_attachments { assert_eq!(texture.write_locks(), a) }
for texture in &self.depth_stencil { assert_eq!(texture.write_locks(), a) }
a
}
fn read_locks(&self) -> usize {
let a = self.access.read_locks();
for texture in &self.color_attachments { assert_eq!(texture.read_locks(), a) }
for texture in &self.depth_stencil { assert_eq!(texture.read_locks(), a) }
a
}
fn acquire_write(&self) {
self.access.acquire_write();
for texture in &self.color_attachments { texture.acquire_write(); }
for texture in &self.depth_stencil { texture.acquire_write(); }
}
fn release_write(&self) {
self.access.release_write();
for texture in &self.color_attachments { texture.release_write(); }
for texture in &self.depth_stencil { texture.release_write(); }
}
fn acquire_read(&self) {
self.access.acquire_read();
for texture in &self.color_attachments { texture.acquire_read(); }
for texture in &self.depth_stencil { texture.acquire_read(); }
}
fn release_read(&self) {
self.access.release_read();
for texture in &self.color_attachments { texture.release_read(); }
for texture in &self.depth_stencil { texture.release_read(); }
}
}
impl Drop for InnerFramebuffer {
fn drop(&mut self) {
unsafe {
let _atom = self.access.acquire_write_guarded();
self.context.delete_framebuffer(self.framebuffer);
}
}
}
#[derive(Debug)]
pub(crate) enum FramebufferVariants {
Default {
color_load_op: LoadOp<Color>,
depth_load_op: LoadOp<f32>,
stencil_load_op: LoadOp<u8>,
},
Custom {
inner: Rc<InnerFramebuffer>
}
}
#[derive(Debug)]
pub struct Framebuffer {
pub(crate) variants: FramebufferVariants
}
impl Framebuffer {
pub(crate) unsafe fn bind(&self, gl: &Context) {
match &self.variants {
FramebufferVariants::Default { .. } => {
gl.bind_framebuffer(glow::FRAMEBUFFER, None);
},
FramebufferVariants::Custom { inner } => {
gl.bind_framebuffer(glow::FRAMEBUFFER, Some(inner.framebuffer));
}
};
}
pub(crate) unsafe fn bind_and_load(&self, gl: &Context) {
let (color, depth, stencil) = match &self.variants {
FramebufferVariants::Default {
color_load_op,
depth_load_op,
stencil_load_op } => {
gl.bind_framebuffer(glow::FRAMEBUFFER, None);
(*color_load_op, *depth_load_op, *stencil_load_op)
},
FramebufferVariants::Custom { inner } => {
gl.bind_framebuffer(glow::FRAMEBUFFER, Some(inner.framebuffer));
(
inner.color_load_op,
inner.depth_load_op,
inner.stencil_load_op
)
}
};
let mut mask = 0;
if let LoadOp::Clear(color) = color {
gl.clear_color(color.red, color.green, color.blue, color.alpha);
mask |= glow::COLOR_BUFFER_BIT;
}
if let LoadOp::Clear(depth) = depth {
gl.clear_depth_f32(depth);
mask |= glow::DEPTH_BUFFER_BIT;
}
if let LoadOp::Clear(stencil) = stencil {
gl.clear_stencil(i32::from(stencil));
mask |= glow::STENCIL_BUFFER_BIT;
}
if mask != 0 {
gl.clear(mask);
}
}
}
impl AccessLock for Framebuffer {
fn write_locks(&self) -> usize {
if let FramebufferVariants::Custom { inner } = &self.variants {
inner.write_locks()
} else { 0 }
}
fn read_locks(&self) -> usize {
if let FramebufferVariants::Custom { inner } = &self.variants {
inner.read_locks()
} else { 0 }
}
fn acquire_write(&self) {
if let FramebufferVariants::Custom { inner } = &self.variants {
inner.acquire_write()
}
}
fn release_write(&self) {
if let FramebufferVariants::Custom { inner } = &self.variants {
inner.release_write()
}
}
fn acquire_read(&self) {
if let FramebufferVariants::Custom { inner } = &self.variants {
inner.acquire_read()
}
}
fn release_read(&self) {
if let FramebufferVariants::Custom { inner } = &self.variants {
inner.release_read()
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
pub struct DefaultFramebufferDescriptor {
pub color_load_op: LoadOp<Color>,
pub depth_load_op: LoadOp<f32>,
pub stencil_load_op: LoadOp<u8>,
}
#[derive(Debug, Copy, Clone)]
pub struct FramebufferDescriptor<'a> {
pub color_attachments: &'a [FramebufferColorAttachmentDescriptor<'a>],
pub depth_stencil_attachment: Option<FramebufferDepthStencilAttachmentDescriptor<'a>>
}
#[derive(Debug, Copy, Clone)]
pub struct FramebufferColorAttachmentDescriptor<'a> {
pub attachment: &'a Texture,
pub load_op: LoadOp<Color>
}
#[derive(Debug, Copy, Clone)]
pub struct FramebufferDepthStencilAttachmentDescriptor<'a> {
pub attachment: &'a Texture,
pub depth_load_op: LoadOp<f32>,
pub stencil_load_op: LoadOp<u8>,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum LoadOp<T> {
Clear(T),
Load,
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
pub struct Color {
pub red: f32,
pub green: f32,
pub blue: f32,
pub alpha: f32,
}
#[derive(Debug, thiserror::Error)]
pub enum FramebufferError {
#[error("could not create framebuffer object: {what}")]
CreationError {
what: String
},
}