#[allow(dead_code)]
use std::path::PathBuf;
use image::GenericImageView;
#[cfg(feature = "nannou")]
use wgpu_for_nannou as wgpu;
#[cfg(not(feature = "nannou"))]
use wgpu_for_latest as wgpu;
use wgpu::util::DeviceExt;
use crate::graphics_ref::DEFAULT_LOADED_TEXTURE_FORMAT;
#[derive(Debug)]
pub struct OwnedDeviceState {
device: wgpu::Device,
queue: wgpu::Queue,
}
impl OwnedDeviceState {
pub fn to_borrowed<'a>(&'a self) -> DeviceState<'a> {
DeviceState {
device: self.device(),
queue: self.queue(),
}
}
pub fn device(&self) -> &wgpu::Device {
&self.device
}
pub fn queue(&self) -> &wgpu::Queue {
&self.queue
}
}
#[derive(Debug)]
pub struct DeviceState<'a> {
device: &'a wgpu::Device,
queue: &'a wgpu::Queue,
}
impl<'a> DeviceState<'a> {
pub fn new(device: &'a wgpu::Device, queue: &'a wgpu::Queue) -> Self {
Self { device, queue }
}
pub fn device(&self) -> &wgpu::Device {
&self.device
}
pub fn queue(&self) -> &wgpu::Queue {
&self.queue
}
}
impl OwnedDeviceState {
pub fn new(device: wgpu::Device, queue: wgpu::Queue) -> Self {
OwnedDeviceState { device, queue }
}
pub async fn new_from_native() -> Self {
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
..Default::default()
});
let adapter = wgpu::util::initialize_adapter_from_env_or_default(&instance, None)
.await
.expect("failed to get adapter");
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor {
#[cfg(feature = "nannou")]
features: adapter.features(),
#[cfg(feature = "nannou")]
limits: adapter.limits(),
#[cfg(not(feature = "nannou"))]
required_features: adapter.features(),
#[cfg(not(feature = "nannou"))]
required_limits: adapter.limits(),
label: Some("Compute/RenderPass Device"),
},
None,
)
.await
.expect("Failed to create device and queue");
Self {
device,
queue,
}
}
}
pub fn align_byte_size(value: u32) -> u32 {
value + (wgpu::COPY_BYTES_PER_ROW_ALIGNMENT - (value % wgpu::COPY_BYTES_PER_ROW_ALIGNMENT))
}
pub fn check_img_size(path: &PathBuf) -> Result<(Vec<u8>, u32, u32), Box<dyn std::error::Error>> {
let img = image::open(path)?;
let img_rgba = img.to_rgba8();
let (img_width, img_height) = img.dimensions();
Ok((img_rgba.to_vec(), img_width, img_height))
}
fn write_png_to_texture(
device_state: &DeviceState,
path: &PathBuf,
texture: &wgpu::Texture,
) -> Result<(), Box<dyn std::error::Error>> {
let img = image::open(path)?;
let img_rgba = img.to_rgba8();
let (img_width, img_height) = img.dimensions();
let bytes_per_row = 4 * img_width;
let padded_row = align_byte_size(bytes_per_row);
let buffer_rows = img_height;
println!("img_width {:?}", img_width);
println!("img_height {:?}", img_height);
println!("buffer_rows {:?}", buffer_rows);
let p = path.file_name().map(|x| x.to_str()).flatten().unwrap_or("");
let mut padded_img = vec![0; (padded_row * buffer_rows).try_into().unwrap()];
for (row_i, data) in img_rgba.chunks(bytes_per_row as usize).enumerate() {
let start = row_i * padded_row as usize;
let end = start + data.len();
padded_img[start..end].copy_from_slice(data);
}
let buffer = device_state
.device()
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(&format!("texture from {}", p)),
contents: &padded_img,
usage: wgpu::BufferUsages::COPY_SRC,
});
let mut encoder = device_state
.device()
.create_command_encoder(&Default::default());
encoder.copy_buffer_to_texture(
wgpu::ImageCopyBuffer {
buffer: &buffer,
layout: wgpu::ImageDataLayout {
offset: 0,
bytes_per_row: Some(padded_row), rows_per_image: Some(buffer_rows),
},
},
wgpu::ImageCopyTexture {
texture,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
aspect: wgpu::TextureAspect::All,
},
wgpu::Extent3d {
width: img_width,
height: img_height,
depth_or_array_layers: 1,
},
);
device_state.queue().submit(Some(encoder.finish()));
Ok(())
}
#[derive(Clone, Debug)]
pub enum GraphicsAssets {
Nothing,
LocalFilesystem(PathBuf),
}
impl GraphicsAssets {
pub fn local_filesystem(path: PathBuf) -> GraphicsAssets {
GraphicsAssets::LocalFilesystem(path)
}
pub fn to_format(&self, default: wgpu::TextureFormat) -> wgpu::TextureFormat {
match self {
GraphicsAssets::Nothing => default,
GraphicsAssets::LocalFilesystem(_) => DEFAULT_LOADED_TEXTURE_FORMAT,
}
}
pub fn is_some(&self) -> bool {
match self {
GraphicsAssets::Nothing => true,
_ => false,
}
}
pub(crate) fn force_path_buf(&self) -> PathBuf {
match self {
GraphicsAssets::Nothing => panic!("expected path!"),
GraphicsAssets::LocalFilesystem(p) => p.clone(),
}
}
pub(crate) fn maybe_load_texture(
&self,
device_state: &DeviceState,
input_texture: &wgpu::Texture,
) {
match self {
GraphicsAssets::Nothing => {}
GraphicsAssets::LocalFilesystem(path) => {
write_png_to_texture(device_state, path, input_texture).ok();
}
}
}
}
#[derive(Clone, Debug)]
pub struct GraphicsWindowConf<'a> {
pub device: &'a DeviceState<'a>,
pub dims: [u32; 2],
pub assets_path: GraphicsAssets,
}
impl<'a> GraphicsWindowConf<'a> {
pub fn new(
device: &'a DeviceState,
dims: [u32; 2],
assets_path: GraphicsAssets,
) -> GraphicsWindowConf<'a> {
GraphicsWindowConf {
device,
dims,
assets_path,
}
}
pub fn multi(&self, multiplier: f32) -> GraphicsWindowConf {
let [x, y] = self.dims;
GraphicsWindowConf {
device: self.device,
dims: [
(x as f32 * multiplier) as u32,
(y as f32 * multiplier) as u32,
],
assets_path: GraphicsAssets::Nothing,
}
}
pub fn dims(&self) -> [u32; 2] {
self.dims
}
pub fn device(&self) -> &wgpu::Device {
&self.device.device()
}
pub fn with_dims(&self, dims: [u32; 2]) -> Self {
Self {
dims,
..self.clone()
}
}
pub fn queue(&self) -> &wgpu::Queue {
self.device.queue()
}
}
pub struct DeviceStateForRender<'a> {
device_state: DeviceState<'a>,
display_view: wgpu::TextureView,
}
impl<'a> DeviceStateForRender<'a> {
pub fn new(device_state: DeviceState<'a>, display_view: wgpu::TextureView) -> Self {
Self {
device_state,
display_view,
}
}
pub fn device_state(&self) -> &DeviceState {
&self.device_state
}
pub fn display_view(&self) -> &wgpu::TextureView {
&self.display_view
}
}