use {
crate::{
color::Rgba,
context::Error,
draw::Draw,
format::Format,
layer::{Layer, SetLayer},
texture::{CopyBuffer, CopyTexture, DrawTexture},
},
std::sync::atomic::{self, AtomicUsize},
wgpu::{Color, CommandEncoder, Device, Instance, LoadOp, Queue, TextureView},
};
#[cfg(feature = "winit")]
use wgpu::Adapter;
pub(crate) struct State {
#[cfg(feature = "winit")]
adapter: Adapter,
device: Device,
queue: Queue,
shader_ids: AtomicUsize,
}
impl State {
pub async fn new(instance: &Instance) -> Result<Self, Error> {
let adapter = {
use wgpu::{PowerPreference, RequestAdapterOptions};
let options = RequestAdapterOptions {
power_preference: PowerPreference::HighPerformance,
..Default::default()
};
instance
.request_adapter(&options)
.await
.ok_or(Error::BackendSelection)?
};
let backend = adapter.get_info().backend;
log::info!("selected backend: {backend:?}");
let (device, queue) = {
use wgpu::{DeviceDescriptor, Limits};
let desc = DeviceDescriptor {
limits: if cfg!(target_arch = "wasm32") {
Limits::downlevel_webgl2_defaults()
} else {
Limits::default()
},
..Default::default()
};
adapter
.request_device(&desc, None)
.await
.map_err(|_| Error::RequestDevice)?
};
Ok(Self {
#[cfg(feature = "winit")]
adapter,
device,
queue,
shader_ids: AtomicUsize::default(),
})
}
#[cfg(feature = "winit")]
pub fn adapter(&self) -> &Adapter {
&self.adapter
}
pub fn device(&self) -> &Device {
&self.device
}
pub fn queue(&self) -> &Queue {
&self.queue
}
pub fn next_shader_id(&self) -> usize {
self.shader_ids.fetch_add(1, atomic::Ordering::Relaxed)
}
pub fn draw<D>(&self, render: &mut Render, view: RenderView, draw: D)
where
D: Draw,
{
self.queue.submit([]);
draw.draw(render.0.make(&self.device, view));
let buffers = render.0.drain().map(CommandEncoder::finish);
self.queue.submit(buffers);
}
}
#[derive(Default)]
pub struct Render(Encoders);
#[derive(Clone, Copy, Default)]
pub struct Options {
clear: Option<Rgba>,
}
impl Options {
pub fn with_clear(mut self, clear: Rgba) -> Self {
self.clear = Some(clear);
self
}
fn clear(self) -> LoadOp<Color> {
self.clear.map_or(LoadOp::Load, |col| {
let [r, g, b, a] = col.0.map(f64::from);
LoadOp::Clear(Color { r, g, b, a })
})
}
}
impl From<Rgba> for Options {
fn from(v: Rgba) -> Self {
Self::default().with_clear(v)
}
}
pub struct Frame<'v, 'e> {
view: RenderView<'v>,
device: &'e Device,
encoders: &'e mut Encoders,
id: usize,
}
impl Frame<'_, '_> {
pub fn subframe<'e, 'v, T>(&'e mut self, texture: &'v T) -> Frame<'v, 'e>
where
T: DrawTexture,
{
let view = texture.draw_texture().render_view();
self.encoders.make(self.device, view)
}
pub fn layer<'p, V, O>(&'p mut self, layer: &'p Layer<V>, opts: O) -> SetLayer<'p, V>
where
O: Into<Options>,
{
use wgpu::*;
assert!(
self.view.format == layer.format(),
"layer format doesn't match frame format",
);
let opts = opts.into();
let attachment = RenderPassColorAttachment {
view: self.view.txview,
resolve_target: None,
ops: Operations {
load: opts.clear(),
store: StoreOp::Store,
},
};
let desc = RenderPassDescriptor {
color_attachments: &[Some(attachment)],
..Default::default()
};
let encoder = self.encoders.get_mut(self.id);
let pass = encoder.begin_render_pass(&desc);
layer.set(pass)
}
pub fn copy_texture<T>(&mut self, buffer: &CopyBuffer, texture: &T)
where
T: CopyTexture,
{
let encoder = self.encoders.get_mut(self.id);
buffer.copy_texture(texture.copy_texture(), encoder);
}
}
#[derive(Default)]
struct Encoders(Vec<CommandEncoder>);
impl Encoders {
fn make<'e, 'v>(&'e mut self, device: &'e Device, view: RenderView<'v>) -> Frame<'v, 'e> {
use wgpu::CommandEncoderDescriptor;
let encoder = {
let desc = CommandEncoderDescriptor::default();
device.create_command_encoder(&desc)
};
let id = self.0.len();
self.0.push(encoder);
Frame {
view,
device,
encoders: self,
id,
}
}
fn get_mut(&mut self, id: usize) -> &mut CommandEncoder {
&mut self.0[id]
}
fn drain(&mut self) -> impl Iterator<Item = CommandEncoder> + '_ {
self.0.drain(..)
}
}
#[derive(Clone, Copy)]
pub(crate) struct RenderView<'v> {
txview: &'v TextureView,
format: Format,
}
impl<'v> RenderView<'v> {
pub fn new(txview: &'v TextureView, format: Format) -> Self {
Self { txview, format }
}
}