-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
8f43728
commit 8f1df5f
Showing
9 changed files
with
360 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,3 +12,4 @@ Cargo.lock | |
|
||
# MSVC Windows builds of rustc generate these, which store debugging information | ||
*.pdb | ||
*.DS_Store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
[package] | ||
name = "wgpu-tools" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[dependencies] | ||
wgpu = "0.20.1" | ||
thiserror = "1.0.57" | ||
nalgebra = "0.33.0" | ||
image = "0.25.1" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,99 @@ | ||
# wgpu-tools | ||
# WGPU Tools | ||
|
||
<p align="left"> | ||
<img src="media/WGPUTools.png", width="160"> | ||
</p> | ||
|
||
A Rust library providing utility functions and abstractions for working with [WGPU](https://wgpu.rs), similar to [MetalTools](https://github.com/computer-graphics-tools/metal-tools). | ||
|
||
## Features | ||
|
||
- Context creation and management | ||
- Texture handling utilities | ||
- GPU operation scheduling | ||
- Error handling | ||
|
||
## Installation | ||
|
||
Add this to your `Cargo.toml`: | ||
|
||
```toml | ||
[dependencies] | ||
wgpu-tools = "0.0.1" | ||
``` | ||
|
||
## Usage | ||
|
||
Here are some examples of how to use wgpu-tools: | ||
|
||
### Creating a standalone context | ||
|
||
```rust | ||
use wgpu_tools::Context; | ||
|
||
async fn create_context() -> Result<Context, wgpu_tools::Error> { | ||
Context::default().await | ||
} | ||
``` | ||
|
||
### Creating a context with a surface | ||
|
||
```rust | ||
use wgpu_tools::Context; | ||
|
||
async fn create_context_with_surface(window: &impl raw_window_handle::HasRawWindowHandle) -> Result<(Context, wgpu::Surface), wgpu_tools::Error> { | ||
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::default()); | ||
let surface = unsafe { instance.create_surface(window) }?; | ||
let context = Context::default_with_surface(instance, Some(&surface)).await?; | ||
Ok((context, surface)) | ||
} | ||
``` | ||
|
||
### Scheduling GPU operations | ||
|
||
```rust | ||
use wgpu_tools::Context; | ||
|
||
fn schedule_gpu_operations(context: &Context) { | ||
context.schedule(|encoder| { | ||
// Perform GPU operations using the encoder | ||
// For example: | ||
let _render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { | ||
label: Some("Render Pass"), | ||
color_attachments: &[], | ||
depth_stencil_attachment: None, | ||
}); | ||
// Add more GPU commands as needed | ||
}); | ||
} | ||
``` | ||
|
||
### Loading a texture from an image file | ||
|
||
```rust | ||
use wgpu_tools::Context; | ||
use image::io::Reader as ImageReader; | ||
|
||
fn load_texture_from_file(context: &Context, path: &std::path::Path) -> Result<wgpu_tools::Texture, wgpu_tools::Error> { | ||
let img = ImageReader::open(path)?.decode()?; | ||
context.texture_from_image(&img, &wgpu::TextureFormat::Rgba8UnormSrgb, Some("Loaded Texture")) | ||
} | ||
``` | ||
|
||
## Main Components | ||
|
||
### Context | ||
|
||
The `Context` struct provides a convenient wrapper around wgpu's instance, adapter, device, and queue. It offers methods for creating contexts, scheduling GPU operations, and creating textures. | ||
|
||
### Texture | ||
|
||
The `Texture` struct encapsulates wgpu texture, view, and sampler objects. | ||
|
||
### Error Handling | ||
|
||
Custom error types are provided to handle various failure scenarios in wgpu operations. | ||
|
||
## License | ||
|
||
WGPUTools is licensed under [MIT license](LICENSE). |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,221 @@ | ||
use super::error::Error; | ||
use super::texture::Texture; | ||
use image::{DynamicImage, GenericImageView}; | ||
use nalgebra::SVector; | ||
use wgpu; | ||
|
||
pub struct ContextDescriptor<'a, 'b> { | ||
request_adapter_options: wgpu::RequestAdapterOptions<'a, 'b>, | ||
device_descriptor: wgpu::DeviceDescriptor<'a>, | ||
trace_path: Option<&'a std::path::Path>, | ||
} | ||
|
||
pub struct Context { | ||
pub instance: wgpu::Instance, | ||
pub adapter: wgpu::Adapter, | ||
pub device: wgpu::Device, | ||
pub queue: wgpu::Queue, | ||
} | ||
|
||
impl Context { | ||
pub async fn new<'a, 'b>( | ||
instance: wgpu::Instance, | ||
descriptor: &'a ContextDescriptor<'a, 'b>, | ||
) -> Result<Self, Error> { | ||
let adapter = instance | ||
.request_adapter(&descriptor.request_adapter_options) | ||
.await | ||
.ok_or(Error::RequestingAdapterFailed)?; | ||
let (device, queue) = adapter | ||
.request_device(&descriptor.device_descriptor, descriptor.trace_path) | ||
.await?; | ||
|
||
Ok(Self { | ||
instance, | ||
adapter, | ||
device, | ||
queue, | ||
}) | ||
} | ||
|
||
pub async fn default_with_surface<'a>( | ||
instance: wgpu::Instance, | ||
compatible_surface: Option<&wgpu::Surface<'a>>, | ||
) -> Result<Self, Error> { | ||
let context = Self::new( | ||
instance, | ||
&ContextDescriptor { | ||
request_adapter_options: wgpu::RequestAdapterOptions { | ||
power_preference: wgpu::PowerPreference::default(), | ||
force_fallback_adapter: false, | ||
compatible_surface, | ||
}, | ||
device_descriptor: wgpu::DeviceDescriptor::default(), | ||
trace_path: None, | ||
}, | ||
) | ||
.await?; | ||
|
||
Ok(context) | ||
} | ||
|
||
pub async fn default() -> Result<Self, Error> { | ||
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::default()); | ||
Self::default_with_surface(instance, None).await | ||
} | ||
|
||
pub fn schedule<O>(&self, operations: O) | ||
where | ||
O: Fn(&mut wgpu::CommandEncoder) -> (), | ||
{ | ||
let mut command_encoder = | ||
self.device | ||
.create_command_encoder(&wgpu::CommandEncoderDescriptor { | ||
label: Some("Command Encoder"), | ||
}); | ||
|
||
operations(&mut command_encoder); | ||
|
||
let command_buffer = command_encoder.finish(); | ||
self.queue.submit(std::iter::once(command_buffer)); | ||
} | ||
|
||
pub fn depth_texture(&self, width: &u32, height: &u32, label: &str) -> Texture { | ||
let size = wgpu::Extent3d { | ||
width: *width, | ||
height: *height, | ||
depth_or_array_layers: 1, | ||
}; | ||
let texture = self.device.create_texture(&wgpu::TextureDescriptor { | ||
label: Some(label), | ||
size, | ||
mip_level_count: 1, | ||
sample_count: 1, | ||
dimension: wgpu::TextureDimension::D2, | ||
format: Texture::DEPTH_FORMAT, | ||
view_formats: &[wgpu::TextureFormat::Depth32Float], | ||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING, | ||
}); | ||
let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); | ||
let sampler = self.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::Linear, | ||
mipmap_filter: wgpu::FilterMode::Nearest, | ||
compare: Some(wgpu::CompareFunction::LessEqual), | ||
lod_min_clamp: 0.0, | ||
lod_max_clamp: 100.0, | ||
..Default::default() | ||
}); | ||
|
||
Texture { | ||
texture, | ||
view, | ||
sampler, | ||
} | ||
} | ||
|
||
pub fn texture_with_data( | ||
&self, | ||
data: &[u8], | ||
width: &u32, | ||
height: &u32, | ||
texture_format: &wgpu::TextureFormat, | ||
label: Option<&str>, | ||
) -> Result<Texture, Error> { | ||
let size = wgpu::Extent3d { | ||
width: *width, | ||
height: *height, | ||
depth_or_array_layers: 1, | ||
}; | ||
let bytes_per_row = 4 * size.width; | ||
let bytes_per_image = bytes_per_row * size.width; | ||
|
||
if bytes_per_row == 0 || data.len() < bytes_per_image as usize { | ||
return Err(Error::TextureCreationFailed); | ||
} | ||
|
||
let texture = self.device.create_texture(&wgpu::TextureDescriptor { | ||
label, | ||
size, | ||
mip_level_count: 1, | ||
sample_count: 1, | ||
dimension: wgpu::TextureDimension::D2, | ||
format: *texture_format, | ||
view_formats: &[*texture_format], | ||
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, | ||
}); | ||
|
||
self.queue.write_texture( | ||
wgpu::ImageCopyTexture { | ||
aspect: wgpu::TextureAspect::All, | ||
texture: &texture, | ||
mip_level: 0, | ||
origin: wgpu::Origin3d::ZERO, | ||
}, | ||
data, | ||
wgpu::ImageDataLayout { | ||
offset: 0, | ||
bytes_per_row: Some(bytes_per_row), | ||
rows_per_image: Some(size.height), | ||
}, | ||
size, | ||
); | ||
|
||
let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); | ||
let sampler = self.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::Linear, | ||
mipmap_filter: wgpu::FilterMode::Linear, | ||
..Default::default() | ||
}); | ||
|
||
Ok(Texture { | ||
texture, | ||
view, | ||
sampler, | ||
}) | ||
} | ||
|
||
pub fn texture_from_image( | ||
&self, | ||
image: &DynamicImage, | ||
texture_format: &wgpu::TextureFormat, | ||
label: Option<&str>, | ||
) -> Result<Texture, Error> { | ||
let (width, height) = image.dimensions(); | ||
let data = image.to_rgba8(); | ||
self.texture_with_data(&data, &width, &height, texture_format, label) | ||
} | ||
|
||
pub fn texture_from_image_data( | ||
&self, | ||
data: &[u8], | ||
texture_format: &wgpu::TextureFormat, | ||
label: Option<&str>, | ||
) -> Result<Texture, Error> { | ||
let image = image::load_from_memory(data)?; | ||
self.texture_from_image(&image, texture_format, label) | ||
} | ||
|
||
pub fn texture_from_color( | ||
&self, | ||
color: &SVector<f32, 4>, | ||
texture_format: &wgpu::TextureFormat, | ||
label: Option<&str>, | ||
) -> Result<Texture, Error> { | ||
let data: [u8; 4] = [ | ||
(color.x * 255_f32) as u8, | ||
(color.y * 255_f32) as u8, | ||
(color.z * 255_f32) as u8, | ||
(color.w * 255_f32) as u8, | ||
]; | ||
let (width, height) = (1, 1); | ||
self.texture_with_data(&data, &width, &height, texture_format, label) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
#[derive(thiserror::Error, Debug)] | ||
pub enum Error { | ||
#[error("requesting adapter failed")] | ||
RequestingAdapterFailed, | ||
#[error(transparent)] | ||
RequestingDeviceFailed(#[from] wgpu::RequestDeviceError), | ||
#[error(transparent)] | ||
ImageError(#[from] image::ImageError), | ||
#[error("texture creation failed")] | ||
TextureCreationFailed, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
mod context; | ||
mod error; | ||
mod texture; | ||
|
||
pub use context::Context; | ||
pub use error::Error; | ||
pub use texture::Texture; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
#[derive(Debug)] | ||
pub struct Texture { | ||
pub texture: wgpu::Texture, | ||
pub view: wgpu::TextureView, | ||
pub sampler: wgpu::Sampler, | ||
} | ||
|
||
impl Texture { | ||
pub const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float; | ||
} |