use std::cell::{Cell, RefCell};
use std::sync::Arc;
thread_local! {
static CONTEXT_SINGLETON: RefCell<Option<Context>> = const { RefCell::new(None) };
static WINDOW_COUNT: Cell<usize> = const { Cell::new(0) };
}
#[derive(Clone)]
pub struct Context {
pub instance: Arc<wgpu::Instance>,
pub device: Arc<wgpu::Device>,
pub queue: Arc<wgpu::Queue>,
pub adapter: Arc<wgpu::Adapter>,
pub surface_format: wgpu::TextureFormat,
}
impl Context {
pub fn init(
instance: wgpu::Instance,
device: wgpu::Device,
queue: wgpu::Queue,
adapter: wgpu::Adapter,
surface_format: wgpu::TextureFormat,
) {
CONTEXT_SINGLETON.with(|cell| {
*cell.borrow_mut() = Some(Context {
instance: Arc::new(instance),
device: Arc::new(device),
queue: Arc::new(queue),
adapter: Arc::new(adapter),
surface_format,
});
});
}
pub fn get() -> Context {
CONTEXT_SINGLETON.with(|cell| {
cell.borrow()
.as_ref()
.expect("wgpu context not initialized. Call Context::init() first.")
.clone()
})
}
pub fn is_initialized() -> bool {
CONTEXT_SINGLETON.with(|cell| cell.borrow().is_some())
}
pub fn reset() {
CONTEXT_SINGLETON.with(|cell| {
if let Some(ctx) = cell.borrow().as_ref() {
ctx.device.destroy();
}
*cell.borrow_mut() = None;
});
}
pub fn increment_window_count() {
WINDOW_COUNT.with(|count| {
count.set(count.get() + 1);
});
}
pub fn decrement_window_count() -> bool {
WINDOW_COUNT.with(|count| {
let current = count.get();
if current > 0 {
count.set(current - 1);
current == 1 } else {
false
}
})
}
pub fn window_count() -> usize {
WINDOW_COUNT.with(|count| count.get())
}
pub fn create_buffer(&self, desc: &wgpu::BufferDescriptor) -> wgpu::Buffer {
self.device.create_buffer(desc)
}
pub fn create_buffer_simple(
&self,
label: Option<&str>,
size: u64,
usage: wgpu::BufferUsages,
) -> wgpu::Buffer {
self.device.create_buffer(&wgpu::BufferDescriptor {
label,
size,
usage,
mapped_at_creation: false,
})
}
pub fn create_buffer_init(
&self,
label: Option<&str>,
contents: &[u8],
usage: wgpu::BufferUsages,
) -> wgpu::Buffer {
use wgpu::util::DeviceExt;
self.device
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label,
contents,
usage,
})
}
pub fn write_buffer(&self, buffer: &wgpu::Buffer, offset: u64, data: &[u8]) {
self.queue.write_buffer(buffer, offset, data);
}
pub fn create_texture(&self, desc: &wgpu::TextureDescriptor) -> wgpu::Texture {
self.device.create_texture(desc)
}
pub fn create_sampler(&self, desc: &wgpu::SamplerDescriptor) -> wgpu::Sampler {
self.device.create_sampler(desc)
}
pub fn create_bind_group_layout(
&self,
desc: &wgpu::BindGroupLayoutDescriptor,
) -> wgpu::BindGroupLayout {
self.device.create_bind_group_layout(desc)
}
pub fn create_bind_group(&self, desc: &wgpu::BindGroupDescriptor) -> wgpu::BindGroup {
self.device.create_bind_group(desc)
}
pub fn create_pipeline_layout(
&self,
desc: &wgpu::PipelineLayoutDescriptor,
) -> wgpu::PipelineLayout {
self.device.create_pipeline_layout(desc)
}
pub fn create_render_pipeline(
&self,
desc: &wgpu::RenderPipelineDescriptor,
) -> wgpu::RenderPipeline {
self.device.create_render_pipeline(desc)
}
pub fn create_shader_module(&self, label: Option<&str>, source: &str) -> wgpu::ShaderModule {
self.device
.create_shader_module(wgpu::ShaderModuleDescriptor {
label,
source: wgpu::ShaderSource::Wgsl(source.into()),
})
}
pub fn create_command_encoder(&self, label: Option<&str>) -> wgpu::CommandEncoder {
self.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor { label })
}
pub fn submit<I: IntoIterator<Item = wgpu::CommandBuffer>>(&self, command_buffers: I) {
self.queue.submit(command_buffers);
}
pub fn write_texture(
&self,
texture: wgpu::TexelCopyTextureInfo,
data: &[u8],
data_layout: wgpu::TexelCopyBufferLayout,
size: wgpu::Extent3d,
) {
self.queue.write_texture(texture, data, data_layout, size);
}
pub fn depth_format() -> wgpu::TextureFormat {
wgpu::TextureFormat::Depth32Float
}
}