mod builder;
pub use builder::GpuBuilder;
pub use wgpu::Backends;
pub use wgpu::Features;
use crate::{BufferBuilder, GpuError, Profiler, ViewportBuilder};
use core::mem::ManuallyDrop;
use raw_window_handle::HasRawWindowHandle;
use std::{
ops::{Deref, DerefMut},
rc::Rc,
};
use winit::window::Window;
#[derive(Debug)]
pub struct GpuCtx {
pub instance: wgpu::Instance,
pub adapter: wgpu::Adapter,
pub device: wgpu::Device,
pub queue: wgpu::Queue,
pub profiler: Profiler,
pub preferred_format: Option<wgpu::TextureFormat>,
}
impl GpuCtx {
#[allow(clippy::new_ret_no_self)]
pub fn new<W>(window: &W) -> Result<Gpu, GpuError>
where
W: HasRawWindowHandle,
{
Self::builder().build(window)
}
#[must_use]
pub fn builder<'a>() -> GpuBuilder<'a> {
GpuBuilder::new()
}
#[must_use]
pub fn into_handle(self) -> Gpu {
Gpu {
context: Rc::new(self),
}
}
}
impl Deref for GpuCtx {
type Target = wgpu::Device;
fn deref(&self) -> &Self::Target {
&self.device
}
}
#[derive(Clone, Debug)]
pub struct Gpu {
context: Rc<GpuCtx>,
}
impl Gpu {
#[must_use]
pub fn new_viewport(&self, window: Window) -> ViewportBuilder {
ViewportBuilder::new(self.clone(), window)
}
#[must_use]
pub fn create_viewport(&self, window: Window) -> crate::Viewport {
ViewportBuilder::new(self.clone(), window).create()
}
#[must_use]
pub fn new_buffer<'a>(&self, label: &'a str) -> BufferBuilder<'a> {
BufferBuilder::new(self.clone(), label)
}
#[must_use]
pub fn new_pipeline<'a>(&self, label: &'a str) -> crate::pipeline::PipelineBuilder<'a> {
crate::pipeline::PipelineBuilder::new(self.clone(), label)
}
pub fn create_command_encoder(&self, label: &str) -> CommandEncoder {
let inner = self
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some(label) });
CommandEncoder {
inner: ManuallyDrop::new(inner),
gpu: self.clone(),
finished: false,
}
}
pub(crate) fn begin_profiler_section<'a>(
&self,
label: &str,
encoder: &'a mut wgpu::CommandEncoder,
) {
self.profiler.begin_section(label);
self.profiler.timestamp(label, encoder);
}
pub(crate) fn begin_pipeline_statistics_query(&self, render_pass: &mut wgpu::RenderPass) {
self.profiler.begin_stats(render_pass);
}
pub fn total_statistics(&self) -> Result<[u64; 5], GpuError> {
let stats = self.profiler.stats.as_ref().ok_or(GpuError::QueryNone)?;
let mut ret = [0; 5];
for (i, stat) in stats
.get(&self.device, self.profiler.query_count())?
.iter()
.enumerate()
{
ret[i % 5] += stat;
}
Ok(ret)
}
pub fn timestamp_report(&self) -> Vec<(String, f32)> {
self.profiler.timestamp_report(&self.device)
}
pub(crate) fn wrap_view(&self, view: wgpu::TextureView) -> crate::TextureView {
crate::TextureView {
gpu: self,
inner: view,
}
}
pub fn builder<'a>() -> GpuBuilder<'a> {
GpuBuilder::new()
}
}
impl Deref for Gpu {
type Target = GpuCtx;
fn deref(&self) -> &Self::Target {
&self.context
}
}
pub struct CommandEncoder {
inner: ManuallyDrop<wgpu::CommandEncoder>,
finished: bool,
pub(crate) gpu: Gpu,
}
impl CommandEncoder {
pub fn finish(mut self) -> wgpu::CommandBuffer {
self.finished = true;
unsafe { ManuallyDrop::take(&mut self.inner).finish() }
}
}
impl Deref for CommandEncoder {
type Target = wgpu::CommandEncoder;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl DerefMut for CommandEncoder {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
impl Drop for CommandEncoder {
fn drop(&mut self) {
if !self.finished {
let inner = unsafe { ManuallyDrop::take(&mut self.inner) };
self.gpu.queue.submit([inner.finish()]);
}
}
}