From fd599aea18d6a6cc97858a894f68363808783737 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 2 Jul 2019 04:02:03 +0200 Subject: [PATCH 01/16] Migrate to the new `winit` event loop. - The new `game::Loop` trait is introduced to remove code duplication in `UserInterface::run`. - The `empty` feature is introduced, it allows testing without a specific backend enabled. --- .travis.yml | 5 +- Cargo.toml | 16 +- build.rs | 3 +- src/game.rs | 158 +----------------- src/game/loop.rs | 230 +++++++++++++++++++++++++++ src/graphics.rs | 6 +- src/graphics/backend_wgpu/mod.rs | 15 +- src/graphics/backend_wgpu/surface.rs | 20 +-- src/graphics/window.rs | 48 +++--- src/graphics/window/settings.rs | 10 +- src/input.rs | 2 +- src/input/keyboard.rs | 2 +- src/input/mouse.rs | 2 +- src/ui.rs | 229 +++++++++----------------- src/ui/core.rs | 2 +- src/ui/core/mouse_cursor.rs | 16 +- 16 files changed, 390 insertions(+), 374 deletions(-) create mode 100644 src/game/loop.rs diff --git a/.travis.yml b/.travis.yml index 7c47787..9700e52 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,5 +29,6 @@ matrix: fast_finish: true script: - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then chmod +x spirvtest && ./spirvtest; fi - - cargo test --verbose --features opengl - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then cargo test --verbose --features vulkan --release; fi + #- cargo test --verbose --features opengl + #- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then cargo test --verbose --features vulkan --release; fi + - cargo test --verbose diff --git a/Cargo.toml b/Cargo.toml index 83b1165..4c5fcde 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,12 +19,13 @@ maintenance = { status = "actively-developed" } features = ["opengl", "debug"] [features] -default = [] +default = ["empty"] opengl = ["gfx", "gfx_core", "glutin", "gfx_device_gl", "gfx_window_glutin", "gfx_glyph", "gfx_winit"] vulkan = ["wgpu", "wgpu/vulkan", "wgpu_glyph"] metal = ["wgpu", "wgpu/metal", "wgpu_glyph"] dx11 = ["wgpu", "wgpu/dx11", "wgpu_glyph"] dx12 = ["wgpu", "wgpu/dx12", "wgpu_glyph"] +empty = ["wgpu", "wgpu_glyph"] debug = [] [dependencies] @@ -46,9 +47,18 @@ gfx_glyph = { version = "0.15", optional = true } gfx_winit = { package = "winit", version = "0.19", optional = true } # wgpu (Vulkan, Metal, D3D) -wgpu = { version = "0.2", optional = true, git = "https://github.com/gfx-rs/wgpu-rs", rev = "a4ad7ae6ff2fc42396b802968dcc95bbf0612235" } -wgpu_glyph = { version = "0.3", optional = true, git = "https://github.com/hecrj/wgpu_glyph" } +wgpu = { version = "0.2", optional = true } +wgpu_glyph = { version = "0.3", optional = true } [dev-dependencies] # Example dependencies rand = "0.6" + +[patch.crates-io] +wgpu = { git = "https://github.com/hecrj/wgpu-rs", branch = "web" } +wgpu_glyph = { git = "https://github.com/hecrj/wgpu_glyph", branch = "web" } +wgpu-native = { git = "https://github.com/hecrj/wgpu", branch = "web" } +gfx-hal = { git = "https://github.com/hecrj/gfx", branch = "web" } +gfx-backend-empty = { git = "https://github.com/hecrj/gfx", branch = "web" } +gfx-backend-gl = { git = "https://github.com/hecrj/gfx", branch = "web" } +winit = { git = "https://github.com/hecrj/winit", branch = "web" } diff --git a/build.rs b/build.rs index 929b179..34b4461 100644 --- a/build.rs +++ b/build.rs @@ -3,7 +3,8 @@ feature = "vulkan", feature = "metal", feature = "dx11", - feature = "dx12" + feature = "dx12", + all(debug_assertions, feature = "empty") )))] compile_error!( "You need to enable a graphics backend feature. \ diff --git a/src/game.rs b/src/game.rs index 0338f0b..f32f2f1 100644 --- a/src/game.rs +++ b/src/game.rs @@ -1,6 +1,9 @@ -use crate::graphics::window; +mod r#loop; + +pub(crate) use r#loop::Loop; + use crate::graphics::{Frame, Window, WindowSettings}; -use crate::input::{self, gamepad, keyboard, mouse, Input}; +use crate::input::{keyboard, Input}; use crate::load::{LoadingScreen, Task}; use crate::{Debug, Result, Timer}; @@ -162,155 +165,8 @@ pub trait Game { /// [`WindowSettings`]: graphics/struct.WindowSettings.html fn run(window_settings: WindowSettings) -> Result<()> where - Self: Sized + 'static, + Self: 'static + Sized, { - // Set up window - let event_loop = &mut window::EventLoop::new(); - let window = &mut Window::new(window_settings, &event_loop)?; - let mut debug = Debug::new(window.gpu()); - - // Load game - debug.loading_started(); - let mut loading_screen = Self::LoadingScreen::new(window.gpu())?; - let game = &mut loading_screen.run(Self::load(window), window)?; - let input = &mut Self::Input::new(); - let mut gamepads = gamepad::Tracker::new(); - debug.loading_finished(); - - // Game loop - let mut timer = Timer::new(Self::TICKS_PER_SECOND); - let mut alive = true; - - while alive { - debug.frame_started(); - timer.update(); - - while timer.tick() { - interact( - game, - input, - &mut debug, - window, - event_loop, - gamepads.as_mut(), - &mut alive, - ); - - debug.update_started(); - game.update(window); - debug.update_finished(); - } - - if !timer.has_ticked() { - interact( - game, - input, - &mut debug, - window, - event_loop, - gamepads.as_mut(), - &mut alive, - ); - } - - debug.draw_started(); - game.draw(&mut window.frame(), &timer); - debug.draw_finished(); - - if debug.is_enabled() { - debug.debug_started(); - game.debug(input, &mut window.frame(), &mut debug); - debug.debug_finished(); - } - - window.swap_buffers(); - debug.frame_finished(); - } - - Ok(()) - } -} - -fn interact( - game: &mut G, - input: &mut G::Input, - debug: &mut Debug, - window: &mut Window, - event_loop: &mut window::EventLoop, - gamepads: Option<&mut gamepad::Tracker>, - alive: &mut bool, -) { - debug.interact_started(); - - event_loop.poll(|event| { - process_window_event(game, input, debug, window, alive, event) - }); - - process_gamepad_events(gamepads, input); - - game.interact(input, window); - input.clear(); - - debug.interact_finished(); -} - -pub(crate) fn process_window_event( - game: &mut G, - input: &mut I, - debug: &mut Debug, - window: &mut Window, - alive: &mut bool, - event: window::Event, -) { - match event { - window::Event::Input(input_event) => { - input.update(input_event); - - #[cfg(any(debug_assertions, feature = "debug"))] - match input_event { - input::Event::Keyboard(keyboard::Event::Input { - state: input::ButtonState::Released, - key_code, - }) if Some(key_code) == G::DEBUG_KEY => { - debug.toggle(); - } - _ => {} - } - } - window::Event::CursorMoved(logical_position) => { - let position = logical_position.to_physical(window.dpi()); - - input.update(input::Event::Mouse(mouse::Event::CursorMoved { - x: position.x as f32, - y: position.y as f32, - })); - } - window::Event::Moved(logical_position) => { - let position = logical_position.to_physical(window.dpi()); - - input.update(input::Event::Window(input::window::Event::Moved { - x: position.x as f32, - y: position.y as f32, - })) - } - window::Event::CloseRequested => { - if game.on_close_request() { - *alive = false; - } - } - window::Event::Resized(new_size) => { - window.resize(new_size); - } - }; -} - -pub(crate) fn process_gamepad_events( - gamepads: Option<&mut gamepad::Tracker>, - input: &mut I, -) { - if let Some(tracker) = gamepads { - while let Some((id, event, time)) = tracker.next_event() { - input.update(input::Event::Gamepad { id, event, time }); - } + >::run(window_settings) } } diff --git a/src/game/loop.rs b/src/game/loop.rs new file mode 100644 index 0000000..e66a088 --- /dev/null +++ b/src/game/loop.rs @@ -0,0 +1,230 @@ +use crate::debug::Debug; +use crate::graphics::window::winit; +use crate::graphics::{Window, WindowSettings}; +use crate::input::{self, gamepad, keyboard, mouse, window, Input}; +use crate::load::{Join, LoadingScreen, Task}; +use crate::{Result, Timer}; + +pub trait Loop { + type Attributes; + + fn new( + configuration: Self::Attributes, + game: &mut Game, + window: &Window, + ) -> Self; + + fn load(window: &Window) -> Task; + + fn on_input(&mut self, input: &mut Game::Input, event: input::Event) { + input.update(event); + } + + fn after_draw( + &mut self, + _game: &mut Game, + _input: &mut Game::Input, + _window: &mut Window, + _debug: &mut Debug, + ) { + } + + fn run(window_settings: WindowSettings) -> Result<()> + where + Self: 'static + Sized, + Game: 'static, + Game::Input: 'static, + { + // Window creation + let event_loop = winit::event_loop::EventLoop::new(); + let mut window = Window::new(window_settings, &event_loop)?; + let mut debug = Debug::new(window.gpu()); + + // Loading + debug.loading_started(); + let (mut game, configuration) = { + let mut loading_screen = Game::LoadingScreen::new(window.gpu())?; + + loading_screen.run( + (Game::load(&window), Self::load(&window)).join(), + &mut window, + )? + }; + + let mut game_loop = Self::new(configuration, &mut game, &mut window); + let mut input = Game::Input::new(); + let mut gamepads = gamepad::Tracker::new(); + debug.loading_finished(); + + let mut timer = Timer::new(Game::TICKS_PER_SECOND); + + // Initialization + debug.frame_started(); + timer.update(); + window.request_redraw(); + + event_loop.run(move |event, _, control_flow| match event { + winit::event::Event::NewEvents(_) => { + debug.interact_started(); + } + winit::event::Event::EventsCleared => { + if let Some(tracker) = &mut gamepads { + while let Some((id, event, time)) = tracker.next_event() { + game_loop.on_input( + &mut input, + input::Event::Gamepad { id, event, time }, + ); + } + } + + game.interact(&mut input, &mut window); + input.clear(); + debug.interact_finished(); + + if timer.tick() { + debug.update_started(); + game.update(&window); + debug.update_finished(); + } + } + winit::event::Event::WindowEvent { event, .. } => match event { + winit::event::WindowEvent::RedrawRequested => { + debug.draw_started(); + game.draw(&mut window.frame(), &timer); + debug.draw_finished(); + + game_loop.after_draw( + &mut game, + &mut input, + &mut window, + &mut debug, + ); + + if debug.is_enabled() { + debug.debug_started(); + game.debug(&input, &mut window.frame(), &mut debug); + debug.debug_finished(); + } + + window.swap_buffers(); + debug.frame_finished(); + + debug.frame_started(); + window.request_redraw(); + timer.update(); + } + winit::event::WindowEvent::CloseRequested => { + if game.on_close_request() { + *control_flow = winit::event_loop::ControlFlow::Exit; + } + } + winit::event::WindowEvent::Resized(logical_size) => { + window.resize(logical_size); + } + _ => { + if let Some(input_event) = + try_into_input_event(&window, event) + { + game_loop.on_input(&mut input, input_event); + } + } + }, + _ => {} + }); + } +} + +fn try_into_input_event( + window: &Window, + event: winit::event::WindowEvent, +) -> Option { + match event { + winit::event::WindowEvent::KeyboardInput { + input: + winit::event::KeyboardInput { + state, + virtual_keycode: Some(key_code), + .. + }, + .. + } => Some(input::Event::Keyboard(keyboard::Event::Input { + state, + key_code, + })), + winit::event::WindowEvent::ReceivedCharacter(codepoint) => { + Some(input::Event::Keyboard(keyboard::Event::TextEntered { + character: codepoint, + })) + } + winit::event::WindowEvent::MouseInput { state, button, .. } => { + Some(input::Event::Mouse(mouse::Event::Input { state, button })) + } + winit::event::WindowEvent::MouseWheel { delta, .. } => match delta { + winit::event::MouseScrollDelta::LineDelta(x, y) => { + Some(input::Event::Mouse(mouse::Event::WheelScrolled { + delta_x: x, + delta_y: y, + })) + } + _ => None, + }, + winit::event::WindowEvent::CursorMoved { position, .. } => { + let position = position.to_physical(window.dpi()); + + Some(input::Event::Mouse(mouse::Event::CursorMoved { + x: position.x as f32, + y: position.y as f32, + })) + } + winit::event::WindowEvent::CursorEntered { .. } => { + Some(input::Event::Mouse(mouse::Event::CursorEntered)) + } + winit::event::WindowEvent::CursorLeft { .. } => { + Some(input::Event::Mouse(mouse::Event::CursorLeft)) + } + winit::event::WindowEvent::Focused(focus) => Some(if focus == true { + input::Event::Window(window::Event::Focused) + } else { + input::Event::Window(window::Event::Unfocused) + }), + winit::event::WindowEvent::Moved(position) => { + let position = position.to_physical(window.dpi()); + + Some(input::Event::Window(window::Event::Moved { + x: position.x as f32, + y: position.y as f32, + })) + } + _ => None, + } +} + +pub struct Default {} + +impl Loop for Default +where + Game: 'static, +{ + type Attributes = (); + + fn new( + _attributes: Self::Attributes, + _game: &mut Game, + _window: &Window, + ) -> Self { + Self {} + } + + fn load(_window: &Window) -> Task { + Task::new(|| ()) + } + + fn after_draw( + &mut self, + _game: &mut Game, + _input: &mut Game::Input, + _window: &mut Window, + _debug: &mut Debug, + ) { + } +} diff --git a/src/graphics.rs b/src/graphics.rs index de8324b..30445e9 100644 --- a/src/graphics.rs +++ b/src/graphics.rs @@ -94,14 +94,16 @@ use backend_gfx as gpu; feature = "vulkan", feature = "metal", feature = "dx11", - feature = "dx12" + feature = "dx12", + all(debug_assertions, feature = "empty") ))] mod backend_wgpu; #[cfg(any( feature = "vulkan", feature = "metal", feature = "dx11", - feature = "dx12" + feature = "dx12", + all(debug_assertions, feature = "empty") ))] use backend_wgpu as gpu; diff --git a/src/graphics/backend_wgpu/mod.rs b/src/graphics/backend_wgpu/mod.rs index 8681ae8..f8138ad 100644 --- a/src/graphics/backend_wgpu/mod.rs +++ b/src/graphics/backend_wgpu/mod.rs @@ -26,9 +26,13 @@ pub struct Gpu { impl Gpu { pub(super) fn for_window( - builder: winit::WindowBuilder, - events_loop: &winit::EventsLoop, + builder: winit::window::WindowBuilder, + event_loop: &winit::event_loop::EventLoop<()>, ) -> Result<(Gpu, Surface)> { + let window = builder + .build(event_loop) + .map_err(|error| Error::WindowCreation(error.to_string()))?; + let instance = wgpu::Instance::new(); let adapter = instance.get_adapter(&wgpu::AdapterDescriptor { @@ -42,14 +46,11 @@ impl Gpu { limits: wgpu::Limits::default(), }); + let surface = Surface::new(window, &instance, &device); + let quad_pipeline = quad::Pipeline::new(&mut device); let triangle_pipeline = triangle::Pipeline::new(&mut device); - let window = builder - .build(events_loop) - .map_err(|error| Error::WindowCreation(error.to_string()))?; - let surface = Surface::new(window, &instance, &device); - let encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { todo: 0, diff --git a/src/graphics/backend_wgpu/surface.rs b/src/graphics/backend_wgpu/surface.rs index 76a0e63..79b877c 100644 --- a/src/graphics/backend_wgpu/surface.rs +++ b/src/graphics/backend_wgpu/surface.rs @@ -4,7 +4,7 @@ use super::{Gpu, TargetView}; pub use wgpu::winit; pub struct Surface { - window: winit::Window, + window: winit::window::Window, surface: wgpu::Surface, swap_chain: wgpu::SwapChain, extent: wgpu::Extent3d, @@ -14,21 +14,13 @@ pub struct Surface { impl Surface { pub fn new( - window: winit::Window, + window: winit::window::Window, instance: &wgpu::Instance, device: &wgpu::Device, ) -> Surface { let surface = instance.create_surface(&window); - let size = window - .get_inner_size() - // TODO: Find out when and why the "inner size" might not be available - // and do something smarter here. - .unwrap_or(winit::dpi::LogicalSize { - width: 1280.0, - height: 1024.0, - }) - .to_physical(window.get_hidpi_factor()); + let size = window.inner_size().to_physical(window.hidpi_factor()); let (swap_chain, extent, buffer, target) = new_swap_chain(device, &surface, size); @@ -43,7 +35,7 @@ impl Surface { } } - pub fn window(&self) -> &winit::Window { + pub fn window(&self) -> &winit::window::Window { &self.window } @@ -98,6 +90,10 @@ impl Surface { gpu.device.get_queue().submit(&[encoder.finish()]); } + + pub fn request_redraw(&mut self) { + self.window.request_redraw(); + } } fn new_swap_chain( diff --git a/src/graphics/window.rs b/src/graphics/window.rs index 7e5db8e..8ced680 100644 --- a/src/graphics/window.rs +++ b/src/graphics/window.rs @@ -1,9 +1,7 @@ -mod event; mod frame; mod settings; pub(crate) use crate::graphics::gpu::winit; -pub(crate) use event::{Event, EventLoop}; pub use frame::Frame; pub use settings::Settings; @@ -27,12 +25,12 @@ pub struct Window { impl Window { pub(crate) fn new( mut settings: Settings, - event_loop: &EventLoop, + event_loop: &winit::event_loop::EventLoop<()>, ) -> Result { let (mut width, mut height) = settings.size; // Try to revert DPI - let dpi = event_loop.raw().get_primary_monitor().get_hidpi_factor(); + let dpi = event_loop.primary_monitor().hidpi_factor(); width = (width as f64 / dpi).round() as u32; height = (height as f64 / dpi).round() as u32; @@ -41,23 +39,20 @@ impl Window { let is_fullscreen = settings.fullscreen; - let (gpu, surface) = Gpu::for_window( - settings.into_builder(event_loop.raw()), - event_loop.raw(), - )?; + let (gpu, surface) = + Gpu::for_window(settings.into_builder(event_loop), event_loop)?; let window = surface.window(); - let (width, height) = window - .get_inner_size() - .map(|inner_size| { - let dpi = window.get_hidpi_factor(); - ( - (inner_size.width * dpi) as f32, - (inner_size.height * dpi) as f32, - ) - }) - .unwrap_or((width as f32, height as f32)); + let (width, height) = { + let inner_size = window.inner_size(); + let dpi = window.hidpi_factor(); + + ( + (inner_size.width * dpi) as f32, + (inner_size.height * dpi) as f32, + ) + }; Ok(Window { is_fullscreen, @@ -89,7 +84,7 @@ impl Window { let monitor = if self.is_fullscreen { None } else { - Some(window.get_primary_monitor()) + Some(window.primary_monitor()) }; window.set_fullscreen(monitor); @@ -112,15 +107,19 @@ impl Window { } pub(crate) fn dpi(&self) -> f64 { - self.surface.window().get_hidpi_factor() + self.surface.window().hidpi_factor() } pub(crate) fn swap_buffers(&mut self) { self.surface.swap_buffers(&mut self.gpu); } + pub(crate) fn request_redraw(&mut self) { + self.surface.request_redraw(); + } + pub(crate) fn resize(&mut self, new_size: winit::dpi::LogicalSize) { - let dpi = self.surface.window().get_hidpi_factor(); + let dpi = self.surface.window().hidpi_factor(); let physical_size = new_size.to_physical(dpi); self.surface.resize(&mut self.gpu, physical_size); @@ -129,8 +128,11 @@ impl Window { self.height = physical_size.height as f32; } - pub(crate) fn update_cursor(&mut self, new_cursor: winit::MouseCursor) { - self.surface.window().set_cursor(new_cursor); + pub(crate) fn update_cursor( + &mut self, + new_cursor: winit::window::CursorIcon, + ) { + self.surface.window().set_cursor_icon(new_cursor); } } diff --git a/src/graphics/window/settings.rs b/src/graphics/window/settings.rs index 34cc60d..45ff896 100644 --- a/src/graphics/window/settings.rs +++ b/src/graphics/window/settings.rs @@ -19,17 +19,17 @@ pub struct Settings { impl Settings { pub(super) fn into_builder( self, - events_loop: &winit::EventsLoop, - ) -> winit::WindowBuilder { + events_loop: &winit::event_loop::EventLoop<()>, + ) -> winit::window::WindowBuilder { let monitor = if self.fullscreen { - Some(events_loop.get_primary_monitor()) + Some(events_loop.primary_monitor()) } else { None }; - winit::WindowBuilder::new() + winit::window::WindowBuilder::new() .with_title(self.title) - .with_dimensions(winit::dpi::LogicalSize { + .with_inner_size(winit::dpi::LogicalSize { width: self.size.0 as f64, height: self.size.1 as f64, }) diff --git a/src/input.rs b/src/input.rs index f58f50c..2aa5eaa 100644 --- a/src/input.rs +++ b/src/input.rs @@ -8,7 +8,7 @@ pub mod window; mod event; mod keyboard_and_mouse; -pub use crate::graphics::window::winit::ElementState as ButtonState; +pub use crate::graphics::window::winit::event::ElementState as ButtonState; pub use event::Event; pub use keyboard_and_mouse::KeyboardAndMouse; diff --git a/src/input/keyboard.rs b/src/input/keyboard.rs index a162a92..663438d 100644 --- a/src/input/keyboard.rs +++ b/src/input/keyboard.rs @@ -2,5 +2,5 @@ mod event; -pub use crate::graphics::window::winit::VirtualKeyCode as KeyCode; +pub use crate::graphics::window::winit::event::VirtualKeyCode as KeyCode; pub use event::Event; diff --git a/src/input/mouse.rs b/src/input/mouse.rs index 7180536..c670e92 100644 --- a/src/input/mouse.rs +++ b/src/input/mouse.rs @@ -2,5 +2,5 @@ mod event; -pub use crate::graphics::window::winit::MouseButton as Button; +pub use crate::graphics::window::winit::event::MouseButton as Button; pub use event::Event; diff --git a/src/ui.rs b/src/ui.rs index 4404035..cb5dc34 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -164,12 +164,12 @@ pub type Row<'a, Message> = widget::Row<'a, Message, Renderer>; /// [`Renderer`]: struct.Renderer.html pub type Element<'a, Message> = self::core::Element<'a, Message, Renderer>; -use crate::game; -use crate::graphics::{window, Point, Window, WindowSettings}; -use crate::input::{self, gamepad, mouse, Input as _}; -use crate::load::{Join, LoadingScreen}; +use crate::game::{self, Loop as _}; +use crate::graphics::{Point, Window, WindowSettings}; +use crate::input::{self, mouse, Input as _}; +use crate::load::Task; use crate::ui::core::{Event, Interface, MouseCursor, Renderer as _}; -use crate::{Debug, Game, Result, Timer}; +use crate::{Debug, Game, Result}; /// The user interface of your game. /// @@ -261,142 +261,40 @@ pub trait UserInterface: Game { where Self: 'static + Sized, { - // Set up window - let event_loop = &mut window::EventLoop::new(); - let window = &mut Window::new(window_settings, &event_loop)?; - let mut debug = Debug::new(window.gpu()); - - // Load game - debug.loading_started(); - let mut loading_screen = Self::LoadingScreen::new(window.gpu())?; - let load = ( - Self::load(window), - Self::Renderer::load(Self::configuration()), - ) - .join(); - let (game, renderer) = &mut loading_screen.run(load, window)?; - let input = &mut Input::new(); - let mut gamepads = gamepad::Tracker::new(); - debug.loading_finished(); - - // Game loop - let mut timer = Timer::new(Self::TICKS_PER_SECOND); - let mut alive = true; - let messages = &mut Vec::new(); - let mut mouse_cursor = MouseCursor::OutOfBounds; - let mut ui_cache = - Interface::compute(game.layout(window), &renderer).cache(); - - while alive { - debug.frame_started(); - timer.update(); - - while timer.tick() { - interact( - game, - input, - &mut debug, - window, - event_loop, - gamepads.as_mut(), - &mut alive, - ); - - debug.update_started(); - game.update(window); - debug.update_finished(); - } - - if !timer.has_ticked() { - interact( - game, - input, - &mut debug, - window, - event_loop, - gamepads.as_mut(), - &mut alive, - ); - } - - debug.draw_started(); - game.draw(&mut window.frame(), &timer); - debug.draw_finished(); - - debug.ui_started(); - let mut interface = Interface::compute_with_cache( - game.layout(window), - &renderer, - ui_cache, - ); - - let cursor_position = input.cursor_position; - input.ui_events.drain(..).for_each(|event| { - interface.on_event(event, cursor_position, messages) - }); - - let new_cursor = interface.draw( - renderer, - &mut window.frame(), - input.cursor_position, - ); - - ui_cache = interface.cache(); - - if new_cursor != mouse_cursor { - if new_cursor == MouseCursor::OutOfBounds { - input.update(input::Event::Mouse( - mouse::Event::CursorReturned, - )); - } else if mouse_cursor == MouseCursor::OutOfBounds { - input - .update(input::Event::Mouse(mouse::Event::CursorTaken)); - } - - window.update_cursor(new_cursor.into()); - mouse_cursor = new_cursor; - } - - for message in messages.drain(..) { - game.react(message); - } - debug.ui_finished(); - - if debug.is_enabled() { - debug.debug_started(); - game.debug( - &mut input.game_input, - &mut window.frame(), - &mut debug, - ); - debug.debug_finished(); - } - - window.swap_buffers(); - debug.frame_finished(); - } - - Ok(()) + Loop::::run(window_settings) } } -struct Input { - game_input: I, +struct Loop { + renderer: UI::Renderer, + messages: Vec, + mouse_cursor: MouseCursor, + cache: Option, cursor_position: Point, - ui_events: Vec, + events: Vec, } -impl input::Input for Input { - fn new() -> Input { - Input { - game_input: I::new(), +impl game::Loop for Loop { + type Attributes = UI::Renderer; + + fn new(renderer: UI::Renderer, game: &mut UI, window: &Window) -> Self { + let cache = Interface::compute(game.layout(window), &renderer).cache(); + Loop { + renderer, + messages: Vec::new(), + mouse_cursor: MouseCursor::OutOfBounds, + cache: Some(cache), cursor_position: Point::new(0.0, 0.0), - ui_events: Vec::new(), + events: Vec::new(), } } - fn update(&mut self, event: input::Event) { - self.game_input.update(event); + fn load(_window: &Window) -> Task { + UI::Renderer::load(UI::configuration()) + } + + fn on_input(&mut self, input: &mut UI::Input, event: input::Event) { + input.update(event); match event { input::Event::Mouse(mouse::Event::CursorMoved { x, y }) => { @@ -406,34 +304,53 @@ impl input::Input for Input { }; if let Some(ui_event) = Event::from_input(event) { - self.ui_events.push(ui_event); + self.events.push(ui_event); } } - fn clear(&mut self) { - self.game_input.clear(); - } -} - -fn interact( - game: &mut G, - input: &mut Input, - debug: &mut Debug, - window: &mut Window, - event_loop: &mut window::EventLoop, - gamepads: Option<&mut gamepad::Tracker>, - alive: &mut bool, -) { - debug.interact_started(); - - event_loop.poll(|event| { - game::process_window_event(game, input, debug, window, alive, event) - }); - - game::process_gamepad_events(gamepads, input); + fn after_draw( + &mut self, + ui: &mut UI, + input: &mut UI::Input, + window: &mut Window, + debug: &mut Debug, + ) { + debug.ui_started(); + let mut interface = Interface::compute_with_cache( + ui.layout(window), + &self.renderer, + self.cache.take().unwrap(), + ); + + let cursor_position = self.cursor_position; + let messages = &mut self.messages; + + self.events.drain(..).for_each(|event| { + interface.on_event(event, cursor_position, messages) + }); + + let new_cursor = interface.draw( + &mut self.renderer, + &mut window.frame(), + cursor_position, + ); + + self.cache = Some(interface.cache()); + + if new_cursor != self.mouse_cursor { + if new_cursor == MouseCursor::OutOfBounds { + input.update(input::Event::Mouse(mouse::Event::CursorReturned)); + } else if self.mouse_cursor == MouseCursor::OutOfBounds { + input.update(input::Event::Mouse(mouse::Event::CursorTaken)); + } - game.interact(&mut input.game_input, window); - input.clear(); + window.update_cursor(new_cursor.into()); + self.mouse_cursor = new_cursor; + } - debug.interact_finished(); + for message in messages.drain(..) { + ui.react(message); + } + debug.ui_finished(); + } } diff --git a/src/ui/core.rs b/src/ui/core.rs index fb7c584..b88c104 100644 --- a/src/ui/core.rs +++ b/src/ui/core.rs @@ -22,7 +22,7 @@ pub use stretch::{geometry::Size, number::Number}; pub use element::Element; pub use event::Event; pub use hasher::Hasher; -pub(crate) use interface::Interface; +pub(crate) use interface::{Cache, Interface}; pub use layout::Layout; pub use mouse_cursor::MouseCursor; pub use node::Node; diff --git a/src/ui/core/mouse_cursor.rs b/src/ui/core/mouse_cursor.rs index c0c9cfd..4b99ee6 100644 --- a/src/ui/core/mouse_cursor.rs +++ b/src/ui/core/mouse_cursor.rs @@ -23,15 +23,15 @@ pub enum MouseCursor { } #[doc(hidden)] -impl From for winit::MouseCursor { - fn from(mouse_cursor: MouseCursor) -> winit::MouseCursor { +impl From for winit::window::CursorIcon { + fn from(mouse_cursor: MouseCursor) -> winit::window::CursorIcon { match mouse_cursor { - MouseCursor::OutOfBounds => winit::MouseCursor::Default, - MouseCursor::Idle => winit::MouseCursor::Default, - MouseCursor::Pointer => winit::MouseCursor::Hand, - MouseCursor::Working => winit::MouseCursor::Progress, - MouseCursor::Grab => winit::MouseCursor::Grab, - MouseCursor::Grabbing => winit::MouseCursor::Grabbing, + MouseCursor::OutOfBounds => winit::window::CursorIcon::Default, + MouseCursor::Idle => winit::window::CursorIcon::Default, + MouseCursor::Pointer => winit::window::CursorIcon::Hand, + MouseCursor::Working => winit::window::CursorIcon::Progress, + MouseCursor::Grab => winit::window::CursorIcon::Grab, + MouseCursor::Grabbing => winit::window::CursorIcon::Grabbing, } } } From a294f32d7d857478f31d8fb1e0b25c4af5e2f018 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sun, 25 Aug 2019 02:57:21 +0200 Subject: [PATCH 02/16] Remove `Batch::len` for now --- src/graphics/batch.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/graphics/batch.rs b/src/graphics/batch.rs index eca1e0d..00f6bd5 100644 --- a/src/graphics/batch.rs +++ b/src/graphics/batch.rs @@ -59,11 +59,6 @@ impl Batch { pub fn clear(&mut self) { self.instances.clear(); } - - /// - pub fn len(&self) -> usize { - self.instances.len() - } } impl std::fmt::Debug for Batch { From fda5bcabcce2c287dce8ace16cc0f85e3e95b7f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 26 Aug 2019 22:37:30 +0200 Subject: [PATCH 03/16] Use improved `RedrawRequested` API for `winit` --- Cargo.toml | 3 +++ src/game/loop.rs | 55 +++++++++++++++++++++++------------------------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 698aa15..526ddbf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,3 +51,6 @@ wgpu_glyph = { version = "0.4", optional = true } [dev-dependencies] rand = "0.6" + +[patch.crates-io] +winit = { git = "https://github.com/hecrj/winit", branch = "redraw-requested-2.0" } diff --git a/src/game/loop.rs b/src/game/loop.rs index 2631255..c5eab60 100644 --- a/src/game/loop.rs +++ b/src/game/loop.rs @@ -65,12 +65,9 @@ pub trait Loop { event_loop.run(move |event, _, control_flow| match event { winit::event::Event::NewEvents(_) => { - // TODO: Wait for `winit` for make `RedrawRequested` consistent - // debug.interact_started(); - } - winit::event::Event::EventsCleared => { debug.interact_started(); - + } + winit::event::Event::MainEventsCleared => { if let Some(tracker) = &mut gamepads { while let Some((id, event, time)) = tracker.next_event() { game_loop.on_input( @@ -90,32 +87,32 @@ pub trait Loop { debug.update_finished(); } } - winit::event::Event::WindowEvent { event, .. } => match event { - winit::event::WindowEvent::RedrawRequested => { - debug.draw_started(); - game.draw(&mut window.frame(), &timer); - debug.draw_finished(); - - game_loop.after_draw( - &mut game, - &mut input, - &mut window, - &mut debug, - ); - - if debug.is_enabled() { - debug.debug_started(); - game.debug(&input, &mut window.frame(), &mut debug); - debug.debug_finished(); - } + winit::event::Event::RedrawRequested { .. } => { + debug.draw_started(); + game.draw(&mut window.frame(), &timer); + debug.draw_finished(); + + game_loop.after_draw( + &mut game, + &mut input, + &mut window, + &mut debug, + ); + + if debug.is_enabled() { + debug.debug_started(); + game.debug(&input, &mut window.frame(), &mut debug); + debug.debug_finished(); + } - window.swap_buffers(); - debug.frame_finished(); + window.swap_buffers(); + debug.frame_finished(); - debug.frame_started(); - window.request_redraw(); - timer.update(); - } + debug.frame_started(); + window.request_redraw(); + timer.update(); + } + winit::event::Event::WindowEvent { event, .. } => match event { winit::event::WindowEvent::CloseRequested => { if game.on_close_request() { *control_flow = winit::event_loop::ControlFlow::Exit; From b3ac36c6fe57804ac1ed1ac0f90467505266b926 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 21 Oct 2019 02:56:02 +0200 Subject: [PATCH 04/16] Force `winit` to use the new redraw API --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 526ddbf..67e5ba3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ stretch = "0.2" twox-hash = "1.3" lyon_tessellation = "0.13" gilrs = "0.7" -winit = "0.20.0-alpha3" +winit = "=0.20.0-alpha3" raw-window-handle = "0.1" # gfx (OpenGL) From 860753b2d44e15ddfa3f8887aa3b9229ddeb4e7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 22 Oct 2019 01:13:39 +0200 Subject: [PATCH 05/16] Request redraw in `MainEventsCleared` --- src/game/loop.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/game/loop.rs b/src/game/loop.rs index 8f661a7..c29e39f 100644 --- a/src/game/loop.rs +++ b/src/game/loop.rs @@ -61,7 +61,6 @@ pub trait Loop { // Initialization debug.frame_started(); timer.update(); - window.request_redraw(); event_loop.run(move |event, _, control_flow| match event { winit::event::Event::NewEvents(_) => { @@ -87,6 +86,8 @@ pub trait Loop { debug.update_finished(); } + window.request_redraw(); + if game.is_finished() { *control_flow = winit::event_loop::ControlFlow::Exit; } From 42aa6a32f23f3dc7e520b142f1f9c76989f1572a Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Mon, 21 Oct 2019 22:53:41 -0400 Subject: [PATCH 06/16] WIP cursor icon support through Game trait --- src/game.rs | 7 +++++- src/game/loop.rs | 6 ++++- src/graphics/window.rs | 6 +++++ src/graphics/window/cursor_icon.rs | 37 ++++++++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 src/graphics/window/cursor_icon.rs diff --git a/src/game.rs b/src/game.rs index f53dfb2..9739dbc 100644 --- a/src/game.rs +++ b/src/game.rs @@ -2,7 +2,7 @@ mod r#loop; pub(crate) use r#loop::Loop; -use crate::graphics::{Frame, Window, WindowSettings}; +use crate::graphics::{window::CursorIcon, Frame, Window, WindowSettings}; use crate::input::{keyboard, Input}; use crate::load::{LoadingScreen, Task}; use crate::{Debug, Result, Timer}; @@ -124,6 +124,11 @@ pub trait Game { /// [`Window`]: graphics/struct.Window.html fn update(&mut self, _window: &Window) {} + /// TODO! + fn cursor_icon(&self) -> CursorIcon { + CursorIcon::Default + } + /// Displays debug information. /// /// This method is called after [`draw`] once per frame when debug has been diff --git a/src/game/loop.rs b/src/game/loop.rs index c29e39f..cffbb58 100644 --- a/src/game/loop.rs +++ b/src/game/loop.rs @@ -1,5 +1,5 @@ use crate::debug::Debug; -use crate::graphics::window::winit; +use crate::graphics::window::{winit, CursorIcon}; use crate::graphics::{Window, WindowSettings}; use crate::input::{self, gamepad, keyboard, mouse, window, Input}; use crate::load::{Join, LoadingScreen, Task}; @@ -97,6 +97,10 @@ pub trait Loop { game.draw(&mut window.frame(), &timer); debug.draw_finished(); + let cursor_icon = game.cursor_icon(); + window.set_cursor_visible(cursor_icon != CursorIcon::Hidden); + window.update_cursor(cursor_icon.into()); + game_loop.after_draw( &mut game, &mut input, diff --git a/src/graphics/window.rs b/src/graphics/window.rs index 51aa2c1..48fd028 100644 --- a/src/graphics/window.rs +++ b/src/graphics/window.rs @@ -1,8 +1,10 @@ +mod cursor_icon; mod frame; mod settings; pub(crate) use winit; +pub use cursor_icon::CursorIcon; pub use frame::Frame; pub use settings::Settings; @@ -135,6 +137,10 @@ impl Window { ) { self.surface.window().set_cursor_icon(new_cursor); } + + pub(crate) fn set_cursor_visible(&self, visible: bool) { + self.surface.window().set_cursor_visible(visible); + } } impl std::fmt::Debug for Window { diff --git a/src/graphics/window/cursor_icon.rs b/src/graphics/window/cursor_icon.rs new file mode 100644 index 0000000..7779249 --- /dev/null +++ b/src/graphics/window/cursor_icon.rs @@ -0,0 +1,37 @@ +use crate::graphics::window::winit; + +/// TODO +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum CursorIcon { + /// The platform-dependent default cursor. + Default, + /// A simple crosshair. + Crosshair, + /// A hand (often used to indicate links in web browsers). + Hand, + /// Hides the cursor. + Hidden, + /// Indicates something is to be moved. + Move, +} + +impl Default for CursorIcon { + fn default() -> Self { + Self::Default + } +} + +impl From for winit::window::CursorIcon { + fn from(cursor_icon: CursorIcon) -> winit::window::CursorIcon { + match cursor_icon { + // If the cursor is hidden, it doesn't matter which type it is, so the default makes + // the most sense. + CursorIcon::Default | CursorIcon::Hidden => { + winit::window::CursorIcon::Default + } + CursorIcon::Crosshair => winit::window::CursorIcon::Crosshair, + CursorIcon::Hand => winit::window::CursorIcon::Hand, + CursorIcon::Move => winit::window::CursorIcon::Move, + } + } +} From 9a2fbed32ae990ad81fc27ed5b990c613ef87248 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Tue, 22 Oct 2019 23:38:06 -0400 Subject: [PATCH 07/16] Update cursor icon in Game only if it's changed --- src/game.rs | 2 +- src/game/loop.rs | 27 +++++++++++++++++---------- src/graphics.rs | 2 +- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/game.rs b/src/game.rs index 9739dbc..d6d6564 100644 --- a/src/game.rs +++ b/src/game.rs @@ -2,7 +2,7 @@ mod r#loop; pub(crate) use r#loop::Loop; -use crate::graphics::{window::CursorIcon, Frame, Window, WindowSettings}; +use crate::graphics::{CursorIcon, Frame, Window, WindowSettings}; use crate::input::{keyboard, Input}; use crate::load::{LoadingScreen, Task}; use crate::{Debug, Result, Timer}; diff --git a/src/game/loop.rs b/src/game/loop.rs index cffbb58..14fb4bf 100644 --- a/src/game/loop.rs +++ b/src/game/loop.rs @@ -1,6 +1,6 @@ use crate::debug::Debug; -use crate::graphics::window::{winit, CursorIcon}; -use crate::graphics::{Window, WindowSettings}; +use crate::graphics::window::winit; +use crate::graphics::{CursorIcon, Window, WindowSettings}; use crate::input::{self, gamepad, keyboard, mouse, window, Input}; use crate::load::{Join, LoadingScreen, Task}; use crate::{Result, Timer}; @@ -97,10 +97,6 @@ pub trait Loop { game.draw(&mut window.frame(), &timer); debug.draw_finished(); - let cursor_icon = game.cursor_icon(); - window.set_cursor_visible(cursor_icon != CursorIcon::Hidden); - window.update_cursor(cursor_icon.into()); - game_loop.after_draw( &mut game, &mut input, @@ -208,7 +204,9 @@ fn try_into_input_event( } } -pub struct Default {} +pub struct Default { + cursor_icon: CursorIcon, +} impl Loop for Default where @@ -221,7 +219,9 @@ where _game: &mut Game, _window: &Window, ) -> Self { - Self {} + Self { + cursor_icon: CursorIcon::default(), + } } fn load(_window: &Window) -> Task { @@ -230,10 +230,17 @@ where fn after_draw( &mut self, - _game: &mut Game, + game: &mut Game, _input: &mut Game::Input, - _window: &mut Window, + window: &mut Window, _debug: &mut Debug, ) { + // Update the cursor icon if it has changed + let game_cursor_icon = game.cursor_icon(); + if self.cursor_icon != game_cursor_icon { + window.set_cursor_visible(game_cursor_icon != CursorIcon::Hidden); + window.update_cursor(game_cursor_icon.into()); + self.cursor_icon = game_cursor_icon; + } } } diff --git a/src/graphics.rs b/src/graphics.rs index 9eca525..f2dcb05 100644 --- a/src/graphics.rs +++ b/src/graphics.rs @@ -141,4 +141,4 @@ pub use text::{HorizontalAlignment, Text, VerticalAlignment}; pub use texture_array::TextureArray; pub use transformation::Transformation; pub use vector::Vector; -pub use window::{Frame, Settings as WindowSettings, Window}; +pub use window::{CursorIcon, Frame, Settings as WindowSettings, Window}; From b60a31f8172ad02b2d981371f35e6f96f4841575 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Wed, 23 Oct 2019 01:34:19 -0400 Subject: [PATCH 08/16] Update cursor icon in UserInterface by taking game cursor into account --- src/ui.rs | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/src/ui.rs b/src/ui.rs index b248652..1784e66 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -174,7 +174,7 @@ pub type Panel<'a, Message> = widget::Panel<'a, Message, Renderer>; pub type Element<'a, Message> = self::core::Element<'a, Message, Renderer>; use crate::game::{self, Loop as _}; -use crate::graphics::{Point, Window, WindowSettings}; +use crate::graphics::{CursorIcon, Point, Window, WindowSettings}; use crate::input::{self, mouse, Input as _}; use crate::load::Task; use crate::ui::core::{Event, Interface, MouseCursor, Renderer as _}; @@ -277,7 +277,8 @@ pub trait UserInterface: Game { struct Loop { renderer: UI::Renderer, messages: Vec, - mouse_cursor: MouseCursor, + game_cursor: CursorIcon, + ui_cursor: MouseCursor, cache: Option, cursor_position: Point, events: Vec, @@ -291,7 +292,8 @@ impl game::Loop for Loop { Loop { renderer, messages: Vec::new(), - mouse_cursor: MouseCursor::OutOfBounds, + game_cursor: CursorIcon::Default, + ui_cursor: MouseCursor::OutOfBounds, cache: Some(cache), cursor_position: Point::new(0.0, 0.0), events: Vec::new(), @@ -338,7 +340,7 @@ impl game::Loop for Loop { interface.on_event(event, cursor_position, messages) }); - let new_cursor = interface.draw( + let new_ui_cursor = interface.draw( &mut self.renderer, &mut window.frame(), cursor_position, @@ -346,15 +348,28 @@ impl game::Loop for Loop { self.cache = Some(interface.cache()); - if new_cursor != self.mouse_cursor { - if new_cursor == MouseCursor::OutOfBounds { + if new_ui_cursor != self.ui_cursor { + if new_ui_cursor == MouseCursor::OutOfBounds { input.update(input::Event::Mouse(mouse::Event::CursorReturned)); - } else if self.mouse_cursor == MouseCursor::OutOfBounds { + } else if self.ui_cursor == MouseCursor::OutOfBounds { input.update(input::Event::Mouse(mouse::Event::CursorTaken)); } - window.update_cursor(new_cursor.into()); - self.mouse_cursor = new_cursor; + self.ui_cursor = new_ui_cursor; + } + let new_game_cursor = ui.cursor_icon(); + if new_game_cursor != self.game_cursor { + self.game_cursor = new_game_cursor; + } + + // TODO: Only update the cursor if it has changed once `MouseCursor` & `CursorIcon` merges + // Use the game cursor if cursor is not on a UI element, use the mouse cursor otherwise + if self.ui_cursor == MouseCursor::OutOfBounds { + window.set_cursor_visible(self.game_cursor != CursorIcon::Hidden); + window.update_cursor(self.game_cursor.into()); + } else { + window.set_cursor_visible(true); + window.update_cursor(self.ui_cursor.into()); } for message in messages.drain(..) { From 13429f2758978231a7888db702c940c4e6a8e913 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Thu, 24 Oct 2019 15:42:49 -0400 Subject: [PATCH 09/16] Simplifies cursor update logic across modules --- src/game/loop.rs | 18 +++++------------- src/graphics/window.rs | 16 +++++++++++++--- src/ui.rs | 31 ++++++++++++------------------- 3 files changed, 30 insertions(+), 35 deletions(-) diff --git a/src/game/loop.rs b/src/game/loop.rs index 14fb4bf..559ecc0 100644 --- a/src/game/loop.rs +++ b/src/game/loop.rs @@ -204,9 +204,7 @@ fn try_into_input_event( } } -pub struct Default { - cursor_icon: CursorIcon, -} +pub struct Default {} impl Loop for Default where @@ -219,9 +217,7 @@ where _game: &mut Game, _window: &Window, ) -> Self { - Self { - cursor_icon: CursorIcon::default(), - } + Self {} } fn load(_window: &Window) -> Task { @@ -235,12 +231,8 @@ where window: &mut Window, _debug: &mut Debug, ) { - // Update the cursor icon if it has changed - let game_cursor_icon = game.cursor_icon(); - if self.cursor_icon != game_cursor_icon { - window.set_cursor_visible(game_cursor_icon != CursorIcon::Hidden); - window.update_cursor(game_cursor_icon.into()); - self.cursor_icon = game_cursor_icon; - } + let cursor_icon = game.cursor_icon(); + window.set_cursor_visible(cursor_icon != CursorIcon::Hidden); + window.update_cursor(cursor_icon.into()); } } diff --git a/src/graphics/window.rs b/src/graphics/window.rs index 48fd028..cc15799 100644 --- a/src/graphics/window.rs +++ b/src/graphics/window.rs @@ -22,6 +22,8 @@ pub struct Window { width: f32, height: f32, is_fullscreen: bool, + cursor_icon: winit::window::CursorIcon, + cursor_visible: bool, } impl Window { @@ -62,6 +64,8 @@ impl Window { surface, width, height, + cursor_icon: winit::window::CursorIcon::Default, + cursor_visible: true, }) } @@ -135,11 +139,17 @@ impl Window { &mut self, new_cursor: winit::window::CursorIcon, ) { - self.surface.window().set_cursor_icon(new_cursor); + if self.cursor_icon != new_cursor { + self.surface.window().set_cursor_icon(new_cursor); + self.cursor_icon = new_cursor; + } } - pub(crate) fn set_cursor_visible(&self, visible: bool) { - self.surface.window().set_cursor_visible(visible); + pub(crate) fn set_cursor_visible(&mut self, visible: bool) { + if self.cursor_visible != visible { + self.surface.window().set_cursor_visible(visible); + self.cursor_visible = visible; + } } } diff --git a/src/ui.rs b/src/ui.rs index 1784e66..da897e3 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -277,8 +277,7 @@ pub trait UserInterface: Game { struct Loop { renderer: UI::Renderer, messages: Vec, - game_cursor: CursorIcon, - ui_cursor: MouseCursor, + mouse_cursor: MouseCursor, cache: Option, cursor_position: Point, events: Vec, @@ -292,8 +291,7 @@ impl game::Loop for Loop { Loop { renderer, messages: Vec::new(), - game_cursor: CursorIcon::Default, - ui_cursor: MouseCursor::OutOfBounds, + mouse_cursor: MouseCursor::OutOfBounds, cache: Some(cache), cursor_position: Point::new(0.0, 0.0), events: Vec::new(), @@ -340,7 +338,7 @@ impl game::Loop for Loop { interface.on_event(event, cursor_position, messages) }); - let new_ui_cursor = interface.draw( + let new_cursor = interface.draw( &mut self.renderer, &mut window.frame(), cursor_position, @@ -348,28 +346,23 @@ impl game::Loop for Loop { self.cache = Some(interface.cache()); - if new_ui_cursor != self.ui_cursor { - if new_ui_cursor == MouseCursor::OutOfBounds { + if new_cursor != self.mouse_cursor { + if new_cursor == MouseCursor::OutOfBounds { input.update(input::Event::Mouse(mouse::Event::CursorReturned)); - } else if self.ui_cursor == MouseCursor::OutOfBounds { + } else if self.mouse_cursor == MouseCursor::OutOfBounds { input.update(input::Event::Mouse(mouse::Event::CursorTaken)); } - self.ui_cursor = new_ui_cursor; + self.mouse_cursor = new_cursor; } - let new_game_cursor = ui.cursor_icon(); - if new_game_cursor != self.game_cursor { - self.game_cursor = new_game_cursor; - } - - // TODO: Only update the cursor if it has changed once `MouseCursor` & `CursorIcon` merges // Use the game cursor if cursor is not on a UI element, use the mouse cursor otherwise - if self.ui_cursor == MouseCursor::OutOfBounds { - window.set_cursor_visible(self.game_cursor != CursorIcon::Hidden); - window.update_cursor(self.game_cursor.into()); + if self.mouse_cursor == MouseCursor::OutOfBounds { + let game_cursor = ui.cursor_icon(); + window.set_cursor_visible(game_cursor != CursorIcon::Hidden); + window.update_cursor(game_cursor.into()); } else { window.set_cursor_visible(true); - window.update_cursor(self.ui_cursor.into()); + window.update_cursor(self.mouse_cursor.into()); } for message in messages.drain(..) { From 66828daceee95b73aa10a45c2d279fe7600e3660 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Fri, 25 Oct 2019 00:47:53 -0400 Subject: [PATCH 10/16] Implements TryFrom for CursorIcon and uses Option for cursor icon caching --- src/game/loop.rs | 7 +++---- src/graphics/window.rs | 22 +++++++++------------- src/graphics/window/cursor_icon.rs | 22 ++++++++++++---------- src/ui.rs | 9 ++++----- 4 files changed, 28 insertions(+), 32 deletions(-) diff --git a/src/game/loop.rs b/src/game/loop.rs index 559ecc0..ef80549 100644 --- a/src/game/loop.rs +++ b/src/game/loop.rs @@ -1,9 +1,10 @@ use crate::debug::Debug; use crate::graphics::window::winit; -use crate::graphics::{CursorIcon, Window, WindowSettings}; +use crate::graphics::{Window, WindowSettings}; use crate::input::{self, gamepad, keyboard, mouse, window, Input}; use crate::load::{Join, LoadingScreen, Task}; use crate::{Result, Timer}; +use std::convert::TryInto; pub trait Loop { type Attributes; @@ -231,8 +232,6 @@ where window: &mut Window, _debug: &mut Debug, ) { - let cursor_icon = game.cursor_icon(); - window.set_cursor_visible(cursor_icon != CursorIcon::Hidden); - window.update_cursor(cursor_icon.into()); + window.update_cursor(game.cursor_icon().try_into().ok()); } } diff --git a/src/graphics/window.rs b/src/graphics/window.rs index cc15799..d45e520 100644 --- a/src/graphics/window.rs +++ b/src/graphics/window.rs @@ -22,8 +22,7 @@ pub struct Window { width: f32, height: f32, is_fullscreen: bool, - cursor_icon: winit::window::CursorIcon, - cursor_visible: bool, + cursor_icon: Option, } impl Window { @@ -64,8 +63,7 @@ impl Window { surface, width, height, - cursor_icon: winit::window::CursorIcon::Default, - cursor_visible: true, + cursor_icon: Some(winit::window::CursorIcon::Default), }) } @@ -137,20 +135,18 @@ impl Window { pub(crate) fn update_cursor( &mut self, - new_cursor: winit::window::CursorIcon, + new_cursor: Option, ) { if self.cursor_icon != new_cursor { - self.surface.window().set_cursor_icon(new_cursor); + if let Some(cursor_icon) = new_cursor { + self.surface.window().set_cursor_icon(cursor_icon); + } + self.surface + .window() + .set_cursor_visible(new_cursor.is_some()); self.cursor_icon = new_cursor; } } - - pub(crate) fn set_cursor_visible(&mut self, visible: bool) { - if self.cursor_visible != visible { - self.surface.window().set_cursor_visible(visible); - self.cursor_visible = visible; - } - } } impl std::fmt::Debug for Window { diff --git a/src/graphics/window/cursor_icon.rs b/src/graphics/window/cursor_icon.rs index 7779249..e76be97 100644 --- a/src/graphics/window/cursor_icon.rs +++ b/src/graphics/window/cursor_icon.rs @@ -1,4 +1,5 @@ use crate::graphics::window::winit; +use std::convert::TryFrom; /// TODO #[derive(Debug, Clone, Copy, PartialEq)] @@ -21,17 +22,18 @@ impl Default for CursorIcon { } } -impl From for winit::window::CursorIcon { - fn from(cursor_icon: CursorIcon) -> winit::window::CursorIcon { +impl TryFrom for winit::window::CursorIcon { + type Error = (); + + fn try_from( + cursor_icon: CursorIcon, + ) -> Result { match cursor_icon { - // If the cursor is hidden, it doesn't matter which type it is, so the default makes - // the most sense. - CursorIcon::Default | CursorIcon::Hidden => { - winit::window::CursorIcon::Default - } - CursorIcon::Crosshair => winit::window::CursorIcon::Crosshair, - CursorIcon::Hand => winit::window::CursorIcon::Hand, - CursorIcon::Move => winit::window::CursorIcon::Move, + CursorIcon::Default => Ok(winit::window::CursorIcon::Default), + CursorIcon::Crosshair => Ok(winit::window::CursorIcon::Crosshair), + CursorIcon::Hand => Ok(winit::window::CursorIcon::Hand), + CursorIcon::Hidden => Err(()), + CursorIcon::Move => Ok(winit::window::CursorIcon::Move), } } } diff --git a/src/ui.rs b/src/ui.rs index da897e3..45a9337 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -174,11 +174,12 @@ pub type Panel<'a, Message> = widget::Panel<'a, Message, Renderer>; pub type Element<'a, Message> = self::core::Element<'a, Message, Renderer>; use crate::game::{self, Loop as _}; -use crate::graphics::{CursorIcon, Point, Window, WindowSettings}; +use crate::graphics::{Point, Window, WindowSettings}; use crate::input::{self, mouse, Input as _}; use crate::load::Task; use crate::ui::core::{Event, Interface, MouseCursor, Renderer as _}; use crate::{Debug, Game, Result}; +use std::convert::TryInto; /// The user interface of your game. /// @@ -358,11 +359,9 @@ impl game::Loop for Loop { // Use the game cursor if cursor is not on a UI element, use the mouse cursor otherwise if self.mouse_cursor == MouseCursor::OutOfBounds { let game_cursor = ui.cursor_icon(); - window.set_cursor_visible(game_cursor != CursorIcon::Hidden); - window.update_cursor(game_cursor.into()); + window.update_cursor(game_cursor.try_into().ok()); } else { - window.set_cursor_visible(true); - window.update_cursor(self.mouse_cursor.into()); + window.update_cursor(Some(self.mouse_cursor.into())); } for message in messages.drain(..) { From 21ce46f57ed26965b4be953bd2a3e7be356d4bc2 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Fri, 25 Oct 2019 00:57:45 -0400 Subject: [PATCH 11/16] Adds documentation for cursor icon --- src/game.rs | 4 +++- src/graphics/window/cursor_icon.rs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/game.rs b/src/game.rs index d6d6564..df38292 100644 --- a/src/game.rs +++ b/src/game.rs @@ -124,7 +124,9 @@ pub trait Game { /// [`Window`]: graphics/struct.Window.html fn update(&mut self, _window: &Window) {} - /// TODO! + /// Defines the cursor icon of the window. + /// + /// By default, it returns platform-dependent default cursor. fn cursor_icon(&self) -> CursorIcon { CursorIcon::Default } diff --git a/src/graphics/window/cursor_icon.rs b/src/graphics/window/cursor_icon.rs index e76be97..786d84c 100644 --- a/src/graphics/window/cursor_icon.rs +++ b/src/graphics/window/cursor_icon.rs @@ -1,7 +1,7 @@ use crate::graphics::window::winit; use std::convert::TryFrom; -/// TODO +/// Describes the appearance of the mouse cursor. #[derive(Debug, Clone, Copy, PartialEq)] pub enum CursorIcon { /// The platform-dependent default cursor. From 6b7ccedbe98776465cb053ebcb740585d74128ce Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Sat, 26 Oct 2019 01:14:23 -0400 Subject: [PATCH 12/16] Updates CHANGELOG to include game::cursor_icon addition --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb839a8..9acc30b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 interface. - `Mesh::new_with_tolerance`, which allows to control the tolerance of line segment approximations. [#100] +- `Game::cursor_icon`, which allows customization of the mouse cursor icon. ### Changed - `Mesh::stroke` now takes an `f32` as `line_width` instead of a `u16`. From d7e38bc3923674b170189ada99f514b7a8a6fe81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sun, 8 Mar 2020 01:27:27 +0100 Subject: [PATCH 13/16] Update `glutin` --- Cargo.toml | 19 ++++++------- src/debug/null.rs | 2 -- src/game/loop.rs | 28 ++++++++++++------ src/graphics/backend_gfx/surface.rs | 10 +++++-- src/graphics/window.rs | 44 ++++++----------------------- src/graphics/window/settings.rs | 6 ++-- 6 files changed, 46 insertions(+), 63 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3975fb6..f3330e2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,10 +21,10 @@ features = ["opengl", "debug"] [features] default = [] opengl = ["gfx", "gfx_core", "glutin", "gfx_device_gl", "gfx_glyph"] -vulkan = ["wgpu", "wgpu/vulkan", "wgpu_glyph"] -metal = ["wgpu", "wgpu/metal", "wgpu_glyph"] -dx11 = ["wgpu", "wgpu/dx11", "wgpu_glyph"] -dx12 = ["wgpu", "wgpu/dx12", "wgpu_glyph"] +vulkan = ["wgpu"] +metal = ["wgpu"] +dx11 = ["wgpu"] +dx12 = ["wgpu"] debug = [] [dependencies] @@ -35,7 +35,7 @@ stretch = "0.2" twox-hash = "1.3" lyon_tessellation = "0.13" gilrs = "0.7" -winit = "=0.20.0-alpha3" +winit = "0.21" raw-window-handle = "0.1" # gfx (OpenGL) @@ -43,15 +43,12 @@ gfx = { version = "0.18", optional = true } gfx_core = { version = "0.9", optional = true } gfx_device_gl = { version = "0.16", optional = true } gfx_glyph = { version = "0.15", optional = true } -glutin = { version = "0.22.0-alpha3", optional = true } +glutin = { version = "0.23", optional = true } # wgpu (Vulkan, Metal, D3D) -wgpu = { version = "0.3", optional = true } -wgpu_glyph = { version = "0.4", optional = true } +wgpu = { version = "0.4", optional = true } +wgpu_glyph = { version = "0.7", optional = true } [dev-dependencies] rand = "0.6" env_logger = "0.6" - -[patch.crates-io] -winit = { git = "https://github.com/hecrj/winit", branch = "redraw-requested-2.0" } diff --git a/src/debug/null.rs b/src/debug/null.rs index 6ce928c..6c4d03c 100644 --- a/src/debug/null.rs +++ b/src/debug/null.rs @@ -1,12 +1,10 @@ use crate::graphics; // Null debug implementation -#[cfg(not(debug_assertions))] #[allow(missing_debug_implementations)] #[allow(missing_docs)] pub struct Debug {} -#[cfg(not(debug_assertions))] impl Debug { pub(crate) fn new(_gpu: &mut graphics::Gpu) -> Self { Self {} diff --git a/src/game/loop.rs b/src/game/loop.rs index ef80549..85beaba 100644 --- a/src/game/loop.rs +++ b/src/game/loop.rs @@ -128,9 +128,24 @@ pub trait Loop { window.resize(logical_size); } _ => { - if let Some(input_event) = - try_into_input_event(&window, event) - { + match event { + winit::event::WindowEvent::KeyboardInput { + input: + winit::event::KeyboardInput { + virtual_keycode, + state: winit::event::ElementState::Released, + .. + }, + .. + } if Game::DEBUG_KEY.is_some() => { + if virtual_keycode == Game::DEBUG_KEY { + debug.toggle(); + } + } + _ => {} + } + + if let Some(input_event) = try_into_input_event(event) { game_loop.on_input(&mut input, input_event); } } @@ -141,8 +156,7 @@ pub trait Loop { } fn try_into_input_event( - window: &Window, - event: winit::event::WindowEvent, + event: winit::event::WindowEvent<'_>, ) -> Option { match event { winit::event::WindowEvent::KeyboardInput { @@ -175,8 +189,6 @@ fn try_into_input_event( _ => None, }, winit::event::WindowEvent::CursorMoved { position, .. } => { - let position = position.to_physical(window.dpi()); - Some(input::Event::Mouse(mouse::Event::CursorMoved { x: position.x as f32, y: position.y as f32, @@ -194,8 +206,6 @@ fn try_into_input_event( input::Event::Window(window::Event::Unfocused) }), winit::event::WindowEvent::Moved(position) => { - let position = position.to_physical(window.dpi()); - Some(input::Event::Window(window::Event::Moved { x: position.x as f32, y: position.y as f32, diff --git a/src/graphics/backend_gfx/surface.rs b/src/graphics/backend_gfx/surface.rs index 8108d13..c310425 100644 --- a/src/graphics/backend_gfx/surface.rs +++ b/src/graphics/backend_gfx/surface.rs @@ -41,7 +41,11 @@ impl Surface { &self.target } - pub fn resize(&mut self, _gpu: &mut Gpu, size: winit::dpi::PhysicalSize) { + pub fn resize( + &mut self, + _gpu: &mut Gpu, + size: winit::dpi::PhysicalSize, + ) { self.context.resize(size); let dimensions = self.target.get_dimensions(); @@ -156,10 +160,12 @@ fn get_window_dimensions( ctx: &glutin::WindowedContext, ) -> gfx::texture::Dimensions { let window = ctx.window(); + let (width, height) = { - let size = window.inner_size().to_physical(window.hidpi_factor()); + let size = window.inner_size(); (size.width as _, size.height as _) }; + let aa = ctx.get_pixel_format().multisampling.unwrap_or(0) as gfx::texture::NumSamples; diff --git a/src/graphics/window.rs b/src/graphics/window.rs index d45e520..995b74c 100644 --- a/src/graphics/window.rs +++ b/src/graphics/window.rs @@ -27,42 +27,21 @@ pub struct Window { impl Window { pub(crate) fn new( - mut settings: Settings, + settings: Settings, event_loop: &winit::event_loop::EventLoop<()>, ) -> Result { - let (mut width, mut height) = settings.size; - - // Try to revert DPI - let dpi = event_loop.primary_monitor().hidpi_factor(); - - width = (width as f64 / dpi).round() as u32; - height = (height as f64 / dpi).round() as u32; - - settings.size = (width, height); - + let (width, height) = settings.size; let is_fullscreen = settings.fullscreen; let (gpu, surface) = Gpu::for_window(settings.into_builder(event_loop), event_loop)?; - let window = surface.window(); - - let (width, height) = { - let inner_size = window.inner_size(); - let dpi = window.hidpi_factor(); - - ( - (inner_size.width * dpi) as f32, - (inner_size.height * dpi) as f32, - ) - }; - Ok(Window { is_fullscreen, gpu, surface, - width, - height, + width: width as f32, + height: height as f32, cursor_icon: Some(winit::window::CursorIcon::Default), }) } @@ -111,10 +90,6 @@ impl Window { self.height } - pub(crate) fn dpi(&self) -> f64 { - self.surface.window().hidpi_factor() - } - pub(crate) fn swap_buffers(&mut self) { self.surface.swap_buffers(&mut self.gpu); } @@ -123,14 +98,11 @@ impl Window { self.surface.request_redraw(); } - pub(crate) fn resize(&mut self, new_size: winit::dpi::LogicalSize) { - let dpi = self.surface.window().hidpi_factor(); - let physical_size = new_size.to_physical(dpi); - - self.surface.resize(&mut self.gpu, physical_size); + pub(crate) fn resize(&mut self, new_size: winit::dpi::PhysicalSize) { + self.surface.resize(&mut self.gpu, new_size); - self.width = physical_size.width as f32; - self.height = physical_size.height as f32; + self.width = new_size.width as f32; + self.height = new_size.height as f32; } pub(crate) fn update_cursor( diff --git a/src/graphics/window/settings.rs b/src/graphics/window/settings.rs index b4645e2..4aebb1b 100644 --- a/src/graphics/window/settings.rs +++ b/src/graphics/window/settings.rs @@ -32,9 +32,9 @@ impl Settings { winit::window::WindowBuilder::new() .with_title(self.title) - .with_inner_size(winit::dpi::LogicalSize { - width: self.size.0 as f64, - height: self.size.1 as f64, + .with_inner_size(winit::dpi::PhysicalSize { + width: self.size.0, + height: self.size.1, }) .with_resizable(self.resizable) .with_fullscreen(monitor.map(winit::window::Fullscreen::Borderless)) From bc46ac67804c778cccfa2d8bf4c1d7f79e276565 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sat, 25 Apr 2020 00:11:13 +0200 Subject: [PATCH 14/16] Update `winit` and `glutin` --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f3330e2..14d94dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ stretch = "0.2" twox-hash = "1.3" lyon_tessellation = "0.13" gilrs = "0.7" -winit = "0.21" +winit = "0.22" raw-window-handle = "0.1" # gfx (OpenGL) @@ -43,7 +43,7 @@ gfx = { version = "0.18", optional = true } gfx_core = { version = "0.9", optional = true } gfx_device_gl = { version = "0.16", optional = true } gfx_glyph = { version = "0.15", optional = true } -glutin = { version = "0.23", optional = true } +glutin = { version = "0.24", optional = true } # wgpu (Vulkan, Metal, D3D) wgpu = { version = "0.4", optional = true } From a1ce0956af6a9bae890f044ef0f517cf734c79ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sat, 25 Apr 2020 00:12:16 +0200 Subject: [PATCH 15/16] Fix a couple of compiler warnings in `load::task` --- src/load/task.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/load/task.rs b/src/load/task.rs index a3b6918..e0fe2af 100644 --- a/src/load/task.rs +++ b/src/load/task.rs @@ -388,7 +388,7 @@ impl Progress { /// /// [`Task`]: struct.Task.html pub fn percentage(&self) -> f32 { - (self.completed_work() as f32 / self.total_work.max(1) as f32 * 100.0) + self.completed_work() as f32 / self.total_work.max(1) as f32 * 100.0 } /// Returns the title of the current [`Task::stage`], if there is one. @@ -425,8 +425,8 @@ impl Join for (Task, Task) { Task::sequence( loader_a.total_work() + loader_b.total_work(), move |task| { - ((loader_a.function)(task) - .and_then(|a| (loader_b.function)(task).map(|b| (a, b)))) + (loader_a.function)(task) + .and_then(|a| (loader_b.function)(task).map(|b| (a, b))) }, ) } From 2599d58ad8309e3ac1da685213a21bee92a61195 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sat, 25 Apr 2020 01:41:18 +0200 Subject: [PATCH 16/16] Update `wgpu` --- Cargo.toml | 15 +- src/graphics/backend_gfx/font.rs | 7 +- src/graphics/backend_gfx/shader/quad.vert | 9 +- src/graphics/backend_gfx/shader/triangle.vert | 9 +- src/graphics/backend_wgpu/font.rs | 1 + src/graphics/backend_wgpu/mod.rs | 54 +++++-- src/graphics/backend_wgpu/quad.rs | 146 ++++++++++-------- src/graphics/backend_wgpu/surface.rs | 109 +++++-------- src/graphics/backend_wgpu/texture.rs | 89 +++++------ src/graphics/backend_wgpu/triangle.rs | 83 +++++----- src/graphics/backend_wgpu/types.rs | 4 +- src/graphics/canvas.rs | 8 +- src/graphics/target.rs | 16 +- src/graphics/transformation.rs | 2 +- src/graphics/window/frame.rs | 14 +- 15 files changed, 281 insertions(+), 285 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 14d94dd..d194547 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,10 +21,10 @@ features = ["opengl", "debug"] [features] default = [] opengl = ["gfx", "gfx_core", "glutin", "gfx_device_gl", "gfx_glyph"] -vulkan = ["wgpu"] -metal = ["wgpu"] -dx11 = ["wgpu"] -dx12 = ["wgpu"] +vulkan = ["wgpu", "wgpu_glyph", "zerocopy", "futures"] +metal = ["wgpu", "wgpu_glyph", "zerocopy", "futures"] +dx11 = ["wgpu", "wgpu_glyph", "zerocopy", "futures"] +dx12 = ["wgpu", "wgpu_glyph", "zerocopy", "futures"] debug = [] [dependencies] @@ -36,7 +36,6 @@ twox-hash = "1.3" lyon_tessellation = "0.13" gilrs = "0.7" winit = "0.22" -raw-window-handle = "0.1" # gfx (OpenGL) gfx = { version = "0.18", optional = true } @@ -46,8 +45,10 @@ gfx_glyph = { version = "0.15", optional = true } glutin = { version = "0.24", optional = true } # wgpu (Vulkan, Metal, D3D) -wgpu = { version = "0.4", optional = true } -wgpu_glyph = { version = "0.7", optional = true } +wgpu = { version = "0.5", optional = true } +wgpu_glyph = { version = "0.8", optional = true } +zerocopy = { version = "0.3", optional = true } +futures = { version = "0.3", optional = true } [dev-dependencies] rand = "0.6" diff --git a/src/graphics/backend_gfx/font.rs b/src/graphics/backend_gfx/font.rs index d7a5e00..8d53a04 100644 --- a/src/graphics/backend_gfx/font.rs +++ b/src/graphics/backend_gfx/font.rs @@ -2,7 +2,7 @@ use gfx_device_gl as gl; use gfx_glyph::GlyphCruncher; use crate::graphics::gpu::{TargetView, Transformation}; -use crate::graphics::{HorizontalAlignment, Text, VerticalAlignment}; +use crate::graphics::{HorizontalAlignment, Text, Vector, VerticalAlignment}; pub struct Font { glyphs: gfx_glyph::GlyphBrush<'static, gl::Resources, gl::Factory>, @@ -46,7 +46,10 @@ impl Font { self.glyphs .use_queue() - .transform(transformation) + .transform( + Transformation::nonuniform_scale(Vector::new(1.0, -1.0)) + * transformation, + ) .draw(encoder, &typed_target) .expect("Font draw"); } diff --git a/src/graphics/backend_gfx/shader/quad.vert b/src/graphics/backend_gfx/shader/quad.vert index da2f62b..919d8e9 100644 --- a/src/graphics/backend_gfx/shader/quad.vert +++ b/src/graphics/backend_gfx/shader/quad.vert @@ -14,13 +14,6 @@ layout (std140) uniform Globals { out vec2 v_Uv; flat out uint v_Layer; -const mat4 INVERT_Y_AXIS = mat4( - vec4(1.0, 0.0, 0.0, 0.0), - vec4(0.0, -1.0, 0.0, 0.0), - vec4(0.0, 0.0, 1.0, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) -); - void main() { v_Uv = a_Pos * a_Src.zw + a_Src.xy; v_Layer = t_Layer; @@ -32,7 +25,7 @@ void main() { vec4(a_Translation, 0.0, 1.0) ); - vec4 position = INVERT_Y_AXIS * u_MVP * instance_transform * vec4(a_Pos, 0.0, 1.0); + vec4 position = u_MVP * instance_transform * vec4(a_Pos, 0.0, 1.0); gl_Position = position; } diff --git a/src/graphics/backend_gfx/shader/triangle.vert b/src/graphics/backend_gfx/shader/triangle.vert index 9e93563..ac12fa0 100644 --- a/src/graphics/backend_gfx/shader/triangle.vert +++ b/src/graphics/backend_gfx/shader/triangle.vert @@ -9,15 +9,8 @@ layout (std140) uniform Globals { out vec4 v_Color; -const mat4 INVERT_Y_AXIS = mat4( - vec4(1.0, 0.0, 0.0, 0.0), - vec4(0.0, -1.0, 0.0, 0.0), - vec4(0.0, 0.0, 1.0, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) -); - void main() { v_Color = a_Color; - gl_Position = INVERT_Y_AXIS * u_MVP * vec4(a_Pos, 0.0, 1.0); + gl_Position = u_MVP * vec4(a_Pos, 0.0, 1.0); } diff --git a/src/graphics/backend_wgpu/font.rs b/src/graphics/backend_wgpu/font.rs index b8ca7a7..6b11949 100644 --- a/src/graphics/backend_wgpu/font.rs +++ b/src/graphics/backend_wgpu/font.rs @@ -13,6 +13,7 @@ impl Font { pub fn from_bytes(device: &mut wgpu::Device, bytes: &'static [u8]) -> Font { Font { glyphs: wgpu_glyph::GlyphBrushBuilder::using_font_bytes(bytes) + .expect("Load font") .texture_filter_method(wgpu::FilterMode::Nearest) .build(device, wgpu::TextureFormat::Bgra8UnormSrgb), } diff --git a/src/graphics/backend_wgpu/mod.rs b/src/graphics/backend_wgpu/mod.rs index 03cd6ed..56b3e86 100644 --- a/src/graphics/backend_wgpu/mod.rs +++ b/src/graphics/backend_wgpu/mod.rs @@ -19,6 +19,7 @@ use crate::{Error, Result}; #[allow(missing_docs)] pub struct Gpu { device: wgpu::Device, + queue: wgpu::Queue, quad_pipeline: quad::Pipeline, triangle_pipeline: triangle::Pipeline, encoder: wgpu::CommandEncoder, @@ -33,32 +34,43 @@ impl Gpu { .build(event_loop) .map_err(|error| Error::WindowCreation(error.to_string()))?; - let instance = wgpu::Instance::new(); - - let adapter = instance.request_adapter(&wgpu::RequestAdapterOptions { - power_preference: wgpu::PowerPreference::HighPerformance, - }); - - let mut device = adapter.request_device(&wgpu::DeviceDescriptor { - extensions: wgpu::Extensions { - anisotropic_filtering: false, - }, - limits: wgpu::Limits::default(), + let (mut device, queue) = futures::executor::block_on(async { + let adapter = wgpu::Adapter::request( + &wgpu::RequestAdapterOptions { + power_preference: wgpu::PowerPreference::HighPerformance, + compatible_surface: None, + }, + wgpu::BackendBit::all(), + ) + .await + .expect("Request adapter"); + + let (device, queue) = adapter + .request_device(&wgpu::DeviceDescriptor { + extensions: wgpu::Extensions { + anisotropic_filtering: false, + }, + limits: wgpu::Limits::default(), + }) + .await; + + (device, queue) }); - let surface = Surface::new(window, &instance, &device); + let surface = Surface::new(window, &device); let quad_pipeline = quad::Pipeline::new(&mut device); let triangle_pipeline = triangle::Pipeline::new(&mut device); let encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { - todo: 0, + label: Some("coffee::backend encoder"), }); Ok(( Gpu { device, + queue, quad_pipeline, triangle_pipeline, encoder, @@ -91,14 +103,19 @@ impl Gpu { &mut self, image: &image::DynamicImage, ) -> Texture { - Texture::new(&mut self.device, &self.quad_pipeline, image) + Texture::new(&mut self.device, &self.queue, &self.quad_pipeline, image) } pub(super) fn upload_texture_array( &mut self, layers: &[image::DynamicImage], ) -> Texture { - Texture::new_array(&mut self.device, &self.quad_pipeline, layers) + Texture::new_array( + &mut self.device, + &self.queue, + &self.quad_pipeline, + layers, + ) } pub(super) fn create_drawable_texture( @@ -108,6 +125,7 @@ impl Gpu { ) -> texture::Drawable { texture::Drawable::new( &mut self.device, + &self.queue, &self.quad_pipeline, width, height, @@ -119,12 +137,14 @@ impl Gpu { drawable: &texture::Drawable, ) -> image::DynamicImage { let new_encoder = self.device.create_command_encoder( - &wgpu::CommandEncoderDescriptor { todo: 0 }, + &wgpu::CommandEncoderDescriptor { + label: Some("coffee::backend encoder"), + }, ); let encoder = std::mem::replace(&mut self.encoder, new_encoder); - drawable.read_pixels(&mut self.device, encoder) + drawable.read_pixels(&mut self.device, &self.queue, encoder) } pub(super) fn upload_font(&mut self, bytes: &'static [u8]) -> Font { diff --git a/src/graphics/backend_wgpu/quad.rs b/src/graphics/backend_wgpu/quad.rs index af86010..ae9939b 100644 --- a/src/graphics/backend_wgpu/quad.rs +++ b/src/graphics/backend_wgpu/quad.rs @@ -1,6 +1,7 @@ use std::mem; use crate::graphics::{self, Transformation}; +use zerocopy::AsBytes; pub struct Pipeline { pipeline: wgpu::RenderPipeline, @@ -23,36 +24,36 @@ impl Pipeline { mipmap_filter: wgpu::FilterMode::Nearest, lod_min_clamp: -100.0, lod_max_clamp: 100.0, - compare_function: wgpu::CompareFunction::Always, + compare: wgpu::CompareFunction::Always, }); let constant_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: Some("coffee::backend::quad constants"), bindings: &[ - wgpu::BindGroupLayoutBinding { + wgpu::BindGroupLayoutEntry { binding: 0, visibility: wgpu::ShaderStage::VERTEX, ty: wgpu::BindingType::UniformBuffer { dynamic: false }, }, - wgpu::BindGroupLayoutBinding { + wgpu::BindGroupLayoutEntry { binding: 1, visibility: wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Sampler, + ty: wgpu::BindingType::Sampler { comparison: false }, }, ], }); let matrix: [f32; 16] = Transformation::identity().into(); - let transform_buffer = device - .create_buffer_mapped( - 16, - wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, - ) - .fill_from_slice(&matrix[..]); + let transform_buffer = device.create_buffer_with_data( + matrix.as_bytes(), + wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, + ); let constant_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("coffee::backend::quad constants"), layout: &constant_layout, bindings: &[ wgpu::Binding { @@ -71,12 +72,14 @@ impl Pipeline { let texture_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - bindings: &[wgpu::BindGroupLayoutBinding { + label: Some("coffee::backend::quad texture"), + bindings: &[wgpu::BindGroupLayoutEntry { binding: 0, visibility: wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::SampledTexture { multisampled: false, dimension: wgpu::TextureViewDimension::D2Array, + component_type: wgpu::TextureComponentType::Float, }, }], }); @@ -132,58 +135,63 @@ impl Pipeline { write_mask: wgpu::ColorWrite::ALL, }], depth_stencil_state: None, - index_format: wgpu::IndexFormat::Uint16, - vertex_buffers: &[ - wgpu::VertexBufferDescriptor { - stride: mem::size_of::() as u64, - step_mode: wgpu::InputStepMode::Vertex, - attributes: &[wgpu::VertexAttributeDescriptor { - shader_location: 0, - format: wgpu::VertexFormat::Float2, - offset: 0, - }], - }, - wgpu::VertexBufferDescriptor { - stride: mem::size_of::() as u64, - step_mode: wgpu::InputStepMode::Instance, - attributes: &[ - wgpu::VertexAttributeDescriptor { - shader_location: 1, - format: wgpu::VertexFormat::Float4, - offset: 0, - }, - wgpu::VertexAttributeDescriptor { - shader_location: 2, + vertex_state: wgpu::VertexStateDescriptor { + index_format: wgpu::IndexFormat::Uint16, + vertex_buffers: &[ + wgpu::VertexBufferDescriptor { + stride: mem::size_of::() as u64, + step_mode: wgpu::InputStepMode::Vertex, + attributes: &[wgpu::VertexAttributeDescriptor { + shader_location: 0, format: wgpu::VertexFormat::Float2, - offset: 4 * 4, - }, - wgpu::VertexAttributeDescriptor { - shader_location: 3, - format: wgpu::VertexFormat::Float2, - offset: 4 * (4 + 2), - }, - wgpu::VertexAttributeDescriptor { - shader_location: 4, - format: wgpu::VertexFormat::Uint, - offset: 4 * (4 + 2 + 2), - }, - ], - }, - ], + offset: 0, + }], + }, + wgpu::VertexBufferDescriptor { + stride: mem::size_of::() as u64, + step_mode: wgpu::InputStepMode::Instance, + attributes: &[ + wgpu::VertexAttributeDescriptor { + shader_location: 1, + format: wgpu::VertexFormat::Float4, + offset: 0, + }, + wgpu::VertexAttributeDescriptor { + shader_location: 2, + format: wgpu::VertexFormat::Float2, + offset: 4 * 4, + }, + wgpu::VertexAttributeDescriptor { + shader_location: 3, + format: wgpu::VertexFormat::Float2, + offset: 4 * (4 + 2), + }, + wgpu::VertexAttributeDescriptor { + shader_location: 4, + format: wgpu::VertexFormat::Uint, + offset: 4 * (4 + 2 + 2), + }, + ], + }, + ], + }, sample_count: 1, sample_mask: !0, alpha_to_coverage_enabled: false, }); - let vertices = device - .create_buffer_mapped(QUAD_VERTS.len(), wgpu::BufferUsage::VERTEX) - .fill_from_slice(&QUAD_VERTS); + let vertices = device.create_buffer_with_data( + QUAD_VERTS.as_bytes(), + wgpu::BufferUsage::VERTEX, + ); - let indices = device - .create_buffer_mapped(QUAD_INDICES.len(), wgpu::BufferUsage::INDEX) - .fill_from_slice(&QUAD_INDICES); + let indices = device.create_buffer_with_data( + QUAD_INDICES.as_bytes(), + wgpu::BufferUsage::INDEX, + ); let instances = device.create_buffer(&wgpu::BufferDescriptor { + label: Some("coffee::backend::quad instances"), size: mem::size_of::() as u64 * Quad::MAX as u64, usage: wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::COPY_DST, }); @@ -205,6 +213,7 @@ impl Pipeline { view: &wgpu::TextureView, ) -> TextureBinding { let binding = device.create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("coffee::backend::quad texture"), layout: &self.texture_layout, bindings: &[wgpu::Binding { binding: 0, @@ -226,9 +235,10 @@ impl Pipeline { ) { let matrix: [f32; 16] = transformation.clone().into(); - let transform_buffer = device - .create_buffer_mapped(16, wgpu::BufferUsage::COPY_SRC) - .fill_from_slice(&matrix[..]); + let transform_buffer = device.create_buffer_with_data( + matrix.as_bytes(), + wgpu::BufferUsage::COPY_SRC, + ); encoder.copy_buffer_to_buffer( &transform_buffer, @@ -245,9 +255,10 @@ impl Pipeline { let end = (i + Quad::MAX).min(total); let amount = end - i; - let instance_buffer = device - .create_buffer_mapped(amount, wgpu::BufferUsage::COPY_SRC) - .fill_from_slice(&instances[i..end]); + let instance_buffer = device.create_buffer_with_data( + instances[i..end].as_bytes(), + wgpu::BufferUsage::COPY_SRC, + ); encoder.copy_buffer_to_buffer( &instance_buffer, @@ -256,6 +267,7 @@ impl Pipeline { 0, (mem::size_of::() * amount) as u64, ); + { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { @@ -279,11 +291,9 @@ impl Pipeline { render_pass.set_pipeline(&self.pipeline); render_pass.set_bind_group(0, &self.constants, &[]); render_pass.set_bind_group(1, &texture.0, &[]); - render_pass.set_index_buffer(&self.indices, 0); - render_pass.set_vertex_buffers( - 0, - &[(&self.vertices, 0), (&self.instances, 0)], - ); + render_pass.set_index_buffer(&self.indices, 0, 0); + render_pass.set_vertex_buffer(0, &self.vertices, 0, 0); + render_pass.set_vertex_buffer(1, &self.instances, 0, 0); render_pass.draw_indexed( 0..QUAD_INDICES.len() as u32, @@ -297,7 +307,8 @@ impl Pipeline { } } -#[derive(Clone, Copy)] +#[derive(Clone, Copy, AsBytes)] +#[repr(C)] pub struct Vertex { _position: [f32; 2], } @@ -319,7 +330,8 @@ const QUAD_VERTS: [Vertex; 4] = [ }, ]; -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, AsBytes)] +#[repr(C)] pub struct Quad { source: [f32; 4], scale: [f32; 2], diff --git a/src/graphics/backend_wgpu/surface.rs b/src/graphics/backend_wgpu/surface.rs index 44bc257..d823781 100644 --- a/src/graphics/backend_wgpu/surface.rs +++ b/src/graphics/backend_wgpu/surface.rs @@ -1,5 +1,3 @@ -use std::rc::Rc; - use super::{Gpu, TargetView}; pub struct Surface { @@ -7,31 +5,25 @@ pub struct Surface { surface: wgpu::Surface, swap_chain: wgpu::SwapChain, extent: wgpu::Extent3d, - buffer: wgpu::Texture, - target: TargetView, + output: Option, } impl Surface { pub fn new( window: winit::window::Window, - instance: &wgpu::Instance, device: &wgpu::Device, ) -> Surface { - use raw_window_handle::HasRawWindowHandle as _; - - let surface = instance.create_surface(window.raw_window_handle()); - let size = window.inner_size().to_physical(window.hidpi_factor()); + let surface = wgpu::Surface::create(&window); + let size = window.inner_size(); - let (swap_chain, extent, buffer, target) = - new_swap_chain(device, &surface, size); + let (swap_chain, extent) = new_swap_chain(device, &surface, size); Surface { window, surface, swap_chain, extent, - buffer, - target, + output: None, } } @@ -39,56 +31,46 @@ impl Surface { &self.window } - pub fn target(&self) -> &TargetView { - &self.target + pub fn target(&mut self) -> &TargetView { + if self.output.is_none() { + let output = self + .swap_chain + .get_next_texture() + .expect("Get next texture"); + + self.output = Some(output); + } + + &self.output.as_ref().unwrap().view } - pub fn resize(&mut self, gpu: &mut Gpu, size: winit::dpi::PhysicalSize) { - let (swap_chain, extent, buffer, target) = + pub fn resize( + &mut self, + gpu: &mut Gpu, + size: winit::dpi::PhysicalSize, + ) { + let (swap_chain, extent) = new_swap_chain(&gpu.device, &self.surface, size); self.swap_chain = swap_chain; self.extent = extent; - self.buffer = buffer; - self.target = target; + self.output = None; } pub fn swap_buffers(&mut self, gpu: &mut Gpu) { - let output = self.swap_chain.get_next_texture(); - let new_encoder = gpu.device.create_command_encoder( - &wgpu::CommandEncoderDescriptor { todo: 0 }, + &wgpu::CommandEncoderDescriptor { + label: Some("coffee::backend::surface blit"), + }, ); // We swap the current decoder by a new one here, so we can finish the // current frame - let mut encoder = std::mem::replace(&mut gpu.encoder, new_encoder); - - encoder.copy_texture_to_texture( - wgpu::TextureCopyView { - texture: &self.buffer, - array_layer: 0, - mip_level: 0, - origin: wgpu::Origin3d { - x: 0.0, - y: 0.0, - z: 0.0, - }, - }, - wgpu::TextureCopyView { - texture: &output.texture, - array_layer: 0, - mip_level: 0, - origin: wgpu::Origin3d { - x: 0.0, - y: 0.0, - z: 0.0, - }, - }, - self.extent, - ); + let encoder = std::mem::replace(&mut gpu.encoder, new_encoder); - gpu.device.get_queue().submit(&[encoder.finish()]); + gpu.queue.submit(&[encoder.finish()]); + + self.output = None; } pub fn request_redraw(&mut self) { @@ -99,38 +81,25 @@ impl Surface { fn new_swap_chain( device: &wgpu::Device, surface: &wgpu::Surface, - size: winit::dpi::PhysicalSize, -) -> (wgpu::SwapChain, wgpu::Extent3d, wgpu::Texture, TargetView) { + size: winit::dpi::PhysicalSize, +) -> (wgpu::SwapChain, wgpu::Extent3d) { let swap_chain = device.create_swap_chain( surface, &wgpu::SwapChainDescriptor { usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT | wgpu::TextureUsage::COPY_DST, - format: wgpu::TextureFormat::Bgra8Unorm, - width: size.width.round() as u32, - height: size.height.round() as u32, - present_mode: wgpu::PresentMode::Vsync, + format: wgpu::TextureFormat::Bgra8UnormSrgb, + width: size.width, + height: size.height, + present_mode: wgpu::PresentMode::Mailbox, }, ); let extent = wgpu::Extent3d { - width: size.width.round() as u32, - height: size.height.round() as u32, + width: size.width, + height: size.height, depth: 1, }; - let buffer = device.create_texture(&wgpu::TextureDescriptor { - size: extent, - dimension: wgpu::TextureDimension::D2, - array_layer_count: 1, - mip_level_count: 1, - sample_count: 1, - format: wgpu::TextureFormat::Bgra8UnormSrgb, - usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT - | wgpu::TextureUsage::COPY_SRC, - }); - - let target = Rc::new(buffer.create_default_view()); - - (swap_chain, extent, buffer, target) + (swap_chain, extent) } diff --git a/src/graphics/backend_wgpu/texture.rs b/src/graphics/backend_wgpu/texture.rs index b939348..d8bff02 100644 --- a/src/graphics/backend_wgpu/texture.rs +++ b/src/graphics/backend_wgpu/texture.rs @@ -8,7 +8,7 @@ use crate::graphics::Transformation; #[derive(Clone)] pub struct Texture { raw: Rc, - view: TargetView, + view: Rc, binding: Rc, width: u16, height: u16, @@ -28,6 +28,7 @@ impl fmt::Debug for Texture { impl Texture { pub(super) fn new( device: &mut wgpu::Device, + queue: &wgpu::Queue, pipeline: &Pipeline, image: &image::DynamicImage, ) -> Texture { @@ -37,9 +38,10 @@ impl Texture { let (texture, view, binding) = create_texture_array( device, + queue, pipeline, - width, - height, + u32::from(width), + u32::from(height), Some(&[&bgra.into_raw()[..]]), wgpu::TextureUsage::COPY_DST | wgpu::TextureUsage::SAMPLED, ); @@ -56,6 +58,7 @@ impl Texture { pub(super) fn new_array( device: &mut wgpu::Device, + queue: &wgpu::Queue, pipeline: &Pipeline, layers: &[image::DynamicImage], ) -> Texture { @@ -70,9 +73,10 @@ impl Texture { let (texture, view, binding) = create_texture_array( device, + queue, pipeline, - width, - height, + u32::from(width), + u32::from(height), Some(&raw_layers[..]), wgpu::TextureUsage::COPY_DST | wgpu::TextureUsage::SAMPLED, ); @@ -112,15 +116,17 @@ pub struct Drawable { impl Drawable { pub fn new( device: &mut wgpu::Device, + queue: &wgpu::Queue, pipeline: &Pipeline, width: u16, height: u16, ) -> Drawable { let (texture, view, binding) = create_texture_array( device, + queue, pipeline, - width, - height, + u32::from(width), + u32::from(height), None, wgpu::TextureUsage::OUTPUT_ATTACHMENT | wgpu::TextureUsage::SAMPLED @@ -150,6 +156,7 @@ impl Drawable { pub fn read_pixels( &self, device: &mut wgpu::Device, + queue: &wgpu::Queue, mut encoder: wgpu::CommandEncoder, ) -> image::DynamicImage { let texture = self.texture(); @@ -157,6 +164,7 @@ impl Drawable { let buffer_size = 4 * texture.width() as u64 * texture.height() as u64; let buffer = device.create_buffer(&wgpu::BufferDescriptor { + label: Some("coffee::backend::texture pixels"), size: buffer_size, usage: wgpu::BufferUsage::COPY_DST | wgpu::BufferUsage::COPY_SRC @@ -168,47 +176,31 @@ impl Drawable { texture: &texture.raw, mip_level: 0, array_layer: 0, - origin: wgpu::Origin3d { - x: 0.0, - y: 0.0, - z: 0.0, - }, + origin: wgpu::Origin3d { x: 0, y: 0, z: 0 }, }, wgpu::BufferCopyView { buffer: &buffer, offset: 0, - row_pitch: 4 * texture.width() as u32, - image_height: texture.height() as u32, + bytes_per_row: 4 * u32::from(texture.width()), + rows_per_image: u32::from(texture.height()), }, wgpu::Extent3d { - width: texture.width() as u32, - height: texture.height() as u32, + width: u32::from(texture.width()), + height: u32::from(texture.height()), depth: 1, }, ); - device.get_queue().submit(&[encoder.finish()]); + queue.submit(&[encoder.finish()]); - use std::cell::RefCell; + use futures::executor::block_on; - let pixels: Rc>>> = Rc::new(RefCell::new(None)); - let write = pixels.clone(); + let result = block_on(buffer.map_read(0, buffer_size)); - buffer.map_read_async(0, buffer_size, move |result| { - match result { - Ok(mapping) => { - *write.borrow_mut() = Some(mapping.data.to_vec()); - } - Err(_) => { - *write.borrow_mut() = Some(vec![]); - } - }; - }); - - device.poll(true); - - let data = pixels.borrow(); - let bgra = data.clone().unwrap(); + let bgra = match result { + Ok(mapping) => mapping.as_slice().to_vec(), + Err(_) => vec![], + }; image::DynamicImage::ImageBgra8( image::ImageBuffer::from_raw( @@ -228,21 +220,23 @@ impl Drawable { // Helpers fn create_texture_array( device: &mut wgpu::Device, + queue: &wgpu::Queue, pipeline: &Pipeline, - width: u16, - height: u16, + width: u32, + height: u32, layers: Option<&[&[u8]]>, usage: wgpu::TextureUsage, ) -> (wgpu::Texture, wgpu::TextureView, quad::TextureBinding) { let extent = wgpu::Extent3d { - width: width as u32, - height: height as u32, + width: width, + height: height, depth: 1, }; let layer_count = layers.map(|l| l.len()).unwrap_or(1) as u32; let texture = device.create_texture(&wgpu::TextureDescriptor { + label: Some("coffee::backend::texture array"), size: extent, array_layer_count: layer_count, mip_level_count: 1, @@ -259,35 +253,30 @@ fn create_texture_array( layers.iter().cloned().flatten().cloned().collect(); let temp_buf = device - .create_buffer_mapped(slice.len(), wgpu::BufferUsage::COPY_SRC) - .fill_from_slice(&slice[..]); + .create_buffer_with_data(&slice[..], wgpu::BufferUsage::COPY_SRC); let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { - todo: 0, + label: Some("coffee::backend::texture upload"), }); encoder.copy_buffer_to_texture( wgpu::BufferCopyView { buffer: &temp_buf, offset: 0, - row_pitch: 4 * width as u32, - image_height: height as u32, + bytes_per_row: 4 * width, + rows_per_image: height, }, wgpu::TextureCopyView { texture: &texture, array_layer: 0, mip_level: 0, - origin: wgpu::Origin3d { - x: 0.0, - y: 0.0, - z: 0.0, - }, + origin: wgpu::Origin3d { x: 0, y: 0, z: 0 }, }, extent, ); - device.get_queue().submit(&[encoder.finish()]); + queue.submit(&[encoder.finish()]); } let view = texture.create_view(&wgpu::TextureViewDescriptor { diff --git a/src/graphics/backend_wgpu/triangle.rs b/src/graphics/backend_wgpu/triangle.rs index f9b73aa..e697613 100644 --- a/src/graphics/backend_wgpu/triangle.rs +++ b/src/graphics/backend_wgpu/triangle.rs @@ -1,6 +1,7 @@ use std::mem; use crate::graphics::Transformation; +use zerocopy::AsBytes; pub struct Pipeline { pipeline: wgpu::RenderPipeline, @@ -17,7 +18,8 @@ impl Pipeline { pub fn new(device: &mut wgpu::Device) -> Pipeline { let transform_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - bindings: &[wgpu::BindGroupLayoutBinding { + label: Some("coffee::backend::triangle transform"), + bindings: &[wgpu::BindGroupLayoutEntry { binding: 0, visibility: wgpu::ShaderStage::VERTEX, ty: wgpu::BindingType::UniformBuffer { dynamic: false }, @@ -26,15 +28,14 @@ impl Pipeline { let matrix: [f32; 16] = Transformation::identity().into(); - let transform_buffer = device - .create_buffer_mapped( - 16, - wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, - ) - .fill_from_slice(&matrix[..]); + let transform_buffer = device.create_buffer_with_data( + matrix.as_bytes(), + wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, + ); let constant_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("coffee::backend::triangle constants"), layout: &transform_layout, bindings: &[wgpu::Binding { binding: 0, @@ -96,35 +97,39 @@ impl Pipeline { write_mask: wgpu::ColorWrite::ALL, }], depth_stencil_state: None, - index_format: wgpu::IndexFormat::Uint32, - vertex_buffers: &[wgpu::VertexBufferDescriptor { - stride: mem::size_of::() as u64, - step_mode: wgpu::InputStepMode::Vertex, - attributes: &[ - wgpu::VertexAttributeDescriptor { - shader_location: 0, - format: wgpu::VertexFormat::Float2, - offset: 0, - }, - wgpu::VertexAttributeDescriptor { - shader_location: 1, - format: wgpu::VertexFormat::Float4, - offset: 4 * 2, - }, - ], - }], + vertex_state: wgpu::VertexStateDescriptor { + index_format: wgpu::IndexFormat::Uint32, + vertex_buffers: &[wgpu::VertexBufferDescriptor { + stride: mem::size_of::() as u64, + step_mode: wgpu::InputStepMode::Vertex, + attributes: &[ + wgpu::VertexAttributeDescriptor { + shader_location: 0, + format: wgpu::VertexFormat::Float2, + offset: 0, + }, + wgpu::VertexAttributeDescriptor { + shader_location: 1, + format: wgpu::VertexFormat::Float4, + offset: 4 * 2, + }, + ], + }], + }, sample_count: 1, sample_mask: !0, alpha_to_coverage_enabled: false, }); let vertices = device.create_buffer(&wgpu::BufferDescriptor { + label: Some("coffee::backend::triangle vertices"), size: mem::size_of::() as u64 * Self::INITIAL_BUFFER_SIZE as u64, usage: wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::COPY_DST, }); let indices = device.create_buffer(&wgpu::BufferDescriptor { + label: Some("coffee::backend::triangle indices"), size: mem::size_of::() as u64 * Self::INITIAL_BUFFER_SIZE as u64, usage: wgpu::BufferUsage::INDEX | wgpu::BufferUsage::COPY_DST, @@ -155,9 +160,10 @@ impl Pipeline { let matrix: [f32; 16] = transformation.clone().into(); - let transform_buffer = device - .create_buffer_mapped(16, wgpu::BufferUsage::COPY_SRC) - .fill_from_slice(&matrix[..]); + let transform_buffer = device.create_buffer_with_data( + matrix.as_bytes(), + wgpu::BufferUsage::COPY_SRC, + ); encoder.copy_buffer_to_buffer( &transform_buffer, @@ -173,11 +179,13 @@ impl Pipeline { let new_size = vertices.len().max(indices.len()) as u32; self.vertices = device.create_buffer(&wgpu::BufferDescriptor { + label: Some("coffee::backend::triangle vertices"), size: mem::size_of::() as u64 * new_size as u64, usage: wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::COPY_DST, }); self.indices = device.create_buffer(&wgpu::BufferDescriptor { + label: Some("coffee::backend::triangle indices"), size: mem::size_of::() as u64 * new_size as u64, usage: wgpu::BufferUsage::INDEX | wgpu::BufferUsage::COPY_DST, }); @@ -185,13 +193,15 @@ impl Pipeline { self.buffer_size = new_size; } - let vertex_buffer = device - .create_buffer_mapped(vertices.len(), wgpu::BufferUsage::COPY_SRC) - .fill_from_slice(vertices); + let vertex_buffer = device.create_buffer_with_data( + vertices.as_bytes(), + wgpu::BufferUsage::COPY_SRC, + ); - let index_buffer = device - .create_buffer_mapped(indices.len(), wgpu::BufferUsage::COPY_SRC) - .fill_from_slice(indices); + let index_buffer = device.create_buffer_with_data( + indices.as_bytes(), + wgpu::BufferUsage::COPY_SRC, + ); encoder.copy_buffer_to_buffer( &vertex_buffer, @@ -231,15 +241,16 @@ impl Pipeline { render_pass.set_pipeline(&self.pipeline); render_pass.set_bind_group(0, &self.constants, &[]); - render_pass.set_index_buffer(&self.indices, 0); - render_pass.set_vertex_buffers(0, &[(&self.vertices, 0)]); + render_pass.set_index_buffer(&self.indices, 0, 0); + render_pass.set_vertex_buffer(0, &self.vertices, 0, 0); render_pass.draw_indexed(0..indices.len() as u32, 0, 0..1); } } } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, AsBytes)] +#[repr(C)] pub struct Vertex { _position: [f32; 2], _color: [f32; 4], diff --git a/src/graphics/backend_wgpu/types.rs b/src/graphics/backend_wgpu/types.rs index d5717fa..55c89e9 100644 --- a/src/graphics/backend_wgpu/types.rs +++ b/src/graphics/backend_wgpu/types.rs @@ -1,3 +1 @@ -use std::rc::Rc; - -pub type TargetView = Rc; +pub type TargetView = wgpu::TextureView; diff --git a/src/graphics/canvas.rs b/src/graphics/canvas.rs index f715411..14793d4 100644 --- a/src/graphics/canvas.rs +++ b/src/graphics/canvas.rs @@ -49,14 +49,14 @@ impl Canvas { /// /// [`Canvas`]: struct.Canvas.html /// [`Target`]: struct.Target.html - pub fn as_target<'a>(&mut self, gpu: &'a mut Gpu) -> Target<'a> { + pub fn as_target<'a>(&'a mut self, gpu: &'a mut Gpu) -> Target<'a> { let texture = self.drawable.texture(); Target::with_transformation( gpu, - self.drawable.target().clone(), - texture.width() as f32, - texture.height() as f32, + self.drawable.target(), + f32::from(texture.width()), + f32::from(texture.height()), texture::Drawable::render_transformation(), ) } diff --git a/src/graphics/target.rs b/src/graphics/target.rs index ee146a4..f8b349f 100644 --- a/src/graphics/target.rs +++ b/src/graphics/target.rs @@ -15,17 +15,17 @@ use crate::graphics::{Color, Transformation}; /// [`Canvas`]: struct.Canvas.html pub struct Target<'a> { gpu: &'a mut Gpu, - view: TargetView, + view: &'a TargetView, transformation: Transformation, } impl<'a> Target<'a> { pub(super) fn new( - gpu: &mut Gpu, - view: TargetView, + gpu: &'a mut Gpu, + view: &'a TargetView, width: f32, height: f32, - ) -> Target<'_> { + ) -> Self { Target { gpu, view, @@ -34,12 +34,12 @@ impl<'a> Target<'a> { } pub(super) fn with_transformation( - gpu: &mut Gpu, - view: TargetView, + gpu: &'a mut Gpu, + view: &'a TargetView, width: f32, height: f32, transformation: Transformation, - ) -> Target<'_> { + ) -> Self { let mut target = Self::new(gpu, view, width, height); target.transformation = transformation * target.transformation; target @@ -82,7 +82,7 @@ impl<'a> Target<'a> { pub fn transform(&mut self, transformation: Transformation) -> Target<'_> { Target { gpu: self.gpu, - view: self.view.clone(), + view: self.view, transformation: self.transformation * transformation, } } diff --git a/src/graphics/transformation.rs b/src/graphics/transformation.rs index 617d4e4..84f4112 100644 --- a/src/graphics/transformation.rs +++ b/src/graphics/transformation.rs @@ -27,7 +27,7 @@ impl Transformation { pub fn orthographic(width: f32, height: f32) -> Transformation { Transformation(nalgebra::Matrix3::new( 2.0 / width, 0.0, -1.0, - 0.0, 2.0 / height, -1.0, + 0.0, -2.0 / height, 1.0, 0.0, 0.0, 1.0 )) } diff --git a/src/graphics/window/frame.rs b/src/graphics/window/frame.rs index 54e549c..f2bc719 100644 --- a/src/graphics/window/frame.rs +++ b/src/graphics/window/frame.rs @@ -48,11 +48,17 @@ impl<'a> Frame<'a> { /// /// [`Target`]: struct.Target.html pub fn as_target(&mut self) -> Target<'_> { - let view = self.window.surface.target().clone(); - let width = self.window.width; - let height = self.window.height; + let Window { + surface, + gpu, + width, + height, + .. + } = &mut self.window; - Target::new(self.window.gpu(), view, width, height) + let view = surface.target(); + + Target::new(gpu, view, *width, *height) } /// Clear the frame with the given [`Color`].