use super::*;
#[non_exhaustive]
pub enum BeginArgs<'a> {
BackBuffer {
viewport: cvmath::Bounds2<i32>,
},
Immediate {
viewport: cvmath::Bounds2<i32>,
color: &'a [Texture2D],
levels: Option<&'a [u8]>,
depth: Texture2D,
},
}
#[derive(Default)]
pub struct ClearArgs {
pub scissor: Option<cvmath::Bounds2<i32>>,
pub color: Option<cvmath::Vec4f>,
pub depth: Option<f32>,
pub stencil: Option<u8>,
}
#[macro_export]
macro_rules! clear {
($g:expr $(, $field:ident : $value:expr )* $(,)? ) => {
$g.clear(&$crate::ClearArgs {
$( $field: Some($value), )*
..Default::default()
});
};
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct DrawMask {
pub red: bool,
pub green: bool,
pub blue: bool,
pub alpha: bool,
pub depth: bool,
pub stencil: u8,
}
impl DrawMask {
pub const ALL: Self = Self { red: true, green: true, blue: true, alpha: true, depth: true, stencil: u8::MAX };
pub const COLOR: Self = Self { red: true, green: true, blue: true, alpha: true, depth: false, stencil: 0 };
pub const DEPTH: Self = Self { red: false, green: false, blue: false, alpha: false, depth: true, stencil: 0 };
pub const STENCIL: Self = Self { red: false, green: false, blue: false, alpha: false, depth: false, stencil: u8::MAX };
pub const NONE: Self = Self { red: false, green: false, blue: false, alpha: false, depth: false, stencil: 0 };
}
impl ops::BitOr<DrawMask> for DrawMask {
type Output = Self;
#[inline]
fn bitor(self, rhs: Self) -> Self::Output {
Self {
red: self.red || rhs.red,
green: self.green || rhs.green,
blue: self.blue || rhs.blue,
alpha: self.alpha || rhs.alpha,
depth: self.depth || rhs.depth,
stencil: self.stencil | rhs.stencil,
}
}
}
pub struct DrawVertexBuffer {
pub buffer: VertexBuffer,
pub divisor: VertexDivisor,
}
pub struct DrawArgs<'a> {
pub scissor: Option<cvmath::Bounds2<i32>>,
pub blend_mode: BlendMode,
pub depth_test: Option<Compare>,
pub cull_mode: Option<CullMode>,
pub mask: DrawMask,
pub prim_type: PrimType,
pub shader: ShaderProgram,
pub uniforms: &'a [&'a dyn UniformVisitor],
pub vertices: &'a [DrawVertexBuffer],
pub vertex_start: u32,
pub vertex_end: u32,
pub instances: i32,
}
pub struct DrawIndexedArgs<'a> {
pub scissor: Option<cvmath::Bounds2<i32>>,
pub blend_mode: BlendMode,
pub depth_test: Option<Compare>,
pub cull_mode: Option<CullMode>,
pub mask: DrawMask,
pub prim_type: PrimType,
pub shader: ShaderProgram,
pub uniforms: &'a [&'a dyn UniformVisitor],
pub vertices: &'a [DrawVertexBuffer],
pub indices: IndexBuffer,
pub index_start: u32,
pub index_end: u32,
pub instances: i32,
}
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
pub struct DrawMetrics {
pub draw_duration: time::Duration,
pub draw_call_count: u32,
pub vertex_count: u32,
pub bytes_uploaded: usize,
pub bytes_downloaded: usize,
}
pub trait IGraphics {
fn get_type(&self, object: BaseObject) -> Option<ObjectType>;
fn add_ref(&mut self, object: BaseObject);
fn release(&mut self, object: BaseObject) -> u32;
fn begin(&mut self, args: &BeginArgs);
fn clear(&mut self, args: &ClearArgs);
fn draw(&mut self, args: &DrawArgs);
fn draw_indexed(&mut self, args: &DrawIndexedArgs);
fn end(&mut self);
fn get_draw_metrics(&mut self, reset: bool) -> DrawMetrics;
fn vertex_buffer_create(&mut self, size: usize, layout: &'static VertexLayout, usage: BufferUsage) -> VertexBuffer;
fn vertex_buffer_write(&mut self, id: VertexBuffer, offset: usize, data: &[u8]);
fn index_buffer_create(&mut self, size: usize, index_ty: IndexType, usage: BufferUsage) -> IndexBuffer;
fn index_buffer_write(&mut self, id: IndexBuffer, offset: usize, data: &[u8]);
fn shader_compile(&mut self, vertex_source: &str, fragment_source: &str) -> ShaderProgram;
fn texture2d_create(&mut self, info: &Texture2DInfo) -> Texture2D;
fn texture2d_get_info(&self, id: Texture2D) -> Option<&Texture2DInfo>;
fn texture2d_generate_mipmap(&mut self, id: Texture2D);
fn texture2d_update(&mut self, id: Texture2D, info: &Texture2DInfo) -> Texture2D;
fn texture2d_write(&mut self, id: Texture2D, level: u8, data: &[u8]);
fn texture2d_read_into(&mut self, id: Texture2D, level: u8, data: &mut [u8]);
}
#[repr(transparent)]
pub struct Graphics {
inner: dyn IGraphics,
}
#[allow(non_snake_case)]
#[inline]
pub fn Graphics(g: &mut dyn IGraphics) -> &mut Graphics {
unsafe { mem::transmute(g) }
}
impl ops::Deref for Graphics {
type Target = dyn IGraphics;
#[inline]
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl ops::DerefMut for Graphics {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
impl Graphics {
#[inline]
pub fn try_cast<T>(&self, object: impl Into<BaseObject>) -> Option<T> where BaseObject: ObjectCast<T> {
object.into().try_cast(self)
}
#[inline]
pub fn add_ref(&mut self, object: impl Into<BaseObject>) {
self.inner.add_ref(object.into())
}
#[inline]
pub fn release(&mut self, object: impl Into<BaseObject>) -> u32 {
self.inner.release(object.into())
}
#[inline]
pub fn image<F: ImageToTexture>(&mut self, image: &F) -> Texture2D {
let info = image.info();
let data = image.data();
let tex = self.texture2d_create(&info);
self.texture2d_write(tex, 0, data);
self.texture2d_generate_mipmap(tex);
return tex;
}
pub fn animated_image(&mut self, image: &image::AnimatedImage, props: &TextureProps) -> AnimatedTexture2D {
let mut frames = Vec::with_capacity(image.frames.len());
for frame in &image.frames {
let tex = {
let info = Texture2DInfo {
format: TextureFormat::SRGBA8,
width: frame.width,
height: frame.height,
props: *props,
};
let tex = self.texture2d_create(&info);
self.texture2d_write(tex, 0, frame.as_bytes());
self.texture2d_generate_mipmap(tex);
tex
};
frames.push(tex);
}
let length = image.delays.iter().sum();
AnimatedTexture2D {
width: image.width,
height: image.height,
frames,
length,
repeat: image.repeat,
}
}
#[inline]
pub fn texture2d(&mut self, info: &Texture2DInfo, data: &[u8]) -> Texture2D {
let texture = self.texture2d_create(info);
self.texture2d_write(texture, 0, data);
self.texture2d_generate_mipmap(texture);
return texture;
}
pub fn texture2d_read<T: dataview::Pod>(&mut self, id: Texture2D, level: u8) -> image::Image<T> {
let info = self.texture2d_get_info(id).unwrap();
let (mip_width, mip_height, byte_size) = info.mip_size(level);
assert!(byte_size % mem::size_of::<T>() == 0, "Texture2D level={level} byte_size={byte_size} is not a multiple of {}", mem::size_of::<T>());
let nelements = byte_size / mem::size_of::<T>();
let mut vec = Vec::with_capacity(nelements);
let data = unsafe { slice::from_raw_parts_mut(vec.as_mut_ptr() as *mut u8, byte_size) };
self.texture2d_read_into(id, level, data);
unsafe { vec.set_len(nelements) };
return image::Image {
width: mip_width,
height: mip_height,
data: vec,
};
}
#[inline]
pub fn vertex_buffer<T: TVertex>(&mut self, data: &[T], usage: BufferUsage) -> VertexBuffer {
let this = &mut self.inner;
let id = this.vertex_buffer_create(mem::size_of_val(data), T::LAYOUT, usage);
this.vertex_buffer_write(id, 0, dataview::bytes(data));
return id;
}
#[inline]
pub fn vertex_buffer_write<T: TVertex>(&mut self, id: VertexBuffer, offset: usize, data: &[T]) {
self.inner.vertex_buffer_write(id, offset, dataview::bytes(data))
}
#[inline]
pub fn index_buffer<T: TIndices + ?Sized>(&mut self, data: &T, _nverts: T::Index, usage: BufferUsage) -> IndexBuffer {
let data = data.as_indices();
#[cfg(debug_assertions)]
if _nverts != Default::default() {
for i in 0..data.len() {
if data[i] >= _nverts {
panic!("Index {:?} out of bounds for {:?} vertices", data[i], _nverts);
}
}
}
let this = &mut self.inner;
let id = this.index_buffer_create(mem::size_of_val(data), T::Index::TYPE, usage);
this.index_buffer_write(id, 0, dataview::bytes(data));
return id;
}
#[inline]
pub fn index_buffer_write<T: TIndex>(&mut self, id: IndexBuffer, offset: usize, data: &[T]) {
self.inner.index_buffer_write(id, offset, dataview::bytes(data))
}
}