bevy/src/application.rs
2019-12-09 23:12:50 -08:00

278 lines
No EOL
9.9 KiB
Rust

use winit::{
event,
event::WindowEvent,
event_loop::{ControlFlow, EventLoop},
window::Window,
};
use zerocopy::AsBytes;
use legion::prelude::*;
use std::sync::Arc;
use std::rc::Rc;
use std::mem;
use wgpu::{Surface, Device, Queue, SwapChain, SwapChainDescriptor};
use crate::{vertex::*, render::*, LocalToWorld, ApplicationStage, Time};
pub struct Application
{
pub universe: Universe,
pub world: World,
pub device: Device,
pub queue: Queue,
pub surface: Surface,
pub window: Window,
pub swap_chain: SwapChain,
pub swap_chain_descriptor: SwapChainDescriptor,
pub scheduler: SystemScheduler<ApplicationStage>,
pub render_passes: Vec<Box<dyn Pass>>,
}
impl Application {
pub const MAX_LIGHTS: usize = 10;
fn add_default_passes(&mut self) {
let light_uniform_size =
(Self::MAX_LIGHTS * mem::size_of::<LightRaw>()) as wgpu::BufferAddress;
let local_bind_group_layout =
Rc::new(self.device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
bindings: &[wgpu::BindGroupLayoutBinding {
binding: 0,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::UniformBuffer { dynamic: false },
}],
}));
let light_uniform_buffer = Arc::new(UniformBuffer {
buffer: self.device.create_buffer(&wgpu::BufferDescriptor {
size: light_uniform_size,
usage: wgpu::BufferUsage::UNIFORM
| wgpu::BufferUsage::COPY_SRC
| wgpu::BufferUsage::COPY_DST,
}),
size: light_uniform_size,
});
let vertex_size = mem::size_of::<Vertex>();
let vb_desc = wgpu::VertexBufferDescriptor {
stride: vertex_size as wgpu::BufferAddress,
step_mode: wgpu::InputStepMode::Vertex,
attributes: &[
wgpu::VertexAttributeDescriptor {
format: wgpu::VertexFormat::Float4,
offset: 0,
shader_location: 0,
},
wgpu::VertexAttributeDescriptor {
format: wgpu::VertexFormat::Float4,
offset: 4 * 4,
shader_location: 1,
},
],
};
let shadow_pass = ShadowPass::new(&mut self.device, &mut self.world, light_uniform_buffer.clone(), vb_desc.clone(), local_bind_group_layout.clone(), Self::MAX_LIGHTS as u32);
let forward_pass = ForwardPass::new(&mut self.device, &self.world, light_uniform_buffer.clone(), &shadow_pass, vb_desc, &local_bind_group_layout, &self.swap_chain_descriptor);
self.render_passes.push(Box::new(shadow_pass));
self.render_passes.push(Box::new(forward_pass));
}
fn update(&mut self) {
{
let mut time = self.world.resources.get_mut::<Time>().unwrap();
time.start();
}
self.scheduler.execute(&mut self.world);
self.render();
{
let mut time = self.world.resources.get_mut::<Time>().unwrap();
time.stop();
}
}
fn resize(&mut self, width: u32, height: u32)
{
self.swap_chain_descriptor.width = width;
self.swap_chain_descriptor.height = height;
self.swap_chain = self.device.create_swap_chain(&self.surface, &self.swap_chain_descriptor);
let mut encoder =
self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { todo: 0 });
for (mut camera, local_to_world) in <(Write<Camera>, Read<LocalToWorld>)>::query().iter(&mut self.world) {
camera.update(self.swap_chain_descriptor.width, self.swap_chain_descriptor.height);
let camera_matrix: [[f32; 4]; 4] = (camera.view_matrix * local_to_world.0).to_cols_array_2d();
let matrix_size = mem::size_of::<[[f32; 4]; 4]>() as u64;
let temp_camera_buffer =
self.device.create_buffer_with_data(camera_matrix.as_bytes(), wgpu::BufferUsage::COPY_SRC);
for pass in self.render_passes.iter() {
if let Some(buffer) = pass.get_camera_uniform_buffer() {
encoder.copy_buffer_to_buffer(&temp_camera_buffer, 0, buffer, 0, matrix_size);
}
}
}
let command_buffer = encoder.finish();
for pass in self.render_passes.iter_mut() {
pass.resize(&mut self.device, &mut self.swap_chain_descriptor);
}
self.queue.submit(&[command_buffer]);
}
fn handle_event(&mut self, _: WindowEvent)
{
}
fn render(&mut self)
{
let mut frame = self.swap_chain
.get_next_texture()
.expect("Timeout when acquiring next swap chain texture");
let mut encoder =
self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { todo: 0 });
let mut entities = <(Write<Material>, Read<LocalToWorld>)>::query();
let entities_count = entities.iter(&mut self.world).count();
let size = mem::size_of::<MaterialUniforms>();
let temp_buf_data = self.device
.create_buffer_mapped(entities_count * size, wgpu::BufferUsage::COPY_SRC);
for ((material, transform), slot) in entities.iter(&mut self.world)
.zip(temp_buf_data.data.chunks_exact_mut(size))
{
slot.copy_from_slice(
MaterialUniforms {
model: transform.0.to_cols_array_2d(),
color: material.color.into(),
}
.as_bytes(),
);
}
let temp_buf = temp_buf_data.finish();
for pass in self.render_passes.iter_mut() {
pass.render(&mut self.device, &mut frame, &mut encoder, &mut self.world);
}
// TODO: this should happen before rendering
for (i, (entity, _)) in entities.iter(&mut self.world).enumerate() {
encoder.copy_buffer_to_buffer(
&temp_buf,
(i * size) as wgpu::BufferAddress,
entity.uniform_buf.as_ref().unwrap(),
0,
size as wgpu::BufferAddress,
);
}
let command_buffer = encoder.finish();
self.queue.submit(&[command_buffer]);
}
#[allow(dead_code)]
pub fn run(universe: Universe, mut world: World, system_scheduler: SystemScheduler<ApplicationStage>) {
env_logger::init();
let event_loop = EventLoop::new();
log::info!("Initializing the window...");
let adapter = wgpu::Adapter::request(
&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::Default,
},
wgpu::BackendBit::PRIMARY,
)
.unwrap();
let (device, queue) = adapter.request_device(&wgpu::DeviceDescriptor {
extensions: wgpu::Extensions {
anisotropic_filtering: false,
},
limits: wgpu::Limits::default(),
});
let (window, hidpi_factor, size, surface) = {
let window = winit::window::Window::new(&event_loop).unwrap();
window.set_title("bevy");
window.set_inner_size((1280, 720).into());
let hidpi_factor = window.hidpi_factor();
let size = window.inner_size().to_physical(hidpi_factor);
let surface = wgpu::Surface::create(&window);
(window, hidpi_factor, size, surface)
};
let swap_chain_descriptor = wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
format: wgpu::TextureFormat::Bgra8UnormSrgb,
width: size.width.round() as u32,
height: size.height.round() as u32,
present_mode: wgpu::PresentMode::Vsync,
};
let swap_chain = device.create_swap_chain(&surface, &swap_chain_descriptor);
world.resources.insert(Time::new());
log::info!("Initializing the example...");
let mut app = Application {
universe,
world,
device,
surface,
window,
queue,
swap_chain,
swap_chain_descriptor,
scheduler: system_scheduler,
render_passes: Vec::new(),
};
app.add_default_passes();
log::info!("Entering render loop...");
event_loop.run(move |event, _, control_flow| {
*control_flow = if cfg!(feature = "metal-auto-capture") {
ControlFlow::Exit
} else {
ControlFlow::Poll
};
match event {
event::Event::WindowEvent {
event: WindowEvent::Resized(size),
..
} => {
let physical = size.to_physical(hidpi_factor);
log::info!("Resizing to {:?}", physical);
let width = physical.width.round() as u32;
let height = physical.height.round() as u32;
app.resize(width, height);
}
event::Event::WindowEvent { event, .. } => match event {
WindowEvent::KeyboardInput {
input:
event::KeyboardInput {
virtual_keycode: Some(event::VirtualKeyCode::Escape),
state: event::ElementState::Pressed,
..
},
..
}
| WindowEvent::CloseRequested => {
*control_flow = ControlFlow::Exit;
}
_ => {
app.handle_event(event);
}
},
event::Event::EventsCleared => {
app.update();
}
_ => (),
}
});
}
}