From a646b11109aa451614d3fdf688e600aa48b87a56 Mon Sep 17 00:00:00 2001 From: Tamás Szelei Date: Sun, 15 Nov 2020 23:51:13 +0100 Subject: Add integration_gl example --- examples/integration_gl/Cargo.toml | 13 +++ examples/integration_gl/README.md | 16 +++ examples/integration_gl/src/controls.rs | 110 ++++++++++++++++++++ examples/integration_gl/src/main.rs | 175 ++++++++++++++++++++++++++++++++ examples/integration_gl/src/scene.rs | 93 +++++++++++++++++ 5 files changed, 407 insertions(+) create mode 100644 examples/integration_gl/Cargo.toml create mode 100644 examples/integration_gl/README.md create mode 100644 examples/integration_gl/src/controls.rs create mode 100644 examples/integration_gl/src/main.rs create mode 100644 examples/integration_gl/src/scene.rs (limited to 'examples') diff --git a/examples/integration_gl/Cargo.toml b/examples/integration_gl/Cargo.toml new file mode 100644 index 00000000..80d7adad --- /dev/null +++ b/examples/integration_gl/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "integration_gl" +version = "0.1.0" +authors = ["Héctor Ramón Jiménez "] +edition = "2018" +publish = false + +[dependencies] +iced_glutin = { path = "../../glutin" } +iced_glow = { path = "../../glow" } +iced_winit = { path = "../../winit" } +env_logger = "0.8" +glow = "0.6" diff --git a/examples/integration_gl/README.md b/examples/integration_gl/README.md new file mode 100644 index 00000000..87491508 --- /dev/null +++ b/examples/integration_gl/README.md @@ -0,0 +1,16 @@ +## Integration + +A demonstration of how to integrate Iced in an existing graphical OpenGL application. + +The __[`main`]__ file contains all the code of the example. + +
+ image host +
+ +You can run it with `cargo run`: +``` +cargo run --package integration_gl +``` + +[`main`]: src/main.rs diff --git a/examples/integration_gl/src/controls.rs b/examples/integration_gl/src/controls.rs new file mode 100644 index 00000000..13b7fbc2 --- /dev/null +++ b/examples/integration_gl/src/controls.rs @@ -0,0 +1,110 @@ +use iced_glow::Renderer; +use iced_glutin::{ + slider, Align, Clipboard, Color, Column, Command, Element, Length, Program, + Row, Slider, Text, +}; + +pub struct Controls { + background_color: Color, + sliders: [slider::State; 3], +} + +#[derive(Debug, Clone)] +pub enum Message { + BackgroundColorChanged(Color), +} + +impl Controls { + pub fn new() -> Controls { + Controls { + background_color: Color::BLACK, + sliders: Default::default(), + } + } + + pub fn background_color(&self) -> Color { + self.background_color + } +} + +impl Program for Controls { + type Renderer = Renderer; + type Message = Message; + type Clipboard = Clipboard; + + fn update( + &mut self, + message: Message, + _clipboard: &mut Clipboard, + ) -> Command { + match message { + Message::BackgroundColorChanged(color) => { + self.background_color = color; + } + } + + Command::none() + } + + fn view(&mut self) -> Element { + let [r, g, b] = &mut self.sliders; + let background_color = self.background_color; + + let sliders = Row::new() + .width(Length::Units(500)) + .spacing(20) + .push( + Slider::new(r, 0.0..=1.0, background_color.r, move |r| { + Message::BackgroundColorChanged(Color { + r, + ..background_color + }) + }) + .step(0.01), + ) + .push( + Slider::new(g, 0.0..=1.0, background_color.g, move |g| { + Message::BackgroundColorChanged(Color { + g, + ..background_color + }) + }) + .step(0.01), + ) + .push( + Slider::new(b, 0.0..=1.0, background_color.b, move |b| { + Message::BackgroundColorChanged(Color { + b, + ..background_color + }) + }) + .step(0.01), + ); + + 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() + } +} diff --git a/examples/integration_gl/src/main.rs b/examples/integration_gl/src/main.rs new file mode 100644 index 00000000..925102e3 --- /dev/null +++ b/examples/integration_gl/src/main.rs @@ -0,0 +1,175 @@ +mod controls; +mod scene; + +use controls::Controls; +use scene::Scene; + +use glow; +use glow::*; +use iced_glow::{Backend, Renderer, Settings, Viewport}; +use iced_glutin::glutin; +use iced_glutin::glutin::event::{Event, WindowEvent}; +use iced_glutin::glutin::event_loop::ControlFlow; +use iced_glutin::{program, Clipboard, Debug, Size}; +use iced_winit::conversion; +use iced_winit::winit; +use winit::{dpi::PhysicalPosition, event::ModifiersState}; + +pub fn main() { + env_logger::init(); + let (gl, event_loop, windowed_context, shader_version) = { + let el = glutin::event_loop::EventLoop::new(); + let wb = glutin::window::WindowBuilder::new() + .with_title("OpenGL integration example") + .with_inner_size(glutin::dpi::LogicalSize::new(1024.0, 768.0)); + let windowed_context = glutin::ContextBuilder::new() + .with_vsync(true) + .build_windowed(wb, &el) + .unwrap(); + unsafe { + let windowed_context = windowed_context.make_current().unwrap(); + let gl = glow::Context::from_loader_function(|s| { + windowed_context.get_proc_address(s) as *const _ + }); + // Enable auto-conversion from/to sRGB + gl.enable(glow::FRAMEBUFFER_SRGB); + + // Enable alpha blending + gl.enable(glow::BLEND); + gl.blend_func(glow::SRC_ALPHA, glow::ONE_MINUS_SRC_ALPHA); + + // Disable multisampling by default + gl.disable(glow::MULTISAMPLE); + (gl, el, windowed_context, "#version 410") + } + }; + + let physical_size = windowed_context.window().inner_size(); + let mut viewport = Viewport::with_physical_size( + Size::new(physical_size.width, physical_size.height), + windowed_context.window().scale_factor(), + ); + + let mut cursor_position = PhysicalPosition::new(-1.0, -1.0); + let mut modifiers = ModifiersState::default(); + let mut clipboard = Clipboard::connect(&windowed_context.window()); + + let mut renderer = Renderer::new(Backend::new(&gl, Settings::default())); + + let mut debug = Debug::new(); + + let controls = Controls::new(); + let mut state = program::State::new( + controls, + viewport.logical_size(), + conversion::cursor_position(cursor_position, viewport.scale_factor()), + &mut renderer, + &mut debug, + ); + let mut resized = false; + + event_loop.run(move |event, _, control_flow| { + let scene = Scene::new(&gl, &shader_version); + *control_flow = ControlFlow::Wait; + + match event { + Event::LoopDestroyed => return, + Event::WindowEvent { event, .. } => { + match event { + WindowEvent::CursorMoved { position, .. } => { + cursor_position = position; + } + WindowEvent::ModifiersChanged(new_modifiers) => { + modifiers = new_modifiers; + } + WindowEvent::Resized(physical_size) => { + viewport = Viewport::with_physical_size( + Size::new( + physical_size.width, + physical_size.height, + ), + windowed_context.window().scale_factor(), + ); + + resized = true; + } + WindowEvent::CloseRequested => { + scene.cleanup(&gl); + *control_flow = ControlFlow::Exit + } + _ => (), + } + + // Map window event to iced event + if let Some(event) = iced_winit::conversion::window_event( + &event, + windowed_context.window().scale_factor(), + modifiers, + ) { + state.queue_event(event); + } + } + Event::MainEventsCleared => { + // If there are events pending + if !state.is_queue_empty() { + // We update iced + let _ = state.update( + viewport.logical_size(), + conversion::cursor_position( + cursor_position, + viewport.scale_factor(), + ), + &mut renderer, + &mut clipboard, + &mut debug, + ); + + // and request a redraw + windowed_context.window().request_redraw(); + } + } + Event::RedrawRequested(_) => { + if resized { + let size = windowed_context.window().inner_size(); + + unsafe { + gl.viewport( + 0, + 0, + size.width as i32, + size.height as i32, + ); + } + + resized = false; + } + + let program = state.program(); + { + // We clear the frame + scene.clear(&gl, program.background_color()); + + // Draw the scene + scene.draw(&gl); + } + + // And then iced on top + let mouse_interaction = renderer.backend_mut().draw( + &gl, + &viewport, + state.primitive(), + &debug.overlay(), + ); + // Update the mouse cursor + windowed_context.window().set_cursor_icon( + iced_winit::conversion::mouse_interaction( + mouse_interaction, + ), + ); + + windowed_context.swap_buffers().unwrap(); + } + _ => (), + } + }); +} diff --git a/examples/integration_gl/src/scene.rs b/examples/integration_gl/src/scene.rs new file mode 100644 index 00000000..596381b3 --- /dev/null +++ b/examples/integration_gl/src/scene.rs @@ -0,0 +1,93 @@ +use glow::*; +use iced_glow::Color; + +pub struct Scene { + program: glow::Program, + vertex_array: glow::VertexArray, +} + +impl Scene { + pub fn new(gl: &glow::Context, shader_version: &str) -> Self { + unsafe { + let vertex_array = gl + .create_vertex_array() + .expect("Cannot create vertex array"); + gl.bind_vertex_array(Some(vertex_array)); + + let program = gl.create_program().expect("Cannot create program"); + + let (vertex_shader_source, fragment_shader_source) = ( + r#"const vec2 verts[3] = vec2[3]( + vec2(0.5f, 1.0f), + vec2(0.0f, 0.0f), + vec2(1.0f, 0.0f) + ); + out vec2 vert; + void main() { + vert = verts[gl_VertexID]; + gl_Position = vec4(vert - 0.5, 0.0, 1.0); + }"#, + r#"precision highp float; + in vec2 vert; + out vec4 color; + void main() { + color = vec4(vert, 0.5, 1.0); + }"#, + ); + + let shader_sources = [ + (glow::VERTEX_SHADER, vertex_shader_source), + (glow::FRAGMENT_SHADER, fragment_shader_source), + ]; + + let mut shaders = Vec::with_capacity(shader_sources.len()); + + for (shader_type, shader_source) in shader_sources.iter() { + let shader = gl + .create_shader(*shader_type) + .expect("Cannot create shader"); + gl.shader_source(shader, &format!("{}\n{}", shader_version, shader_source)); + gl.compile_shader(shader); + if !gl.get_shader_compile_status(shader) { + panic!(gl.get_shader_info_log(shader)); + } + gl.attach_shader(program, shader); + shaders.push(shader); + } + + gl.link_program(program); + if !gl.get_program_link_status(program) { + panic!(gl.get_program_info_log(program)); + } + + for shader in shaders { + gl.detach_shader(program, shader); + gl.delete_shader(shader); + } + + gl.use_program(Some(program)); + Self { program, vertex_array } + } + } + + pub fn clear(&self, gl: &glow::Context, background_color: Color) { + let [r, g, b, a] = background_color.into_linear(); + unsafe { + gl.clear_color(r, g, b, a); + gl.clear(glow::COLOR_BUFFER_BIT); + } + } + + pub fn draw(&self, gl: &glow::Context) { + unsafe { + gl.draw_arrays(glow::TRIANGLES, 0, 3); + } + } + + pub fn cleanup(&self, gl: &glow::Context) { + unsafe { + gl.delete_program(self.program); + gl.delete_vertex_array(self.vertex_array); + } + } +} \ No newline at end of file -- cgit From 77a0b68aa176782e95048795aec15c20a3e60ec6 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 13 Aug 2021 20:01:28 +0700 Subject: Rename `integration` examples --- examples/integration/Cargo.toml | 11 -- examples/integration/README.md | 18 -- examples/integration/src/controls.rs | 110 ------------ examples/integration/src/main.rs | 239 -------------------------- examples/integration/src/scene.rs | 99 ----------- examples/integration/src/shader/frag.spv | Bin 352 -> 0 bytes examples/integration/src/shader/vert.spv | Bin 904 -> 0 bytes examples/integration_gl/Cargo.toml | 13 -- examples/integration_gl/README.md | 16 -- examples/integration_gl/src/controls.rs | 110 ------------ examples/integration_gl/src/main.rs | 175 ------------------- examples/integration_gl/src/scene.rs | 93 ---------- examples/integration_opengl/Cargo.toml | 13 ++ examples/integration_opengl/README.md | 16 ++ examples/integration_opengl/src/controls.rs | 110 ++++++++++++ examples/integration_opengl/src/main.rs | 175 +++++++++++++++++++ examples/integration_opengl/src/scene.rs | 93 ++++++++++ examples/integration_wgpu/Cargo.toml | 11 ++ examples/integration_wgpu/README.md | 20 +++ examples/integration_wgpu/src/controls.rs | 110 ++++++++++++ examples/integration_wgpu/src/main.rs | 239 ++++++++++++++++++++++++++ examples/integration_wgpu/src/scene.rs | 99 +++++++++++ examples/integration_wgpu/src/shader/frag.spv | Bin 0 -> 352 bytes examples/integration_wgpu/src/shader/vert.spv | Bin 0 -> 904 bytes 24 files changed, 886 insertions(+), 884 deletions(-) delete mode 100644 examples/integration/Cargo.toml delete mode 100644 examples/integration/README.md delete mode 100644 examples/integration/src/controls.rs delete mode 100644 examples/integration/src/main.rs delete mode 100644 examples/integration/src/scene.rs delete mode 100644 examples/integration/src/shader/frag.spv delete mode 100644 examples/integration/src/shader/vert.spv delete mode 100644 examples/integration_gl/Cargo.toml delete mode 100644 examples/integration_gl/README.md delete mode 100644 examples/integration_gl/src/controls.rs delete mode 100644 examples/integration_gl/src/main.rs delete mode 100644 examples/integration_gl/src/scene.rs create mode 100644 examples/integration_opengl/Cargo.toml create mode 100644 examples/integration_opengl/README.md create mode 100644 examples/integration_opengl/src/controls.rs create mode 100644 examples/integration_opengl/src/main.rs create mode 100644 examples/integration_opengl/src/scene.rs create mode 100644 examples/integration_wgpu/Cargo.toml create mode 100644 examples/integration_wgpu/README.md create mode 100644 examples/integration_wgpu/src/controls.rs create mode 100644 examples/integration_wgpu/src/main.rs create mode 100644 examples/integration_wgpu/src/scene.rs create mode 100644 examples/integration_wgpu/src/shader/frag.spv create mode 100644 examples/integration_wgpu/src/shader/vert.spv (limited to 'examples') diff --git a/examples/integration/Cargo.toml b/examples/integration/Cargo.toml deleted file mode 100644 index 4515502f..00000000 --- a/examples/integration/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "integration" -version = "0.1.0" -authors = ["Héctor Ramón Jiménez "] -edition = "2018" -publish = false - -[dependencies] -iced_winit = { path = "../../winit" } -iced_wgpu = { path = "../../wgpu" } -env_logger = "0.8" diff --git a/examples/integration/README.md b/examples/integration/README.md deleted file mode 100644 index d5aabc19..00000000 --- a/examples/integration/README.md +++ /dev/null @@ -1,18 +0,0 @@ -## Integration - -A demonstration of how to integrate Iced in an existing graphical application. - -The __[`main`]__ file contains all the code of the example. - - - -You can run it with `cargo run`: -``` -cargo run --package integration -``` - -[`main`]: src/main.rs diff --git a/examples/integration/src/controls.rs b/examples/integration/src/controls.rs deleted file mode 100644 index 36ee9b7e..00000000 --- a/examples/integration/src/controls.rs +++ /dev/null @@ -1,110 +0,0 @@ -use iced_wgpu::Renderer; -use iced_winit::{ - slider, Align, Clipboard, Color, Column, Command, Element, Length, Program, - Row, Slider, Text, -}; - -pub struct Controls { - background_color: Color, - sliders: [slider::State; 3], -} - -#[derive(Debug, Clone)] -pub enum Message { - BackgroundColorChanged(Color), -} - -impl Controls { - pub fn new() -> Controls { - Controls { - background_color: Color::BLACK, - sliders: Default::default(), - } - } - - pub fn background_color(&self) -> Color { - self.background_color - } -} - -impl Program for Controls { - type Renderer = Renderer; - type Message = Message; - type Clipboard = Clipboard; - - fn update( - &mut self, - message: Message, - _clipboard: &mut Clipboard, - ) -> Command { - match message { - Message::BackgroundColorChanged(color) => { - self.background_color = color; - } - } - - Command::none() - } - - fn view(&mut self) -> Element { - let [r, g, b] = &mut self.sliders; - let background_color = self.background_color; - - let sliders = Row::new() - .width(Length::Units(500)) - .spacing(20) - .push( - Slider::new(r, 0.0..=1.0, background_color.r, move |r| { - Message::BackgroundColorChanged(Color { - r, - ..background_color - }) - }) - .step(0.01), - ) - .push( - Slider::new(g, 0.0..=1.0, background_color.g, move |g| { - Message::BackgroundColorChanged(Color { - g, - ..background_color - }) - }) - .step(0.01), - ) - .push( - Slider::new(b, 0.0..=1.0, background_color.b, move |b| { - Message::BackgroundColorChanged(Color { - b, - ..background_color - }) - }) - .step(0.01), - ); - - 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() - } -} diff --git a/examples/integration/src/main.rs b/examples/integration/src/main.rs deleted file mode 100644 index 6f319466..00000000 --- a/examples/integration/src/main.rs +++ /dev/null @@ -1,239 +0,0 @@ -mod controls; -mod scene; - -use controls::Controls; -use scene::Scene; - -use iced_wgpu::{wgpu, Backend, Renderer, Settings, Viewport}; -use iced_winit::{conversion, futures, program, winit, Clipboard, Debug, Size}; - -use futures::task::SpawnExt; -use winit::{ - dpi::PhysicalPosition, - event::{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 physical_size = window.inner_size(); - let mut viewport = Viewport::with_physical_size( - Size::new(physical_size.width, physical_size.height), - window.scale_factor(), - ); - let mut cursor_position = PhysicalPosition::new(-1.0, -1.0); - let mut modifiers = ModifiersState::default(); - let mut clipboard = Clipboard::connect(&window); - - // Initialize wgpu - let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY); - let surface = unsafe { instance.create_surface(&window) }; - - let (format, (mut device, queue)) = futures::executor::block_on(async { - let adapter = instance - .request_adapter(&wgpu::RequestAdapterOptions { - power_preference: wgpu::PowerPreference::HighPerformance, - compatible_surface: Some(&surface), - }) - .await - .expect("Request adapter"); - - ( - adapter - .get_swap_chain_preferred_format(&surface) - .expect("Get preferred format"), - adapter - .request_device( - &wgpu::DeviceDescriptor { - label: None, - features: wgpu::Features::empty(), - limits: wgpu::Limits::default(), - }, - None, - ) - .await - .expect("Request device"), - ) - }); - - let mut swap_chain = { - let size = window.inner_size(); - - device.create_swap_chain( - &surface, - &wgpu::SwapChainDescriptor { - usage: wgpu::TextureUsage::RENDER_ATTACHMENT, - format, - width: size.width, - height: size.height, - present_mode: wgpu::PresentMode::Mailbox, - }, - ) - }; - let mut resized = false; - - // Initialize staging belt and local pool - let mut staging_belt = wgpu::util::StagingBelt::new(5 * 1024); - let mut local_pool = futures::executor::LocalPool::new(); - - // Initialize scene and GUI controls - let scene = Scene::new(&mut device); - let controls = Controls::new(); - - // Initialize iced - let mut debug = Debug::new(); - let mut renderer = - Renderer::new(Backend::new(&mut device, Settings::default(), format)); - - let mut state = program::State::new( - controls, - viewport.logical_size(), - conversion::cursor_position(cursor_position, viewport.scale_factor()), - &mut renderer, - &mut debug, - ); - - // 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::WindowEvent { event, .. } => { - match event { - WindowEvent::CursorMoved { position, .. } => { - cursor_position = position; - } - WindowEvent::ModifiersChanged(new_modifiers) => { - modifiers = new_modifiers; - } - WindowEvent::Resized(new_size) => { - viewport = Viewport::with_physical_size( - Size::new(new_size.width, new_size.height), - 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, - ) { - state.queue_event(event); - } - } - Event::MainEventsCleared => { - // If there are events pending - if !state.is_queue_empty() { - // We update iced - let _ = state.update( - viewport.logical_size(), - conversion::cursor_position( - cursor_position, - viewport.scale_factor(), - ), - &mut renderer, - &mut clipboard, - &mut debug, - ); - - // and request a redraw - window.request_redraw(); - } - } - Event::RedrawRequested(_) => { - if resized { - let size = window.inner_size(); - - swap_chain = device.create_swap_chain( - &surface, - &wgpu::SwapChainDescriptor { - usage: wgpu::TextureUsage::RENDER_ATTACHMENT, - format: format, - width: size.width, - height: size.height, - present_mode: wgpu::PresentMode::Mailbox, - }, - ); - - resized = false; - } - - match swap_chain.get_current_frame() { - Ok(frame) => { - let mut encoder = device.create_command_encoder( - &wgpu::CommandEncoderDescriptor { label: None }, - ); - - let program = state.program(); - - { - // We clear the frame - let mut render_pass = scene.clear( - &frame.output.view, - &mut encoder, - program.background_color(), - ); - - // Draw the scene - scene.draw(&mut render_pass); - } - - // And then iced on top - let mouse_interaction = renderer.backend_mut().draw( - &mut device, - &mut staging_belt, - &mut encoder, - &frame.output.view, - &viewport, - state.primitive(), - &debug.overlay(), - ); - - // Then we submit the work - staging_belt.finish(); - queue.submit(Some(encoder.finish())); - - // Update the mouse cursor - window.set_cursor_icon( - iced_winit::conversion::mouse_interaction( - mouse_interaction, - ), - ); - - // And recall staging buffers - local_pool - .spawner() - .spawn(staging_belt.recall()) - .expect("Recall staging buffers"); - - local_pool.run_until_stalled(); - } - Err(error) => match error { - wgpu::SwapChainError::OutOfMemory => { - panic!("Swapchain error: {}. Rendering cannot continue.", error) - } - _ => { - // Try rendering again next frame. - window.request_redraw(); - } - }, - } - } - _ => {} - } - }) -} diff --git a/examples/integration/src/scene.rs b/examples/integration/src/scene.rs deleted file mode 100644 index 3e8277c8..00000000 --- a/examples/integration/src/scene.rs +++ /dev/null @@ -1,99 +0,0 @@ -use iced_wgpu::wgpu; -use iced_winit::Color; - -pub struct Scene { - pipeline: wgpu::RenderPipeline, -} - -impl Scene { - pub fn new(device: &wgpu::Device) -> Scene { - let pipeline = build_pipeline(device); - - Scene { pipeline } - } - - pub fn clear<'a>( - &self, - target: &'a wgpu::TextureView, - encoder: &'a mut wgpu::CommandEncoder, - background_color: Color, - ) -> wgpu::RenderPass<'a> { - encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: None, - color_attachments: &[wgpu::RenderPassColorAttachment { - view: target, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear({ - let [r, g, b, a] = background_color.into_linear(); - - wgpu::Color { - r: r as f64, - g: g as f64, - b: b as f64, - a: a as f64, - } - }), - store: true, - }, - }], - depth_stencil_attachment: None, - }) - } - - pub fn draw<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) { - render_pass.set_pipeline(&self.pipeline); - render_pass.draw(0..3, 0..1); - } -} - -fn build_pipeline(device: &wgpu::Device) -> wgpu::RenderPipeline { - let vs_module = - device.create_shader_module(&wgpu::include_spirv!("shader/vert.spv")); - - let fs_module = - device.create_shader_module(&wgpu::include_spirv!("shader/frag.spv")); - - let pipeline_layout = - device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: None, - push_constant_ranges: &[], - bind_group_layouts: &[], - }); - - let pipeline = - device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: None, - layout: Some(&pipeline_layout), - vertex: wgpu::VertexState { - module: &vs_module, - entry_point: "main", - buffers: &[], - }, - fragment: Some(wgpu::FragmentState { - module: &fs_module, - entry_point: "main", - targets: &[wgpu::ColorTargetState { - format: wgpu::TextureFormat::Bgra8UnormSrgb, - blend: Some(wgpu::BlendState { - color: wgpu::BlendComponent::REPLACE, - alpha: wgpu::BlendComponent::REPLACE, - }), - write_mask: wgpu::ColorWrite::ALL, - }], - }), - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - front_face: wgpu::FrontFace::Ccw, - ..Default::default() - }, - depth_stencil: None, - multisample: wgpu::MultisampleState { - count: 1, - mask: !0, - alpha_to_coverage_enabled: false, - }, - }); - - pipeline -} diff --git a/examples/integration/src/shader/frag.spv b/examples/integration/src/shader/frag.spv deleted file mode 100644 index 9d6807c9..00000000 Binary files a/examples/integration/src/shader/frag.spv and /dev/null differ diff --git a/examples/integration/src/shader/vert.spv b/examples/integration/src/shader/vert.spv deleted file mode 100644 index 0cabc9c0..00000000 Binary files a/examples/integration/src/shader/vert.spv and /dev/null differ diff --git a/examples/integration_gl/Cargo.toml b/examples/integration_gl/Cargo.toml deleted file mode 100644 index 80d7adad..00000000 --- a/examples/integration_gl/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "integration_gl" -version = "0.1.0" -authors = ["Héctor Ramón Jiménez "] -edition = "2018" -publish = false - -[dependencies] -iced_glutin = { path = "../../glutin" } -iced_glow = { path = "../../glow" } -iced_winit = { path = "../../winit" } -env_logger = "0.8" -glow = "0.6" diff --git a/examples/integration_gl/README.md b/examples/integration_gl/README.md deleted file mode 100644 index 87491508..00000000 --- a/examples/integration_gl/README.md +++ /dev/null @@ -1,16 +0,0 @@ -## Integration - -A demonstration of how to integrate Iced in an existing graphical OpenGL application. - -The __[`main`]__ file contains all the code of the example. - -
- image host -
- -You can run it with `cargo run`: -``` -cargo run --package integration_gl -``` - -[`main`]: src/main.rs diff --git a/examples/integration_gl/src/controls.rs b/examples/integration_gl/src/controls.rs deleted file mode 100644 index 13b7fbc2..00000000 --- a/examples/integration_gl/src/controls.rs +++ /dev/null @@ -1,110 +0,0 @@ -use iced_glow::Renderer; -use iced_glutin::{ - slider, Align, Clipboard, Color, Column, Command, Element, Length, Program, - Row, Slider, Text, -}; - -pub struct Controls { - background_color: Color, - sliders: [slider::State; 3], -} - -#[derive(Debug, Clone)] -pub enum Message { - BackgroundColorChanged(Color), -} - -impl Controls { - pub fn new() -> Controls { - Controls { - background_color: Color::BLACK, - sliders: Default::default(), - } - } - - pub fn background_color(&self) -> Color { - self.background_color - } -} - -impl Program for Controls { - type Renderer = Renderer; - type Message = Message; - type Clipboard = Clipboard; - - fn update( - &mut self, - message: Message, - _clipboard: &mut Clipboard, - ) -> Command { - match message { - Message::BackgroundColorChanged(color) => { - self.background_color = color; - } - } - - Command::none() - } - - fn view(&mut self) -> Element { - let [r, g, b] = &mut self.sliders; - let background_color = self.background_color; - - let sliders = Row::new() - .width(Length::Units(500)) - .spacing(20) - .push( - Slider::new(r, 0.0..=1.0, background_color.r, move |r| { - Message::BackgroundColorChanged(Color { - r, - ..background_color - }) - }) - .step(0.01), - ) - .push( - Slider::new(g, 0.0..=1.0, background_color.g, move |g| { - Message::BackgroundColorChanged(Color { - g, - ..background_color - }) - }) - .step(0.01), - ) - .push( - Slider::new(b, 0.0..=1.0, background_color.b, move |b| { - Message::BackgroundColorChanged(Color { - b, - ..background_color - }) - }) - .step(0.01), - ); - - 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() - } -} diff --git a/examples/integration_gl/src/main.rs b/examples/integration_gl/src/main.rs deleted file mode 100644 index 925102e3..00000000 --- a/examples/integration_gl/src/main.rs +++ /dev/null @@ -1,175 +0,0 @@ -mod controls; -mod scene; - -use controls::Controls; -use scene::Scene; - -use glow; -use glow::*; -use iced_glow::{Backend, Renderer, Settings, Viewport}; -use iced_glutin::glutin; -use iced_glutin::glutin::event::{Event, WindowEvent}; -use iced_glutin::glutin::event_loop::ControlFlow; -use iced_glutin::{program, Clipboard, Debug, Size}; -use iced_winit::conversion; -use iced_winit::winit; -use winit::{dpi::PhysicalPosition, event::ModifiersState}; - -pub fn main() { - env_logger::init(); - let (gl, event_loop, windowed_context, shader_version) = { - let el = glutin::event_loop::EventLoop::new(); - let wb = glutin::window::WindowBuilder::new() - .with_title("OpenGL integration example") - .with_inner_size(glutin::dpi::LogicalSize::new(1024.0, 768.0)); - let windowed_context = glutin::ContextBuilder::new() - .with_vsync(true) - .build_windowed(wb, &el) - .unwrap(); - unsafe { - let windowed_context = windowed_context.make_current().unwrap(); - let gl = glow::Context::from_loader_function(|s| { - windowed_context.get_proc_address(s) as *const _ - }); - // Enable auto-conversion from/to sRGB - gl.enable(glow::FRAMEBUFFER_SRGB); - - // Enable alpha blending - gl.enable(glow::BLEND); - gl.blend_func(glow::SRC_ALPHA, glow::ONE_MINUS_SRC_ALPHA); - - // Disable multisampling by default - gl.disable(glow::MULTISAMPLE); - (gl, el, windowed_context, "#version 410") - } - }; - - let physical_size = windowed_context.window().inner_size(); - let mut viewport = Viewport::with_physical_size( - Size::new(physical_size.width, physical_size.height), - windowed_context.window().scale_factor(), - ); - - let mut cursor_position = PhysicalPosition::new(-1.0, -1.0); - let mut modifiers = ModifiersState::default(); - let mut clipboard = Clipboard::connect(&windowed_context.window()); - - let mut renderer = Renderer::new(Backend::new(&gl, Settings::default())); - - let mut debug = Debug::new(); - - let controls = Controls::new(); - let mut state = program::State::new( - controls, - viewport.logical_size(), - conversion::cursor_position(cursor_position, viewport.scale_factor()), - &mut renderer, - &mut debug, - ); - let mut resized = false; - - event_loop.run(move |event, _, control_flow| { - let scene = Scene::new(&gl, &shader_version); - *control_flow = ControlFlow::Wait; - - match event { - Event::LoopDestroyed => return, - Event::WindowEvent { event, .. } => { - match event { - WindowEvent::CursorMoved { position, .. } => { - cursor_position = position; - } - WindowEvent::ModifiersChanged(new_modifiers) => { - modifiers = new_modifiers; - } - WindowEvent::Resized(physical_size) => { - viewport = Viewport::with_physical_size( - Size::new( - physical_size.width, - physical_size.height, - ), - windowed_context.window().scale_factor(), - ); - - resized = true; - } - WindowEvent::CloseRequested => { - scene.cleanup(&gl); - *control_flow = ControlFlow::Exit - } - _ => (), - } - - // Map window event to iced event - if let Some(event) = iced_winit::conversion::window_event( - &event, - windowed_context.window().scale_factor(), - modifiers, - ) { - state.queue_event(event); - } - } - Event::MainEventsCleared => { - // If there are events pending - if !state.is_queue_empty() { - // We update iced - let _ = state.update( - viewport.logical_size(), - conversion::cursor_position( - cursor_position, - viewport.scale_factor(), - ), - &mut renderer, - &mut clipboard, - &mut debug, - ); - - // and request a redraw - windowed_context.window().request_redraw(); - } - } - Event::RedrawRequested(_) => { - if resized { - let size = windowed_context.window().inner_size(); - - unsafe { - gl.viewport( - 0, - 0, - size.width as i32, - size.height as i32, - ); - } - - resized = false; - } - - let program = state.program(); - { - // We clear the frame - scene.clear(&gl, program.background_color()); - - // Draw the scene - scene.draw(&gl); - } - - // And then iced on top - let mouse_interaction = renderer.backend_mut().draw( - &gl, - &viewport, - state.primitive(), - &debug.overlay(), - ); - // Update the mouse cursor - windowed_context.window().set_cursor_icon( - iced_winit::conversion::mouse_interaction( - mouse_interaction, - ), - ); - - windowed_context.swap_buffers().unwrap(); - } - _ => (), - } - }); -} diff --git a/examples/integration_gl/src/scene.rs b/examples/integration_gl/src/scene.rs deleted file mode 100644 index 596381b3..00000000 --- a/examples/integration_gl/src/scene.rs +++ /dev/null @@ -1,93 +0,0 @@ -use glow::*; -use iced_glow::Color; - -pub struct Scene { - program: glow::Program, - vertex_array: glow::VertexArray, -} - -impl Scene { - pub fn new(gl: &glow::Context, shader_version: &str) -> Self { - unsafe { - let vertex_array = gl - .create_vertex_array() - .expect("Cannot create vertex array"); - gl.bind_vertex_array(Some(vertex_array)); - - let program = gl.create_program().expect("Cannot create program"); - - let (vertex_shader_source, fragment_shader_source) = ( - r#"const vec2 verts[3] = vec2[3]( - vec2(0.5f, 1.0f), - vec2(0.0f, 0.0f), - vec2(1.0f, 0.0f) - ); - out vec2 vert; - void main() { - vert = verts[gl_VertexID]; - gl_Position = vec4(vert - 0.5, 0.0, 1.0); - }"#, - r#"precision highp float; - in vec2 vert; - out vec4 color; - void main() { - color = vec4(vert, 0.5, 1.0); - }"#, - ); - - let shader_sources = [ - (glow::VERTEX_SHADER, vertex_shader_source), - (glow::FRAGMENT_SHADER, fragment_shader_source), - ]; - - let mut shaders = Vec::with_capacity(shader_sources.len()); - - for (shader_type, shader_source) in shader_sources.iter() { - let shader = gl - .create_shader(*shader_type) - .expect("Cannot create shader"); - gl.shader_source(shader, &format!("{}\n{}", shader_version, shader_source)); - gl.compile_shader(shader); - if !gl.get_shader_compile_status(shader) { - panic!(gl.get_shader_info_log(shader)); - } - gl.attach_shader(program, shader); - shaders.push(shader); - } - - gl.link_program(program); - if !gl.get_program_link_status(program) { - panic!(gl.get_program_info_log(program)); - } - - for shader in shaders { - gl.detach_shader(program, shader); - gl.delete_shader(shader); - } - - gl.use_program(Some(program)); - Self { program, vertex_array } - } - } - - pub fn clear(&self, gl: &glow::Context, background_color: Color) { - let [r, g, b, a] = background_color.into_linear(); - unsafe { - gl.clear_color(r, g, b, a); - gl.clear(glow::COLOR_BUFFER_BIT); - } - } - - pub fn draw(&self, gl: &glow::Context) { - unsafe { - gl.draw_arrays(glow::TRIANGLES, 0, 3); - } - } - - pub fn cleanup(&self, gl: &glow::Context) { - unsafe { - gl.delete_program(self.program); - gl.delete_vertex_array(self.vertex_array); - } - } -} \ No newline at end of file diff --git a/examples/integration_opengl/Cargo.toml b/examples/integration_opengl/Cargo.toml new file mode 100644 index 00000000..0917f2ec --- /dev/null +++ b/examples/integration_opengl/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "integration_opengl" +version = "0.1.0" +authors = ["Héctor Ramón Jiménez "] +edition = "2018" +publish = false + +[dependencies] +iced_glutin = { path = "../../glutin" } +iced_glow = { path = "../../glow" } +iced_winit = { path = "../../winit" } +env_logger = "0.8" +glow = "0.6" diff --git a/examples/integration_opengl/README.md b/examples/integration_opengl/README.md new file mode 100644 index 00000000..08cce7a6 --- /dev/null +++ b/examples/integration_opengl/README.md @@ -0,0 +1,16 @@ +## OpenGL integration + +A demonstration of how to integrate Iced in an existing graphical OpenGL application. + +The __[`main`]__ file contains all the code of the example. + +
+ image host +
+ +You can run it with `cargo run`: +``` +cargo run --package integration_gl +``` + +[`main`]: src/main.rs diff --git a/examples/integration_opengl/src/controls.rs b/examples/integration_opengl/src/controls.rs new file mode 100644 index 00000000..13b7fbc2 --- /dev/null +++ b/examples/integration_opengl/src/controls.rs @@ -0,0 +1,110 @@ +use iced_glow::Renderer; +use iced_glutin::{ + slider, Align, Clipboard, Color, Column, Command, Element, Length, Program, + Row, Slider, Text, +}; + +pub struct Controls { + background_color: Color, + sliders: [slider::State; 3], +} + +#[derive(Debug, Clone)] +pub enum Message { + BackgroundColorChanged(Color), +} + +impl Controls { + pub fn new() -> Controls { + Controls { + background_color: Color::BLACK, + sliders: Default::default(), + } + } + + pub fn background_color(&self) -> Color { + self.background_color + } +} + +impl Program for Controls { + type Renderer = Renderer; + type Message = Message; + type Clipboard = Clipboard; + + fn update( + &mut self, + message: Message, + _clipboard: &mut Clipboard, + ) -> Command { + match message { + Message::BackgroundColorChanged(color) => { + self.background_color = color; + } + } + + Command::none() + } + + fn view(&mut self) -> Element { + let [r, g, b] = &mut self.sliders; + let background_color = self.background_color; + + let sliders = Row::new() + .width(Length::Units(500)) + .spacing(20) + .push( + Slider::new(r, 0.0..=1.0, background_color.r, move |r| { + Message::BackgroundColorChanged(Color { + r, + ..background_color + }) + }) + .step(0.01), + ) + .push( + Slider::new(g, 0.0..=1.0, background_color.g, move |g| { + Message::BackgroundColorChanged(Color { + g, + ..background_color + }) + }) + .step(0.01), + ) + .push( + Slider::new(b, 0.0..=1.0, background_color.b, move |b| { + Message::BackgroundColorChanged(Color { + b, + ..background_color + }) + }) + .step(0.01), + ); + + 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() + } +} diff --git a/examples/integration_opengl/src/main.rs b/examples/integration_opengl/src/main.rs new file mode 100644 index 00000000..925102e3 --- /dev/null +++ b/examples/integration_opengl/src/main.rs @@ -0,0 +1,175 @@ +mod controls; +mod scene; + +use controls::Controls; +use scene::Scene; + +use glow; +use glow::*; +use iced_glow::{Backend, Renderer, Settings, Viewport}; +use iced_glutin::glutin; +use iced_glutin::glutin::event::{Event, WindowEvent}; +use iced_glutin::glutin::event_loop::ControlFlow; +use iced_glutin::{program, Clipboard, Debug, Size}; +use iced_winit::conversion; +use iced_winit::winit; +use winit::{dpi::PhysicalPosition, event::ModifiersState}; + +pub fn main() { + env_logger::init(); + let (gl, event_loop, windowed_context, shader_version) = { + let el = glutin::event_loop::EventLoop::new(); + let wb = glutin::window::WindowBuilder::new() + .with_title("OpenGL integration example") + .with_inner_size(glutin::dpi::LogicalSize::new(1024.0, 768.0)); + let windowed_context = glutin::ContextBuilder::new() + .with_vsync(true) + .build_windowed(wb, &el) + .unwrap(); + unsafe { + let windowed_context = windowed_context.make_current().unwrap(); + let gl = glow::Context::from_loader_function(|s| { + windowed_context.get_proc_address(s) as *const _ + }); + // Enable auto-conversion from/to sRGB + gl.enable(glow::FRAMEBUFFER_SRGB); + + // Enable alpha blending + gl.enable(glow::BLEND); + gl.blend_func(glow::SRC_ALPHA, glow::ONE_MINUS_SRC_ALPHA); + + // Disable multisampling by default + gl.disable(glow::MULTISAMPLE); + (gl, el, windowed_context, "#version 410") + } + }; + + let physical_size = windowed_context.window().inner_size(); + let mut viewport = Viewport::with_physical_size( + Size::new(physical_size.width, physical_size.height), + windowed_context.window().scale_factor(), + ); + + let mut cursor_position = PhysicalPosition::new(-1.0, -1.0); + let mut modifiers = ModifiersState::default(); + let mut clipboard = Clipboard::connect(&windowed_context.window()); + + let mut renderer = Renderer::new(Backend::new(&gl, Settings::default())); + + let mut debug = Debug::new(); + + let controls = Controls::new(); + let mut state = program::State::new( + controls, + viewport.logical_size(), + conversion::cursor_position(cursor_position, viewport.scale_factor()), + &mut renderer, + &mut debug, + ); + let mut resized = false; + + event_loop.run(move |event, _, control_flow| { + let scene = Scene::new(&gl, &shader_version); + *control_flow = ControlFlow::Wait; + + match event { + Event::LoopDestroyed => return, + Event::WindowEvent { event, .. } => { + match event { + WindowEvent::CursorMoved { position, .. } => { + cursor_position = position; + } + WindowEvent::ModifiersChanged(new_modifiers) => { + modifiers = new_modifiers; + } + WindowEvent::Resized(physical_size) => { + viewport = Viewport::with_physical_size( + Size::new( + physical_size.width, + physical_size.height, + ), + windowed_context.window().scale_factor(), + ); + + resized = true; + } + WindowEvent::CloseRequested => { + scene.cleanup(&gl); + *control_flow = ControlFlow::Exit + } + _ => (), + } + + // Map window event to iced event + if let Some(event) = iced_winit::conversion::window_event( + &event, + windowed_context.window().scale_factor(), + modifiers, + ) { + state.queue_event(event); + } + } + Event::MainEventsCleared => { + // If there are events pending + if !state.is_queue_empty() { + // We update iced + let _ = state.update( + viewport.logical_size(), + conversion::cursor_position( + cursor_position, + viewport.scale_factor(), + ), + &mut renderer, + &mut clipboard, + &mut debug, + ); + + // and request a redraw + windowed_context.window().request_redraw(); + } + } + Event::RedrawRequested(_) => { + if resized { + let size = windowed_context.window().inner_size(); + + unsafe { + gl.viewport( + 0, + 0, + size.width as i32, + size.height as i32, + ); + } + + resized = false; + } + + let program = state.program(); + { + // We clear the frame + scene.clear(&gl, program.background_color()); + + // Draw the scene + scene.draw(&gl); + } + + // And then iced on top + let mouse_interaction = renderer.backend_mut().draw( + &gl, + &viewport, + state.primitive(), + &debug.overlay(), + ); + // Update the mouse cursor + windowed_context.window().set_cursor_icon( + iced_winit::conversion::mouse_interaction( + mouse_interaction, + ), + ); + + windowed_context.swap_buffers().unwrap(); + } + _ => (), + } + }); +} diff --git a/examples/integration_opengl/src/scene.rs b/examples/integration_opengl/src/scene.rs new file mode 100644 index 00000000..596381b3 --- /dev/null +++ b/examples/integration_opengl/src/scene.rs @@ -0,0 +1,93 @@ +use glow::*; +use iced_glow::Color; + +pub struct Scene { + program: glow::Program, + vertex_array: glow::VertexArray, +} + +impl Scene { + pub fn new(gl: &glow::Context, shader_version: &str) -> Self { + unsafe { + let vertex_array = gl + .create_vertex_array() + .expect("Cannot create vertex array"); + gl.bind_vertex_array(Some(vertex_array)); + + let program = gl.create_program().expect("Cannot create program"); + + let (vertex_shader_source, fragment_shader_source) = ( + r#"const vec2 verts[3] = vec2[3]( + vec2(0.5f, 1.0f), + vec2(0.0f, 0.0f), + vec2(1.0f, 0.0f) + ); + out vec2 vert; + void main() { + vert = verts[gl_VertexID]; + gl_Position = vec4(vert - 0.5, 0.0, 1.0); + }"#, + r#"precision highp float; + in vec2 vert; + out vec4 color; + void main() { + color = vec4(vert, 0.5, 1.0); + }"#, + ); + + let shader_sources = [ + (glow::VERTEX_SHADER, vertex_shader_source), + (glow::FRAGMENT_SHADER, fragment_shader_source), + ]; + + let mut shaders = Vec::with_capacity(shader_sources.len()); + + for (shader_type, shader_source) in shader_sources.iter() { + let shader = gl + .create_shader(*shader_type) + .expect("Cannot create shader"); + gl.shader_source(shader, &format!("{}\n{}", shader_version, shader_source)); + gl.compile_shader(shader); + if !gl.get_shader_compile_status(shader) { + panic!(gl.get_shader_info_log(shader)); + } + gl.attach_shader(program, shader); + shaders.push(shader); + } + + gl.link_program(program); + if !gl.get_program_link_status(program) { + panic!(gl.get_program_info_log(program)); + } + + for shader in shaders { + gl.detach_shader(program, shader); + gl.delete_shader(shader); + } + + gl.use_program(Some(program)); + Self { program, vertex_array } + } + } + + pub fn clear(&self, gl: &glow::Context, background_color: Color) { + let [r, g, b, a] = background_color.into_linear(); + unsafe { + gl.clear_color(r, g, b, a); + gl.clear(glow::COLOR_BUFFER_BIT); + } + } + + pub fn draw(&self, gl: &glow::Context) { + unsafe { + gl.draw_arrays(glow::TRIANGLES, 0, 3); + } + } + + pub fn cleanup(&self, gl: &glow::Context) { + unsafe { + gl.delete_program(self.program); + gl.delete_vertex_array(self.vertex_array); + } + } +} \ No newline at end of file diff --git a/examples/integration_wgpu/Cargo.toml b/examples/integration_wgpu/Cargo.toml new file mode 100644 index 00000000..743c655e --- /dev/null +++ b/examples/integration_wgpu/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "integration_wgpu" +version = "0.1.0" +authors = ["Héctor Ramón Jiménez "] +edition = "2018" +publish = false + +[dependencies] +iced_winit = { path = "../../winit" } +iced_wgpu = { path = "../../wgpu" } +env_logger = "0.8" diff --git a/examples/integration_wgpu/README.md b/examples/integration_wgpu/README.md new file mode 100644 index 00000000..53ffd3e5 --- /dev/null +++ b/examples/integration_wgpu/README.md @@ -0,0 +1,20 @@ +## `wgpu` integration + +A demonstration of how to integrate Iced in an existing graphical application +that leverages [`wgpu`]. + +The __[`main`]__ file contains all the code of the example. + + + +You can run it with `cargo run`: +``` +cargo run --package integration +``` + +[`main`]: src/main.rs +[`wgpu`]: https://github.com/gfx-rs/wgpu diff --git a/examples/integration_wgpu/src/controls.rs b/examples/integration_wgpu/src/controls.rs new file mode 100644 index 00000000..36ee9b7e --- /dev/null +++ b/examples/integration_wgpu/src/controls.rs @@ -0,0 +1,110 @@ +use iced_wgpu::Renderer; +use iced_winit::{ + slider, Align, Clipboard, Color, Column, Command, Element, Length, Program, + Row, Slider, Text, +}; + +pub struct Controls { + background_color: Color, + sliders: [slider::State; 3], +} + +#[derive(Debug, Clone)] +pub enum Message { + BackgroundColorChanged(Color), +} + +impl Controls { + pub fn new() -> Controls { + Controls { + background_color: Color::BLACK, + sliders: Default::default(), + } + } + + pub fn background_color(&self) -> Color { + self.background_color + } +} + +impl Program for Controls { + type Renderer = Renderer; + type Message = Message; + type Clipboard = Clipboard; + + fn update( + &mut self, + message: Message, + _clipboard: &mut Clipboard, + ) -> Command { + match message { + Message::BackgroundColorChanged(color) => { + self.background_color = color; + } + } + + Command::none() + } + + fn view(&mut self) -> Element { + let [r, g, b] = &mut self.sliders; + let background_color = self.background_color; + + let sliders = Row::new() + .width(Length::Units(500)) + .spacing(20) + .push( + Slider::new(r, 0.0..=1.0, background_color.r, move |r| { + Message::BackgroundColorChanged(Color { + r, + ..background_color + }) + }) + .step(0.01), + ) + .push( + Slider::new(g, 0.0..=1.0, background_color.g, move |g| { + Message::BackgroundColorChanged(Color { + g, + ..background_color + }) + }) + .step(0.01), + ) + .push( + Slider::new(b, 0.0..=1.0, background_color.b, move |b| { + Message::BackgroundColorChanged(Color { + b, + ..background_color + }) + }) + .step(0.01), + ); + + 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() + } +} diff --git a/examples/integration_wgpu/src/main.rs b/examples/integration_wgpu/src/main.rs new file mode 100644 index 00000000..6f319466 --- /dev/null +++ b/examples/integration_wgpu/src/main.rs @@ -0,0 +1,239 @@ +mod controls; +mod scene; + +use controls::Controls; +use scene::Scene; + +use iced_wgpu::{wgpu, Backend, Renderer, Settings, Viewport}; +use iced_winit::{conversion, futures, program, winit, Clipboard, Debug, Size}; + +use futures::task::SpawnExt; +use winit::{ + dpi::PhysicalPosition, + event::{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 physical_size = window.inner_size(); + let mut viewport = Viewport::with_physical_size( + Size::new(physical_size.width, physical_size.height), + window.scale_factor(), + ); + let mut cursor_position = PhysicalPosition::new(-1.0, -1.0); + let mut modifiers = ModifiersState::default(); + let mut clipboard = Clipboard::connect(&window); + + // Initialize wgpu + let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY); + let surface = unsafe { instance.create_surface(&window) }; + + let (format, (mut device, queue)) = futures::executor::block_on(async { + let adapter = instance + .request_adapter(&wgpu::RequestAdapterOptions { + power_preference: wgpu::PowerPreference::HighPerformance, + compatible_surface: Some(&surface), + }) + .await + .expect("Request adapter"); + + ( + adapter + .get_swap_chain_preferred_format(&surface) + .expect("Get preferred format"), + adapter + .request_device( + &wgpu::DeviceDescriptor { + label: None, + features: wgpu::Features::empty(), + limits: wgpu::Limits::default(), + }, + None, + ) + .await + .expect("Request device"), + ) + }); + + let mut swap_chain = { + let size = window.inner_size(); + + device.create_swap_chain( + &surface, + &wgpu::SwapChainDescriptor { + usage: wgpu::TextureUsage::RENDER_ATTACHMENT, + format, + width: size.width, + height: size.height, + present_mode: wgpu::PresentMode::Mailbox, + }, + ) + }; + let mut resized = false; + + // Initialize staging belt and local pool + let mut staging_belt = wgpu::util::StagingBelt::new(5 * 1024); + let mut local_pool = futures::executor::LocalPool::new(); + + // Initialize scene and GUI controls + let scene = Scene::new(&mut device); + let controls = Controls::new(); + + // Initialize iced + let mut debug = Debug::new(); + let mut renderer = + Renderer::new(Backend::new(&mut device, Settings::default(), format)); + + let mut state = program::State::new( + controls, + viewport.logical_size(), + conversion::cursor_position(cursor_position, viewport.scale_factor()), + &mut renderer, + &mut debug, + ); + + // 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::WindowEvent { event, .. } => { + match event { + WindowEvent::CursorMoved { position, .. } => { + cursor_position = position; + } + WindowEvent::ModifiersChanged(new_modifiers) => { + modifiers = new_modifiers; + } + WindowEvent::Resized(new_size) => { + viewport = Viewport::with_physical_size( + Size::new(new_size.width, new_size.height), + 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, + ) { + state.queue_event(event); + } + } + Event::MainEventsCleared => { + // If there are events pending + if !state.is_queue_empty() { + // We update iced + let _ = state.update( + viewport.logical_size(), + conversion::cursor_position( + cursor_position, + viewport.scale_factor(), + ), + &mut renderer, + &mut clipboard, + &mut debug, + ); + + // and request a redraw + window.request_redraw(); + } + } + Event::RedrawRequested(_) => { + if resized { + let size = window.inner_size(); + + swap_chain = device.create_swap_chain( + &surface, + &wgpu::SwapChainDescriptor { + usage: wgpu::TextureUsage::RENDER_ATTACHMENT, + format: format, + width: size.width, + height: size.height, + present_mode: wgpu::PresentMode::Mailbox, + }, + ); + + resized = false; + } + + match swap_chain.get_current_frame() { + Ok(frame) => { + let mut encoder = device.create_command_encoder( + &wgpu::CommandEncoderDescriptor { label: None }, + ); + + let program = state.program(); + + { + // We clear the frame + let mut render_pass = scene.clear( + &frame.output.view, + &mut encoder, + program.background_color(), + ); + + // Draw the scene + scene.draw(&mut render_pass); + } + + // And then iced on top + let mouse_interaction = renderer.backend_mut().draw( + &mut device, + &mut staging_belt, + &mut encoder, + &frame.output.view, + &viewport, + state.primitive(), + &debug.overlay(), + ); + + // Then we submit the work + staging_belt.finish(); + queue.submit(Some(encoder.finish())); + + // Update the mouse cursor + window.set_cursor_icon( + iced_winit::conversion::mouse_interaction( + mouse_interaction, + ), + ); + + // And recall staging buffers + local_pool + .spawner() + .spawn(staging_belt.recall()) + .expect("Recall staging buffers"); + + local_pool.run_until_stalled(); + } + Err(error) => match error { + wgpu::SwapChainError::OutOfMemory => { + panic!("Swapchain error: {}. Rendering cannot continue.", error) + } + _ => { + // Try rendering again next frame. + window.request_redraw(); + } + }, + } + } + _ => {} + } + }) +} diff --git a/examples/integration_wgpu/src/scene.rs b/examples/integration_wgpu/src/scene.rs new file mode 100644 index 00000000..3e8277c8 --- /dev/null +++ b/examples/integration_wgpu/src/scene.rs @@ -0,0 +1,99 @@ +use iced_wgpu::wgpu; +use iced_winit::Color; + +pub struct Scene { + pipeline: wgpu::RenderPipeline, +} + +impl Scene { + pub fn new(device: &wgpu::Device) -> Scene { + let pipeline = build_pipeline(device); + + Scene { pipeline } + } + + pub fn clear<'a>( + &self, + target: &'a wgpu::TextureView, + encoder: &'a mut wgpu::CommandEncoder, + background_color: Color, + ) -> wgpu::RenderPass<'a> { + encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: None, + color_attachments: &[wgpu::RenderPassColorAttachment { + view: target, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear({ + let [r, g, b, a] = background_color.into_linear(); + + wgpu::Color { + r: r as f64, + g: g as f64, + b: b as f64, + a: a as f64, + } + }), + store: true, + }, + }], + depth_stencil_attachment: None, + }) + } + + pub fn draw<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) { + render_pass.set_pipeline(&self.pipeline); + render_pass.draw(0..3, 0..1); + } +} + +fn build_pipeline(device: &wgpu::Device) -> wgpu::RenderPipeline { + let vs_module = + device.create_shader_module(&wgpu::include_spirv!("shader/vert.spv")); + + let fs_module = + device.create_shader_module(&wgpu::include_spirv!("shader/frag.spv")); + + let pipeline_layout = + device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: None, + push_constant_ranges: &[], + bind_group_layouts: &[], + }); + + let pipeline = + device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: None, + layout: Some(&pipeline_layout), + vertex: wgpu::VertexState { + module: &vs_module, + entry_point: "main", + buffers: &[], + }, + fragment: Some(wgpu::FragmentState { + module: &fs_module, + entry_point: "main", + targets: &[wgpu::ColorTargetState { + format: wgpu::TextureFormat::Bgra8UnormSrgb, + blend: Some(wgpu::BlendState { + color: wgpu::BlendComponent::REPLACE, + alpha: wgpu::BlendComponent::REPLACE, + }), + write_mask: wgpu::ColorWrite::ALL, + }], + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + front_face: wgpu::FrontFace::Ccw, + ..Default::default() + }, + depth_stencil: None, + multisample: wgpu::MultisampleState { + count: 1, + mask: !0, + alpha_to_coverage_enabled: false, + }, + }); + + pipeline +} diff --git a/examples/integration_wgpu/src/shader/frag.spv b/examples/integration_wgpu/src/shader/frag.spv new file mode 100644 index 00000000..9d6807c9 Binary files /dev/null and b/examples/integration_wgpu/src/shader/frag.spv differ diff --git a/examples/integration_wgpu/src/shader/vert.spv b/examples/integration_wgpu/src/shader/vert.spv new file mode 100644 index 00000000..0cabc9c0 Binary files /dev/null and b/examples/integration_wgpu/src/shader/vert.spv differ -- cgit From 956780863646c1c17bc2670789459a4d311388fc Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 13 Aug 2021 20:01:50 +0700 Subject: Fix formatting of `integration_opengl` example --- examples/integration_opengl/src/scene.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'examples') diff --git a/examples/integration_opengl/src/scene.rs b/examples/integration_opengl/src/scene.rs index 596381b3..2a27be11 100644 --- a/examples/integration_opengl/src/scene.rs +++ b/examples/integration_opengl/src/scene.rs @@ -46,7 +46,10 @@ impl Scene { let shader = gl .create_shader(*shader_type) .expect("Cannot create shader"); - gl.shader_source(shader, &format!("{}\n{}", shader_version, shader_source)); + gl.shader_source( + shader, + &format!("{}\n{}", shader_version, shader_source), + ); gl.compile_shader(shader); if !gl.get_shader_compile_status(shader) { panic!(gl.get_shader_info_log(shader)); @@ -66,7 +69,10 @@ impl Scene { } gl.use_program(Some(program)); - Self { program, vertex_array } + Self { + program, + vertex_array, + } } } @@ -90,4 +96,4 @@ impl Scene { gl.delete_vertex_array(self.vertex_array); } } -} \ No newline at end of file +} -- cgit From f1f58b2a8ad8907337e1d542fa8f17990f053862 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 13 Aug 2021 20:05:10 +0700 Subject: Fix `panic!` warnings in `integration_opengl` example --- examples/integration_opengl/src/main.rs | 6 ++++++ examples/integration_opengl/src/scene.rs | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'examples') diff --git a/examples/integration_opengl/src/main.rs b/examples/integration_opengl/src/main.rs index 925102e3..f80915d2 100644 --- a/examples/integration_opengl/src/main.rs +++ b/examples/integration_opengl/src/main.rs @@ -19,18 +19,23 @@ pub fn main() { env_logger::init(); let (gl, event_loop, windowed_context, shader_version) = { let el = glutin::event_loop::EventLoop::new(); + let wb = glutin::window::WindowBuilder::new() .with_title("OpenGL integration example") .with_inner_size(glutin::dpi::LogicalSize::new(1024.0, 768.0)); + let windowed_context = glutin::ContextBuilder::new() .with_vsync(true) .build_windowed(wb, &el) .unwrap(); + unsafe { let windowed_context = windowed_context.make_current().unwrap(); + let gl = glow::Context::from_loader_function(|s| { windowed_context.get_proc_address(s) as *const _ }); + // Enable auto-conversion from/to sRGB gl.enable(glow::FRAMEBUFFER_SRGB); @@ -40,6 +45,7 @@ pub fn main() { // Disable multisampling by default gl.disable(glow::MULTISAMPLE); + (gl, el, windowed_context, "#version 410") } }; diff --git a/examples/integration_opengl/src/scene.rs b/examples/integration_opengl/src/scene.rs index 2a27be11..ccca0d29 100644 --- a/examples/integration_opengl/src/scene.rs +++ b/examples/integration_opengl/src/scene.rs @@ -52,7 +52,7 @@ impl Scene { ); gl.compile_shader(shader); if !gl.get_shader_compile_status(shader) { - panic!(gl.get_shader_info_log(shader)); + panic!("{}", gl.get_shader_info_log(shader)); } gl.attach_shader(program, shader); shaders.push(shader); @@ -60,7 +60,7 @@ impl Scene { gl.link_program(program); if !gl.get_program_link_status(program) { - panic!(gl.get_program_info_log(program)); + panic!("{}", gl.get_program_info_log(program)); } for shader in shaders { -- cgit From 138132758a1e9b61619b7eeb82c74bb172a37ad8 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 13 Aug 2021 20:59:42 +0700 Subject: Update `README` in `examples` directory --- examples/README.md | 4 +++- examples/integration_wgpu/README.md | 3 +-- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'examples') diff --git a/examples/README.md b/examples/README.md index 10c28cf5..1b9f4b56 100644 --- a/examples/README.md +++ b/examples/README.md @@ -98,7 +98,8 @@ A bunch of simpler examples exist: - [`download_progress`](download_progress), a basic application that asynchronously downloads a dummy file of 100 MB and tracks the download progress. - [`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. +- [`integration_opengl`](integration_opengl), a demonstration of how to integrate Iced in an existing OpenGL application. +- [`integration_wgpu`](integration_wgpu), a demonstration of how to integrate Iced in an existing [`wgpu`] application. - [`pane_grid`](pane_grid), a grid of panes that can be split, resized, and reorganized. - [`pick_list`](pick_list), a dropdown list of selectable options. - [`pokedex`](pokedex), an application that displays a random Pokédex entry (sprite included!) by using the [PokéAPI]. @@ -116,6 +117,7 @@ cargo run --package [`lyon`]: https://github.com/nical/lyon [PokéAPI]: https://pokeapi.co/ [Ghostscript Tiger]: https://commons.wikimedia.org/wiki/File:Ghostscript_Tiger.svg +[`wgpu`]: https://github.com/gfx-rs/wgpu ## [Coffee] Since [Iced was born in May 2019], it has been powering the user interfaces in diff --git a/examples/integration_wgpu/README.md b/examples/integration_wgpu/README.md index 53ffd3e5..c51c2c65 100644 --- a/examples/integration_wgpu/README.md +++ b/examples/integration_wgpu/README.md @@ -1,7 +1,6 @@ ## `wgpu` integration -A demonstration of how to integrate Iced in an existing graphical application -that leverages [`wgpu`]. +A demonstration of how to integrate Iced in an existing [`wgpu`] application. The __[`main`]__ file contains all the code of the example. -- cgit