use anyhow::{Result, anyhow}; use pollster::FutureExt; use wgpu::{ Backends, BindGroup, BindGroupDescriptor, BindGroupLayoutDescriptor, BlendState, Color, ColorTargetState, ColorWrites, CommandEncoderDescriptor, Device, DeviceDescriptor, Features, FragmentState, FrontFace, Instance, InstanceDescriptor, Limits, LoadOp, MaintainBase, MultisampleState, Operations, PipelineCompilationOptions, PipelineLayoutDescriptor, PolygonMode, PowerPreference, PrimitiveState, PrimitiveTopology, Queue, RenderPassColorAttachment, RenderPassDescriptor, RenderPipeline, RenderPipelineDescriptor, RequestAdapterOptions, StoreOp, Surface, SurfaceConfiguration, TextureViewDescriptor, VertexState, include_wgsl, }; use winit::window::Window; pub struct Renderer<'a> { surface: Surface<'a>, pipeline: RenderPipeline, bind_group: BindGroup, queue: Queue, device: Device, surface_configuration: SurfaceConfiguration, } impl<'a> Renderer<'a> { pub fn new(window: &'a Window) -> Result { let instance = Instance::new(InstanceDescriptor { backends: Backends::all(), ..Default::default() }); let surface = instance.create_surface(window)?; let adapter = instance .request_adapter(&RequestAdapterOptions { compatible_surface: Some(&surface), power_preference: PowerPreference::HighPerformance, ..Default::default() }) .block_on() .ok_or(anyhow!("no adapter found"))?; let (device, queue) = adapter .request_device( &DeviceDescriptor { required_features: Features::default(), required_limits: Limits::downlevel_defaults(), ..Default::default() }, None, ) .block_on()?; let surface_configuration = surface .get_default_config(&adapter, 256, 256) .ok_or(anyhow!("no default config"))?; let module = device.create_shader_module(include_wgsl!("shader.wgsl")); let bind_group_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor { entries: &[], label: None, }); let bind_group = device.create_bind_group(&BindGroupDescriptor { label: None, layout: &bind_group_layout, entries: &[], }); let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor { label: None, bind_group_layouts: &[&bind_group_layout], push_constant_ranges: &[], }); let pipeline = device.create_render_pipeline(&RenderPipelineDescriptor { label: None, layout: Some(&pipeline_layout), fragment: Some(FragmentState { module: &module, entry_point: Some("fs_main"), targets: &[Some(ColorTargetState { blend: Some(BlendState::PREMULTIPLIED_ALPHA_BLENDING), format: surface_configuration.format, write_mask: ColorWrites::all(), })], compilation_options: PipelineCompilationOptions::default(), }), vertex: VertexState { module: &module, entry_point: Some("vs_main"), buffers: &[], compilation_options: PipelineCompilationOptions::default(), }, primitive: PrimitiveState { topology: PrimitiveTopology::TriangleList, front_face: FrontFace::Ccw, cull_mode: None, //Some(Face::Back), polygon_mode: PolygonMode::Fill, ..Default::default() }, depth_stencil: Default::default(), multisample: MultisampleState::default(), multiview: None, cache: None, }); surface.configure(&device, &surface_configuration); Ok(Self { surface, pipeline, bind_group, device, queue, surface_configuration, }) } pub fn resize(&mut self, width: u32, height: u32) { self.surface_configuration.width = width; self.surface_configuration.height = height; self.surface .configure(&self.device, &self.surface_configuration); } pub fn draw(&mut self) -> Result<()> { let target = self.surface.get_current_texture()?; let target_view = target .texture .create_view(&TextureViewDescriptor::default()); let mut commands = self .device .create_command_encoder(&CommandEncoderDescriptor { label: None }); { let mut rpass = commands.begin_render_pass(&RenderPassDescriptor { label: None, color_attachments: &[Some(RenderPassColorAttachment { view: &target_view, resolve_target: None, ops: Operations { store: StoreOp::Store, load: LoadOp::Clear(Color::BLUE), }, })], ..Default::default() }); rpass.set_bind_group(0, &self.bind_group, &[]); rpass.set_pipeline(&self.pipeline); rpass.draw(0..3, 0..1); } let i = self.queue.submit(Some(commands.finish())); self.device.poll(MaintainBase::WaitForSubmissionIndex(i)); target.present(); Ok(()) } }