Skip to content

Commit

Permalink
Merge pull request #183 from hecrj/feature/wgpu-integration
Browse files Browse the repository at this point in the history
Integration with existing `wgpu` projects
  • Loading branch information
hecrj authored Feb 10, 2020
2 parents 95880ca + 4337dad commit 5d16f43
Show file tree
Hide file tree
Showing 25 changed files with 893 additions and 377 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ members = [
"examples/custom_widget",
"examples/events",
"examples/geometry",
"examples/integration",
"examples/pokedex",
"examples/progress_bar",
"examples/stopwatch",
Expand Down
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ A bunch of simpler examples exist:
- [`custom_widget`](custom_widget), a demonstration of how to build a custom widget that draws a circle.
- [`events`](events), a log of native events displayed using a conditional `Subscription`.
- [`geometry`](geometry), a custom widget showcasing how to draw geometry with the `Mesh2D` primitive in [`iced_wgpu`](../wgpu).
- [`integration`](integration), a demonstration of how to integrate Iced in an existing graphical application.
- [`pokedex`](pokedex), an application that displays a random Pokédex entry (sprite included!) by using the [PokéAPI].
- [`progress_bar`](progress_bar), a simple progress bar that can be filled by using a slider.
- [`stopwatch`](stopwatch), a watch with start/stop and reset buttons showcasing how to listen to time.
Expand Down
11 changes: 11 additions & 0 deletions examples/integration/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "integration"
version = "0.1.0"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2018"
publish = false

[dependencies]
iced_winit = { path = "../../winit" }
iced_wgpu = { path = "../../wgpu" }
env_logger = "0.7"
102 changes: 102 additions & 0 deletions examples/integration/src/controls.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use crate::Scene;

use iced_wgpu::Renderer;
use iced_winit::{
slider, Align, Color, Column, Element, Length, Row, Slider, Text,
};

pub struct Controls {
sliders: [slider::State; 3],
}

#[derive(Debug)]
pub enum Message {
BackgroundColorChanged(Color),
}

impl Controls {
pub fn new() -> Controls {
Controls {
sliders: Default::default(),
}
}

pub fn update(&self, message: Message, scene: &mut Scene) {
match message {
Message::BackgroundColorChanged(color) => {
scene.background_color = color;
}
}
}

pub fn view<'a>(
&'a mut self,
scene: &Scene,
) -> Element<'a, Message, Renderer> {
let [r, g, b] = &mut self.sliders;
let background_color = scene.background_color;

let sliders = Row::new()
.width(Length::Units(500))
.spacing(20)
.push(Slider::new(
r,
0.0..=1.0,
scene.background_color.r,
move |r| {
Message::BackgroundColorChanged(Color {
r,
..background_color
})
},
))
.push(Slider::new(
g,
0.0..=1.0,
scene.background_color.g,
move |g| {
Message::BackgroundColorChanged(Color {
g,
..background_color
})
},
))
.push(Slider::new(
b,
0.0..=1.0,
scene.background_color.b,
move |b| {
Message::BackgroundColorChanged(Color {
b,
..background_color
})
},
));

Row::new()
.width(Length::Fill)
.height(Length::Fill)
.align_items(Align::End)
.push(
Column::new()
.width(Length::Fill)
.align_items(Align::End)
.push(
Column::new()
.padding(10)
.spacing(10)
.push(
Text::new("Background color")
.color(Color::WHITE),
)
.push(sliders)
.push(
Text::new(format!("{:?}", background_color))
.size(14)
.color(Color::WHITE),
),
),
)
.into()
}
}
204 changes: 204 additions & 0 deletions examples/integration/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
mod controls;
mod scene;

use controls::Controls;
use scene::Scene;

use iced_wgpu::{
wgpu, window::SwapChain, Primitive, Renderer, Settings, Target,
};
use iced_winit::{winit, Cache, Clipboard, MouseCursor, Size, UserInterface};

use winit::{
event::{DeviceEvent, Event, ModifiersState, WindowEvent},
event_loop::{ControlFlow, EventLoop},
};

pub fn main() {
env_logger::init();

// Initialize winit
let event_loop = EventLoop::new();
let window = winit::window::Window::new(&event_loop).unwrap();
let mut logical_size =
window.inner_size().to_logical(window.scale_factor());
let mut modifiers = ModifiersState::default();

// Initialize WGPU
let adapter = wgpu::Adapter::request(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::Default,
backends: wgpu::BackendBit::PRIMARY,
})
.expect("Request adapter");

