1use crate::prelude::*;
2use crate::core::Mat4Trait;
3use wgpu;
4use winit;
5use std::sync::OnceLock;
6
7#[cfg(not(target_os = "macos"))]
10struct SendableEventLoop(winit::event_loop::EventLoop<()>);
11#[cfg(not(target_os = "macos"))]
12unsafe impl Send for SendableEventLoop {}
13#[cfg(not(target_os = "macos"))]
14unsafe impl Sync for SendableEventLoop {}
15
16#[cfg(not(target_os = "macos"))]
18static GLOBAL_EVENT_LOOP: OnceLock<Mutex<SendableEventLoop>> = OnceLock::new();
19
20#[cfg(not(target_os = "macos"))]
22static WINDOW_EVENTS: OnceLock<Mutex<HashMap<winit::window::WindowId, Vec<crate::core::Event>>>> = OnceLock::new();
23
24pub(crate) struct SharedGpu {
26 pub(crate) instance: Arc<wgpu::Instance>,
27 pub(crate) adapter: Arc<wgpu::Adapter>,
28 pub(crate) device: Arc<wgpu::Device>,
29 pub(crate) queue: Arc<wgpu::Queue>,
30}
31
32const MAX_BUFFERS: usize = 10;
33
34use crate::core::Vertex;
35
36const VERTEX_STRIDE: u64 = std::mem::size_of::<Vertex>() as u64;
37
38const TEXTURE_UV_OFFSET: u64 = 44;
41
42pub mod public {}
47
48#[derive(Debug)]
53pub enum Error {
54 OsError(String),
55 Incompatible(String),
56 NotAvailable(String),
57 WindowCreation(String),
58 WgpuError(String),
59 Unknown,
60}
61
62impl From<wgpu::CreateSurfaceError> for crate::core::Error {
63 fn from(error: wgpu::CreateSurfaceError) -> crate::core::Error {
64 crate::core::Error::BackendError(Error::WindowCreation(format!("Surface creation failed: {:?}", error)))
65 }
66}
67
68#[derive(Hash, PartialEq, Eq, Clone, Copy)]
73struct BlendStateKey(u8, u8, u8, u8, u8, u8);
74
75struct BlitResources {
80 pipeline: Arc<wgpu::RenderPipeline>,
81 bind_group_layout: Arc<wgpu::BindGroupLayout>,
82 nearest_sampler: Arc<wgpu::Sampler>,
83 linear_sampler: Arc<wgpu::Sampler>,
84}
85
86impl BlitResources {
87 fn new(device: &wgpu::Device, format: wgpu::TextureFormat) -> Arc<Self> {
88 let vert = device.create_shader_module(wgpu::ShaderModuleDescriptor {
89 label: Some("Blit Vert"),
90 source: wgpu::ShaderSource::Wgsl(include_str!("../shader/texture.wgsl").into()),
91 });
92 let frag = device.create_shader_module(wgpu::ShaderModuleDescriptor {
93 label: Some("Blit Frag"),
94 source: wgpu::ShaderSource::Wgsl(include_str!("../shader/texture_frag.wgsl").into()),
95 });
96 let bind_group_layout = Arc::new(device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
97 label: Some("Blit BGL"),
98 entries: &[
99 Context::bgl_uniform(0, wgpu::ShaderStages::VERTEX),
100 Context::bgl_texture_2d(1, wgpu::ShaderStages::FRAGMENT),
101 Context::bgl_sampler(2, wgpu::ShaderStages::FRAGMENT),
102 ],
103 }));
104 let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
105 label: Some("Blit Layout"),
106 bind_group_layouts: &[Some(&*bind_group_layout)],
107 immediate_size: 0,
108 });
109 let pipeline = Arc::new(Context::build_texture_pipeline(
110 device, &vert, &frag, &layout, format, wgpu::BlendState::REPLACE,
111 ));
112 let nearest_sampler = Arc::new(device.create_sampler(&wgpu::SamplerDescriptor {
113 label: Some("Blit Nearest"),
114 mag_filter: wgpu::FilterMode::Nearest,
115 min_filter: wgpu::FilterMode::Nearest,
116 address_mode_u: wgpu::AddressMode::ClampToEdge,
117 address_mode_v: wgpu::AddressMode::ClampToEdge,
118 address_mode_w: wgpu::AddressMode::ClampToEdge,
119 ..Default::default()
120 }));
121 let linear_sampler = Arc::new(device.create_sampler(&wgpu::SamplerDescriptor {
122 label: Some("Blit Linear"),
123 mag_filter: wgpu::FilterMode::Linear,
124 min_filter: wgpu::FilterMode::Linear,
125 address_mode_u: wgpu::AddressMode::ClampToEdge,
126 address_mode_v: wgpu::AddressMode::ClampToEdge,
127 address_mode_w: wgpu::AddressMode::ClampToEdge,
128 ..Default::default()
129 }));
130 Arc::new(BlitResources { pipeline, bind_group_layout, nearest_sampler, linear_sampler })
131 }
132}
133
134impl From<&wgpu::BlendState> for BlendStateKey {
135 fn from(bs: &wgpu::BlendState) -> Self {
136 BlendStateKey(
137 bs.color.src_factor as u8,
138 bs.color.dst_factor as u8,
139 bs.color.operation as u8,
140 bs.alpha.src_factor as u8,
141 bs.alpha.dst_factor as u8,
142 bs.alpha.operation as u8,
143 )
144 }
145}
146
147#[cfg(not(target_os = "macos"))]
152struct EventCollector {
154 window_events: Vec<(winit::window::WindowId, crate::core::Event)>,
155 mouse_delta: Option<(i32, i32)>,
157}
158
159#[cfg(not(target_os = "macos"))]
160impl winit::application::ApplicationHandler for EventCollector {
161 fn resumed(&mut self, _: &winit::event_loop::ActiveEventLoop) {}
162
163 fn window_event(
164 &mut self,
165 _: &winit::event_loop::ActiveEventLoop,
166 window_id: winit::window::WindowId,
167 event: winit::event::WindowEvent,
168 ) {
169 use winit::event::{WindowEvent, ElementState};
170 use winit::keyboard::PhysicalKey;
171 use crate::core::Event;
172 let ev = match event {
173 WindowEvent::CloseRequested => Some(Event::Close),
174 WindowEvent::Focused(focused) => Some(if focused { Event::Focus } else { Event::Blur }),
175 WindowEvent::CursorMoved { position, .. } => {
176 Some(Event::MousePosition(position.x as i32, position.y as i32))
177 }
178 WindowEvent::MouseInput { state, button, .. } => {
179 mouse_button_to_id(button)
180 .map(|id| Event::MouseInput(id, state == ElementState::Pressed))
181 }
182 WindowEvent::KeyboardInput {
183 event: winit::event::KeyEvent {
184 physical_key: PhysicalKey::Code(code), state, ..
185 }, ..
186 } => {
187 keycode_to_id(code)
188 .map(|id| Event::KeyboardInput(id, state == ElementState::Pressed))
189 }
190 _ => None,
191 };
192 if let Some(ev) = ev {
193 self.window_events.push((window_id, ev));
194 }
195 }
196
197 fn device_event(
198 &mut self,
199 _: &winit::event_loop::ActiveEventLoop,
200 _: winit::event::DeviceId,
201 event: winit::event::DeviceEvent,
202 ) {
203 if let winit::event::DeviceEvent::MouseMotion { delta } = event {
204 self.mouse_delta = Some((delta.0 as i32, delta.1 as i32));
205 }
206 }
207}
208
209#[cfg(not(target_os = "macos"))]
210fn mouse_button_to_id(button: winit::event::MouseButton) -> Option<usize> {
211 let id = match button {
212 winit::event::MouseButton::Left => 0,
213 winit::event::MouseButton::Right => 1,
214 winit::event::MouseButton::Middle => 2,
215 winit::event::MouseButton::Back => 3,
216 winit::event::MouseButton::Forward => 4,
217 winit::event::MouseButton::Other(n) => {
218 let id = n as usize + 5;
219 if id < crate::core::NUM_BUTTONS { id } else { return None; }
220 }
221 };
222 if id < crate::core::NUM_BUTTONS { Some(id) } else { None }
223}
224
225#[cfg(not(target_os = "macos"))]
226fn keycode_to_id(code: winit::keyboard::KeyCode) -> Option<usize> {
227 use winit::keyboard::KeyCode;
228 use crate::core::InputId;
229 let id = match code {
230 KeyCode::Digit1 => InputId::Key1 as usize,
232 KeyCode::Digit2 => InputId::Key2 as usize,
233 KeyCode::Digit3 => InputId::Key3 as usize,
234 KeyCode::Digit4 => InputId::Key4 as usize,
235 KeyCode::Digit5 => InputId::Key5 as usize,
236 KeyCode::Digit6 => InputId::Key6 as usize,
237 KeyCode::Digit7 => InputId::Key7 as usize,
238 KeyCode::Digit8 => InputId::Key8 as usize,
239 KeyCode::Digit9 => InputId::Key9 as usize,
240 KeyCode::Digit0 => InputId::Key0 as usize,
241 KeyCode::KeyA => InputId::A as usize,
243 KeyCode::KeyB => InputId::B as usize,
244 KeyCode::KeyC => InputId::C as usize,
245 KeyCode::KeyD => InputId::D as usize,
246 KeyCode::KeyE => InputId::E as usize,
247 KeyCode::KeyF => InputId::F as usize,
248 KeyCode::KeyG => InputId::G as usize,
249 KeyCode::KeyH => InputId::H as usize,
250 KeyCode::KeyI => InputId::I as usize,
251 KeyCode::KeyJ => InputId::J as usize,
252 KeyCode::KeyK => InputId::K as usize,
253 KeyCode::KeyL => InputId::L as usize,
254 KeyCode::KeyM => InputId::M as usize,
255 KeyCode::KeyN => InputId::N as usize,
256 KeyCode::KeyO => InputId::O as usize,
257 KeyCode::KeyP => InputId::P as usize,
258 KeyCode::KeyQ => InputId::Q as usize,
259 KeyCode::KeyR => InputId::R as usize,
260 KeyCode::KeyS => InputId::S as usize,
261 KeyCode::KeyT => InputId::T as usize,
262 KeyCode::KeyU => InputId::U as usize,
263 KeyCode::KeyV => InputId::V as usize,
264 KeyCode::KeyW => InputId::W as usize,
265 KeyCode::KeyX => InputId::X as usize,
266 KeyCode::KeyY => InputId::Y as usize,
267 KeyCode::KeyZ => InputId::Z as usize,
268 KeyCode::Escape => InputId::Escape as usize,
270 KeyCode::F1 => InputId::F1 as usize,
271 KeyCode::F2 => InputId::F2 as usize,
272 KeyCode::F3 => InputId::F3 as usize,
273 KeyCode::F4 => InputId::F4 as usize,
274 KeyCode::F5 => InputId::F5 as usize,
275 KeyCode::F6 => InputId::F6 as usize,
276 KeyCode::F7 => InputId::F7 as usize,
277 KeyCode::F8 => InputId::F8 as usize,
278 KeyCode::F9 => InputId::F9 as usize,
279 KeyCode::F10 => InputId::F10 as usize,
280 KeyCode::F11 => InputId::F11 as usize,
281 KeyCode::F12 => InputId::F12 as usize,
282 KeyCode::F13 => InputId::F13 as usize,
283 KeyCode::F14 => InputId::F14 as usize,
284 KeyCode::F15 => InputId::F15 as usize,
285 KeyCode::PrintScreen => InputId::Snapshot as usize,
286 KeyCode::ScrollLock => InputId::Scroll as usize,
287 KeyCode::Pause => InputId::Pause as usize,
288 KeyCode::Insert => InputId::Insert as usize,
289 KeyCode::Home => InputId::Home as usize,
290 KeyCode::Delete => InputId::Delete as usize,
291 KeyCode::End => InputId::End as usize,
292 KeyCode::PageDown => InputId::PageDown as usize,
293 KeyCode::PageUp => InputId::PageUp as usize,
294 KeyCode::ArrowLeft => InputId::CursorLeft as usize,
295 KeyCode::ArrowUp => InputId::CursorUp as usize,
296 KeyCode::ArrowRight => InputId::CursorRight as usize,
297 KeyCode::ArrowDown => InputId::CursorDown as usize,
298 KeyCode::Backspace => InputId::Backspace as usize,
299 KeyCode::Enter => InputId::Return as usize,
300 KeyCode::Space => InputId::Space as usize,
301 KeyCode::NumLock => InputId::Numlock as usize,
303 KeyCode::Numpad0 => InputId::Numpad0 as usize,
304 KeyCode::Numpad1 => InputId::Numpad1 as usize,
305 KeyCode::Numpad2 => InputId::Numpad2 as usize,
306 KeyCode::Numpad3 => InputId::Numpad3 as usize,
307 KeyCode::Numpad4 => InputId::Numpad4 as usize,
308 KeyCode::Numpad5 => InputId::Numpad5 as usize,
309 KeyCode::Numpad6 => InputId::Numpad6 as usize,
310 KeyCode::Numpad7 => InputId::Numpad7 as usize,
311 KeyCode::Numpad8 => InputId::Numpad8 as usize,
312 KeyCode::Numpad9 => InputId::Numpad9 as usize,
313 KeyCode::NumpadAdd => InputId::Add as usize,
314 KeyCode::NumpadDecimal => InputId::Decimal as usize,
315 KeyCode::NumpadDivide => InputId::Divide as usize,
316 KeyCode::NumpadMultiply=> InputId::Multiply as usize,
317 KeyCode::NumpadSubtract=> InputId::Subtract as usize,
318 KeyCode::NumpadEnter => InputId::NumpadEnter as usize,
319 KeyCode::NumpadEqual => InputId::NumpadEquals as usize,
320 KeyCode::NumpadComma => InputId::NumpadComma as usize,
321 KeyCode::Quote => InputId::Apostrophe as usize,
323 KeyCode::Backslash => InputId::Backslash as usize,
324 KeyCode::CapsLock => InputId::Capital as usize,
325 KeyCode::Comma => InputId::Comma as usize,
326 KeyCode::Convert => InputId::Convert as usize,
327 KeyCode::Equal => InputId::Equals as usize,
328 KeyCode::Backquote => InputId::Grave as usize,
329 KeyCode::KanaMode => InputId::Kana as usize,
330 KeyCode::AltLeft => InputId::LAlt as usize,
331 KeyCode::BracketLeft => InputId::LBracket as usize,
332 KeyCode::ControlLeft => InputId::LControl as usize,
333 KeyCode::ShiftLeft => InputId::LShift as usize,
334 KeyCode::SuperLeft => InputId::LWin as usize,
335 KeyCode::LaunchMail => InputId::Mail as usize,
336 KeyCode::MediaSelect => InputId::MediaSelect as usize,
337 KeyCode::MediaStop => InputId::MediaStop as usize,
338 KeyCode::Minus => InputId::Minus as usize,
339 KeyCode::AudioVolumeMute => InputId::Mute as usize,
340 KeyCode::MediaTrackNext => InputId::NextTrack as usize,
341 KeyCode::NonConvert => InputId::NoConvert as usize,
342 KeyCode::IntlBackslash => InputId::OEM102 as usize,
343 KeyCode::Period => InputId::Period as usize,
344 KeyCode::MediaPlayPause => InputId::PlayPause as usize,
345 KeyCode::Power => InputId::Power as usize,
346 KeyCode::MediaTrackPrevious => InputId::PrevTrack as usize,
347 KeyCode::AltRight => InputId::RAlt as usize,
348 KeyCode::BracketRight => InputId::RBracket as usize,
349 KeyCode::ControlRight => InputId::RControl as usize,
350 KeyCode::ShiftRight => InputId::RShift as usize,
351 KeyCode::SuperRight => InputId::RWin as usize,
352 KeyCode::Semicolon => InputId::Semicolon as usize,
353 KeyCode::Slash => InputId::Slash as usize,
354 KeyCode::Sleep => InputId::Sleep as usize,
355 KeyCode::Tab => InputId::Tab as usize,
356 KeyCode::AudioVolumeDown => InputId::VolumeDown as usize,
357 KeyCode::AudioVolumeUp => InputId::VolumeUp as usize,
358 KeyCode::WakeUp => InputId::Wake as usize,
359 KeyCode::BrowserBack => InputId::WebBack as usize,
360 KeyCode::BrowserFavorites => InputId::WebFavorites as usize,
361 KeyCode::BrowserForward => InputId::WebForward as usize,
362 KeyCode::BrowserHome => InputId::WebHome as usize,
363 KeyCode::BrowserRefresh => InputId::WebRefresh as usize,
364 KeyCode::BrowserSearch => InputId::WebSearch as usize,
365 KeyCode::BrowserStop => InputId::WebStop as usize,
366 KeyCode::IntlYen => InputId::Yen as usize,
367 KeyCode::ContextMenu => InputId::Apps as usize,
368 _ => return None,
369 };
370 Some(id)
371}
372
373#[derive(Clone)]
378pub struct Display {
379 inner: Arc<DisplayInner>,
380}
381
382struct DisplayInner {
383 window: Arc<winit::window::Window>,
384 instance: Arc<wgpu::Instance>,
385 adapter: Arc<wgpu::Adapter>,
386 surface: wgpu::Surface<'static>,
387 device: Arc<wgpu::Device>,
388 queue: Arc<wgpu::Queue>,
389 config: Mutex<wgpu::SurfaceConfiguration>,
390 blit_resources: Arc<BlitResources>,
391}
392
393impl Display {
394 pub fn new(descriptor: crate::core::DisplayBuilder, shared: Option<SharedGpu>) -> crate::core::Result<Display> {
395 pollster::block_on(Self::create_async(descriptor, shared))
396 }
397
398 async fn create_async(descriptor: crate::core::DisplayBuilder, shared: Option<SharedGpu>) -> crate::core::Result<Display> {
399
400 #[cfg(not(target_os = "macos"))]
402 if GLOBAL_EVENT_LOOP.get().is_none() {
403 let el_result = {
404 #[cfg(target_os = "linux")]
405 {
406 use winit::platform::x11::EventLoopBuilderExtX11;
407 winit::event_loop::EventLoop::builder()
408 .with_any_thread(true)
409 .build()
410 }
411 #[cfg(not(target_os = "linux"))]
412 {
413 winit::event_loop::EventLoop::builder().build()
414 }
415 };
416 match el_result {
417 Ok(el) => { let _ = GLOBAL_EVENT_LOOP.set(Mutex::new(SendableEventLoop(el))); }
418 Err(_) if GLOBAL_EVENT_LOOP.get().is_some() => {} Err(e) => return Err(crate::core::Error::BackendError(Error::OsError(
420 format!("Failed to create event loop: {:?}", e)
421 ))),
422 }
423 }
424
425 let window_attributes = winit::window::WindowAttributes::default()
426 .with_inner_size(winit::dpi::PhysicalSize::new(descriptor.width, descriptor.height))
427 .with_title(&descriptor.title)
428 .with_transparent(descriptor.transparent)
429 .with_decorations(descriptor.decorations)
430 .with_visible(descriptor.visible)
431 .with_resizable(true);
432
433 #[allow(deprecated)]
434 let window = {
435 #[cfg(not(target_os = "macos"))]
436 {
437 GLOBAL_EVENT_LOOP.get()
438 .ok_or_else(|| crate::core::Error::BackendError(Error::OsError("Event loop unavailable".to_string())))?
439 .lock().unwrap().0
440 .create_window(window_attributes)
441 .map_err(|e| crate::core::Error::BackendError(Error::WindowCreation(format!("Failed to create window: {:?}", e))))?
442 }
443 #[cfg(target_os = "macos")]
444 {
445 return Err(crate::core::Error::BackendError(Error::NotAvailable(
446 "Multi-window / non-main-thread display creation not supported on macOS".to_string()
447 )));
448 }
449 };
450 let window = Arc::new(window);
451
452 let (instance, adapter, device, queue) = if let Some(s) = shared {
454 (s.instance, s.adapter, s.device, s.queue)
455 } else {
456 let instance = Arc::new(wgpu::Instance::new(wgpu::InstanceDescriptor {
457 backends: wgpu::Backends::all(),
458 ..wgpu::InstanceDescriptor::new_without_display_handle()
459 }));
460
461 let tmp_surface = instance.create_surface(window.clone()).map_err(crate::core::Error::from)?;
463
464 let adapter = Arc::new(instance.request_adapter(&wgpu::RequestAdapterOptions {
465 power_preference: if descriptor.vsync {
466 wgpu::PowerPreference::HighPerformance
467 } else {
468 wgpu::PowerPreference::LowPower
469 },
470 force_fallback_adapter: false,
471 compatible_surface: Some(&tmp_surface),
472 }).await.map_err(|_| {
473 crate::core::Error::BackendError(Error::NotAvailable("No suitable GPU adapter found".to_string()))
474 })?);
475
476 let (device, queue) = adapter.request_device(&wgpu::DeviceDescriptor {
477 label: Some("Radiant Device"),
478 required_features: wgpu::Features::empty(),
479 required_limits: wgpu::Limits::default(),
480 ..Default::default()
481 }).await.map_err(|e| {
482 crate::core::Error::BackendError(Error::WgpuError(format!("Failed to request device: {:?}", e)))
483 })?;
484
485 (instance, adapter, Arc::new(device), Arc::new(queue))
486 };
487
488 let surface = instance.create_surface(window.clone()).map_err(crate::core::Error::from)?;
489
490 let size = window.inner_size();
491 let surface_caps = surface.get_capabilities(&*adapter);
492
493 let present_mode = if descriptor.vsync {
494 if surface_caps.present_modes.contains(&wgpu::PresentMode::Mailbox) {
495 wgpu::PresentMode::Mailbox
496 } else {
497 wgpu::PresentMode::Fifo
498 }
499 } else if surface_caps.present_modes.contains(&wgpu::PresentMode::Immediate) {
500 wgpu::PresentMode::Immediate
501 } else {
502 wgpu::PresentMode::FifoRelaxed
503 };
504
505 let config = wgpu::SurfaceConfiguration {
506 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
507 format: surface_caps.formats[0],
508 width: size.width,
509 height: size.height,
510 present_mode,
511 desired_maximum_frame_latency: 2,
512 alpha_mode: wgpu::CompositeAlphaMode::Auto,
513 view_formats: vec![],
514 };
515 surface.configure(&*device, &config);
516
517 let blit_resources = BlitResources::new(&device, config.format);
518
519 let inner = Arc::new(DisplayInner {
520 window,
521 instance,
522 adapter,
523 surface,
524 device,
525 queue,
526 config: Mutex::new(config),
527 blit_resources,
528 });
529
530 Ok(Display { inner })
531 }
532
533 pub fn draw(self: &Self) -> WgpuFrame {
534 let frame = loop {
535 match self.inner.surface.get_current_texture() {
536 wgpu::CurrentSurfaceTexture::Success(frame) | wgpu::CurrentSurfaceTexture::Suboptimal(frame) => break frame,
537 wgpu::CurrentSurfaceTexture::Outdated | wgpu::CurrentSurfaceTexture::Lost => {
538 let size = self.inner.window.inner_size();
539 let mut config = self.inner.config.lock().unwrap();
540 config.width = size.width.max(1);
541 config.height = size.height.max(1);
542 self.inner.surface.configure(&self.inner.device, &config);
543 }
544 e => panic!("Failed to get swapchain frame: {:?}", e),
545 }
546 };
547 let view = Arc::new(frame.texture.create_view(&wgpu::TextureViewDescriptor::default()));
548
549 let command_encoder = self.inner.device.create_command_encoder(
550 &wgpu::CommandEncoderDescriptor { label: Some("Radiant Command Encoder") }
551 );
552
553 WgpuFrame {
554 view,
555 command_encoder,
556 surface_texture: frame,
557 clear_color: None,
558 first_pass: true,
559 device: self.inner.device.clone(),
560 queue: self.inner.queue.clone(),
561 blit_resources: self.inner.blit_resources.clone(),
562 }
563 }
564
565 pub fn framebuffer_dimensions(self: &Self) -> crate::core::Point2<u32> {
566 let size = self.inner.window.inner_size();
567 (size.width, size.height)
568 }
569
570 pub fn window_dimensions(self: &Self) -> crate::core::Point2<u32> {
571 let size = self.inner.window.inner_size();
572 (size.width, size.height)
573 }
574
575 pub fn set_cursor_position(self: &Self, position: crate::core::Point2<i32>) {
576 let _ = self.inner.window.set_cursor_position(winit::dpi::PhysicalPosition::new(position.0 as f64, position.1 as f64));
577 }
578
579 pub fn set_cursor_state(self: &Self, state: crate::core::CursorState) {
580 use crate::core::CursorState as CS;
581 use winit::window::CursorGrabMode;
582 match state {
583 CS::Normal => {
584 self.inner.window.set_cursor_grab(CursorGrabMode::None).ok();
585 self.inner.window.set_cursor_visible(true);
586 }
587 CS::Hide => {
588 self.inner.window.set_cursor_visible(false);
589 }
590 CS::Grab => {
591 self.inner.window.set_cursor_grab(CursorGrabMode::Confined).ok();
592 self.inner.window.set_cursor_visible(false);
593 }
594 }
595 }
596
597 pub fn set_fullscreen(self: &Self, monitor: Option<crate::core::Monitor>) -> bool {
598 if let Some(monitor) = monitor {
599 self.inner.window.set_fullscreen(Some(winit::window::Fullscreen::Borderless(Some(monitor.inner.inner.winit_monitor))));
600 } else {
601 self.inner.window.set_fullscreen(None);
602 }
603 true
604 }
605
606 pub fn poll_events<F>(self: &Self, mut callback: F) where F: FnMut(crate::core::Event) {
607 #[cfg(not(target_os = "macos"))]
608 {
609 use winit::platform::pump_events::EventLoopExtPumpEvents;
610
611 let collected = GLOBAL_EVENT_LOOP.get().and_then(|el_mutex| {
614 el_mutex.lock().ok().map(|mut el| {
615 let mut collector = EventCollector {
616 window_events: Vec::new(),
617 mouse_delta: None,
618 };
619 el.0.pump_app_events(Some(std::time::Duration::ZERO), &mut collector);
620 collector
621 })
622 });
623
624 let queues = WINDOW_EVENTS.get_or_init(|| Mutex::new(HashMap::new()));
626 if let Some(collector) = collected {
627 if let Ok(mut map) = queues.lock() {
628 for (win_id, event) in collector.window_events {
629 map.entry(win_id).or_default().push(event);
630 }
631 if let Some((dx, dy)) = collector.mouse_delta {
632 for events in map.values_mut() {
633 events.push(crate::core::Event::MouseDelta(dx, dy));
634 }
635 }
636 }
637 }
638
639 if let Ok(mut map) = queues.lock() {
641 if let Some(events) = map.remove(&self.inner.window.id()) {
642 for event in events {
643 callback(event);
644 }
645 }
646 }
647 }
648 #[cfg(target_os = "macos")]
650 let _ = callback;
651 }
652
653 pub fn show(self: &Self) {
654 self.inner.window.set_visible(true);
655 }
656
657 pub fn hide(self: &Self) {
658 self.inner.window.set_visible(false);
659 }
660
661 pub fn set_title(self: &Self, title: &str) {
662 self.inner.window.set_title(title);
663 }
664}
665
666pub struct WgpuFrame {
671 view: Arc<wgpu::TextureView>,
672 command_encoder: wgpu::CommandEncoder,
673 surface_texture: wgpu::SurfaceTexture,
674 clear_color: Option<[f64; 4]>,
675 first_pass: bool,
676 device: Arc<wgpu::Device>,
677 queue: Arc<wgpu::Queue>,
678 blit_resources: Arc<BlitResources>,
679}
680
681impl WgpuFrame {
682 pub fn clear(self: &mut Self, color: crate::core::Color) {
683 let crate::core::Color(r, g, b, a) = color;
684 self.clear_color = Some([r as f64, g as f64, b as f64, a as f64]);
685 }
686
687 pub fn finish(mut self: Self) {
688 if self.first_pass {
690 if let Some(c) = self.clear_color {
691 let encoder = &mut self.command_encoder;
692 let view = &*self.view;
693 let _pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
694 label: Some("Clear Pass"),
695 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
696 view,
697 depth_slice: None,
698 resolve_target: None,
699 ops: wgpu::Operations {
700 load: wgpu::LoadOp::Clear(wgpu::Color { r: c[0], g: c[1], b: c[2], a: c[3] }),
701 store: wgpu::StoreOp::Store,
702 },
703 })],
704 depth_stencil_attachment: None,
705 timestamp_writes: None,
706 occlusion_query_set: None,
707 multiview_mask: None,
708 });
709 }
710 }
711 self.queue.submit(std::iter::once(self.command_encoder.finish()));
712 self.surface_texture.present();
713 }
716
717 pub fn dimensions(self: &Self) -> crate::core::Point2<u32> {
718 let size = self.surface_texture.texture.size();
719 (size.width, size.height)
720 }
721
722 pub fn copy_from_texture(self: &mut Self, source: &crate::core::Texture, filter: crate::core::TextureFilter) {
723 use crate::core::Mat4Trait;
724 let uniforms = TextureUniforms {
725 u_view: <[[f32; 4]; 4] as Mat4Trait<f32>>::viewport(1.0, 1.0),
726 u_model: <[[f32; 4]; 4] as Mat4Trait<f32>>::identity(),
727 _rd_color: [1.0, 1.0, 1.0, 1.0],
728 _rd_offset: [0.0, 0.0],
729 _rd_dimensions: [1.0, 1.0],
730 _rd_flags: [0.0, 0.0, 0.0, 0.0],
731 };
732 let load_op = self.next_load_op();
733 let view = self.view.clone();
734 let blit = self.blit_resources.clone();
735 let sampler = Context::sampler_for_filter(&blit.nearest_sampler, &blit.linear_sampler, filter);
736 render_texture_quad(
737 &mut self.command_encoder,
738 &view,
739 &self.device,
740 &self.queue,
741 &uniforms,
742 &BLIT_QUAD,
743 &*source.handle.view,
744 sampler,
745 &blit.pipeline,
746 &blit.bind_group_layout,
747 load_op,
748 );
749 }
750
751 pub fn copy_rect(self: &mut Self, _source_rect: crate::core::Rect<i32>, _target_rect: crate::core::Rect<i32>, _filter: crate::core::TextureFilter) {
752 }
754
755 pub fn copy_rect_from_texture(self: &mut Self, source: &crate::core::Texture, source_rect: crate::core::Rect<i32>, target_rect: crate::core::Rect<i32>, filter: crate::core::TextureFilter) {
756 use crate::core::Mat4Trait;
757
758 let tw = source.handle.width as f32;
759 let th = source.handle.height as f32;
760 let frame_size = self.surface_texture.texture.size();
761 let fw = frame_size.width as f32;
762 let fh = frame_size.height as f32;
763
764 let sx = (source_rect.0).0 as f32;
766 let sy = (source_rect.0).1 as f32;
767 let sw = (source_rect.1).0 as f32;
768 let sh = (source_rect.1).1 as f32;
769 let u0 = sx / tw;
770 let u1 = (sx + sw) / tw;
771 let v0 = sy / th;
772 let v1 = (sy + sh) / th;
773
774 let dx = (target_rect.0).0 as f32;
776 let dy = (target_rect.0).1 as f32;
777 let dw = (target_rect.1).0 as f32;
778 let dh = (target_rect.1).1 as f32;
779
780 let quad = [
781 Vertex { position: [0.0, 0.0], texture_uv: [u0, v0], ..Vertex::default() },
782 Vertex { position: [1.0, 0.0], texture_uv: [u1, v0], ..Vertex::default() },
783 Vertex { position: [0.0, 1.0], texture_uv: [u0, v1], ..Vertex::default() },
784 Vertex { position: [1.0, 1.0], texture_uv: [u1, v1], ..Vertex::default() },
785 ];
786 let uniforms = TextureUniforms {
787 u_view: <[[f32; 4]; 4] as Mat4Trait<f32>>::viewport(1.0, 1.0),
788 u_model: <[[f32; 4]; 4] as Mat4Trait<f32>>::identity(),
789 _rd_color: [1.0, 1.0, 1.0, 1.0],
790 _rd_offset: [dx / fw, dy / fh],
791 _rd_dimensions: [dw / fw, dh / fh],
792 _rd_flags: [0.0, 0.0, 0.0, 0.0],
793 };
794 let load_op = self.next_load_op();
795 let view = self.view.clone();
796 let blit = self.blit_resources.clone();
797 let sampler = Context::sampler_for_filter(&blit.nearest_sampler, &blit.linear_sampler, filter);
798 render_texture_quad(
799 &mut self.command_encoder,
800 &view,
801 &self.device,
802 &self.queue,
803 &uniforms,
804 &quad,
805 &*source.handle.view,
806 sampler,
807 &blit.pipeline,
808 &blit.bind_group_layout,
809 load_op,
810 );
811 }
812
813 fn next_load_op(&mut self) -> wgpu::LoadOp<wgpu::Color> {
815 if self.first_pass {
816 self.first_pass = false;
817 if let Some(c) = self.clear_color {
818 return wgpu::LoadOp::Clear(wgpu::Color { r: c[0], g: c[1], b: c[2], a: c[3] });
819 }
820 }
821 wgpu::LoadOp::Load
822 }
823
824 pub fn draw_sprites(
826 &mut self,
827 vertices: &[Vertex],
828 dirty: bool,
829 layer_hint: usize,
830 component: u32,
831 blend: wgpu::BlendState,
832 font_texture_view: &wgpu::TextureView,
833 tex_arrays: &[crate::core::RawFrameArray],
834 sprite_uniforms: &SpriteUniforms,
835 context: &mut Context,
836 custom_program: Option<&Program>,
837 ) {
838 let num_vertices = vertices.len();
839 let num_sprites = num_vertices / 4;
840 if num_vertices == 0 {
841 return;
842 }
843
844 let (vb_index, vb_dirty) = context.select_vertex_buffer(layer_hint, num_vertices);
845
846 if dirty || vb_dirty {
847 let vertex_bytes = unsafe {
848 std::slice::from_raw_parts(
849 vertices.as_ptr() as *const u8,
850 num_vertices * std::mem::size_of::<Vertex>(),
851 )
852 };
853 self.queue.write_buffer(&context.vertex_buffers[vb_index].buffer, 0, vertex_bytes);
854 }
855
856 context.update_index_buffer(num_sprites);
857
858 let uniform_bytes = unsafe {
859 std::slice::from_raw_parts(
860 sprite_uniforms as *const SpriteUniforms as *const u8,
861 std::mem::size_of::<SpriteUniforms>(),
862 )
863 };
864 let uniform_buffer = self.device.create_buffer(&wgpu::BufferDescriptor {
865 label: Some("Sprite Uniform Buffer"),
866 size: uniform_bytes.len() as u64,
867 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
868 mapped_at_creation: false,
869 });
870 self.queue.write_buffer(&uniform_buffer, 0, uniform_bytes);
871
872 let comp_data = [component, 0u32, 0u32, 0u32];
874 let comp_bytes = unsafe {
875 std::slice::from_raw_parts(comp_data.as_ptr() as *const u8, 16)
876 };
877 let comp_buffer = self.device.create_buffer(&wgpu::BufferDescriptor {
878 label: Some("Component Uniform Buffer"),
879 size: 16,
880 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
881 mapped_at_creation: false,
882 });
883 self.queue.write_buffer(&comp_buffer, 0, comp_bytes);
884
885 let sprite_bgl = custom_program.map_or(&context.sprite_bind_group_layout, |p| &*p.bind_group_layout);
886 let bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
887 label: Some("Sprite Bind Group"),
888 layout: sprite_bgl,
889 entries: &[
890 wgpu::BindGroupEntry {
891 binding: 0,
892 resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding { buffer: &uniform_buffer, offset: 0, size: None }),
893 },
894 wgpu::BindGroupEntry {
895 binding: 1,
896 resource: wgpu::BindingResource::TextureView(font_texture_view),
897 },
898 wgpu::BindGroupEntry {
899 binding: 2,
900 resource: wgpu::BindingResource::Sampler(&context.nearest_sampler),
901 },
902 wgpu::BindGroupEntry { binding: 3, resource: wgpu::BindingResource::TextureView(&tex_arrays[1].data.view) },
903 wgpu::BindGroupEntry { binding: 4, resource: wgpu::BindingResource::TextureView(&tex_arrays[2].data.view) },
904 wgpu::BindGroupEntry { binding: 5, resource: wgpu::BindingResource::TextureView(&tex_arrays[3].data.view) },
905 wgpu::BindGroupEntry { binding: 6, resource: wgpu::BindingResource::TextureView(&tex_arrays[4].data.view) },
906 wgpu::BindGroupEntry { binding: 7, resource: wgpu::BindingResource::TextureView(&tex_arrays[5].data.view) },
907 wgpu::BindGroupEntry {
908 binding: 8,
909 resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding { buffer: &comp_buffer, offset: 0, size: None }),
910 },
911 ],
912 });
913
914 let pipeline = match custom_program {
915 Some(prog) => prog.get_or_create_pipeline(blend, context.format),
916 None => context.get_or_create_sprite_pipeline(blend, context.format),
917 };
918 let load_op = self.next_load_op();
919 let view = self.view.clone();
920
921 let mut render_pass = self.command_encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
922 label: Some("Radiant Sprite Pass"),
923 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
924 view: &view,
925 depth_slice: None,
926 resolve_target: None,
927 ops: wgpu::Operations { load: load_op, store: wgpu::StoreOp::Store },
928 })],
929 depth_stencil_attachment: None,
930 timestamp_writes: None,
931 occlusion_query_set: None,
932 multiview_mask: None,
933 });
934
935 render_pass.set_pipeline(&pipeline);
936 render_pass.set_blend_constant(wgpu::Color::TRANSPARENT);
937 render_pass.set_bind_group(0, &bind_group, &[]);
938 render_pass.set_vertex_buffer(0, context.vertex_buffers[vb_index].buffer.slice(..));
939 render_pass.set_index_buffer(context.index_buffer.slice(..), wgpu::IndexFormat::Uint32);
940 render_pass.draw_indexed(0..(num_sprites as u32 * 6), 0, 0..1);
941 }
942
943 pub fn draw_quad(
945 &mut self,
946 uniforms: &TextureUniforms,
947 tex_view: &wgpu::TextureView,
948 filter: crate::core::TextureFilter,
949 blend: wgpu::BlendState,
950 context: &mut Context,
951 ) {
952 let pipeline = context.get_or_create_texture_pipeline(blend, context.format);
953 let sampler = Context::sampler_for_filter(&context.nearest_sampler, &context.linear_sampler, filter);
954 let load_op = self.next_load_op();
955 let view = self.view.clone();
956 render_texture_quad(
957 &mut self.command_encoder,
958 &view,
959 &self.device,
960 &self.queue,
961 uniforms,
962 &BLIT_QUAD,
963 tex_view,
964 sampler,
965 &pipeline,
966 &context.texture_bind_group_layout,
967 load_op,
968 );
969 }
970}
971
972pub type Frame = WgpuFrame;
973
974pub(crate) enum ProgramKind {
979 Default,
980 Combine,
981 Sprite,
982}
983
984pub struct Program {
985 pub(crate) kind: ProgramKind,
986 pub(crate) is_custom: bool,
987 device: Arc<wgpu::Device>,
988 vert_module: Arc<wgpu::ShaderModule>,
989 frag_module: Arc<wgpu::ShaderModule>,
990 pub(crate) bind_group_layout: Arc<wgpu::BindGroupLayout>,
991 pipeline_layout: Arc<wgpu::PipelineLayout>,
992 pipelines: std::sync::Mutex<HashMap<(BlendStateKey, wgpu::TextureFormat), Arc<wgpu::RenderPipeline>>>,
993 pub(crate) sampler: Arc<wgpu::Sampler>,
994}
995
996impl Program {
997 pub fn new(context: &Context, fragment_shader: &str) -> crate::core::Result<Program> {
998 Self::new_inner(context, fragment_shader, true)
999 }
1000
1001 fn new_inner(context: &Context, fragment_shader: &str, is_custom: bool) -> crate::core::Result<Program> {
1002 let device = &context.device;
1003
1004 let kind = if fragment_shader.contains("SpriteFragmentInput") {
1005 ProgramKind::Sprite
1006 } else if fragment_shader.contains("sample4") {
1007 ProgramKind::Combine
1008 } else {
1009 ProgramKind::Default
1010 };
1011
1012 let vert_module = Arc::new(device.create_shader_module(wgpu::ShaderModuleDescriptor {
1013 label: Some("Custom Vert"),
1014 source: wgpu::ShaderSource::Wgsl(match kind {
1015 ProgramKind::Sprite => include_str!("../shader/sprite.wgsl").into(),
1016 _ => include_str!("../shader/texture.wgsl").into(),
1017 }),
1018 }));
1019
1020 let frag_module = Arc::new(match kind {
1021 ProgramKind::Sprite | ProgramKind::Default => device.create_shader_module(wgpu::ShaderModuleDescriptor {
1022 label: Some("Custom Frag"),
1023 source: wgpu::ShaderSource::Wgsl(fragment_shader.into()),
1024 }),
1025 ProgramKind::Combine => device.create_shader_module(wgpu::ShaderModuleDescriptor {
1026 label: Some("Combine Frag"),
1027 source: wgpu::ShaderSource::Wgsl(fragment_shader.into()),
1028 }),
1029 });
1030
1031 let vert_frag = wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT;
1032 let uniform_visibility = if is_custom { vert_frag } else { wgpu::ShaderStages::VERTEX };
1035
1036 let bind_group_layout = Arc::new(match kind {
1037 ProgramKind::Combine => device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
1038 label: Some("Combine BGL"),
1039 entries: &[
1040 wgpu::BindGroupLayoutEntry {
1041 binding: 0,
1042 visibility: vert_frag,
1043 ty: wgpu::BindingType::Buffer {
1044 ty: wgpu::BufferBindingType::Uniform,
1045 has_dynamic_offset: false,
1046 min_binding_size: None,
1047 },
1048 count: None,
1049 },
1050 Context::bgl_texture_2d(1, wgpu::ShaderStages::FRAGMENT),
1051 Context::bgl_texture_2d(2, wgpu::ShaderStages::FRAGMENT),
1052 Context::bgl_texture_2d(3, wgpu::ShaderStages::FRAGMENT),
1053 Context::bgl_texture_2d(4, wgpu::ShaderStages::FRAGMENT),
1054 Context::bgl_texture_2d(5, wgpu::ShaderStages::FRAGMENT),
1055 Context::bgl_sampler(6, wgpu::ShaderStages::FRAGMENT),
1056 ],
1057 }),
1058 ProgramKind::Default => device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
1059 label: Some("Default Custom BGL"),
1060 entries: &[
1061 wgpu::BindGroupLayoutEntry {
1062 binding: 0,
1063 visibility: uniform_visibility,
1064 ty: wgpu::BindingType::Buffer {
1065 ty: wgpu::BufferBindingType::Uniform,
1066 has_dynamic_offset: false,
1067 min_binding_size: None,
1068 },
1069 count: None,
1070 },
1071 Context::bgl_texture_2d(1, wgpu::ShaderStages::FRAGMENT),
1072 Context::bgl_sampler(2, wgpu::ShaderStages::FRAGMENT),
1073 ],
1074 }),
1075 ProgramKind::Sprite => device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
1076 label: Some("Custom Sprite BGL"),
1077 entries: &[
1078 Context::bgl_uniform(0, wgpu::ShaderStages::VERTEX),
1079 Context::bgl_texture_2d(1, wgpu::ShaderStages::FRAGMENT),
1080 Context::bgl_sampler(2, wgpu::ShaderStages::FRAGMENT),
1081 Context::bgl_texture_2d_array(3, wgpu::ShaderStages::FRAGMENT),
1082 Context::bgl_texture_2d_array(4, wgpu::ShaderStages::FRAGMENT),
1083 Context::bgl_texture_2d_array(5, wgpu::ShaderStages::FRAGMENT),
1084 Context::bgl_texture_2d_array(6, wgpu::ShaderStages::FRAGMENT),
1085 Context::bgl_texture_2d_array(7, wgpu::ShaderStages::FRAGMENT),
1086 Context::bgl_uniform(8, wgpu::ShaderStages::FRAGMENT),
1087 ],
1088 }),
1089 });
1090
1091 let pipeline_layout = Arc::new(device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
1092 label: Some("Custom Pipeline Layout"),
1093 bind_group_layouts: &[Some(&*bind_group_layout)],
1094 immediate_size: 0,
1095 }));
1096
1097 let sampler = Arc::new(device.create_sampler(&wgpu::SamplerDescriptor {
1098 label: Some("Custom Program Sampler"),
1099 mag_filter: wgpu::FilterMode::Linear,
1100 min_filter: wgpu::FilterMode::Linear,
1101 address_mode_u: wgpu::AddressMode::ClampToEdge,
1102 address_mode_v: wgpu::AddressMode::ClampToEdge,
1103 address_mode_w: wgpu::AddressMode::ClampToEdge,
1104 ..Default::default()
1105 }));
1106
1107 Ok(Program {
1108 kind,
1109 is_custom,
1110 device: context.device.clone(),
1111 vert_module,
1112 frag_module,
1113 bind_group_layout,
1114 pipeline_layout,
1115 pipelines: std::sync::Mutex::new(HashMap::new()),
1116 sampler,
1117 })
1118 }
1119
1120 pub fn new_default_texture(context: &Context) -> crate::core::Result<Program> {
1121 Self::new_inner(context, include_str!("../shader/texture_frag.wgsl"), false)
1122 }
1123
1124 pub fn get_or_create_pipeline(&self, blend: wgpu::BlendState, format: wgpu::TextureFormat) -> Arc<wgpu::RenderPipeline> {
1125 let key = (BlendStateKey::from(&blend), format);
1126 let mut pipelines = self.pipelines.lock().unwrap();
1127 if !pipelines.contains_key(&key) {
1128 let pipeline = Arc::new(match self.kind {
1129 ProgramKind::Sprite => Context::build_sprite_pipeline(
1130 &self.device, &self.vert_module, &self.frag_module,
1131 &self.pipeline_layout, format, blend,
1132 ),
1133 _ => Context::build_texture_pipeline(
1134 &self.device, &self.vert_module, &self.frag_module,
1135 &self.pipeline_layout, format, blend,
1136 ),
1137 });
1138 pipelines.insert(key, pipeline);
1139 }
1140 pipelines[&key].clone()
1141 }
1142}
1143
1144#[repr(C)]
1149#[derive(Copy, Clone, Debug)]
1150pub(crate) struct SpriteUniforms {
1151 pub u_view: [[f32; 4]; 4],
1152 pub u_model: [[f32; 4]; 4],
1153 pub _rd_color: [f32; 4],
1154}
1155
1156#[repr(C)]
1157#[derive(Copy, Clone, Debug)]
1158pub(crate) struct TextureUniforms {
1159 pub u_view: [[f32; 4]; 4], pub u_model: [[f32; 4]; 4], pub _rd_color: [f32; 4], pub _rd_offset: [f32; 2], pub _rd_dimensions: [f32; 2], pub _rd_flags: [f32; 4], }
1167
1168impl TextureUniforms {
1169 pub fn identity() -> Self {
1171 TextureUniforms {
1172 u_view: crate::core::Mat4::viewport(1.0, 1.0).into(),
1173 u_model: crate::core::Mat4::<f32>::identity().into(),
1174 _rd_color: [1.0, 1.0, 1.0, 1.0],
1175 _rd_offset: [0.0, 0.0],
1176 _rd_dimensions: [1.0, 1.0],
1177 _rd_flags: [0.0, 0.0, 0.0, 0.0],
1178 }
1179 }
1180}
1181
1182#[derive(Clone)]
1187pub struct Monitor {
1188 pub inner: MonitorInner,
1189}
1190
1191#[derive(Clone)]
1192pub struct MonitorInner {
1193 pub winit_monitor: winit::monitor::MonitorHandle,
1194}
1195
1196impl Monitor {
1197 pub fn get_dimensions(self: &Self) -> crate::core::Point2<u32> {
1198 let size = self.inner.winit_monitor.size();
1199 (size.width, size.height)
1200 }
1201
1202 pub fn get_name(self: &Self) -> Option<String> {
1203 self.inner.winit_monitor.name()
1204 }
1205}
1206
1207pub struct MonitorIterator {
1208 monitors: std::vec::IntoIter<winit::monitor::MonitorHandle>,
1209}
1210
1211impl MonitorIterator {
1212 pub fn new() -> Self {
1213 MonitorIterator { monitors: Vec::new().into_iter() }
1214 }
1215}
1216
1217impl Iterator for MonitorIterator {
1218 type Item = Monitor;
1219 fn next(&mut self) -> Option<Monitor> {
1220 self.monitors.next().map(|m| Monitor { inner: MonitorInner { winit_monitor: m } })
1221 }
1222}
1223
1224pub struct Texture2d {
1229 texture: Arc<wgpu::Texture>,
1230 pub(crate) view: Arc<wgpu::TextureView>,
1231 device: Arc<wgpu::Device>,
1232 queue: Arc<wgpu::Queue>,
1233 pub(crate) width: u32,
1234 pub(crate) height: u32,
1235 bytes_per_pixel: u32,
1236 blit_res: Arc<BlitResources>,
1237}
1238
1239impl Texture2d {
1240 pub fn new(context: &Context, width: u32, height: u32, format: crate::core::TextureFormat, data: Option<crate::core::RawFrame>) -> Self {
1241 let wgpu_format = Self::convert_format(format);
1242 let bpp = Self::bytes_per_pixel_for(format);
1243
1244 let texture = context.device.create_texture(&wgpu::TextureDescriptor {
1245 label: Some("Radiant Texture2d"),
1246 size: wgpu::Extent3d { width, height, depth_or_array_layers: 1 },
1247 mip_level_count: 1,
1248 sample_count: 1,
1249 dimension: wgpu::TextureDimension::D2,
1250 format: wgpu_format,
1251 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST | wgpu::TextureUsages::RENDER_ATTACHMENT,
1252 view_formats: &[],
1253 });
1254
1255 let view = Arc::new(texture.create_view(&wgpu::TextureViewDescriptor::default()));
1256
1257 if let Some(raw) = data {
1258 context.queue.write_texture(
1259 wgpu::TexelCopyTextureInfo {
1260 texture: &texture,
1261 mip_level: 0,
1262 origin: wgpu::Origin3d::ZERO,
1263 aspect: wgpu::TextureAspect::All,
1264 },
1265 &raw.data,
1266 wgpu::TexelCopyBufferLayout {
1267 offset: 0,
1268 bytes_per_row: Some(raw.width * raw.channels as u32),
1269 rows_per_image: Some(raw.height),
1270 },
1271 wgpu::Extent3d { width: raw.width, height: raw.height, depth_or_array_layers: 1 },
1272 );
1273 }
1274
1275 let blit_res = BlitResources::new(&context.device, wgpu_format);
1276
1277 Texture2d {
1278 texture: Arc::new(texture),
1279 view,
1280 device: context.device.clone(),
1281 queue: context.queue.clone(),
1282 width,
1283 height,
1284 bytes_per_pixel: bpp,
1285 blit_res,
1286 }
1287 }
1288
1289 pub fn clear(self: &Self, color: crate::core::Color) {
1290 let crate::core::Color(r, g, b, a) = color;
1291 let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some("Texture Clear") });
1292 {
1293 let _pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1294 label: Some("Texture Clear Pass"),
1295 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1296 view: &self.view,
1297 depth_slice: None,
1298 resolve_target: None,
1299 ops: wgpu::Operations {
1300 load: wgpu::LoadOp::Clear(wgpu::Color { r: r as f64, g: g as f64, b: b as f64, a: a as f64 }),
1301 store: wgpu::StoreOp::Store,
1302 },
1303 })],
1304 depth_stencil_attachment: None,
1305 timestamp_writes: None,
1306 occlusion_query_set: None,
1307 multiview_mask: None,
1308 });
1309 }
1310 self.queue.submit(std::iter::once(encoder.finish()));
1311 }
1312
1313 pub fn write(self: &Self, rect: &crate::core::Rect<u32>, data: &Vec<u8>) {
1314 let x = (rect.0).0;
1315 let y = (rect.0).1;
1316 let w = (rect.1).0 - x;
1317 let h = (rect.1).1 - y;
1318 if w == 0 || h == 0 {
1319 return;
1320 }
1321 self.queue.write_texture(
1322 wgpu::TexelCopyTextureInfo {
1323 texture: &self.texture,
1324 mip_level: 0,
1325 origin: wgpu::Origin3d { x, y, z: 0 },
1326 aspect: wgpu::TextureAspect::All,
1327 },
1328 data,
1329 wgpu::TexelCopyBufferLayout {
1330 offset: 0,
1331 bytes_per_row: Some(w * self.bytes_per_pixel),
1332 rows_per_image: Some(h),
1333 },
1334 wgpu::Extent3d { width: w, height: h, depth_or_array_layers: 1 },
1335 );
1336 }
1337
1338 pub fn copy_from(self: &Self, src_texture: &crate::core::Texture, filter: crate::core::TextureFilter) {
1339 let uniforms = TextureUniforms::identity();
1340 let sampler = Context::sampler_for_filter(&self.blit_res.nearest_sampler, &self.blit_res.linear_sampler, filter);
1341 let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some("Tex CopyFrom") });
1342 render_texture_quad(
1343 &mut encoder, &self.view,
1344 &self.device, &self.queue,
1345 &uniforms, &BLIT_QUAD,
1346 &*src_texture.handle.view,
1347 sampler,
1348 &self.blit_res.pipeline,
1349 &self.blit_res.bind_group_layout,
1350 wgpu::LoadOp::Load,
1351 );
1352 self.queue.submit(std::iter::once(encoder.finish()));
1353 }
1354
1355 pub fn copy_rect_from(self: &Self, src_texture: &crate::core::Texture, source_rect: crate::core::Rect<i32>, target_rect: crate::core::Rect<i32>, filter: crate::core::TextureFilter) {
1356 let tw = src_texture.handle.width as f32;
1357 let th = src_texture.handle.height as f32;
1358 let dw_full = self.width as f32;
1359 let dh_full = self.height as f32;
1360
1361 let sx = (source_rect.0).0 as f32; let sy = (source_rect.0).1 as f32;
1362 let sw = (source_rect.1).0 as f32; let sh = (source_rect.1).1 as f32;
1363 let u0 = sx / tw; let u1 = (sx + sw) / tw;
1364 let v0 = sy / th; let v1 = (sy + sh) / th;
1365
1366 let dx = (target_rect.0).0 as f32; let dy = (target_rect.0).1 as f32;
1367 let dw = (target_rect.1).0 as f32; let dh = (target_rect.1).1 as f32;
1368
1369 let quad = [
1370 Vertex { position: [0.0, 0.0], texture_uv: [u0, v0], ..Vertex::default() },
1371 Vertex { position: [1.0, 0.0], texture_uv: [u1, v0], ..Vertex::default() },
1372 Vertex { position: [0.0, 1.0], texture_uv: [u0, v1], ..Vertex::default() },
1373 Vertex { position: [1.0, 1.0], texture_uv: [u1, v1], ..Vertex::default() },
1374 ];
1375 let uniforms = TextureUniforms {
1376 u_view: crate::core::Mat4::viewport(1.0, 1.0).into(),
1377 u_model: crate::core::Mat4::<f32>::identity().into(),
1378 _rd_color: [1.0, 1.0, 1.0, 1.0],
1379 _rd_offset: [dx / dw_full, dy / dh_full],
1380 _rd_dimensions: [dw / dw_full, dh / dh_full],
1381 _rd_flags: [0.0, 0.0, 0.0, 0.0],
1382 };
1383 let sampler = Context::sampler_for_filter(&self.blit_res.nearest_sampler, &self.blit_res.linear_sampler, filter);
1384 let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some("Tex CopyRectFrom") });
1385 render_texture_quad(
1386 &mut encoder, &self.view,
1387 &self.device, &self.queue,
1388 &uniforms, &quad,
1389 &*src_texture.handle.view,
1390 sampler,
1391 &self.blit_res.pipeline,
1392 &self.blit_res.bind_group_layout,
1393 wgpu::LoadOp::Load,
1394 );
1395 self.queue.submit(std::iter::once(encoder.finish()));
1396 }
1397
1398 pub fn copy_from_frame(self: &Self, _src_frame: &WgpuFrame, _filter: crate::core::TextureFilter) {}
1399 pub fn copy_rect_from_frame(self: &Self, _src_frame: &WgpuFrame, _source_rect: crate::core::Rect<i32>, _target_rect: crate::core::Rect<i32>, _filter: crate::core::TextureFilter) {}
1400
1401 fn convert_format(format: crate::core::TextureFormat) -> wgpu::TextureFormat {
1402 use crate::core::TextureFormat as RF;
1403 match format {
1404 RF::U8 => wgpu::TextureFormat::R8Unorm,
1405 RF::U16 => wgpu::TextureFormat::R16Uint,
1406 RF::U8U8 => wgpu::TextureFormat::Rg8Unorm,
1407 RF::U16U16 => wgpu::TextureFormat::Rg16Uint,
1408 RF::U10U10U10 => wgpu::TextureFormat::Rgb10a2Unorm,
1409 RF::U12U12U12 => wgpu::TextureFormat::Rgba8Unorm, RF::U16U16U16 => wgpu::TextureFormat::Rgba16Uint, RF::U2U2U2U2 => wgpu::TextureFormat::Rgba8Unorm, RF::U4U4U4U4 => wgpu::TextureFormat::Rgba8Unorm, RF::U5U5U5U1 => wgpu::TextureFormat::Rgba8Unorm, RF::U8U8U8U8 => wgpu::TextureFormat::Rgba8Unorm,
1415 RF::U10U10U10U2 => wgpu::TextureFormat::Rgb10a2Unorm,
1416 RF::U12U12U12U12 => wgpu::TextureFormat::Rgba16Uint, RF::U16U16U16U16 => wgpu::TextureFormat::Rgba16Uint,
1418 RF::I16I16I16I16 => wgpu::TextureFormat::Rgba16Sint,
1419 RF::F16 => wgpu::TextureFormat::R16Float,
1420 RF::F16F16 => wgpu::TextureFormat::Rg16Float,
1421 RF::F16F16F16F16 => wgpu::TextureFormat::Rgba16Float,
1422 RF::F32 => wgpu::TextureFormat::R32Float,
1423 RF::F32F32 => wgpu::TextureFormat::Rg32Float,
1424 RF::F32F32F32F32 => wgpu::TextureFormat::Rgba32Float,
1425 RF::F11F11F10 => wgpu::TextureFormat::Rg11b10Ufloat,
1426 }
1427 }
1428
1429 fn bytes_per_pixel_for(format: crate::core::TextureFormat) -> u32 {
1430 use crate::core::TextureFormat as RF;
1431 match format {
1432 RF::U8 | RF::F16 => 1,
1433 RF::U8U8 | RF::U16 | RF::F16F16 | RF::F32 => 2,
1434 RF::U8U8U8U8 | RF::U10U10U10U2 | RF::U10U10U10 | RF::U12U12U12
1435 | RF::U2U2U2U2 | RF::U4U4U4U4 | RF::U5U5U5U1 => 4,
1436 RF::U16U16 | RF::F32F32 => 4,
1437 RF::U16U16U16 | RF::U12U12U12U12 | RF::U16U16U16U16
1438 | RF::I16I16I16I16 | RF::F16F16F16F16 => 8,
1439 RF::F11F11F10 => 4,
1440 RF::F32F32F32F32 => 16,
1441 }
1442 }
1443}
1444
1445pub struct Texture2dArray {
1450 pub(crate) view: wgpu::TextureView,
1451}
1452
1453impl Texture2dArray {
1454 pub fn new(context: &Context, raw: &Vec<crate::core::RawFrame>) -> Self {
1455 let layer_count = if raw.is_empty() { 1 } else { raw.len() as u32 };
1456 let width = raw.first().map_or(1, |f| f.width);
1457 let height = raw.first().map_or(1, |f| f.height);
1458
1459 let texture = context.device.create_texture(&wgpu::TextureDescriptor {
1460 label: Some("Radiant Texture2dArray"),
1461 size: wgpu::Extent3d { width, height, depth_or_array_layers: layer_count },
1462 mip_level_count: 1,
1463 sample_count: 1,
1464 dimension: wgpu::TextureDimension::D2,
1465 format: wgpu::TextureFormat::Rgba8Unorm,
1466 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
1467 view_formats: &[],
1468 });
1469
1470 let view = texture.create_view(&wgpu::TextureViewDescriptor {
1471 label: Some("Texture Array View"),
1472 dimension: Some(wgpu::TextureViewDimension::D2Array),
1473 ..Default::default()
1474 });
1475
1476 for (i, frame) in raw.iter().enumerate() {
1477 context.queue.write_texture(
1478 wgpu::TexelCopyTextureInfo {
1479 texture: &texture,
1480 mip_level: 0,
1481 origin: wgpu::Origin3d { x: 0, y: 0, z: i as u32 },
1482 aspect: wgpu::TextureAspect::All,
1483 },
1484 &frame.data,
1485 wgpu::TexelCopyBufferLayout {
1486 offset: 0,
1487 bytes_per_row: Some(frame.width * frame.channels as u32),
1488 rows_per_image: Some(frame.height),
1489 },
1490 wgpu::Extent3d { width: frame.width, height: frame.height, depth_or_array_layers: 1 },
1491 );
1492 }
1493
1494 Texture2dArray { view }
1495 }
1496}
1497
1498struct VertexBufferCacheItem {
1503 hint: usize,
1504 age: usize,
1505 buffer: wgpu::Buffer,
1506 capacity: usize,
1507}
1508
1509pub struct Context {
1510 pub(crate) instance: Arc<wgpu::Instance>,
1511 pub(crate) adapter: Arc<wgpu::Adapter>,
1512 pub(crate) device: Arc<wgpu::Device>,
1513 pub(crate) queue: Arc<wgpu::Queue>,
1514 format: wgpu::TextureFormat,
1515 index_buffer: wgpu::Buffer,
1516 index_count: usize,
1517 vertex_buffers: Vec<VertexBufferCacheItem>,
1518 pub(crate) nearest_sampler: wgpu::Sampler,
1519 pub(crate) linear_sampler: wgpu::Sampler,
1520 pub(crate) sprite_bind_group_layout: wgpu::BindGroupLayout,
1521 pub(crate) texture_bind_group_layout: wgpu::BindGroupLayout,
1522 sprite_vert_module: wgpu::ShaderModule,
1524 sprite_frag_module: wgpu::ShaderModule,
1525 texture_vert_module: wgpu::ShaderModule,
1526 texture_frag_module: wgpu::ShaderModule,
1527 sprite_pipeline_layout: wgpu::PipelineLayout,
1528 texture_pipeline_layout: wgpu::PipelineLayout,
1529 sprite_pipelines: HashMap<(BlendStateKey, wgpu::TextureFormat), Arc<wgpu::RenderPipeline>>,
1530 texture_pipelines: HashMap<(BlendStateKey, wgpu::TextureFormat), Arc<wgpu::RenderPipeline>>,
1531 pub(crate) placeholder_view: Arc<wgpu::TextureView>,
1533}
1534
1535impl Context {
1536 pub fn shared_gpu(&self) -> SharedGpu {
1537 SharedGpu {
1538 instance: self.instance.clone(),
1539 adapter: self.adapter.clone(),
1540 device: self.device.clone(),
1541 queue: self.queue.clone(),
1542 }
1543 }
1544
1545 pub fn new(display: &Display, initial_capacity: usize) -> Self {
1546 let format = display.inner.config.lock().unwrap().format;
1547 let instance = display.inner.instance.clone();
1548 let adapter = display.inner.adapter.clone();
1549 let device = display.inner.device.clone();
1550 let queue = display.inner.queue.clone();
1551
1552 let index_buffer = Self::create_index_buffer(&device, &queue, initial_capacity);
1553
1554 let nearest_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
1555 label: Some("Nearest Sampler"),
1556 mag_filter: wgpu::FilterMode::Nearest,
1557 min_filter: wgpu::FilterMode::Nearest,
1558 mipmap_filter: wgpu::MipmapFilterMode::Nearest,
1559 address_mode_u: wgpu::AddressMode::ClampToEdge,
1560 address_mode_v: wgpu::AddressMode::ClampToEdge,
1561 address_mode_w: wgpu::AddressMode::ClampToEdge,
1562 ..Default::default()
1563 });
1564
1565 let linear_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
1566 label: Some("Linear Sampler"),
1567 mag_filter: wgpu::FilterMode::Linear,
1568 min_filter: wgpu::FilterMode::Linear,
1569 mipmap_filter: wgpu::MipmapFilterMode::Linear,
1570 address_mode_u: wgpu::AddressMode::ClampToEdge,
1571 address_mode_v: wgpu::AddressMode::ClampToEdge,
1572 address_mode_w: wgpu::AddressMode::ClampToEdge,
1573 ..Default::default()
1574 });
1575
1576 let sprite_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
1578 label: Some("Sprite Bind Group Layout"),
1579 entries: &[
1580 Self::bgl_uniform(0, wgpu::ShaderStages::VERTEX),
1581 Self::bgl_texture_2d(1, wgpu::ShaderStages::FRAGMENT),
1582 Self::bgl_sampler(2, wgpu::ShaderStages::FRAGMENT),
1583 Self::bgl_texture_2d_array(3, wgpu::ShaderStages::FRAGMENT),
1584 Self::bgl_texture_2d_array(4, wgpu::ShaderStages::FRAGMENT),
1585 Self::bgl_texture_2d_array(5, wgpu::ShaderStages::FRAGMENT),
1586 Self::bgl_texture_2d_array(6, wgpu::ShaderStages::FRAGMENT),
1587 Self::bgl_texture_2d_array(7, wgpu::ShaderStages::FRAGMENT),
1588 Self::bgl_uniform(8, wgpu::ShaderStages::FRAGMENT),
1589 ],
1590 });
1591
1592 let texture_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
1594 label: Some("Texture Bind Group Layout"),
1595 entries: &[
1596 Self::bgl_uniform(0, wgpu::ShaderStages::VERTEX),
1597 Self::bgl_texture_2d(1, wgpu::ShaderStages::FRAGMENT),
1598 Self::bgl_sampler(2, wgpu::ShaderStages::FRAGMENT),
1599 ],
1600 });
1601
1602 let sprite_vert_module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
1604 label: Some("Sprite Vertex Shader"),
1605 source: wgpu::ShaderSource::Wgsl(include_str!("../shader/sprite.wgsl").into()),
1606 });
1607 let sprite_frag_module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
1608 label: Some("Sprite Fragment Shader"),
1609 source: wgpu::ShaderSource::Wgsl(include_str!("../shader/sprite_frag.wgsl").into()),
1610 });
1611 let texture_vert_module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
1612 label: Some("Texture Vertex Shader"),
1613 source: wgpu::ShaderSource::Wgsl(include_str!("../shader/texture.wgsl").into()),
1614 });
1615 let texture_frag_module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
1616 label: Some("Texture Fragment Shader"),
1617 source: wgpu::ShaderSource::Wgsl(include_str!("../shader/texture_frag.wgsl").into()),
1618 });
1619
1620 let sprite_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
1621 label: Some("Sprite Pipeline Layout"),
1622 bind_group_layouts: &[Some(&sprite_bind_group_layout)],
1623 immediate_size: 0,
1624 });
1625 let texture_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
1626 label: Some("Texture Pipeline Layout"),
1627 bind_group_layouts: &[Some(&texture_bind_group_layout)],
1628 immediate_size: 0,
1629 });
1630
1631 let placeholder_tex = device.create_texture(&wgpu::TextureDescriptor {
1633 label: Some("Placeholder Texture"),
1634 size: wgpu::Extent3d { width: 1, height: 1, depth_or_array_layers: 1 },
1635 mip_level_count: 1,
1636 sample_count: 1,
1637 dimension: wgpu::TextureDimension::D2,
1638 format: wgpu::TextureFormat::Rgba8Unorm,
1639 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
1640 view_formats: &[],
1641 });
1642 queue.write_texture(
1643 wgpu::TexelCopyTextureInfo { texture: &placeholder_tex, mip_level: 0, origin: wgpu::Origin3d::ZERO, aspect: wgpu::TextureAspect::All },
1644 &[255u8, 255, 255, 255],
1645 wgpu::TexelCopyBufferLayout { offset: 0, bytes_per_row: Some(4), rows_per_image: Some(1) },
1646 wgpu::Extent3d { width: 1, height: 1, depth_or_array_layers: 1 },
1647 );
1648 let placeholder_view = Arc::new(placeholder_tex.create_view(&wgpu::TextureViewDescriptor::default()));
1649
1650 let mut sprite_pipelines = HashMap::new();
1652 let mut texture_pipelines = HashMap::new();
1653
1654 let default_blend = wgpu::BlendState::ALPHA_BLENDING;
1655 let blend_key = BlendStateKey::from(&default_blend);
1656 let composite_key = (blend_key, format);
1657 sprite_pipelines.insert(composite_key, Arc::new(Self::build_sprite_pipeline(
1658 &device, &sprite_vert_module, &sprite_frag_module, &sprite_pipeline_layout, format, default_blend,
1659 )));
1660 texture_pipelines.insert(composite_key, Arc::new(Self::build_texture_pipeline(
1661 &device, &texture_vert_module, &texture_frag_module, &texture_pipeline_layout, format, default_blend,
1662 )));
1663
1664 Context {
1665 instance,
1666 adapter,
1667 device,
1668 queue,
1669 format,
1670 index_buffer,
1671 index_count: initial_capacity * 6,
1672 vertex_buffers: Vec::new(),
1673 nearest_sampler,
1674 linear_sampler,
1675 sprite_bind_group_layout,
1676 texture_bind_group_layout,
1677 sprite_vert_module,
1678 sprite_frag_module,
1679 texture_vert_module,
1680 texture_frag_module,
1681 sprite_pipeline_layout,
1682 texture_pipeline_layout,
1683 sprite_pipelines,
1684 texture_pipelines,
1685 placeholder_view,
1686 }
1687 }
1688
1689 fn bgl_uniform(binding: u32, visibility: wgpu::ShaderStages) -> wgpu::BindGroupLayoutEntry {
1691 wgpu::BindGroupLayoutEntry {
1692 binding,
1693 visibility,
1694 ty: wgpu::BindingType::Buffer {
1695 ty: wgpu::BufferBindingType::Uniform,
1696 has_dynamic_offset: false,
1697 min_binding_size: None,
1698 },
1699 count: None,
1700 }
1701 }
1702 fn bgl_texture_2d(binding: u32, visibility: wgpu::ShaderStages) -> wgpu::BindGroupLayoutEntry {
1703 wgpu::BindGroupLayoutEntry {
1704 binding,
1705 visibility,
1706 ty: wgpu::BindingType::Texture {
1707 sample_type: wgpu::TextureSampleType::Float { filterable: true },
1708 view_dimension: wgpu::TextureViewDimension::D2,
1709 multisampled: false,
1710 },
1711 count: None,
1712 }
1713 }
1714 fn bgl_texture_2d_array(binding: u32, visibility: wgpu::ShaderStages) -> wgpu::BindGroupLayoutEntry {
1715 wgpu::BindGroupLayoutEntry {
1716 binding,
1717 visibility,
1718 ty: wgpu::BindingType::Texture {
1719 sample_type: wgpu::TextureSampleType::Float { filterable: true },
1720 view_dimension: wgpu::TextureViewDimension::D2Array,
1721 multisampled: false,
1722 },
1723 count: None,
1724 }
1725 }
1726 fn bgl_sampler(binding: u32, visibility: wgpu::ShaderStages) -> wgpu::BindGroupLayoutEntry {
1727 wgpu::BindGroupLayoutEntry {
1728 binding,
1729 visibility,
1730 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
1731 count: None,
1732 }
1733 }
1734
1735 pub fn get_or_create_sprite_pipeline(&mut self, blend: wgpu::BlendState, target_format: wgpu::TextureFormat) -> Arc<wgpu::RenderPipeline> {
1736 let key = (BlendStateKey::from(&blend), target_format);
1737 if !self.sprite_pipelines.contains_key(&key) {
1738 let pipeline = Arc::new(Self::build_sprite_pipeline(
1739 &self.device, &self.sprite_vert_module, &self.sprite_frag_module,
1740 &self.sprite_pipeline_layout, target_format, blend,
1741 ));
1742 self.sprite_pipelines.insert(key, pipeline);
1743 }
1744 self.sprite_pipelines[&key].clone()
1745 }
1746
1747 pub fn get_or_create_texture_pipeline(&mut self, blend: wgpu::BlendState, target_format: wgpu::TextureFormat) -> Arc<wgpu::RenderPipeline> {
1748 let key = (BlendStateKey::from(&blend), target_format);
1749 if !self.texture_pipelines.contains_key(&key) {
1750 let pipeline = Arc::new(Self::build_texture_pipeline(
1751 &self.device, &self.texture_vert_module, &self.texture_frag_module,
1752 &self.texture_pipeline_layout, target_format, blend,
1753 ));
1754 self.texture_pipelines.insert(key, pipeline);
1755 }
1756 self.texture_pipelines[&key].clone()
1757 }
1758
1759 fn build_sprite_pipeline(
1760 device: &wgpu::Device,
1761 vert: &wgpu::ShaderModule,
1762 frag: &wgpu::ShaderModule,
1763 layout: &wgpu::PipelineLayout,
1764 format: wgpu::TextureFormat,
1765 blend: wgpu::BlendState,
1766 ) -> wgpu::RenderPipeline {
1767 device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
1768 label: Some("Sprite Pipeline"),
1769 layout: Some(layout),
1770 vertex: wgpu::VertexState {
1771 module: vert,
1772 entry_point: Some("main"),
1773 buffers: &[wgpu::VertexBufferLayout {
1774 array_stride: VERTEX_STRIDE,
1775 step_mode: wgpu::VertexStepMode::Vertex,
1776 attributes: &wgpu::vertex_attr_array![
1777 0 => Float32x2, 1 => Float32x2, 2 => Float32, 3 => Float32x4, 4 => Uint32, 5 => Uint32, 6 => Float32x2, 7 => Uint32, ],
1786 }],
1787 compilation_options: Default::default(),
1788 },
1789 fragment: Some(wgpu::FragmentState {
1790 module: frag,
1791 entry_point: Some("main"),
1792 targets: &[Some(wgpu::ColorTargetState {
1793 format,
1794 blend: Some(blend),
1795 write_mask: wgpu::ColorWrites::ALL,
1796 })],
1797 compilation_options: Default::default(),
1798 }),
1799 primitive: wgpu::PrimitiveState {
1800 topology: wgpu::PrimitiveTopology::TriangleList,
1801 cull_mode: None,
1802 ..Default::default()
1803 },
1804 depth_stencil: None,
1805 multisample: wgpu::MultisampleState::default(),
1806 multiview_mask: None,
1807 cache: None,
1808 })
1809 }
1810
1811 fn build_texture_pipeline(
1812 device: &wgpu::Device,
1813 vert: &wgpu::ShaderModule,
1814 frag: &wgpu::ShaderModule,
1815 layout: &wgpu::PipelineLayout,
1816 format: wgpu::TextureFormat,
1817 blend: wgpu::BlendState,
1818 ) -> wgpu::RenderPipeline {
1819 device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
1820 label: Some("Texture Pipeline"),
1821 layout: Some(layout),
1822 vertex: wgpu::VertexState {
1823 module: vert,
1824 entry_point: Some("main"),
1825 buffers: &[wgpu::VertexBufferLayout {
1826 array_stride: VERTEX_STRIDE,
1827 step_mode: wgpu::VertexStepMode::Vertex,
1828 attributes: &[
1831 wgpu::VertexAttribute {
1832 format: wgpu::VertexFormat::Float32x2,
1833 offset: 0,
1834 shader_location: 0,
1835 },
1836 wgpu::VertexAttribute {
1837 format: wgpu::VertexFormat::Float32x2,
1838 offset: TEXTURE_UV_OFFSET,
1839 shader_location: 6,
1840 },
1841 ],
1842 }],
1843 compilation_options: Default::default(),
1844 },
1845 fragment: Some(wgpu::FragmentState {
1846 module: frag,
1847 entry_point: Some("main"),
1848 targets: &[Some(wgpu::ColorTargetState {
1849 format,
1850 blend: Some(blend),
1851 write_mask: wgpu::ColorWrites::ALL,
1852 })],
1853 compilation_options: Default::default(),
1854 }),
1855 primitive: wgpu::PrimitiveState {
1856 topology: wgpu::PrimitiveTopology::TriangleList,
1857 cull_mode: None,
1858 ..Default::default()
1859 },
1860 depth_stencil: None,
1861 multisample: wgpu::MultisampleState::default(),
1862 multiview_mask: None,
1863 cache: None,
1864 })
1865 }
1866
1867 fn create_index_buffer(device: &Arc<wgpu::Device>, queue: &Arc<wgpu::Queue>, max_sprites: usize) -> wgpu::Buffer {
1868 let mut data: Vec<u32> = Vec::with_capacity(max_sprites * 6);
1869 for i in 0..max_sprites {
1870 let base = i as u32 * 4;
1871 data.extend_from_slice(&[base, base+1, base+2, base+1, base+3, base+2]);
1872 }
1873 let buffer = device.create_buffer(&wgpu::BufferDescriptor {
1874 label: Some("Index Buffer"),
1875 size: (data.len() * 4) as u64,
1876 usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
1877 mapped_at_creation: false,
1878 });
1879 let bytes: Vec<u8> = data.iter().flat_map(|v| v.to_ne_bytes()).collect();
1880 queue.write_buffer(&buffer, 0, &bytes);
1881 buffer
1882 }
1883
1884 pub fn select_vertex_buffer(&mut self, buffer_hint: usize, num_vertices: usize) -> (usize, bool) {
1885 for buffer in self.vertex_buffers.iter_mut() {
1886 buffer.age += 1;
1887 }
1888 if let Some(id) = self.vertex_buffers.iter().position(|item| item.hint == buffer_hint && item.capacity >= num_vertices) {
1889 self.vertex_buffers[id].age = 0;
1890 (id, false)
1891 } else if self.vertex_buffers.len() < MAX_BUFFERS {
1892 let buffer = Self::create_vertex_buffer(&self.device, num_vertices);
1893 self.vertex_buffers.push(VertexBufferCacheItem { hint: buffer_hint, age: 0, buffer, capacity: num_vertices });
1894 (self.vertex_buffers.len() - 1, true)
1895 } else {
1896 let (id, _) = self.vertex_buffers.iter().enumerate().max_by(|&(_, a), &(_, b)| a.age.cmp(&b.age)).unwrap();
1897 let buffer = Self::create_vertex_buffer(&self.device, num_vertices);
1898 self.vertex_buffers[id] = VertexBufferCacheItem { hint: buffer_hint, age: 0, buffer, capacity: num_vertices };
1899 (id, true)
1900 }
1901 }
1902
1903 fn create_vertex_buffer(device: &Arc<wgpu::Device>, num_vertices: usize) -> wgpu::Buffer {
1904 device.create_buffer(&wgpu::BufferDescriptor {
1905 label: Some("Vertex Buffer"),
1906 size: (num_vertices * std::mem::size_of::<Vertex>()) as u64,
1907 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
1908 mapped_at_creation: false,
1909 })
1910 }
1911
1912 pub fn update_index_buffer(&mut self, max_sprites: usize) {
1913 if max_sprites * 6 > self.index_count {
1914 self.index_buffer = Self::create_index_buffer(&self.device, &self.queue, max_sprites);
1915 self.index_count = max_sprites * 6;
1916 }
1917 }
1918
1919 pub fn blendmode_to_wgpu(blendmode: &crate::core::BlendMode) -> wgpu::BlendState {
1920 use crate::core::{BlendingFunction, LinearBlendingFactor};
1921
1922 fn convert(factor: LinearBlendingFactor) -> wgpu::BlendFactor {
1923 match factor {
1924 LinearBlendingFactor::Zero => wgpu::BlendFactor::Zero,
1925 LinearBlendingFactor::One => wgpu::BlendFactor::One,
1926 LinearBlendingFactor::SourceColor => wgpu::BlendFactor::Src,
1927 LinearBlendingFactor::OneMinusSourceColor => wgpu::BlendFactor::OneMinusSrc,
1928 LinearBlendingFactor::DestinationColor => wgpu::BlendFactor::Dst,
1929 LinearBlendingFactor::OneMinusDestinationColor => wgpu::BlendFactor::OneMinusDst,
1930 LinearBlendingFactor::SourceAlpha => wgpu::BlendFactor::SrcAlpha,
1931 LinearBlendingFactor::SourceAlphaSaturate => wgpu::BlendFactor::SrcAlphaSaturated,
1932 LinearBlendingFactor::OneMinusSourceAlpha => wgpu::BlendFactor::OneMinusSrcAlpha,
1933 LinearBlendingFactor::DestinationAlpha => wgpu::BlendFactor::DstAlpha,
1934 LinearBlendingFactor::OneMinusDestinationAlpha => wgpu::BlendFactor::OneMinusDstAlpha,
1935 LinearBlendingFactor::ConstantColor => wgpu::BlendFactor::Constant,
1936 LinearBlendingFactor::OneMinusConstantColor => wgpu::BlendFactor::OneMinusConstant,
1937 LinearBlendingFactor::ConstantAlpha => wgpu::BlendFactor::Constant,
1938 LinearBlendingFactor::OneMinusConstantAlpha => wgpu::BlendFactor::OneMinusConstant,
1939 }
1940 }
1941
1942 fn blend_component(func: BlendingFunction) -> wgpu::BlendComponent {
1943 match func {
1944 BlendingFunction::AlwaysReplace => wgpu::BlendComponent::REPLACE,
1945 BlendingFunction::Min => wgpu::BlendComponent { operation: wgpu::BlendOperation::Min, src_factor: wgpu::BlendFactor::One, dst_factor: wgpu::BlendFactor::One },
1946 BlendingFunction::Max => wgpu::BlendComponent { operation: wgpu::BlendOperation::Max, src_factor: wgpu::BlendFactor::One, dst_factor: wgpu::BlendFactor::One },
1947 BlendingFunction::Addition { source, destination } => wgpu::BlendComponent { operation: wgpu::BlendOperation::Add, src_factor: convert(source), dst_factor: convert(destination) },
1948 BlendingFunction::Subtraction { source, destination } => wgpu::BlendComponent { operation: wgpu::BlendOperation::Subtract, src_factor: convert(source), dst_factor: convert(destination) },
1949 BlendingFunction::ReverseSubtraction { source, destination } => wgpu::BlendComponent { operation: wgpu::BlendOperation::ReverseSubtract, src_factor: convert(source), dst_factor: convert(destination) },
1950 }
1951 }
1952
1953 wgpu::BlendState {
1954 color: blend_component(blendmode.color),
1955 alpha: blend_component(blendmode.alpha),
1956 }
1957 }
1958
1959 pub fn sampler_for_filter<'a>(nearest: &'a wgpu::Sampler, linear: &'a wgpu::Sampler, filter: crate::core::TextureFilter) -> &'a wgpu::Sampler {
1960 if filter == crate::core::TextureFilter::Linear { linear } else { nearest }
1961 }
1962
1963}
1964
1965const QUAD_INDICES: [u32; 6] = [0, 1, 2, 1, 3, 2];
1971
1972const BLIT_QUAD: [Vertex; 4] = [
1975 Vertex { position: [0.0, 0.0], offset: [0.0, 0.0], rotation: 0.0, color: [1.0, 1.0, 1.0, 1.0], bucket_id: 0, texture_id: 0, texture_uv: [0.0, 0.0], components: 0 },
1976 Vertex { position: [1.0, 0.0], offset: [0.0, 0.0], rotation: 0.0, color: [1.0, 1.0, 1.0, 1.0], bucket_id: 0, texture_id: 0, texture_uv: [1.0, 0.0], components: 0 },
1977 Vertex { position: [0.0, 1.0], offset: [0.0, 0.0], rotation: 0.0, color: [1.0, 1.0, 1.0, 1.0], bucket_id: 0, texture_id: 0, texture_uv: [0.0, 1.0], components: 0 },
1978 Vertex { position: [1.0, 1.0], offset: [0.0, 0.0], rotation: 0.0, color: [1.0, 1.0, 1.0, 1.0], bucket_id: 0, texture_id: 0, texture_uv: [1.0, 1.0], components: 0 },
1979];
1980
1981fn render_texture_quad(
1983 encoder: &mut wgpu::CommandEncoder,
1984 target_view: &wgpu::TextureView,
1985 device: &wgpu::Device,
1986 queue: &wgpu::Queue,
1987 uniforms: &TextureUniforms,
1988 quad: &[Vertex; 4],
1989 tex_view: &wgpu::TextureView,
1990 sampler: &wgpu::Sampler,
1991 pipeline: &wgpu::RenderPipeline,
1992 layout: &wgpu::BindGroupLayout,
1993 load_op: wgpu::LoadOp<wgpu::Color>,
1994) {
1995 let indices = QUAD_INDICES;
1996
1997 let vb = device.create_buffer(&wgpu::BufferDescriptor {
1998 label: Some("Quad VB"),
1999 size: (4 * std::mem::size_of::<Vertex>()) as u64,
2000 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
2001 mapped_at_creation: false,
2002 });
2003 let ib = device.create_buffer(&wgpu::BufferDescriptor {
2004 label: Some("Quad IB"),
2005 size: (6 * 4) as u64,
2006 usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
2007 mapped_at_creation: false,
2008 });
2009 let ub = device.create_buffer(&wgpu::BufferDescriptor {
2010 label: Some("Quad UB"),
2011 size: std::mem::size_of::<TextureUniforms>() as u64,
2012 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
2013 mapped_at_creation: false,
2014 });
2015
2016 let vertex_bytes = unsafe {
2017 std::slice::from_raw_parts(quad.as_ptr() as *const u8, 4 * std::mem::size_of::<Vertex>())
2018 };
2019 let index_bytes = unsafe {
2020 std::slice::from_raw_parts(indices.as_ptr() as *const u8, 6 * 4)
2021 };
2022 let uniform_bytes = unsafe {
2023 std::slice::from_raw_parts(uniforms as *const TextureUniforms as *const u8, std::mem::size_of::<TextureUniforms>())
2024 };
2025
2026 queue.write_buffer(&vb, 0, vertex_bytes);
2027 queue.write_buffer(&ib, 0, index_bytes);
2028 queue.write_buffer(&ub, 0, uniform_bytes);
2029
2030 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
2031 label: Some("Quad Bind Group"),
2032 layout,
2033 entries: &[
2034 wgpu::BindGroupEntry { binding: 0, resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding { buffer: &ub, offset: 0, size: None }) },
2035 wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::TextureView(tex_view) },
2036 wgpu::BindGroupEntry { binding: 2, resource: wgpu::BindingResource::Sampler(sampler) },
2037 ],
2038 });
2039
2040 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2041 label: Some("Quad Render Pass"),
2042 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2043 view: target_view,
2044 depth_slice: None,
2045 resolve_target: None,
2046 ops: wgpu::Operations { load: load_op, store: wgpu::StoreOp::Store },
2047 })],
2048 depth_stencil_attachment: None,
2049 timestamp_writes: None,
2050 occlusion_query_set: None,
2051 multiview_mask: None,
2052 });
2053 pass.set_pipeline(pipeline);
2054 pass.set_bind_group(0, &bind_group, &[]);
2055 pass.set_vertex_buffer(0, vb.slice(..));
2056 pass.set_index_buffer(ib.slice(..), wgpu::IndexFormat::Uint32);
2057 pass.draw_indexed(0..6, 0, 0..1);
2058}
2059
2060fn render_combine_quad(
2061 encoder: &mut wgpu::CommandEncoder,
2062 target_view: &wgpu::TextureView,
2063 device: &wgpu::Device,
2064 queue: &wgpu::Queue,
2065 uniforms: &TextureUniforms,
2066 textures: [&wgpu::TextureView; 5],
2067 sampler: &wgpu::Sampler,
2068 pipeline: &wgpu::RenderPipeline,
2069 layout: &wgpu::BindGroupLayout,
2070 load_op: wgpu::LoadOp<wgpu::Color>,
2071) {
2072 let indices = QUAD_INDICES;
2073 let vb = device.create_buffer(&wgpu::BufferDescriptor {
2074 label: Some("Combine VB"),
2075 size: (4 * std::mem::size_of::<Vertex>()) as u64,
2076 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
2077 mapped_at_creation: false,
2078 });
2079 let ib = device.create_buffer(&wgpu::BufferDescriptor {
2080 label: Some("Combine IB"),
2081 size: (6 * 4) as u64,
2082 usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
2083 mapped_at_creation: false,
2084 });
2085 let ub = device.create_buffer(&wgpu::BufferDescriptor {
2086 label: Some("Combine UB"),
2087 size: std::mem::size_of::<TextureUniforms>() as u64,
2088 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
2089 mapped_at_creation: false,
2090 });
2091 let vertex_bytes = unsafe {
2092 std::slice::from_raw_parts(BLIT_QUAD.as_ptr() as *const u8, 4 * std::mem::size_of::<Vertex>())
2093 };
2094 let index_bytes = unsafe {
2095 std::slice::from_raw_parts(indices.as_ptr() as *const u8, 6 * 4)
2096 };
2097 let uniform_bytes = unsafe {
2098 std::slice::from_raw_parts(uniforms as *const TextureUniforms as *const u8, std::mem::size_of::<TextureUniforms>())
2099 };
2100 queue.write_buffer(&vb, 0, vertex_bytes);
2101 queue.write_buffer(&ib, 0, index_bytes);
2102 queue.write_buffer(&ub, 0, uniform_bytes);
2103
2104 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
2105 label: Some("Combine Bind Group"),
2106 layout,
2107 entries: &[
2108 wgpu::BindGroupEntry { binding: 0, resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding { buffer: &ub, offset: 0, size: None }) },
2109 wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::TextureView(textures[0]) },
2110 wgpu::BindGroupEntry { binding: 2, resource: wgpu::BindingResource::TextureView(textures[1]) },
2111 wgpu::BindGroupEntry { binding: 3, resource: wgpu::BindingResource::TextureView(textures[2]) },
2112 wgpu::BindGroupEntry { binding: 4, resource: wgpu::BindingResource::TextureView(textures[3]) },
2113 wgpu::BindGroupEntry { binding: 5, resource: wgpu::BindingResource::TextureView(textures[4]) },
2114 wgpu::BindGroupEntry { binding: 6, resource: wgpu::BindingResource::Sampler(sampler) },
2115 ],
2116 });
2117
2118 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2119 label: Some("Combine Render Pass"),
2120 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2121 view: target_view,
2122 depth_slice: None,
2123 resolve_target: None,
2124 ops: wgpu::Operations { load: load_op, store: wgpu::StoreOp::Store },
2125 })],
2126 depth_stencil_attachment: None,
2127 timestamp_writes: None,
2128 occlusion_query_set: None,
2129 multiview_mask: None,
2130 });
2131 pass.set_pipeline(pipeline);
2132 pass.set_bind_group(0, &bind_group, &[]);
2133 pass.set_vertex_buffer(0, vb.slice(..));
2134 pass.set_index_buffer(ib.slice(..), wgpu::IndexFormat::Uint32);
2135 pass.draw_indexed(0..6, 0, 0..1);
2136}
2137
2138fn draw_rect_custom<T>(
2139 target: &crate::core::RenderTarget,
2140 program: &crate::core::Program,
2141 backend_prog: &Program,
2142 backend_context: &mut Context,
2143 wgpu_blend: wgpu::BlendState,
2144 info: &crate::core::DrawBuilder<T>,
2145 view_matrix: crate::core::Mat4,
2146 model_matrix: crate::core::Mat4,
2147 color: crate::core::Color,
2148 texture: Option<&crate::core::Texture>,
2149) {
2150 use crate::core::Point2Trait;
2151 use crate::core::Uniform;
2152
2153 let horizontal = match program.uniforms.0.get("horizontal") {
2155 Some(Uniform::Bool(b)) => if *b { 1.0f32 } else { 0.0 },
2156 _ => 0.0,
2157 };
2158 let brightness = match program.uniforms.0.get("brightness") {
2159 Some(Uniform::Float(f)) => *f,
2160 _ => 1.0,
2161 };
2162
2163 let uniforms = TextureUniforms {
2164 u_view: view_matrix.into(),
2165 u_model: model_matrix.into(),
2166 _rd_color: color.into(),
2167 _rd_offset: info.rect.0.as_array(),
2168 _rd_dimensions: info.rect.1.as_array(),
2169 _rd_flags: [horizontal, brightness, 0.0, 0.0],
2170 };
2171
2172 match backend_prog.kind {
2173 ProgramKind::Default => {
2174 let tex_view: Arc<wgpu::TextureView> = if let Some(tex) = texture {
2175 tex.handle.view.clone()
2176 } else {
2177 backend_context.placeholder_view.clone()
2178 };
2179
2180 match &target.0 {
2181 crate::core::RenderTargetInner::Frame(frame_rc) => {
2182 let pipeline = backend_prog.get_or_create_pipeline(wgpu_blend, backend_context.format);
2183 let mut frame = frame_rc.borrow_mut();
2184 let frame = frame.as_mut().expect("No frame prepared");
2185 let load_op = frame.next_load_op();
2186 let view = frame.view.clone();
2187 render_texture_quad(
2188 &mut frame.command_encoder, &view,
2189 &frame.device, &frame.queue,
2190 &uniforms, &BLIT_QUAD, &*tex_view,
2191 &*backend_prog.sampler,
2192 &pipeline, &backend_prog.bind_group_layout,
2193 load_op,
2194 );
2195 }
2196 crate::core::RenderTargetInner::Texture(dest_texture) => {
2197 let dest_view = dest_texture.handle.view.clone();
2198 let dest_format = dest_texture.handle.texture.format();
2199 let pipeline = backend_prog.get_or_create_pipeline(wgpu_blend, dest_format);
2200 let mut encoder = backend_context.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some("Custom RTT") });
2201 render_texture_quad(
2202 &mut encoder, &dest_view,
2203 &backend_context.device, &backend_context.queue,
2204 &uniforms, &BLIT_QUAD, &*tex_view,
2205 &*backend_prog.sampler,
2206 &pipeline, &backend_prog.bind_group_layout,
2207 wgpu::LoadOp::Load,
2208 );
2209 backend_context.queue.submit(std::iter::once(encoder.finish()));
2210 }
2211 crate::core::RenderTargetInner::None => {}
2212 }
2213 }
2214 ProgramKind::Combine => {
2215 let placeholder = &backend_context.placeholder_view;
2217 let get_tex = |name: &str| -> Arc<wgpu::TextureView> {
2218 match program.uniforms.0.get(name) {
2219 Some(Uniform::Texture(t)) => t.handle.view.clone(),
2220 _ => placeholder.clone(),
2221 }
2222 };
2223 let s0 = get_tex("sample0");
2224 let s1 = get_tex("sample1");
2225 let s2 = get_tex("sample2");
2226 let s3 = get_tex("sample3");
2227 let s4 = get_tex("sample4");
2228 let textures = [&*s0, &*s1, &*s2, &*s3, &*s4];
2229
2230 match &target.0 {
2231 crate::core::RenderTargetInner::Frame(frame_rc) => {
2232 let pipeline = backend_prog.get_or_create_pipeline(wgpu_blend, backend_context.format);
2233 let mut frame = frame_rc.borrow_mut();
2234 let frame = frame.as_mut().expect("No frame prepared");
2235 let load_op = frame.next_load_op();
2236 let view = frame.view.clone();
2237 render_combine_quad(
2238 &mut frame.command_encoder, &view,
2239 &frame.device, &frame.queue,
2240 &uniforms, textures,
2241 &*backend_prog.sampler,
2242 &pipeline, &backend_prog.bind_group_layout,
2243 load_op,
2244 );
2245 }
2246 crate::core::RenderTargetInner::Texture(dest_texture) => {
2247 let dest_view = dest_texture.handle.view.clone();
2248 let dest_format = dest_texture.handle.texture.format();
2249 let pipeline = backend_prog.get_or_create_pipeline(wgpu_blend, dest_format);
2250 let mut encoder = backend_context.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some("Combine RTT") });
2251 render_combine_quad(
2252 &mut encoder, &dest_view,
2253 &backend_context.device, &backend_context.queue,
2254 &uniforms, textures,
2255 &*backend_prog.sampler,
2256 &pipeline, &backend_prog.bind_group_layout,
2257 wgpu::LoadOp::Load,
2258 );
2259 backend_context.queue.submit(std::iter::once(encoder.finish()));
2260 }
2261 crate::core::RenderTargetInner::None => {}
2262 }
2263 }
2264 ProgramKind::Sprite => {
2265 }
2267 }
2268}
2269
2270pub fn draw_layer(target: &crate::core::RenderTarget, program: &crate::core::Program, context: &mut crate::core::ContextData, layer: &crate::core::Layer, component: u32) {
2275 let vertices = layer.vertices();
2276 let vertices = vertices.deref();
2277 let dirty = layer.undirty();
2278 let layer_id = layer.id();
2279
2280 let view_matrix = *layer.view_matrix().deref().deref();
2281 let model_matrix = *layer.model_matrix().deref().deref();
2282 let color = layer.color().deref().clone();
2283 let blendmode = layer.blendmode().clone();
2284 let wgpu_blend = Context::blendmode_to_wgpu(&blendmode);
2285
2286 let sprite_uniforms = SpriteUniforms {
2287 u_view: view_matrix,
2288 u_model: model_matrix,
2289 _rd_color: color.into(),
2290 };
2291
2292 let font_view = context.font_texture.as_ref().map(|t| t.view.clone());
2293 let custom_sprite_prog = program.sprite_program.as_deref()
2294 .filter(|p| matches!(p.kind, ProgramKind::Sprite));
2295
2296 let backend_context = context.backend_context.as_mut().unwrap();
2297
2298 match &target.0 {
2299 crate::core::RenderTargetInner::Frame(frame_rc) => {
2300 let mut frame = frame_rc.borrow_mut();
2301 let frame = frame.as_mut().expect("No frame prepared");
2302 let font_view_ref = font_view.as_ref().map(|v| v.as_ref());
2303 if let Some(fv) = font_view_ref {
2304 frame.draw_sprites(
2305 vertices, dirty, layer_id, component,
2306 wgpu_blend, fv, &context.tex_arrays,
2307 &sprite_uniforms, backend_context, custom_sprite_prog,
2308 );
2309 }
2310 }
2311 crate::core::RenderTargetInner::Texture(texture) => {
2312 let dest_format = texture.handle.texture.format();
2314 let font_view_ref = font_view.as_ref().map(|v| v.as_ref());
2315 if let Some(fv) = font_view_ref {
2316 draw_sprites_to_texture(
2317 vertices, dirty, layer_id, component,
2318 wgpu_blend, fv, &context.tex_arrays,
2319 &sprite_uniforms, backend_context, &*texture.handle.view, dest_format,
2320 custom_sprite_prog,
2321 );
2322 }
2323 }
2324 crate::core::RenderTargetInner::None => {}
2325 }
2326}
2327
2328fn draw_sprites_to_texture(
2330 vertices: &[Vertex],
2331 dirty: bool,
2332 layer_hint: usize,
2333 component: u32,
2334 blend: wgpu::BlendState,
2335 font_texture_view: &wgpu::TextureView,
2336 tex_arrays: &[crate::core::RawFrameArray],
2337 sprite_uniforms: &SpriteUniforms,
2338 context: &mut Context,
2339 target_view: &wgpu::TextureView,
2340 dest_format: wgpu::TextureFormat,
2341 custom_program: Option<&Program>,
2342) {
2343 let num_vertices = vertices.len();
2344 let num_sprites = num_vertices / 4;
2345 if num_vertices == 0 {
2346 return;
2347 }
2348
2349 let (vb_index, vb_dirty) = context.select_vertex_buffer(layer_hint, num_vertices);
2350
2351 let mut encoder = context.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some("RTT Encoder") });
2352
2353 if dirty || vb_dirty {
2354 let vertex_bytes = unsafe {
2355 std::slice::from_raw_parts(vertices.as_ptr() as *const u8, num_vertices * std::mem::size_of::<Vertex>())
2356 };
2357 context.queue.write_buffer(&context.vertex_buffers[vb_index].buffer, 0, vertex_bytes);
2358 }
2359 context.update_index_buffer(num_sprites);
2360
2361 let uniform_bytes = unsafe {
2362 std::slice::from_raw_parts(sprite_uniforms as *const SpriteUniforms as *const u8, std::mem::size_of::<SpriteUniforms>())
2363 };
2364 let uniform_buffer = context.device.create_buffer(&wgpu::BufferDescriptor {
2365 label: None, size: uniform_bytes.len() as u64,
2366 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, mapped_at_creation: false,
2367 });
2368 context.queue.write_buffer(&uniform_buffer, 0, uniform_bytes);
2369
2370 let comp_data = [component, 0u32, 0u32, 0u32];
2371 let comp_bytes = unsafe { std::slice::from_raw_parts(comp_data.as_ptr() as *const u8, 16) };
2372 let comp_buffer = context.device.create_buffer(&wgpu::BufferDescriptor {
2373 label: None, size: 16,
2374 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, mapped_at_creation: false,
2375 });
2376 context.queue.write_buffer(&comp_buffer, 0, comp_bytes);
2377
2378 let sprite_bgl = custom_program.map_or(&context.sprite_bind_group_layout, |p| &*p.bind_group_layout);
2379 let bind_group = context.device.create_bind_group(&wgpu::BindGroupDescriptor {
2380 label: None,
2381 layout: sprite_bgl,
2382 entries: &[
2383 wgpu::BindGroupEntry { binding: 0, resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding { buffer: &uniform_buffer, offset: 0, size: None }) },
2384 wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::TextureView(font_texture_view) },
2385 wgpu::BindGroupEntry { binding: 2, resource: wgpu::BindingResource::Sampler(&context.nearest_sampler) },
2386 wgpu::BindGroupEntry { binding: 3, resource: wgpu::BindingResource::TextureView(&tex_arrays[1].data.view) },
2387 wgpu::BindGroupEntry { binding: 4, resource: wgpu::BindingResource::TextureView(&tex_arrays[2].data.view) },
2388 wgpu::BindGroupEntry { binding: 5, resource: wgpu::BindingResource::TextureView(&tex_arrays[3].data.view) },
2389 wgpu::BindGroupEntry { binding: 6, resource: wgpu::BindingResource::TextureView(&tex_arrays[4].data.view) },
2390 wgpu::BindGroupEntry { binding: 7, resource: wgpu::BindingResource::TextureView(&tex_arrays[5].data.view) },
2391 wgpu::BindGroupEntry { binding: 8, resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding { buffer: &comp_buffer, offset: 0, size: None }) },
2392 ],
2393 });
2394
2395 let pipeline = match custom_program {
2396 Some(prog) => prog.get_or_create_pipeline(blend, dest_format),
2397 None => context.get_or_create_sprite_pipeline(blend, dest_format),
2398 };
2399 {
2400 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2401 label: Some("RTT Sprite Pass"),
2402 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2403 view: target_view,
2404 depth_slice: None,
2405 resolve_target: None,
2406 ops: wgpu::Operations { load: wgpu::LoadOp::Load, store: wgpu::StoreOp::Store },
2407 })],
2408 depth_stencil_attachment: None,
2409 timestamp_writes: None,
2410 occlusion_query_set: None,
2411 multiview_mask: None,
2412 });
2413 pass.set_pipeline(&pipeline);
2414 pass.set_blend_constant(wgpu::Color::TRANSPARENT);
2415 pass.set_bind_group(0, &bind_group, &[]);
2416 pass.set_vertex_buffer(0, context.vertex_buffers[vb_index].buffer.slice(..));
2417 pass.set_index_buffer(context.index_buffer.slice(..), wgpu::IndexFormat::Uint32);
2418 pass.draw_indexed(0..(num_sprites as u32 * 6), 0, 0..1);
2419 }
2420 context.queue.submit(std::iter::once(encoder.finish()));
2421}
2422
2423pub fn draw_rect<T>(
2424 target: &crate::core::RenderTarget,
2425 program: &crate::core::Program,
2426 context: &mut crate::core::ContextData,
2427 blend: crate::core::BlendMode,
2428 info: crate::core::DrawBuilder<T>,
2429 view_matrix: crate::core::Mat4,
2430 model_matrix: crate::core::Mat4,
2431 color: crate::core::Color,
2432 texture: Option<&crate::core::Texture>,
2433) {
2434 use crate::core::Point2Trait;
2435
2436 let wgpu_blend = Context::blendmode_to_wgpu(&blend);
2437
2438 let backend_context = context.backend_context.as_mut().unwrap();
2439
2440 let backend_prog = program.texture_program.clone();
2442 if backend_prog.is_custom {
2443 draw_rect_custom(target, program, &backend_prog, backend_context, wgpu_blend, &info, view_matrix, model_matrix, color, texture);
2444 return;
2445 }
2446
2447 let uniforms = TextureUniforms {
2448 u_view: view_matrix.into(),
2449 u_model: model_matrix.into(),
2450 _rd_color: color.into(),
2451 _rd_offset: info.rect.0.as_array(),
2452 _rd_dimensions: info.rect.1.as_array(),
2453 _rd_flags: [0.0, 0.0, 0.0, 0.0],
2454 };
2455
2456 let tex_view: Arc<wgpu::TextureView> = if let Some(tex) = texture {
2458 tex.handle.view.clone()
2459 } else {
2460 backend_context.placeholder_view.clone()
2461 };
2462
2463 let filter = texture.map_or(crate::core::TextureFilter::Nearest, |t| t.magnify);
2464
2465 match &target.0 {
2466 crate::core::RenderTargetInner::Frame(frame_rc) => {
2467 let mut frame = frame_rc.borrow_mut();
2468 let frame = frame.as_mut().expect("No frame prepared");
2469 frame.draw_quad(&uniforms, &*tex_view, filter, wgpu_blend, backend_context);
2470 }
2471 crate::core::RenderTargetInner::Texture(dest_texture) => {
2472 let dest_view = dest_texture.handle.view.clone();
2473 let dest_format = dest_texture.handle.texture.format();
2474 let pipeline = backend_context.get_or_create_texture_pipeline(wgpu_blend, dest_format);
2475 let sampler = Context::sampler_for_filter(&backend_context.nearest_sampler, &backend_context.linear_sampler, filter);
2476 let mut encoder = backend_context.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some("DrawRect RTT") });
2477 render_texture_quad(
2478 &mut encoder,
2479 &dest_view,
2480 &backend_context.device,
2481 &backend_context.queue,
2482 &uniforms,
2483 &BLIT_QUAD,
2484 &*tex_view,
2485 sampler,
2486 &pipeline,
2487 &backend_context.texture_bind_group_layout,
2488 wgpu::LoadOp::Load,
2489 );
2490 backend_context.queue.submit(std::iter::once(encoder.finish()));
2491 }
2492 crate::core::RenderTargetInner::None => {}
2493 }
2494}