Skip to main content

oxigdal_gpu/
lib.rs

1//! GPU-accelerated geospatial operations for OxiGDAL.
2//!
3//! This crate provides GPU acceleration for raster operations using WGPU,
4//! enabling 10-100x speedup for large-scale geospatial data processing.
5//!
6//! # Features
7//!
8//! - **Cross-platform GPU support**: Vulkan, Metal, DX12, DirectML, WebGPU
9//! - **Backend-specific optimizations**: CUDA, Vulkan, Metal, DirectML
10//! - **Multi-GPU support**: Distribute work across multiple GPUs
11//! - **Advanced memory management**: Memory pooling, staging buffers, VRAM budget tracking
12//! - **Element-wise operations**: Add, subtract, multiply, divide, etc.
13//! - **Statistical operations**: Parallel reduction, histogram, min/max, advanced statistics
14//! - **Resampling**: Nearest neighbor, bilinear, bicubic, Lanczos interpolation
15//! - **Convolution**: Gaussian blur, edge detection, FFT-based, custom filters
16//! - **Pipeline API**: Chain operations without CPU transfers
17//! - **Pure Rust**: No C/C++ dependencies
18//! - **Safe**: Comprehensive error handling, no unwrap()
19//!
20//! # Quick Start
21//!
22//! ```rust,no_run
23//! use oxigdal_gpu::*;
24//!
25//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
26//! // Initialize GPU context
27//! let gpu = GpuContext::new().await?;
28//!
29//! // Create compute pipeline
30//! let data: Vec<f32> = vec![1.0; 1024 * 1024];
31//! let result = ComputePipeline::from_data(&gpu, &data, 1024, 1024)?
32//!     .gaussian_blur(2.0)?
33//!     .multiply(1.5)?
34//!     .clamp(0.0, 255.0)?
35//!     .read_blocking()?;
36//! # Ok(())
37//! # }
38//! ```
39//!
40//! # GPU Backend Selection
41//!
42//! ```rust,no_run
43//! use oxigdal_gpu::*;
44//!
45//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
46//! // Auto-select best backend for platform
47//! let gpu = GpuContext::new().await?;
48//!
49//! // Or specify backend explicitly
50//! let config = GpuContextConfig::new()
51//!     .with_backend(BackendPreference::Vulkan)
52//!     .with_power_preference(GpuPowerPreference::HighPerformance);
53//!
54//! let gpu = GpuContext::with_config(config).await?;
55//! # Ok(())
56//! # }
57//! ```
58//!
59//! # NDVI Computation Example
60//!
61//! ```rust,no_run
62//! use oxigdal_gpu::*;
63//!
64//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
65//! let gpu = GpuContext::new().await?;
66//!
67//! // Load multispectral imagery (R, G, B, NIR bands)
68//! let bands_data: Vec<Vec<f32>> = vec![
69//!     vec![0.0; 512 * 512], // Red
70//!     vec![0.0; 512 * 512], // Green
71//!     vec![0.0; 512 * 512], // Blue
72//!     vec![0.0; 512 * 512], // NIR
73//! ];
74//!
75//! // Create GPU raster buffer
76//! let raster = GpuRasterBuffer::from_bands(
77//!     &gpu,
78//!     512,
79//!     512,
80//!     &bands_data,
81//!     wgpu::BufferUsages::STORAGE,
82//! )?;
83//!
84//! // Compute NDVI
85//! let pipeline = MultibandPipeline::new(&gpu, &raster)?;
86//! let ndvi = pipeline.ndvi()?;
87//!
88//! // Apply threshold and export
89//! let vegetation = ndvi
90//!     .threshold(0.3, 1.0, 0.0)?
91//!     .read_blocking()?;
92//! # Ok(())
93//! # }
94//! ```
95//!
96//! # Performance
97//!
98//! GPU acceleration provides significant speedups for large rasters:
99//!
100//! | Operation | CPU (single-thread) | GPU | Speedup |
101//! |-----------|---------------------|-----|---------|
102//! | Element-wise ops | 100 ms | 1 ms | 100x |
103//! | Gaussian blur | 500 ms | 5 ms | 100x |
104//! | Resampling | 200 ms | 10 ms | 20x |
105//! | Statistics | 150 ms | 2 ms | 75x |
106//!
107//! # Error Handling
108//!
109//! All GPU operations return `GpuResult<T>` and handle errors gracefully:
110//!
111//! ```rust,no_run
112//! use oxigdal_gpu::*;
113//!
114//! # async fn example() {
115//! match GpuContext::new().await {
116//!     Ok(gpu) => {
117//!         // Use GPU acceleration
118//!     }
119//!     Err(e) if e.should_fallback_to_cpu() => {
120//!         // Fallback to CPU implementation
121//!         println!("GPU not available, using CPU: {}", e);
122//!     }
123//!     Err(e) => {
124//!         eprintln!("GPU error: {}", e);
125//!     }
126//! }
127//! # }
128//! ```
129
130// Primary warnings/denials first
131#![warn(clippy::all)]
132#![deny(clippy::unwrap_used)]
133#![deny(clippy::panic)]
134// GPU crate is still under development - allow partial documentation
135#![allow(missing_docs)]
136// Allow dead code for internal structures not yet fully utilized
137#![allow(dead_code)]
138// Allow manual div_ceil for compatibility with older Rust versions
139#![allow(clippy::manual_div_ceil)]
140// Allow method name conflicts for builder patterns
141#![allow(clippy::should_implement_trait)]
142// Private type leakage allowed for internal APIs
143#![allow(private_interfaces)]
144// Allow unused_must_use for wgpu buffer creation patterns
145#![allow(unused_must_use)]
146// Allow complex type definitions in GPU interfaces
147#![allow(clippy::type_complexity)]
148// Allow expect() for GPU device invariants
149#![allow(clippy::expect_used)]
150// Allow manual clamp for GPU value normalization
151#![allow(clippy::manual_clamp)]
152// Allow first element access with get(0)
153#![allow(clippy::get_first)]
154// Allow collapsible matches for clarity
155#![allow(clippy::collapsible_match)]
156// Allow redundant closures for explicit code
157#![allow(clippy::redundant_closure)]
158// Allow vec push after creation for GPU buffer building
159#![allow(clippy::vec_init_then_push)]
160// Allow iterating on map values pattern
161#![allow(clippy::iter_kv_map)]
162// Allow needless question mark for explicit error handling
163#![allow(clippy::needless_question_mark)]
164// Allow confusing lifetimes in memory management
165#![allow(clippy::needless_lifetimes)]
166// Allow map iteration patterns
167#![allow(clippy::for_kv_map)]
168// Allow elided lifetime patterns
169#![allow(elided_lifetimes_in_associated_constant)]
170
171pub mod algebra;
172pub mod backends;
173pub mod band_math;
174pub mod buffer;
175pub mod compositing;
176pub mod compute;
177pub mod context;
178pub mod convolution_fft;
179pub mod cooperative_matrix;
180pub mod cpu_fallback;
181pub mod device_lost_test;
182pub mod error;
183pub mod fft;
184pub mod indirect_dispatch;
185pub mod kernels;
186pub mod memory;
187pub mod multi_gpu;
188pub mod pipeline_cache;
189pub mod profiling;
190pub mod push_constants;
191pub mod ray_march;
192pub mod reprojection;
193pub mod shader_helpers;
194pub mod shader_reload;
195#[cfg(feature = "shader-hot-reload")]
196pub mod shader_reload_native;
197pub mod shaders;
198pub mod storage_texture;
199pub mod texture_compress;
200pub mod texture_resample;
201pub mod tiled;
202pub mod webgpu_compat;
203pub mod workgroup_tuner;
204
205// Re-export commonly used items
206pub use algebra::{AlgebraOp, BandExpression, GpuAlgebra};
207pub use band_math::{BandMathError, band_expression_to_wgsl, parse_band_expression};
208pub use buffer::{
209    BufferElementType, GpuBuffer, GpuRasterBuffer, f16_to_f32_slice, f32_to_f16_slice,
210    from_f16_slice_native, from_f16_slice_widening, read_f16_from_f32_buffer,
211};
212pub use compute::{ComputePipeline, MultibandPipeline};
213pub use context::{BackendPreference, GpuContext, GpuContextConfig, GpuPowerPreference};
214pub use convolution_fft::{
215    FftConvolution, MAX_FFT_CONVOLUTION_SIZE, complex_multiply, convolve_reference,
216};
217pub use cooperative_matrix::{
218    CoopMatrixComponentType, CoopMatrixDescriptor, CoopMatrixDim, CoopMatrixGemmConfig,
219    CoopMatrixUse, build_cooperative_matrix_gemm_pipeline, dispatch_cooperative_gemm,
220    make_gemm_wgsl, make_gemm_wgsl_fallback, max_cooperative_matrix_dim,
221    supports_cooperative_matrix,
222};
223pub use cpu_fallback::cpu;
224pub use cpu_fallback::{
225    ExecutionPath, FallbackConfig, FallbackContext, FallbackResult, execute_with_fallback,
226    execute_with_fallback_timed,
227};
228pub use error::{GpuError, GpuResult};
229pub use fft::{Fft1d, bit_reverse, make_fft_shader_source, twiddle_factor, validate_size};
230/// Re-export `half` so callers can use `oxigdal_gpu::half::f16` without
231/// an explicit dependency on the `half` crate.
232pub use half;
233pub use indirect_dispatch::{
234    DispatchIndirectArgs, IndirectDispatchBuffer, args_for_elements, dispatch_indirect_on_pass,
235    workgroup_count_1d, workgroup_count_2d, workgroup_count_3d,
236};
237pub use kernels::{
238    convolution::{Filters, gaussian_blur},
239    raster::{ElementWiseOp, RasterKernel, ScalarOp, UnaryOp},
240    resampling::{ResamplingMethod, resize},
241    statistics::{HistogramParams, ReductionOp, Statistics, compute_statistics},
242};
243pub use memory::{MemoryPool, MemoryPoolConfig, StagingBufferManager, VramBudgetManager};
244pub use multi_gpu::{
245    DistributionStrategy, InterGpuTransfer, MultiGpuConfig, MultiGpuManager, WorkDistributor,
246};
247pub use pipeline_cache::{
248    PipelineCache, PipelineCacheKey, SharedPipelineCache, fnv1a_64, new_shared_pipeline_cache,
249};
250pub use profiling::{GpuTimestampProfiler, PassTiming};
251pub use push_constants::{
252    MAX_PUSH_CONSTANTS_SIZE_BYTES, PushConstantRange, PushConstantRangeDesc, PushConstantsBuffer,
253    PushConstantsLayout, build_push_constants_pipeline, dispatch_with_push_constants,
254    make_push_constants_shader_source, max_push_constants_size, supports_push_constants,
255};
256pub use ray_march::{
257    DemRayMarcher, RayMarchConfig, RayMarchResult, make_ray_march_shader_source, normalize3,
258    ray_march_cpu, sample_dem_bilinear,
259};
260pub use reprojection::{GpuReprojector, ReprojectionConfig, ResampleMethod};
261pub use shader_helpers::make_element_wise_shader_source;
262pub use storage_texture::{
263    StorageTextureBinding, StorageTextureKernel, build_storage_texture_kernel,
264    is_supported_storage_format, make_storage_texture_shader_source, new_storage_texture,
265    read_texture_to_vec_f32,
266};
267pub use texture_compress::{
268    TextureCompressor, TextureFormat, compress_bc1_block_cpu, compress_bc4_block_cpu,
269    dequantize_rgb565, nearest_index_4, quantize_rgb565, validate_texture_dimensions,
270};
271pub use texture_resample::{
272    TextureFilterMethod, TextureResampler, make_texture_resample_shader_source,
273    new_input_texture_r32float, texture_filter_for_resampling,
274};
275pub use tiled::{
276    RasterTile, TiledConfig, auto_tile_size, execute_tiled, split_into_tiles, stitch_tiles,
277    vram_per_tile,
278};
279pub use webgpu_compat::{GpuCapabilities, ShaderRegistry};
280pub use workgroup_tuner::WorkgroupTuner;
281
282#[cfg(feature = "shader-hot-reload")]
283pub use shader_reload_native::{
284    FilesystemPoller, PolledChange, PolledChangeKind, read_shader_source,
285};
286
287/// Library version.
288pub const VERSION: &str = env!("CARGO_PKG_VERSION");
289
290/// Check if GPU is available on the current system.
291///
292/// This is a convenience function that attempts to create a GPU context
293/// and returns whether it succeeded.
294///
295/// # Examples
296///
297/// ```rust,no_run
298/// use oxigdal_gpu::is_gpu_available;
299///
300/// # async fn example() {
301/// if is_gpu_available().await {
302///     println!("GPU acceleration available!");
303/// } else {
304///     println!("GPU not available, falling back to CPU");
305/// }
306/// # }
307/// ```
308pub async fn is_gpu_available() -> bool {
309    GpuContext::new().await.is_ok()
310}
311
312/// Get information about available GPU adapters.
313///
314/// Returns a list of available GPU adapter names and backends.
315///
316/// # Examples
317///
318/// ```rust,no_run
319/// use oxigdal_gpu::get_available_adapters;
320///
321/// # async fn example() {
322/// let adapters = get_available_adapters().await;
323/// for (name, backend) in adapters {
324///     println!("GPU: {} ({:?})", name, backend);
325/// }
326/// # }
327/// ```
328pub async fn get_available_adapters() -> Vec<(String, String)> {
329    use wgpu::{Backends, Instance, InstanceDescriptor, RequestAdapterOptions};
330
331    let _instance = Instance::new(InstanceDescriptor {
332        backends: Backends::all(),
333        ..InstanceDescriptor::new_without_display_handle()
334    });
335
336    let mut adapters = Vec::new();
337
338    // Try to enumerate all adapters
339    for backend in &[
340        Backends::VULKAN,
341        Backends::METAL,
342        Backends::DX12,
343        Backends::BROWSER_WEBGPU,
344    ] {
345        let instance = Instance::new(InstanceDescriptor {
346            backends: *backend,
347            ..InstanceDescriptor::new_without_display_handle()
348        });
349
350        if let Ok(adapter) = instance
351            .request_adapter(&RequestAdapterOptions::default())
352            .await
353        {
354            let info = adapter.get_info();
355            adapters.push((info.name, format!("{:?}", info.backend)));
356        }
357    }
358
359    adapters
360}
361
362#[cfg(test)]
363mod tests {
364    use super::*;
365
366    #[test]
367    fn test_version() {
368        assert!(!VERSION.is_empty());
369    }
370
371    #[tokio::test]
372    async fn test_gpu_availability() {
373        let available = is_gpu_available().await;
374        println!("GPU available: {}", available);
375    }
376
377    #[tokio::test]
378    async fn test_get_adapters() {
379        let adapters = get_available_adapters().await;
380        println!("Available adapters:");
381        for (name, backend) in adapters {
382            println!("  - {} ({:?})", name, backend);
383        }
384    }
385}