Graphite/src/texture.rs

102 lines
3.4 KiB
Rust

use std::fs;
use image::GenericImageView;
use crate::resource_cache::ResourceCache;
pub struct Texture {
pub texture: wgpu::Texture,
pub texture_view: wgpu::TextureView,
pub sampler: wgpu::Sampler,
}
impl Texture {
pub fn cached_load<'a>(device: &wgpu::Device, queue: &mut wgpu::Queue, path: &str, texture_cache: &'a mut ResourceCache<Texture>) -> &'a Texture {
// If uncached, construct a texture loaded from the image file
if texture_cache.get(path).is_none() {
let texture = Texture::from_filepath(device, queue, path).unwrap();
texture_cache.set(path, texture);
}
texture_cache.get(path).unwrap()
}
pub fn from_filepath(device: &wgpu::Device, queue: &mut wgpu::Queue, path: &str) -> Result<Self, failure::Error> {
// Read the raw bytes from the specified file
let bytes = fs::read(path)?;
// Construct and return a Texture from the bytes
Texture::from_bytes(device, queue, &bytes[..])
}
pub fn from_bytes(device: &wgpu::Device, queue: &mut wgpu::Queue, bytes: &[u8]) -> Result<Self, failure::Error> {
// Create an image with the Image library
let image = image::load_from_memory(bytes)?;
// Construct and return a Texture from the Image
Self::from_image(device, queue, &image)
}
pub fn from_image(device: &wgpu::Device, queue: &mut wgpu::Queue, image: &image::DynamicImage) -> Result<Self, failure::Error> {
// Get data from image
let rgba = image.as_rgba8().unwrap();
let dimensions = image.dimensions();
let size = wgpu::Extent3d {
width: dimensions.0,
height: dimensions.1,
depth: 1,
};
// Create a buffer on the GPU and load it with the image pixel data
let buffer = device.create_buffer_with_data(&rgba, wgpu::BufferUsage::COPY_SRC);
// Create an empty texture on the GPU of the correct size for the buffer
let texture = device.create_texture(&wgpu::TextureDescriptor {
label: None,
size,
array_layer_count: 1,
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8UnormSrgb,
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST,
});
// Use a command encoder to transfer the pixel data buffer into the texture
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
encoder.copy_buffer_to_texture(
wgpu::BufferCopyView {
buffer: &buffer,
offset: 0,
bytes_per_row: 4 * dimensions.0,
rows_per_image: dimensions.1,
},
wgpu::TextureCopyView {
texture: &texture,
mip_level: 0,
array_layer: 0,
origin: wgpu::Origin3d::ZERO,
},
size,
);
// Finishing the encoding yields the resulting command buffer that is submitted to the GPU's command queue
let command_buffer = encoder.finish();
queue.submit(&[command_buffer]);
// Create the TextureView for this texture
let view = texture.create_default_view();
// Create the Sampler for this texture
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge,
mag_filter: wgpu::FilterMode::Linear,
min_filter: wgpu::FilterMode::Nearest,
mipmap_filter: wgpu::FilterMode::Nearest,
lod_min_clamp: -100.0,
lod_max_clamp: 100.0,
compare: wgpu::CompareFunction::Always,
});
Ok(Self { texture, texture_view: view, sampler })
}
}