use std::{
cell::RefCell,
mem::MaybeUninit,
sync::{
atomic::{AtomicU32, Ordering},
Arc, Condvar, Mutex, Once,
},
task::Waker,
};
pub use pix::Raster;
pub use pix::Region;
pub mod color {
pub use pix::bgr::*;
pub use pix::cmy::*;
pub use pix::gray::*;
pub use pix::hsl::*;
pub use pix::hsv::*;
pub use pix::hwb::*;
pub use pix::matte::*;
pub use pix::rgb::*;
pub use pix::ycc::*;
}
pub(super) enum GpuCmd {
Background(f32, f32, f32),
Draw(u32, u32),
DrawGraphic(u32, u32, u32),
SetCamera(Transform),
SetTint(u32, [f32; 4]),
RasterId(pix::Raster<pix::rgb::SRgba8>, u32),
ShaderId(ShaderBuilder, u32),
ShapeId(ShapeBuilder, u32, u32),
GroupId(u32),
GroupWrite(u32, u32, u32, Transform),
GroupWriteTex(u32, u32, u32, Transform, ([f32; 2], [f32; 2])),
}
pub(super) struct FrameInternal {
pub(super) waker: Option<Waker>,
pub(super) frame: Option<(std::time::Duration, f32, bool)>,
}
type Location = Vec<(usize, usize)>;
pub(super) struct Internal {
pub(super) cmds: Mutex<Vec<GpuCmd>>,
pub(super) frame: Mutex<FrameInternal>,
pub(super) pair: Arc<(Mutex<bool>, Condvar)>,
raster_garbage: Mutex<Vec<u32>>,
rasters: RefCell<Vec<window::RasterId>>,
shader_garbage: Mutex<Vec<u32>>,
shaders: RefCell<Vec<window::Shader>>,
shape_garbage: Mutex<Vec<u32>>,
shapes: RefCell<Vec<window::Shape>>,
group_garbage: Mutex<Vec<u32>>,
groups: RefCell<Vec<(window::Group, Location)>>,
}
static mut INTERNAL: MaybeUninit<Internal> = MaybeUninit::uninit();
static INIT: Once = Once::new();
static NEXT_RASTER_ID: AtomicU32 = AtomicU32::new(0);
static NEXT_SHADER_ID: AtomicU32 = AtomicU32::new(0);
static NEXT_SHAPE_ID: AtomicU32 = AtomicU32::new(0);
static NEXT_GROUP_ID: AtomicU32 = AtomicU32::new(0);
impl Internal {
pub(super) fn new_lazy() -> &'static Self {
#[allow(clippy::mutex_atomic)]
unsafe {
INIT.call_once(|| {
INTERNAL = MaybeUninit::new(Internal {
cmds: Mutex::new(Vec::new()),
frame: Mutex::new(FrameInternal {
waker: None,
frame: None,
}),
pair: Arc::new((Mutex::new(false), Condvar::new())),
raster_garbage: Mutex::new(Vec::new()),
rasters: RefCell::new(Vec::new()),
shader_garbage: Mutex::new(Vec::new()),
shaders: RefCell::new(Vec::new()),
shape_garbage: Mutex::new(Vec::new()),
shapes: RefCell::new(Vec::new()),
group_garbage: Mutex::new(Vec::new()),
groups: RefCell::new(Vec::new()),
});
});
&*INTERNAL.as_ptr()
}
}
}
pub struct Texture(pub(super) u32);
impl Texture {
pub fn new<P: pix::el::Pixel>(raster: &pix::Raster<P>) -> Self
where
pix::chan::Ch8: From<<P as pix::el::Pixel>::Chan>,
{
let internal = Internal::new_lazy();
let id = if let Some(id) = internal.raster_garbage.lock().unwrap().pop()
{
id
} else {
NEXT_RASTER_ID.fetch_add(1, Ordering::Relaxed)
};
let raster = pix::Raster::<pix::rgb::SRgba8>::with_raster(&raster);
let mut lock = internal.cmds.lock().unwrap();
lock.push(GpuCmd::RasterId(raster, id));
Texture(id)
}
}
impl Drop for Texture {
fn drop(&mut self) {
let internal = Internal::new_lazy();
internal.raster_garbage.lock().unwrap().push(self.0);
}
}
pub struct Shader(pub(super) u32);
impl Shader {
pub fn new(builder: ShaderBuilder) -> Shader {
let internal = Internal::new_lazy();
let id = if let Some(id) = internal.shader_garbage.lock().unwrap().pop()
{
id
} else {
NEXT_SHADER_ID.fetch_add(1, Ordering::Relaxed)
};
let mut lock = internal.cmds.lock().unwrap();
lock.push(GpuCmd::ShaderId(builder, id));
Shader(id)
}
}
impl Drop for Shader {
fn drop(&mut self) {
let internal = Internal::new_lazy();
internal.shader_garbage.lock().unwrap().push(self.0);
}
}
pub struct Shape(u32);
impl Drop for Shape {
fn drop(&mut self) {
let internal = Internal::new_lazy();
internal.shape_garbage.lock().unwrap().push(self.0);
}
}
pub struct Group(pub(crate) u32);
impl Default for Group {
fn default() -> Self {
Group::new()
}
}
impl Group {
pub fn new() -> Self {
let internal = Internal::new_lazy();
let id = if let Some(id) = internal.group_garbage.lock().unwrap().pop()
{
id
} else {
NEXT_GROUP_ID.fetch_add(1, Ordering::Relaxed)
};
let mut lock = internal.cmds.lock().unwrap();
lock.push(GpuCmd::GroupId(id));
Group(id)
}
pub fn write(&mut self, id: u32, shape: &Shape, transform: &Transform) {
let internal = Internal::new_lazy();
let mut lock = internal.cmds.lock().unwrap();
lock.push(GpuCmd::GroupWrite(self.0, id, shape.0, *transform));
}
pub fn write_tex(
&mut self,
id: u32,
shape: &Shape,
transform: &Transform,
tex_coords: ([f32; 2], [f32; 2]),
) {
let internal = Internal::new_lazy();
let mut lock = internal.cmds.lock().unwrap();
lock.push(GpuCmd::GroupWriteTex(
self.0, id, shape.0, *transform, tex_coords,
));
}
}
impl Drop for Group {
fn drop(&mut self) {
let internal = Internal::new_lazy();
internal.group_garbage.lock().unwrap().push(self.0);
}
}
static ASPECT: AtomicU32 = AtomicU32::new(0);
fn async_runner(window: &mut window::Window, elapsed: std::time::Duration) {
let aspect = window.aspect();
let new_aspect = u32::from_ne_bytes(aspect.to_ne_bytes());
let old_aspect = ASPECT.swap(new_aspect, Ordering::Relaxed);
let resized = new_aspect != old_aspect;
let pair = {
let internal = Internal::new_lazy();
internal.pair.clone()
};
let (lock, cvar) = &*pair;
*lock.lock().unwrap() = false;
{
let internal = Internal::new_lazy();
let mut lock = internal.frame.lock().unwrap();
lock.waker.take().unwrap().wake();
lock.frame = Some((elapsed, aspect, resized));
}
let mut started = lock.lock().unwrap();
while !*started {
started = cvar.wait(started).unwrap();
}
let mut lock = Internal::new_lazy().cmds.lock().unwrap();
for cmd in lock.drain(..) {
use GpuCmd::*;
match cmd {
Background(r, g, b) => window.background(r, g, b),
Draw(shader, group) => {
let shaders = Internal::new_lazy().shaders.borrow();
let groups = Internal::new_lazy().groups.borrow();
window
.draw(&shaders[shader as usize], &groups[group as usize].0);
}
DrawGraphic(shader, group, raster) => {
let shaders = Internal::new_lazy().shaders.borrow();
let groups = Internal::new_lazy().groups.borrow();
window.draw_graphic(
&shaders[shader as usize],
&groups[group as usize].0,
&Internal::new_lazy().rasters.borrow()[raster as usize],
);
}
SetCamera(camera) => {
window.camera(camera);
}
SetTint(shader, tint) => {
let shaders = Internal::new_lazy().shaders.borrow();
window.tint(&shaders[shader as usize], tint);
}
RasterId(raster, id) => {
let gpu_raster = window.graphic(
raster.as_u8_slice(),
raster.width() as usize,
raster.height() as usize,
);
let mut rasters = Internal::new_lazy().rasters.borrow_mut();
if id as usize == rasters.len() {
rasters.push(gpu_raster);
} else {
rasters[id as usize] = gpu_raster;
}
}
ShaderId(shader, id) => {
let shader = window.shader_new(shader);
let mut shaders = Internal::new_lazy().shaders.borrow_mut();
if id as usize == shaders.len() {
shaders.push(shader);
} else {
shaders[id as usize] = shader;
}
}
ShapeId(shape_builder, id, shader) => {
let mut shapes = Internal::new_lazy().shapes.borrow_mut();
let mut shaders = Internal::new_lazy().shaders.borrow_mut();
let mut shape =
window::ShapeBuilder::new(&mut shaders[shader as usize]);
for face in shape_builder.faces {
if let Some(vertices) = face.vertices {
shape = shape.vert(vertices.as_slice());
}
if let Some(transform) = face.transform {
shape = shape.face(transform);
}
}
if id as usize == shapes.len() {
shapes.push(shape.finish());
} else {
shapes[id as usize] = shape.finish();
}
}
GroupId(id) => {
let mut groups = Internal::new_lazy().groups.borrow_mut();
if id as usize == groups.len() {
groups.push((window.group_new(), Vec::new()));
} else {
groups[id as usize] = (window.group_new(), Vec::new());
}
}
GroupWrite(group, id, shape, transform) => {
let mut groups = Internal::new_lazy().groups.borrow_mut();
let shapes = Internal::new_lazy().shapes.borrow();
if id >= groups[group as usize].1.len() as u32 {
let location = if id == 0 {
(0, 0)
} else {
groups[group as usize].1[id as usize - 1]
};
let location = groups[group as usize].0.write(
location,
&shapes[shape as usize],
&transform,
);
groups[group as usize].1.push(location);
} else {
let location = if id == 0 {
(0, 0)
} else {
groups[group as usize].1[id as usize - 1]
};
groups[group as usize].1[id as usize] = groups
[group as usize]
.0
.write(location, &shapes[shape as usize], &transform);
}
}
GroupWriteTex(group, id, shape, transform, texcoords) => {
let mut groups = Internal::new_lazy().groups.borrow_mut();
let shapes = Internal::new_lazy().shapes.borrow();
if id >= groups[group as usize].1.len() as u32 {
let location = if id == 0 {
(0, 0)
} else {
groups[group as usize].1[id as usize - 1]
};
let location = groups[group as usize].0.write_tex(
location,
&shapes[shape as usize],
&transform,
texcoords,
);
groups[group as usize].1.push(location);
} else {
let location = if id == 0 {
(0, 0)
} else {
groups[group as usize].1[id as usize - 1]
};
groups[group as usize].1[id as usize] =
groups[group as usize].0.write_tex(
location,
&shapes[shape as usize],
&transform,
texcoords,
);
}
}
}
}
}
pub fn draw_thread() {
let fallback_window_title = env!("CARGO_PKG_NAME");
let mut window = window::Window::new(fallback_window_title, async_runner);
loop {
window.run();
}
}
pub use window::{shader, ShaderBuilder, Transform};
struct Face {
vertices: Option<Vec<f32>>,
transform: Option<Transform>,
}
pub struct ShapeBuilder {
faces: Vec<Face>,
}
impl Default for ShapeBuilder {
fn default() -> Self {
ShapeBuilder::new()
}
}
impl ShapeBuilder {
pub fn new() -> Self {
ShapeBuilder { faces: Vec::new() }
}
pub fn vert(mut self, vertices: &[f32]) -> Self {
self.faces.push(Face {
vertices: Some(vertices.to_vec()),
transform: None,
});
self
}
pub fn face(mut self, transform: Transform) -> Self {
if let Some(mut face) = self.faces.pop() {
if face.transform.is_none() {
face.transform = Some(transform);
self.faces.push(face);
} else {
self.faces.push(face);
self.faces.push(Face {
vertices: None,
transform: Some(transform),
});
}
} else {
panic!("Can't have a face without vertices!");
}
self
}
pub fn finish(self, shader: &Shader) -> Shape {
let internal = Internal::new_lazy();
let id = if let Some(id) = internal.shape_garbage.lock().unwrap().pop()
{
id
} else {
NEXT_SHAPE_ID.fetch_add(1, Ordering::Relaxed)
};
let mut lock = internal.cmds.lock().unwrap();
lock.push(GpuCmd::ShapeId(self, id, shader.0));
Shape(id)
}
}
pub trait Canvas {
fn draw(&mut self, shader: &Shader, group: &Group);
fn set_camera(&mut self, camera: Transform);
fn set_tint<P: pix::el::Pixel>(&mut self, shader: &Shader, tint: P)
where
pix::chan::Ch32: From<<P as pix::el::Pixel>::Chan>;
fn draw_graphic(
&mut self,
shader: &Shader,
group: &Group,
graphic: &Texture,
);
fn elapsed(&self) -> std::time::Duration;
fn height(&self) -> f32;
fn resized(&self) -> bool;
}