1use winit::window::Window;
2
3#[derive(Debug)]
4pub struct WgpuSurface {
5 surface: wgpu::Surface,
8 window: Window,
9}
10
11impl WgpuSurface {
12 pub fn for_window(instance: &wgpu::Instance, window: Window) -> Self {
13 let surface = unsafe { instance.create_surface(&window) };
14
15 Self { surface, window }
16 }
17
18 pub fn best_adapter<F, B>(self, instance: &wgpu::Instance, compare: F) -> Result<WgpuAdapter, WgpuSurface>
19 where
20 B: Ord,
21 F: Fn(&wgpu::Adapter) -> B,
22 {
23 if let Some(adapter) = instance.enumerate_adapters(wgpu::Backends::all()).filter(|adapter| {
24 adapter.is_surface_supported(&self.surface)
25 }).max_by_key(compare) {
26 Ok(WgpuAdapter {
27 adapter,
28 surface: self,
29 })
30 } else {
31 Err(self)
32 }
33 }
34
35 pub fn prefer_low_power(adapter: &wgpu::Adapter) -> u8 {
36 match adapter.get_info().device_type {
37 wgpu::DeviceType::IntegratedGpu => 5,
38 wgpu::DeviceType::VirtualGpu => 4,
39 wgpu::DeviceType::DiscreteGpu => 3,
40 wgpu::DeviceType::Cpu => 2,
41 wgpu::DeviceType::Other => 1,
42 }
43 }
44
45 pub fn prefer_high_power(adapter: &wgpu::Adapter) -> u8 {
46 match adapter.get_info().device_type {
47 wgpu::DeviceType::DiscreteGpu => 5,
48 wgpu::DeviceType::VirtualGpu => 4,
49 wgpu::DeviceType::IntegratedGpu => 3,
50 wgpu::DeviceType::Cpu => 2,
51 wgpu::DeviceType::Other => 1,
52 }
53 }
54}
55
56#[derive(Debug)]
57pub struct WgpuAdapter {
58 pub(crate) adapter: wgpu::Adapter,
59 surface: WgpuSurface,
60}
61
62impl WgpuAdapter {
63 pub async fn create_device(self) -> Result<WgpuDevice, (WgpuAdapter, wgpu::RequestDeviceError)> {
64 let result = self.adapter.request_device(
65 &wgpu::DeviceDescriptor {
66 features: wgpu::Features::empty(),
67 limits: wgpu::Limits::default(),
68 label: None,
69 },
70 None
71 ).await;
72
73 match result {
74 Ok((device, queue)) => {
75 let size = self.surface.window.inner_size();
76 if size.width == 0 || size.height == 0 {
77 panic!("Window size is 0. Cannot create device.");
78 }
79
80 let config = wgpu::SurfaceConfiguration {
81 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
82 format: self.surface.surface.get_preferred_format(&self.adapter).unwrap(),
83 width: size.width,
84 height: size.height,
85 present_mode: wgpu::PresentMode::Fifo,
86 };
87
88 self.surface.surface.configure(&device, &config);
89
90 Ok(WgpuDevice {
91 adapter: self,
92 device,
93 queue,
94 config,
95 })
96 }
97 Err(error) => Err((self, error))
98 }
99 }
100}
101
102#[derive(Debug)]
103pub struct WgpuDevice {
104 pub device: wgpu::Device,
105 pub queue: wgpu::Queue,
106 adapter: WgpuAdapter,
107 config: wgpu::SurfaceConfiguration,
108}
109
110impl WgpuDevice {
111 pub fn resize(&mut self, size: winit::dpi::PhysicalSize<u32>) {
112 self.config.width = size.width;
113 self.config.height = size.height;
114 self.adapter.surface.surface.configure(&self.device, &self.config);
115 }
116
117 pub fn request_redraw(&self) {
118 self.adapter.surface.window.request_redraw()
119 }
120
121 pub fn inner_size(&self) -> winit::dpi::PhysicalSize<u32> {
122 self.adapter.surface.window.inner_size()
123 }
124
125 pub fn scale_factor(&self) -> f64 {
126 self.adapter.surface.window.scale_factor()
127 }
128
129 pub fn redraw<F>(&mut self, render: F) -> Result<(), wgpu::SurfaceError>
130 where
131 F: FnOnce(&wgpu::TextureView, &mut wgpu::CommandEncoder)
132 {
133 match self.adapter.surface.surface.get_current_texture() {
134 Ok(output) => {
135 let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default());
136
137 let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
138 label: Some("Render Encoder"),
139 });
140
141 render(&view, &mut encoder);
142
143 self.queue.submit(std::iter::once(encoder.finish()));
145 output.present();
146
147 Ok(())
148 }
149 Err(err @ wgpu::SurfaceError::Lost) => {
151 Err(err)
153 },
154 Err(err @ wgpu::SurfaceError::Outdated) => {
155 Err(err)
157 },
158 Err(err @ wgpu::SurfaceError::OutOfMemory) => {
160 eprintln!("Out of memory. Unable to create render buffer.");
161 Err(err)
162 },
163 Err(e) => {
165 eprintln!("{:?}", e);
166 Err(e)
167 }
168 }
169 }
170
171 pub fn create_pipeline(&mut self, shader_source: &str, bind_group_layouts: &[&wgpu::BindGroupLayout], buffers: &[wgpu::VertexBufferLayout]) -> wgpu::RenderPipeline {
172 let shader = self.device.create_shader_module(&wgpu::ShaderModuleDescriptor {
173 label: Some("Shader"),
174 source: wgpu::ShaderSource::Wgsl(shader_source.into()),
175 });
176
177 let render_pipeline_layout = self.device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
178 label: Some("Render Pipeline Layout"),
179 bind_group_layouts,
180 push_constant_ranges: &[],
181 });
182
183 let render_pipeline = self.device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
184 label: Some("Render Pipeline"),
185 layout: Some(&render_pipeline_layout),
186 vertex: wgpu::VertexState {
187 module: &shader,
188 entry_point: "v_main",
189 buffers,
190 },
191 fragment: Some(wgpu::FragmentState {
192 module: &shader,
193 entry_point: "f_main",
194 targets: &[wgpu::ColorTargetState {
195 format: self.config.format,
196 blend: Some(wgpu::BlendState::ALPHA_BLENDING),
197 write_mask: wgpu::ColorWrites::ALL,
198 }],
199 }),
200 primitive: wgpu::PrimitiveState {
201 topology: wgpu::PrimitiveTopology::TriangleStrip,
202 strip_index_format: None,
203 front_face: wgpu::FrontFace::Cw,
204 cull_mode: Some(wgpu::Face::Back),
205 polygon_mode: wgpu::PolygonMode::Fill,
206 unclipped_depth: false,
207 conservative: false,
208 },
209 depth_stencil: None,
210 multisample: wgpu::MultisampleState {
211 count: 1,
212 mask: !0,
213 alpha_to_coverage_enabled: false,
214 },
215 multiview: None,
216 });
217
218 render_pipeline
219 }
220}