mirror of
				https://github.com/slint-ui/slint.git
				synced 2025-11-03 21:24:17 +00:00 
			
		
		
		
	- Use a version of FemtoVG that uses WGPU 25 - Rename the unstable-wgpu-24 feature and wgpu_24 module - Fetch bevy from a revision on master branch that switches to WGPU 25 - Move the bevy example out of the common workspace: That way users of the repo don't get bevy pulled from git, unless they want to build the bevy example.
		
			
				
	
	
		
			194 lines
		
	
	
	
		
			7 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			194 lines
		
	
	
	
		
			7 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
// Copyright © SixtyFPS GmbH <info@slint.dev>
 | 
						|
// SPDX-License-Identifier: MIT
 | 
						|
 | 
						|
slint::include_modules!();
 | 
						|
 | 
						|
use slint::wgpu_25::{wgpu, WGPUConfiguration, WGPUSettings};
 | 
						|
 | 
						|
struct DemoRenderer {
 | 
						|
    device: wgpu::Device,
 | 
						|
    queue: wgpu::Queue,
 | 
						|
    pipeline: wgpu::RenderPipeline,
 | 
						|
    displayed_texture: wgpu::Texture,
 | 
						|
    next_texture: wgpu::Texture,
 | 
						|
    start_time: std::time::Instant,
 | 
						|
}
 | 
						|
 | 
						|
#[repr(C)]
 | 
						|
#[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
 | 
						|
struct PushConstants {
 | 
						|
    light_color_and_time: [f32; 4],
 | 
						|
}
 | 
						|
 | 
						|
