| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429 |
- use std::iter;
- use rand::Rng;
- use wgpu::util::DeviceExt;
- use winit::{
- event::*,
- event_loop::{ControlFlow, EventLoop},
- window::{Window, WindowBuilder},
- };
- use sapvi::gui::texture;
- #[repr(C)]
- #[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
- struct Vertex {
- position: [f32; 3],
- tex_coords: [f32; 2],
- }
- impl Vertex {
- fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
- use std::mem;
- wgpu::VertexBufferLayout {
- array_stride: mem::size_of::<Vertex>() as wgpu::BufferAddress,
- step_mode: wgpu::InputStepMode::Vertex,
- attributes: &[
- wgpu::VertexAttribute {
- offset: 0,
- shader_location: 0,
- format: wgpu::VertexFormat::Float3,
- },
- wgpu::VertexAttribute {
- offset: mem::size_of::<[f32; 3]>() as wgpu::BufferAddress,
- shader_location: 1,
- format: wgpu::VertexFormat::Float2,
- },
- ],
- }
- }
- }
- // (-1, 1) (1, 1)
- // +-----------------------+
- // | |
- // | |
- // | |
- // | |
- // | |
- // | |
- // | |
- // +-----------------------+
- // (-1, -1) (1, -1)
- const VERTICES: &[Vertex] = &[
- Vertex {
- position: [-1.0, 1.0, 0.0],
- tex_coords: [0.0, 0.0],
- },
- Vertex {
- position: [-1.0, -1.0, 0.0],
- tex_coords: [0.0, 1.0],
- },
- Vertex {
- position: [1.0, -1.0, 0.0],
- tex_coords: [1.0, 1.0],
- },
- Vertex {
- position: [1.0, 1.0, 0.0],
- tex_coords: [1.0, 0.0],
- },
- /*
- Vertex {
- position: [-0.0868241, 0.49240386, 0.0],
- tex_coords: [0.4131759, 0.00759614],
- }, // A
- Vertex {
- position: [-0.49513406, 0.06958647, 0.0],
- tex_coords: [0.0048659444, 0.43041354],
- }, // B
- Vertex {
- position: [-0.21918549, -0.44939706, 0.0],
- tex_coords: [0.28081453, 0.949397057],
- }, // C
- Vertex {
- position: [0.35966998, -0.3473291, 0.0],
- tex_coords: [0.85967, 0.84732911],
- }, // D
- Vertex {
- position: [0.44147372, 0.2347359, 0.0],
- tex_coords: [0.9414737, 0.2652641],
- }, // E
- */
- ];
- //const INDICES: &[u16] = &[0, 1, 4, 1, 2, 4, 2, 3, 4];
- const INDICES: &[u16] = &[0, 1, 2, 0, 2, 3];
- struct State {
- surface: wgpu::Surface,
- device: wgpu::Device,
- queue: wgpu::Queue,
- sc_desc: wgpu::SwapChainDescriptor,
- swap_chain: wgpu::SwapChain,
- size: winit::dpi::PhysicalSize<u32>,
- render_pipeline: wgpu::RenderPipeline,
- vertex_buffer: wgpu::Buffer,
- index_buffer: wgpu::Buffer,
- num_indices: u32,
- #[allow(dead_code)]
- diffuse_texture: texture::Texture,
- diffuse_bind_group: wgpu::BindGroup,
- #[allow(dead_code)]
- cartoon_texture: texture::Texture,
- cartoon_bind_group: wgpu::BindGroup,
- is_space_pressed: bool,
- }
- impl State {
- async fn new(window: &Window) -> Self {
- let size = window.inner_size();
- // The instance is a handle to our GPU
- // BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
- let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY);
- let surface = unsafe { instance.create_surface(window) };
- let adapter = instance
- .request_adapter(&wgpu::RequestAdapterOptions {
- power_preference: wgpu::PowerPreference::default(),
- compatible_surface: Some(&surface),
- })
- .await
- .unwrap();
- let (device, queue) = adapter
- .request_device(
- &wgpu::DeviceDescriptor {
- label: None,
- features: wgpu::Features::empty(),
- limits: wgpu::Limits::default(),
- },
- None, // Trace path
- )
- .await
- .unwrap();
- let sc_desc = wgpu::SwapChainDescriptor {
- usage: wgpu::TextureUsage::RENDER_ATTACHMENT,
- format: adapter.get_swap_chain_preferred_format(&surface),
- width: size.width,
- height: size.height,
- present_mode: wgpu::PresentMode::Fifo,
- };
- let swap_chain = device.create_swap_chain(&surface, &sc_desc);
- let texture_bind_group_layout =
- device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
- entries: &[
- wgpu::BindGroupLayoutEntry {
- binding: 0,
- visibility: wgpu::ShaderStage::FRAGMENT,
- ty: wgpu::BindingType::Texture {
- multisampled: false,
- view_dimension: wgpu::TextureViewDimension::D2,
- sample_type: wgpu::TextureSampleType::Float { filterable: false },
- },
- count: None,
- },
- wgpu::BindGroupLayoutEntry {
- binding: 1,
- visibility: wgpu::ShaderStage::FRAGMENT,
- ty: wgpu::BindingType::Sampler {
- comparison: false,
- filtering: true,
- },
- count: None,
- },
- ],
- label: Some("texture_bind_group_layout"),
- });
- let diffuse_bytes = include_bytes!("assets/absolutely-proprietary.png");
- let diffuse_texture =
- texture::Texture::from_bytes(&device, &queue, diffuse_bytes, "assets/absolutely-proprietary.png").unwrap();
- let diffuse_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
- layout: &texture_bind_group_layout,
- entries: &[
- wgpu::BindGroupEntry {
- binding: 0,
- resource: wgpu::BindingResource::TextureView(&diffuse_texture.view),
- },
- wgpu::BindGroupEntry {
- binding: 1,
- resource: wgpu::BindingResource::Sampler(&diffuse_texture.sampler),
- },
- ],
- label: Some("diffuse_bind_group"),
- });
- let cartoon_bytes = include_bytes!("assets/absolutely-proprietary2.png");
- let cartoon_texture =
- texture::Texture::from_bytes(&device, &queue, cartoon_bytes, "happy-tree-cartoon.png")
- .unwrap();
- let cartoon_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
- layout: &texture_bind_group_layout,
- entries: &[
- wgpu::BindGroupEntry {
- binding: 0,
- resource: wgpu::BindingResource::TextureView(&cartoon_texture.view),
- },
- wgpu::BindGroupEntry {
- binding: 1,
- resource: wgpu::BindingResource::Sampler(&cartoon_texture.sampler),
- },
- ],
- label: Some("cartoon_bind_group"),
- });
- let vs_module = device.create_shader_module(&wgpu::include_spirv!("assets/shader.vert.spv"));
- let fs_module = device.create_shader_module(&wgpu::include_spirv!("assets/shader.frag.spv"));
- let render_pipeline_layout =
- device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
- label: Some("Render Pipeline Layout"),
- bind_group_layouts: &[&texture_bind_group_layout],
- push_constant_ranges: &[],
- });
- let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
- label: Some("Render Pipeline"),
- layout: Some(&render_pipeline_layout),
- vertex: wgpu::VertexState {
- module: &vs_module,
- entry_point: "main",
- buffers: &[Vertex::desc()],
- },
- fragment: Some(wgpu::FragmentState {
- module: &fs_module,
- entry_point: "main",
- targets: &[wgpu::ColorTargetState {
- format: sc_desc.format,
- alpha_blend: wgpu::BlendState::REPLACE,
- color_blend: wgpu::BlendState::REPLACE,
- write_mask: wgpu::ColorWrite::ALL,
- }],
- }),
- primitive: wgpu::PrimitiveState {
- topology: wgpu::PrimitiveTopology::TriangleList,
- strip_index_format: None,
- front_face: wgpu::FrontFace::Ccw,
- cull_mode: wgpu::CullMode::Back,
- // Setting this to anything other than Fill requires Features::NON_FILL_POLYGON_MODE
- polygon_mode: wgpu::PolygonMode::Fill,
- },
- depth_stencil: None,
- multisample: wgpu::MultisampleState {
- count: 1,
- mask: !0,
- alpha_to_coverage_enabled: false,
- },
- });
- let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
- label: Some("Vertex Buffer"),
- contents: bytemuck::cast_slice(VERTICES),
- usage: wgpu::BufferUsage::VERTEX,
- });
- let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
- label: Some("Index Buffer"),
- contents: bytemuck::cast_slice(INDICES),
- usage: wgpu::BufferUsage::INDEX,
- });
- let num_indices = INDICES.len() as u32;
- Self {
- surface,
- device,
- queue,
- sc_desc,
- swap_chain,
- render_pipeline,
- vertex_buffer,
- index_buffer,
- num_indices,
- diffuse_texture,
- diffuse_bind_group,
- cartoon_texture,
- cartoon_bind_group,
- size,
- is_space_pressed: false,
- }
- }
- fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
- self.size = new_size;
- self.sc_desc.width = new_size.width;
- self.sc_desc.height = new_size.height;
- self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc);
- }
- fn input(&mut self, event: &WindowEvent) -> bool {
- match event {
- WindowEvent::KeyboardInput {
- input:
- KeyboardInput {
- state,
- virtual_keycode: Some(VirtualKeyCode::Space),
- ..
- },
- ..
- } => {
- self.is_space_pressed = *state == ElementState::Pressed;
- true
- }
- _ => false,
- }
- }
- fn update(&mut self) {
- if rand::thread_rng().gen_range(0, 10) == 0 {
- self.is_space_pressed = !self.is_space_pressed;
- }
- }
- fn render(&mut self) -> Result<(), wgpu::SwapChainError> {
- let frame = self.swap_chain.get_current_frame()?.output;
- let mut encoder = self
- .device
- .create_command_encoder(&wgpu::CommandEncoderDescriptor {
- label: Some("Render Encoder"),
- });
- {
- let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
- label: Some("Render Pass"),
- color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
- attachment: &frame.view,
- resolve_target: None,
- ops: wgpu::Operations {
- load: wgpu::LoadOp::Clear(wgpu::Color {
- r: 0.1,
- g: 0.2,
- b: 0.3,
- a: 1.0,
- }),
- store: true,
- },
- }],
- depth_stencil_attachment: None,
- });
- let bind_group = if self.is_space_pressed {
- &self.cartoon_bind_group
- } else {
- &self.diffuse_bind_group
- };
- render_pass.set_pipeline(&self.render_pipeline);
- render_pass.set_bind_group(0, bind_group, &[]);
- render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
- render_pass.set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint16);
- render_pass.draw_indexed(0..self.num_indices, 0, 0..1);
- }
- self.queue.submit(iter::once(encoder.finish()));
- Ok(())
- }
- }
- fn main() {
- env_logger::init();
- let event_loop = EventLoop::new();
- let window = WindowBuilder::new().build(&event_loop).unwrap();
- use futures::executor::block_on;
- // Since main can't be async, we're going to need to block
- let mut state = block_on(State::new(&window));
- event_loop.run(move |event, _, control_flow| {
- match event {
- Event::WindowEvent {
- ref event,
- window_id,
- } if window_id == window.id() => {
- if !state.input(event) {
- match event {
- WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
- WindowEvent::KeyboardInput { input, .. } => match input {
- KeyboardInput {
- state: ElementState::Pressed,
- virtual_keycode: Some(VirtualKeyCode::Escape),
- ..
- } => *control_flow = ControlFlow::Exit,
- _ => {}
- },
- WindowEvent::Resized(physical_size) => {
- state.resize(*physical_size);
- }
- WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
- // new_inner_size is &mut so w have to dereference it twice
- state.resize(**new_inner_size);
- }
- _ => {}
- }
- }
- }
- Event::RedrawRequested(_) => {
- state.update();
- match state.render() {
- Ok(_) => {}
- // Recreate the swap_chain if lost
- Err(wgpu::SwapChainError::Lost) => state.resize(state.size),
- // The system is out of memory, we should probably quit
- Err(wgpu::SwapChainError::OutOfMemory) => *control_flow = ControlFlow::Exit,
- // All other errors (Outdated, Timeout) should be resolved by the next frame
- Err(e) => eprintln!("{:?}", e),
- }
- }
- Event::MainEventsCleared => {
- // RedrawRequested will only trigger once, unless we manually
- // request it.
- window.request_redraw();
- }
- _ => {}
- }
- });
- }
|