1use std::sync::Arc;
6
7use sable_platform::prelude::Window;
8use tracing::{debug, info};
9use wgpu::{
10 Adapter, Device, DeviceDescriptor, Features, Instance, InstanceDescriptor, Limits, PresentMode,
11 Queue, Surface, SurfaceCapabilities, SurfaceConfiguration, TextureFormat, TextureUsages,
12};
13
14use crate::Result;
15
16pub struct GpuContext {
18 #[allow(dead_code)]
19 instance: Instance,
20 adapter: Adapter,
21 device: Arc<Device>,
22 queue: Arc<Queue>,
23 surface: Surface<'static>,
24 surface_config: SurfaceConfiguration,
25 #[allow(dead_code)]
26 surface_capabilities: SurfaceCapabilities,
27}
28
29impl GpuContext {
30 pub async fn new(window: &Window) -> Result<Self> {
41 let instance = Instance::new(&InstanceDescriptor {
43 backends: wgpu::Backends::all(),
44 ..Default::default()
45 });
46
47 let surface = unsafe {
50 let target = wgpu::SurfaceTargetUnsafe::from_window(&window.winit_window())?;
51 instance.create_surface_unsafe(target)?
52 };
53
54 let adapter = instance
56 .request_adapter(&wgpu::RequestAdapterOptions {
57 power_preference: wgpu::PowerPreference::HighPerformance,
58 force_fallback_adapter: false,
59 compatible_surface: Some(&surface),
60 })
61 .await?;
62
63 info!(
64 "Selected GPU adapter: {} ({:?})",
65 adapter.get_info().name,
66 adapter.get_info().backend
67 );
68 debug!("Adapter features: {:?}", adapter.features());
69 debug!("Adapter limits: {:?}", adapter.limits());
70
71 let (device, queue) = adapter
73 .request_device(&DeviceDescriptor {
74 label: Some("Sable GPU Device"),
75 required_features: Features::empty(),
76 required_limits: Limits::default(),
77 memory_hints: wgpu::MemoryHints::Performance,
78 trace: wgpu::Trace::Off,
79 })
80 .await?;
81
82 let device = Arc::new(device);
83 let queue = Arc::new(queue);
84
85 let surface_capabilities = surface.get_capabilities(&adapter);
87 let surface_format = surface_capabilities
88 .formats
89 .iter()
90 .find(|f| f.is_srgb())
91 .copied()
92 .unwrap_or(surface_capabilities.formats[0]);
93
94 debug!("Surface format: {:?}", surface_format);
95 debug!(
96 "Available present modes: {:?}",
97 surface_capabilities.present_modes
98 );
99
100 let size = window.inner_size();
102 let present_mode = if window.vsync() {
103 if surface_capabilities
104 .present_modes
105 .contains(&PresentMode::Fifo)
106 {
107 PresentMode::Fifo
108 } else {
109 surface_capabilities.present_modes[0]
110 }
111 } else if surface_capabilities
112 .present_modes
113 .contains(&PresentMode::Immediate)
114 {
115 PresentMode::Immediate
116 } else if surface_capabilities
117 .present_modes
118 .contains(&PresentMode::Mailbox)
119 {
120 PresentMode::Mailbox
121 } else {
122 surface_capabilities.present_modes[0]
123 };
124
125 let surface_config = SurfaceConfiguration {
126 usage: TextureUsages::RENDER_ATTACHMENT,
127 format: surface_format,
128 width: size.width.max(1),
129 height: size.height.max(1),
130 present_mode,
131 alpha_mode: surface_capabilities.alpha_modes[0],
132 view_formats: vec![],
133 desired_maximum_frame_latency: 2,
134 };
135
136 surface.configure(&device, &surface_config);
137
138 Ok(Self {
139 instance,
140 adapter,
141 device,
142 queue,
143 surface,
144 surface_config,
145 surface_capabilities,
146 })
147 }
148
149 pub fn resize(&mut self, width: u32, height: u32) {
153 if width > 0 && height > 0 {
154 self.surface_config.width = width;
155 self.surface_config.height = height;
156 self.surface.configure(&self.device, &self.surface_config);
157 }
158 }
159
160 pub fn get_current_texture(&self) -> Result<wgpu::SurfaceTexture> {
166 Ok(self.surface.get_current_texture()?)
167 }
168
169 #[must_use]
171 pub fn device(&self) -> &Arc<Device> {
172 &self.device
173 }
174
175 #[must_use]
177 pub fn queue(&self) -> &Arc<Queue> {
178 &self.queue
179 }
180
181 #[must_use]
183 pub fn surface_format(&self) -> TextureFormat {
184 self.surface_config.format
185 }
186
187 #[must_use]
189 pub fn surface_size(&self) -> (u32, u32) {
190 (self.surface_config.width, self.surface_config.height)
191 }
192
193 #[must_use]
195 pub fn adapter_info(&self) -> wgpu::AdapterInfo {
196 self.adapter.get_info()
197 }
198
199 #[must_use]
201 pub fn create_command_encoder(&self) -> wgpu::CommandEncoder {
202 self.device
203 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
204 label: Some("Sable Command Encoder"),
205 })
206 }
207
208 pub fn submit<I: IntoIterator<Item = wgpu::CommandBuffer>>(&self, commands: I) {
210 self.queue.submit(commands);
211 }
212}