impl DemoRenderer {
 | 
						|
    fn new(device: &wgpu::Device, queue: &wgpu::Queue) -> Self {
 | 
						|
        let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
 | 
						|
            label: None,
 | 
						|
            source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(include_str!(
 | 
						|
                "shader.wgsl"
 | 
						|
            ))),
 | 
						|
        });
 | 
						|
 | 
						|
        let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
 | 
						|
            label: None,
 | 
						|
            bind_group_layouts: &[],
 | 
						|
            push_constant_ranges: &[wgpu::PushConstantRange {
 | 
						|
                stages: wgpu::ShaderStages::FRAGMENT,
 | 
						|
                range: 0..16, // full size in bytes, aligned
 | 
						|
            }],
 | 
						|
        });
 | 
						|
 | 
						|
        let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
 | 
						|
            label: None,
 | 
						|
            layout: Some(&pipeline_layout),
 | 
						|
            vertex: wgpu::VertexState {
 | 
						|
                module: &shader,
 | 
						|
                entry_point: Some("vs_main"),
 | 
						|
                buffers: &[],
 | 
						|
                compilation_options: Default::default(),
 | 
						|
            },
 | 
						|
            fragment: Some(wgpu::FragmentState {
 | 
						|
                module: &shader,
 | 
						|
                entry_point: Some("fs_main"),
 | 
						|
                compilation_options: Default::default(),
 | 
						|
                targets: &[Some(wgpu::TextureFormat::Rgba8Unorm.into())],
 | 
						|
            }),
 | 
						|
            primitive: wgpu::PrimitiveState::default(),
 | 
						|
            depth_stencil: None,
 | 
						|
            multisample: wgpu::MultisampleState::default(),
 | 
						|
            multiview: None,
 | 
						|
            cache: None,
 | 
						|
        });
 | 
						|
 | 
						|
        let displayed_texture = Self::create_texture(&device, 320, 200);
 | 
						|
        let next_texture = Self::create_texture(&device, 320, 200);
 | 
						|
 | 
						|
        Self {
 | 
						|
            device: device.clone(),
 | 
						|
            queue: queue.clone(),
 | 
						|
            pipeline,
 | 
						|
            displayed_texture,
 | 
						|
            next_texture,
 | 
						|
            start_time: std::time::Instant::now(),
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    fn create_texture(device: &wgpu::Device, width: u32, height: u32) -> wgpu::Texture {
 | 
						|
        device.create_texture(&wgpu::TextureDescriptor {
 | 
						|
            label: None,
 | 
						|
            size: wgpu::Extent3d { width, height, depth_or_array_layers: 1 },
 | 
						|
            mip_level_count: 1,
 | 
						|
            sample_count: 1,
 | 
						|
            dimension: wgpu::TextureDimension::D2,
 | 
						|
            format: wgpu::TextureFormat::Rgba8Unorm,
 | 
						|
            usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
 | 
						|
            view_formats: &[],
 | 
						|
        })
 | 
						|
    }
 | 
						|
 | 
						|
    fn render(
 | 
						|
        &mut self,
 | 
						|
        light_red: f32,
 | 
						|
        light_green: f32,
 | 
						|
        light_blue: f32,
 | 
						|
        width: u32,
 | 
						|
        height: u32,
 | 
						|
    ) -> wgpu::Texture {
 | 
						|
        if self.next_texture.size().width != width || self.next_texture.size().height != height {
 | 
						|
            let mut new_texture = Self::create_texture(&self.device, width, height);
 | 
						|
            std::mem::swap(&mut self.next_texture, &mut new_texture);
 | 
						|
        }
 | 
						|
 | 
						|
        let elapsed: f32 = self.start_time.elapsed().as_millis() as f32 / 500.;
 | 
						|
        let push_constants =
 | 
						|
            PushConstants { light_color_and_time: [light_red, light_green, light_blue, elapsed] };
 | 
						|
 | 
						|
        let mut encoder =
 | 
						|
            self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
 | 
						|
        {
 | 
						|
            let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
 | 
						|
                label: None,
 | 
						|
                color_attachments: &[Some(wgpu::RenderPassColorAttachment {
 | 
						|
                    view: &self.next_texture.create_view(&wgpu::TextureViewDescriptor::default()),
 | 
						|
                    resolve_target: None,
 | 
						|
                    ops: wgpu::Operations {
 | 
						|
                        load: wgpu::LoadOp::Clear(wgpu::Color::GREEN),
 | 
						|
                        store: wgpu::StoreOp::Store,
 | 
						|
                    },
 | 
						|
                })],
 | 
						|
                depth_stencil_attachment: None,
 | 
						|
                timestamp_writes: None,
 | 
						|
                occlusion_query_set: None,
 | 
						|
            });
 | 
						|
            rpass.set_pipeline(&self.pipeline);
 | 
						|
            rpass.set_push_constants(
 | 
						|
                wgpu::ShaderStages::FRAGMENT, // Stage (your constants are for fragment shader)
 | 
						|
                0,                            // Offset in bytes (start at 0)
 | 
						|
                bytemuck::bytes_of(&push_constants),
 | 
						|
            );
 | 
						|
            rpass.draw(0..3, 0..1);
 | 
						|
        }
 | 
						|
 | 
						|
        self.queue.submit(Some(encoder.finish()));
 | 
						|
 | 
						|
        let result_texture = self.next_texture.clone();
 | 
						|
 | 
						|
        std::mem::swap(&mut self.next_texture, &mut self.displayed_texture);
 | 
						|
 | 
						|
        result_texture
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
fn main() {
 | 
						|
    let mut wgpu_settings = WGPUSettings::default();
 | 
						|
    wgpu_settings.device_required_features = wgpu::Features::PUSH_CONSTANTS;
 | 
						|
    wgpu_settings.device_required_limits.max_push_constant_size = 16;
 | 
						|
 | 
						|
    slint::BackendSelector::new()
 | 
						|
        .require_wgpu_25(WGPUConfiguration::Automatic(wgpu_settings))
 | 
						|
        .select()
 | 
						|
        .expect("Unable to create Slint backend with WGPU based renderer");
 | 
						|
 | 
						|
    let app = App::new().unwrap();
 | 
						|
 | 
						|
    let mut underlay = None;
 | 
						|
 | 
						|
    let app_weak = app.as_weak();
 | 
						|
 | 
						|
    app.window()
 | 
						|
        .set_rendering_notifier(move |state, graphics_api| {
 | 
						|
            //eprintln!("rendering state {:#?} {:#?}", state, graphics_api);
 | 
						|
 | 
						|
            match state {
 | 
						|
                slint::RenderingState::RenderingSetup => {
 | 
						|
                    match graphics_api {
 | 
						|
                        slint::GraphicsAPI::WGPU25 { device, queue, .. } => {
 | 
						|
                            underlay = Some(DemoRenderer::new(device, queue));
 | 
						|
                        }
 | 
						|
                        _ => return,
 | 
						|
                    };
 | 
						|
                }
 | 
						|
                slint::RenderingState::BeforeRendering => {
 | 
						|
                    if let (Some(underlay), Some(app)) = (underlay.as_mut(), app_weak.upgrade()) {
 | 
						|
                        let texture = underlay.render(
 | 
						|
                            app.get_selected_red(),
 | 
						|
                            app.get_selected_green(),
 | 
						|
                            app.get_selected_blue(),
 | 
						|
                            app.get_requested_texture_width() as u32,
 | 
						|
                            app.get_requested_texture_height() as u32,
 | 
						|
                        );
 | 
						|
                        app.set_texture(slint::Image::try_from(texture).unwrap());
 | 
						|
                        app.window().request_redraw();
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                slint::RenderingState::AfterRendering => {}
 | 
						|
                slint::RenderingState::RenderingTeardown => {
 | 
						|
                    drop(underlay.take());
 | 
						|
                }
 | 
						|
                _ => {}
 | 
						|
            }
 | 
						|
        })
 | 
						|
        .expect("Unable to set rendering notifier");
 | 
						|
 | 
						|
    app.run().unwrap();
 | 
						|
}
 |