mod data;
mod def;
mod driver;
mod model;
mod op;
mod pool;
mod render;
mod spirv {
include!(concat!(env!("OUT_DIR"), "/spirv/mod.rs"));
}
mod swapchain;
mod texture;
pub use self::{
model::{MeshFilter, Model, Pose, Vertex},
op::{Bitmap, Draw, Font, Material, Skydome, Write, WriteMode},
pool::Pool,
render::Render,
swapchain::Swapchain,
texture::Texture,
};
pub(crate) use self::{driver::Driver, op::Op};
use {
self::{
data::{Data, Mapping},
driver::{Device, Image2d, Surface},
op::BitmapOp,
pool::{Lease, PoolRef},
},
crate::{
error::Error,
math::Extent,
pak::{model::Mesh, AnimationId, BitmapId, IndexType, ModelId, Pak},
},
gfx_hal::{
adapter::Adapter, buffer::Usage, device::Device as _, queue::QueueFamily,
window::Surface as _, Instance as _,
},
gfx_impl::{Backend as _Backend, Instance},
num_traits::Num,
std::{
cell::RefCell,
fmt::Debug,
io::{Read, Seek},
rc::Rc,
},
winit::window::Window,
};
#[cfg(debug_assertions)]
use {
num_format::{Locale, ToFormattedString},
std::time::Instant,
};
pub type Texture2d = TextureRef<Image2d>;
pub type BitmapRef = Rc<Bitmap>;
pub type ModelRef = Rc<Model>;
pub(crate) type TextureRef<I> = Rc<RefCell<Texture<I>>>;
type LoadCache = RefCell<Pool>;
type OpCache = RefCell<Option<Vec<Box<dyn Op>>>>;
fn align_down<N: Copy + Num>(size: N, atom: N) -> N {
size - size % atom
}
fn align_up<N: Copy + Num>(size: N, atom: N) -> N {
(size - <N>::one()) - (size - <N>::one()) % atom + atom
}
fn create_instance() -> (Adapter<_Backend>, Instance) {
let instance = Instance::create("attackgoat/screen-13", 1).unwrap();
let mut adapters = instance.enumerate_adapters();
if adapters.is_empty() {
}
let adapter = adapters.remove(0);
(adapter, instance)
}
fn create_surface(window: &Window) -> (Adapter<_Backend>, Surface) {
let (adapter, instance) = create_instance();
let surface = Surface::new(instance, window).unwrap();
(adapter, surface)
}
#[derive(Debug)]
pub struct BadData;
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum BlendMode {
Add,
AlphaAdd,
ColorBurn,
ColorDodge,
Color,
Darken,
DarkerColor,
Difference,
Divide,
Exclusion,
HardLight,
HardMix,
LinearBurn,
Multiply,
Normal,
Overlay,
Screen,
Subtract,
VividLight,
}
impl Default for BlendMode {
fn default() -> Self {
Self::Normal
}
}
#[derive(Default)]
pub struct Cache(PoolRef<Pool>);
pub struct Gpu {
driver: Driver,
loads: LoadCache,
ops: OpCache,
renders: Cache,
}
impl Gpu {
pub(super) fn new(window: &Window) -> (Self, Driver, Surface) {
let (adapter, surface) = create_surface(window);
info!(
"Device: {} ({:?})",
&adapter.info.name, adapter.info.device_type
);
let queue = adapter
.queue_families
.iter()
.find(|family| {
let ty = family.queue_type();
surface.supports_queue_family(family)
&& ty.supports_graphics()
&& ty.supports_compute()
})
.ok_or_else(Error::graphics_queue_family)
.unwrap();
let driver = Driver::new(RefCell::new(
Device::new(adapter.physical_device, queue).unwrap(),
));
let driver_copy = Driver::clone(&driver);
(
Self {
driver,
loads: Default::default(),
ops: Default::default(),
renders: Default::default(),
},
driver_copy,
surface,
)
}
pub fn offscreen() -> Self {
let (adapter, _) = create_instance();
let queue = adapter
.queue_families
.iter()
.find(|family| {
let ty = family.queue_type();
ty.supports_graphics() && ty.supports_compute()
})
.ok_or_else(Error::graphics_queue_family)
.unwrap();
let driver = Driver::new(RefCell::new(
Device::new(adapter.physical_device, queue).unwrap(),
));
Self {
driver,
loads: Default::default(),
ops: Default::default(),
renders: Default::default(),
}
}
pub fn load_indexed_model<
M: IntoIterator<Item = Mesh>,
I: IntoIterator<Item = u32>,
V: IntoIterator<Item = VV>,
VV: Copy + Into<Vertex>,
>(
&self,
#[cfg(feature = "debug-names")] name: &str,
meshes: M,
_indices: I,
_vertices: V,
) -> Result<Model, BadData> {
let meshes = meshes.into_iter().collect::<Vec<_>>();
for mesh in &meshes {
if mesh.vertex_count() % 3 != 0 {
return Err(BadData);
}
}
let mut pool = self.loads.borrow_mut();
let idx_buf_len = 0;
let idx_buf = pool.data_usage(
#[cfg(feature = "debug-names")]
name,
&self.driver,
idx_buf_len,
Usage::INDEX | Usage::STORAGE,
);
let vertex_buf_len = 0;
let vertex_buf = pool.data_usage(
#[cfg(feature = "debug-names")]
name,
&self.driver,
vertex_buf_len,
Usage::VERTEX | Usage::STORAGE,
);
let staging_buf_len = 0;
let staging_buf = pool.data_usage(
#[cfg(feature = "debug-names")]
name,
&self.driver,
staging_buf_len,
Usage::VERTEX | Usage::STORAGE,
);
let write_mask_len = 0;
let write_mask = pool.data_usage(
#[cfg(feature = "debug-names")]
name,
&self.driver,
write_mask_len,
Usage::STORAGE,
);
Ok(Model::new(
meshes,
IndexType::U32,
(idx_buf, idx_buf_len),
(vertex_buf, vertex_buf_len),
(staging_buf, staging_buf_len, write_mask),
))
}
pub fn load_model<
IM: IntoIterator<Item = M>,
IV: IntoIterator<Item = V>,
M: Into<Mesh>,
V: Copy + Into<Vertex>,
>(
&self,
#[cfg(feature = "debug-names")] name: &str,
meshes: IM,
vertices: IV,
) -> Result<Model, BadData> {
let mut meshes = meshes
.into_iter()
.map(|mesh| mesh.into())
.collect::<Vec<_>>();
let vertices = vertices.into_iter().collect::<Vec<_>>();
let mut indices = vec![];
let mut base_vertex = 0;
for mesh in &mut meshes {
let base_idx = indices.len();
let mut cache: Vec<Vertex> = vec![];
let vertex_count = mesh.vertex_count() as usize;
for idx in base_vertex..base_vertex + vertex_count {
let vertex = if let Some(vertex) = vertices.get(idx) {
(*vertex).into()
} else {
return Err(BadData);
};
debug_assert!(vertex.is_finite());
if let Err(idx) = cache.binary_search_by(|probe| probe.cmp(&vertex)) {
cache.insert(idx, vertex);
}
}
for idx in base_vertex..base_vertex + vertex_count {
let vertex = vertices.get(idx).unwrap();
let vertex = (*vertex).into();
let idx = cache.binary_search_by(|probe| probe.cmp(&vertex)).unwrap();
indices.push(idx as _);
}
mesh.indices = base_idx as u32..(base_idx + indices.len()) as u32;
base_vertex += vertex_count;
}
self.load_indexed_model(
#[cfg(feature = "debug-names")]
name,
meshes,
indices,
vertices,
)
}
pub fn read_animation<R: Read + Seek>(
&self,
#[cfg(debug_assertions)] _name: &str,
_pak: &mut Pak<R>,
_id: AnimationId,
) -> ModelRef {
todo!()
}
pub fn read_bitmap<K: AsRef<str>, R: Read + Seek>(
&self,
#[cfg(feature = "debug-names")] name: &str,
pak: &mut Pak<R>,
key: K,
) -> Bitmap {
let id = pak.bitmap_id(key).unwrap();
self.read_bitmap_with_id(
#[cfg(feature = "debug-names")]
name,
pak,
id,
)
}
pub fn read_bitmap_with_id<R: Read + Seek>(
&self,
#[cfg(feature = "debug-names")] name: &str,
pak: &mut Pak<R>,
id: BitmapId,
) -> Bitmap {
let bitmap = pak.read_bitmap(id);
let mut pool = self.loads.borrow_mut();
unsafe {
BitmapOp::new(
#[cfg(feature = "debug-names")]
name,
&self.driver,
&mut pool,
&bitmap,
)
.record()
}
}
pub fn read_font<F: AsRef<str>, R: Read + Seek>(&self, pak: &mut Pak<R>, face: F) -> Font {
#[cfg(debug_assertions)]
debug!("Loading font `{}`", face.as_ref());
Font::load(
&self.driver,
&mut self.loads.borrow_mut(),
pak,
face.as_ref(),
)
}
pub fn read_model<K: AsRef<str>, R: Read + Seek>(
&self,
#[cfg(feature = "debug-names")] name: &str,
pak: &mut Pak<R>,
key: K,
) -> Model {
let id = pak.model_id(key).unwrap();
self.read_model_with_id(
#[cfg(feature = "debug-names")]
name,
pak,
id,
)
}
pub fn read_model_with_id<R: Read + Seek>(
&self,
#[cfg(feature = "debug-names")] name: &str,
pak: &mut Pak<R>,
id: ModelId,
) -> Model {
let mut pool = self.loads.borrow_mut();
let model = pak.read_model(id);
let (idx_buf, idx_buf_len) = {
let src = model.indices();
let len = src.len() as _;
let mut buf = pool.data_usage(
#[cfg(feature = "debug-names")]
name,
&self.driver,
len,
Usage::INDEX | Usage::STORAGE,
);
{
let mut mapped_range = buf.map_range_mut(0..len).unwrap();
mapped_range.copy_from_slice(src);
Mapping::flush(&mut mapped_range).unwrap();
}
(buf, len)
};
let (staging_buf, staging_buf_len) = {
let src = model.vertices();
let len = src.len() as _;
let mut buf = pool.data_usage(
#[cfg(feature = "debug-names")]
name,
&self.driver,
len,
Usage::STORAGE,
);
{
let mut mapped_range = buf.map_range_mut(0..len).unwrap();
mapped_range.copy_from_slice(src);
Mapping::flush(&mut mapped_range).unwrap();
}
(buf, len)
};
let write_mask = {
let src = model.write_mask();
let len = src.len() as _;
let mut buf = pool.data_usage(
#[cfg(feature = "debug-names")]
name,
&self.driver,
len,
Usage::STORAGE,
);
{
let mut mapped_range = buf.map_range_mut(0..len).unwrap();
mapped_range.copy_from_slice(src);
Mapping::flush(&mut mapped_range).unwrap();
}
buf
};
let idx_ty = model.idx_ty();
let mut meshes = model.take_meshes();
let mut vertex_buf_len = 0;
for mesh in &mut meshes {
let stride = if mesh.is_animated() { 80 } else { 48 };
vertex_buf_len += vertex_buf_len % 240;
mesh.set_base_vertex((vertex_buf_len / stride) as _);
vertex_buf_len += mesh.vertex_count() as u64 * stride;
}
let vertex_buf = pool.data_usage(
#[cfg(feature = "debug-names")]
name,
&self.driver,
vertex_buf_len,
Usage::STORAGE | Usage::VERTEX,
);
Model::new(
meshes,
idx_ty,
(idx_buf, idx_buf_len),
(vertex_buf, vertex_buf_len),
(staging_buf, staging_buf_len, write_mask),
)
}
pub fn render<D: Into<Extent>>(
&self,
#[cfg(feature = "debug-names")] name: &str,
dims: D,
) -> Render {
self.render_with_cache(
#[cfg(feature = "debug-names")]
name,
dims,
&self.renders,
)
}
pub fn render_with_cache<D: Into<Extent>>(
&self,
#[cfg(feature = "debug-names")] name: &str,
dims: D,
cache: &Cache,
) -> Render {
let ops = if let Some(ops) = self.ops.borrow_mut().take() {
ops
} else {
Default::default()
};
let pool = if let Some(pool) = cache.0.borrow_mut().pop_back() {
pool
} else {
debug!("Creating new render pool");
Default::default()
};
let pool = Lease::new(pool, &cache.0);
Render::new(
#[cfg(feature = "debug-names")]
name,
&self.driver,
dims.into(),
pool,
ops,
)
}
pub fn resolve(&self, render: Render) -> Lease<Texture2d> {
let (target, ops) = render.resolve();
let mut cache = self.ops.borrow_mut();
if let Some(cache) = cache.as_mut() {
cache.extend(ops);
} else {
cache.replace(ops);
}
target
}
pub(crate) fn wait_idle(&self) {
#[cfg(debug_assertions)]
let started = Instant::now();
self.driver.borrow().wait_idle().unwrap();
#[cfg(debug_assertions)]
{
let elapsed = Instant::now() - started;
debug!(
"Wait for GPU idle took {}ms",
elapsed.as_millis().to_formatted_string(&Locale::en)
);
}
}
}
impl Drop for Gpu {
fn drop(&mut self) {
self.wait_idle();
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum MaskMode {
Add,
Darken,
Difference,
Intersect,
Lighten,
Subtract,
}
impl Default for MaskMode {
fn default() -> Self {
Self::Subtract
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum MatteMode {
Alpha,
AlphaInverted,
Luminance,
LuminanceInverted,
}
impl Default for MatteMode {
fn default() -> Self {
Self::Alpha
}
}