use log::debug;
use std::sync::Arc;
use winit::window::Window;
pub use anyhow;
pub use bytemuck;
pub use egui;
pub use env_logger;
pub use wgpu;
pub use winit;
pub use bytemuck::{Pod, Zeroable};
pub use winit::event::WindowEvent;
#[derive(Debug)]
pub enum SurfaceError {
SkipFrame,
Outdated,
Lost,
OutOfMemory,
}
impl std::fmt::Display for SurfaceError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::SkipFrame => write!(f, "Surface not ready, skip frame"),
Self::Outdated => write!(f, "Surface outdated"),
Self::Lost => write!(f, "Surface lost"),
Self::OutOfMemory => write!(f, "Out of memory"),
}
}
}
impl std::error::Error for SurfaceError {}
mod app;
pub mod compute;
mod controls;
mod export;
mod font;
mod fps;
#[cfg(feature = "media")]
pub mod gst;
pub mod hdri;
mod hot;
mod keyinputs;
mod mouse;
pub mod gaussian;
pub mod ply;
pub mod radix_sort;
mod renderer;
mod renderkit;
mod shader;
mod spectrum;
mod texture;
mod uniforms;
pub use app::*;
pub use controls::{ControlsRequest, ShaderControls};
pub use export::{save_frame, ExportError, ExportManager, ExportSettings, ExportUiState};
pub use font::{CharInfo, FontSystem, FontUniforms};
pub use hdri::*;
pub use hot::ShaderHotReload;
pub use keyinputs::KeyInputHandler;
pub use mouse::*;
pub use gaussian::*;
pub use ply::*;
pub use renderer::*;
pub use renderkit::*;
pub use shader::*;
pub use texture::*;
pub use uniforms::*;
#[cfg(feature = "media")]
pub mod audio {
pub use crate::gst::audio::{
AudioDataProvider, AudioSynthManager, AudioSynthUniform, AudioWaveform, EnvelopeConfig,
MusicalNote, PcmStreamManager, SynthesisManager, SynthesisUniform, SynthesisWaveform,
};
}
pub mod prelude {
pub use crate::{
compute::ComputeShader, compute::ComputeShaderBuilder, compute::MultiPassManager,
compute::PassDescription, compute::StorageBufferSpec,
compute::COMPUTE_TEXTURE_FORMAT_RGBA16, compute::COMPUTE_TEXTURE_FORMAT_RGBA8,
save_frame, CharInfo, ControlsRequest, Core, ExportManager, FontSystem,
FontUniforms, KeyInputHandler, RenderKit, Renderer, ShaderApp, ShaderControls,
FrameContext, ShaderHotReload, ShaderManager, TextureManager, UniformBinding,
UniformProvider,
};
#[cfg(feature = "media")]
pub use crate::{
audio::{
AudioWaveform, MusicalNote, PcmStreamManager, SynthesisManager, SynthesisUniform,
SynthesisWaveform,
},
gst,
};
pub use crate::anyhow;
pub use crate::bytemuck;
pub use crate::egui;
pub use crate::wgpu;
pub use crate::winit;
pub use crate::SurfaceError;
pub use crate::WindowEvent;
pub use env_logger;
pub use bytemuck::{bytes_of, cast_slice, Pod, Zeroable};
pub use wgpu::{
BindGroup, BindGroupLayout, Buffer, ComputePipeline, Device, Queue, RenderPipeline,
ShaderModule, Surface, SurfaceConfiguration, TextureFormat, TextureView,
};
pub use winit::{dpi::PhysicalSize, event_loop::EventLoop, window::Window};
}
#[macro_export]
macro_rules! uniform_params {
(
$(#[$meta:meta])*
$vis:vis struct $name:ident {
$($field_vis:vis $field:ident : $ty:ty),* $(,)?
}
) => {
#[repr(C)]
#[derive(Copy, Clone, Debug, $crate::bytemuck::Pod, $crate::bytemuck::Zeroable)]
$(#[$meta])*
$vis struct $name {
$($field_vis $field : $ty),*
}
impl $crate::UniformProvider for $name {
fn as_bytes(&self) -> &[u8] {
$crate::bytemuck::bytes_of(self)
}
}
const _: () = {
assert!(
::core::mem::size_of::<$name>() % 16 == 0,
concat!(
"uniform_params!: struct `",
stringify!($name),
"` size must be a multiple of 16 bytes (add padding fields)"
)
);
};
};
}
#[macro_export]
macro_rules! compute_shader {
($core:expr, $shader_path:literal, $config:expr) => {{
let mut config = $config;
let caller_file = file!();
let caller_dir = match caller_file.rfind('/') {
Some(pos) => &caller_file[..pos],
None => match caller_file.rfind('\\') {
Some(pos) => &caller_file[..pos],
None => "",
},
};
let hot_reload_path = if caller_dir.is_empty() {
$shader_path.to_string()
} else {
format!("{}/{}", caller_dir, $shader_path)
};
config.hot_reload_path = Some(std::path::PathBuf::from(hot_reload_path));
$crate::compute::ComputeShader::from_builder($core, include_str!($shader_path), config)
}};
}
pub struct Core {
pub surface: wgpu::Surface<'static>,
pub device: Arc<wgpu::Device>,
pub queue: wgpu::Queue,
pub config: wgpu::SurfaceConfiguration,
pub size: winit::dpi::PhysicalSize<u32>,
pub window: Window,
}
impl Core {
pub async fn new(window: Window) -> Self {
let size = window.inner_size();
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle());
let window_box = Box::new(window);
let window_ptr = Box::into_raw(window_box);
let surface = unsafe { instance.create_surface(&*window_ptr) }.unwrap();
let adapters = instance.enumerate_adapters(wgpu::Backends::all()).await;
let power_preference = adapters
.iter()
.find(|p| p.get_info().device_type == wgpu::DeviceType::DiscreteGpu)
.map(|_| wgpu::PowerPreference::HighPerformance)
.unwrap_or(wgpu::PowerPreference::default());
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
power_preference,
compatible_surface: Some(&surface),
force_fallback_adapter: false,
})
.await
.unwrap();
let (device, queue) = adapter
.request_device(&wgpu::DeviceDescriptor {
label: None,
required_features: wgpu::Features::empty(),
required_limits: wgpu::Limits::default(),
memory_hints: Default::default(),
experimental_features: Default::default(),
trace: wgpu::Trace::default(),
})
.await
.unwrap();
let device = Arc::new(device);
let surface_caps = surface.get_capabilities(&adapter);
let surface_format = surface_caps
.formats
.iter()
.copied()
.find(|f| f.is_srgb() && *f == CAPTURE_FORMAT)
.unwrap_or(surface_caps.formats[0]);
let config = wgpu::SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
format: surface_format,
width: size.width,
height: size.height,
present_mode: wgpu::PresentMode::Fifo,
alpha_mode: surface_caps.alpha_modes[0],
view_formats: vec![],
desired_maximum_frame_latency: 2,
};
surface.configure(&device, &config);
let window = unsafe { *Box::from_raw(window_ptr) };
Self {
surface,
device,
queue,
config,
size,
window,
}
}
pub fn window(&self) -> &Window {
&self.window
}
pub fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
debug!("Core resize: {new_size:?}");
if new_size.width > 0 && new_size.height > 0 {
self.size = new_size;
self.config.width = new_size.width;
self.config.height = new_size.height;
self.surface.configure(&self.device, &self.config);
debug!("Surface reconfigured");
}
}
pub fn flush_encoder(&self, encoder: wgpu::CommandEncoder) -> wgpu::CommandEncoder {
self.queue.submit(Some(encoder.finish()));
self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Continued Encoder"),
})
}
}