let (mut device, mut queue) =
adapter.request_device(&wgpu::DeviceDescriptor {
extensions: wgpu::Extensions {
anisotropic_filtering: false,
},
limits: wgpu::Limits::default(),
});

let surface = wgpu::Surface::create(&window);

let mut swap_chain = {
let size = window.inner_size();

SwapChain::new(&device, &surface, size.width, size.height)
};
let mut resized = false;

// Initialize iced
let mut events = Vec::new();
let mut cache = Some(Cache::default());
let mut renderer = Renderer::new(&mut device, Settings::default());
let mut output = (Primitive::None, MouseCursor::OutOfBounds);
let clipboard = Clipboard::new(&window);

// Initialize scene and GUI controls
let mut scene = Scene::new(&device);
let mut controls = Controls::new();

// Run event loop
event_loop.run(move |event, _, control_flow| {
// You should change this if you want to render continuosly
*control_flow = ControlFlow::Wait;

match event {
Event::DeviceEvent {
event: DeviceEvent::ModifiersChanged(new_modifiers),
..
} => {
modifiers = new_modifiers;
}
Event::WindowEvent { event, .. } => {
match event {
WindowEvent::Resized(new_size) => {
logical_size =
new_size.to_logical(window.scale_factor());
resized = true;
}
WindowEvent::CloseRequested => {
*control_flow = ControlFlow::Exit;
}
_ => {}
}

// Map window event to iced event
if let Some(event) = iced_winit::conversion::window_event(
event,
window.scale_factor(),
modifiers,
) {
events.push(event);
}
}
Event::MainEventsCleared => {
// If no relevant events happened, we can simply skip this
if events.is_empty() {
return;
}

// We need to:
// 1. Process events of our user interface.
// 2. Update state as a result of any interaction.
// 3. Generate a new output for our renderer.

// First, we build our user interface.
let mut user_interface = UserInterface::build(
controls.view(&scene),
Size::new(logical_size.width, logical_size.height),
cache.take().unwrap(),
&mut renderer,
);

// Then, we process the events, obtaining messages in return.
let messages = user_interface.update(
events.drain(..),
clipboard.as_ref().map(|c| c as _),
&renderer,
);

let user_interface = if messages.is_empty() {
// If there are no messages, no interactions we care about have
// happened. We can simply leave our user interface as it is.
user_interface
} else {
// If there are messages, we need to update our state
// accordingly and rebuild our user interface.
// We can only do this if we drop our user interface first
// by turning it into its cache.
cache = Some(user_interface.into_cache());

// In this example, `Controls` is the only part that cares
// about messages, so updating our state is pretty
// straightforward.
for message in messages {
controls.update(message, &mut scene);
}

// Once the state has been changed, we rebuild our updated
// user interface.
UserInterface::build(
controls.view(&scene),
Size::new(logical_size.width, logical_size.height),
cache.take().unwrap(),
&mut renderer,
)
};

// Finally, we just need to draw a new output for our renderer,
output = user_interface.draw(&mut renderer);

// update our cache,
cache = Some(user_interface.into_cache());

// and request a redraw
window.request_redraw();
}
Event::RedrawRequested(_) => {
if resized {
let size = window.inner_size();

swap_chain = SwapChain::new(
&device,
&surface,
size.width,
size.height,
);
}

let (frame, viewport) = swap_chain.next_frame();

let mut encoder = device.create_command_encoder(
&wgpu::CommandEncoderDescriptor { todo: 0 },
);

// We draw the scene first
scene.draw(&mut encoder, &frame.view);

// And then iced on top
let mouse_cursor = renderer.draw(
&mut device,
&mut encoder,
Target {
texture: &frame.view,
viewport,
},
&output,
window.scale_factor(),
&["Some debug information!"],
);

// Then we submit the work
queue.submit(&[encoder.finish()]);

// And update the mouse cursor
window.set_cursor_icon(iced_winit::conversion::mouse_cursor(
mouse_cursor,
));
}
_ => {}
}
})
}
Loading

0 comments on commit 5d16f43

Please sign in to comment.