rework surface and swap chain creation to support arbitrary number of surfaces/windows

This commit is contained in:
Carter Anderson 2020-03-30 19:21:12 -07:00
parent 70b4100132
commit 17d4bec08c
13 changed files with 267 additions and 245 deletions

View file

@ -1,4 +1,4 @@
use super::{CreateWindow, Time, WindowCreated, WindowResize, Windows, Event, WindowDescriptor};
use super::{CreateWindow, Time, WindowCreated, WindowResized, Windows, Event, WindowDescriptor};
use crate::app::{plugin::AppPlugin, AppBuilder};
use bevy_transform::transform_system_bundle;
@ -20,7 +20,7 @@ impl AppPlugin for CorePlugin {
app = app.add_system(transform_system);
}
app = app.add_event::<WindowResize>()
app = app.add_event::<WindowResized>()
.add_event::<CreateWindow>()
.add_event::<WindowCreated>()
.add_resource(Windows::default())

View file

@ -70,7 +70,7 @@ where
self.event_count += 1;
}
pub fn iter(&self, event_handle: &mut EventHandle<T>) -> impl Iterator<Item = &T> {
pub fn iter(&self, event_handle: &mut EventHandle<T>) -> impl DoubleEndedIterator<Item = &T> {
let a_index = self.a_start_event_count - event_handle.last_event_count;
let b_index = self.b_start_event_count - event_handle.last_event_count;
event_handle.last_event_count = self.event_count;

View file

@ -1,10 +1,11 @@
use super::{WindowDescriptor, WindowId};
#[derive(Debug, Clone)]
pub struct WindowResize {
pub struct WindowResized {
pub id: WindowId,
pub width: u32,
pub height: u32,
pub is_primary: bool,
}
#[derive(Debug, Clone)]

View file

@ -3,7 +3,7 @@ pub use winit_windows::*;
use crate::prelude::*;
use super::{CreateWindow, Window, WindowCreated, WindowResize, Windows};
use super::{CreateWindow, Window, WindowCreated, WindowResized, Windows};
use winit::{
event,
event::WindowEvent,
@ -15,8 +15,7 @@ pub struct WinitPlugin;
impl AppPlugin for WinitPlugin {
fn build(&self, app: AppBuilder) -> AppBuilder {
app
.add_resource(WinitWindows::default())
app.add_resource(WinitWindows::default())
.set_runner(winit_runner)
}
@ -52,15 +51,20 @@ pub fn winit_runner(mut app: App) {
let winit_windows = app.resources.get_mut::<WinitWindows>().unwrap();
let mut windows = app.resources.get_mut::<Windows>().unwrap();
let window_id = winit_windows.get_window_id(winit_window_id).unwrap();
let is_primary = windows
.get_primary()
.map(|primary_window| primary_window.id == window_id)
.unwrap_or(false);
let mut window = windows.get_mut(window_id).unwrap();
window.width = size.width;
window.height = size.height;
let mut resize_event = app.resources.get_mut::<Event<WindowResize>>().unwrap();
resize_event.send(WindowResize {
let mut resize_event = app.resources.get_mut::<Event<WindowResized>>().unwrap();
resize_event.send(WindowResized {
id: window_id,
height: window.height,
width: window.width,
is_primary,
});
}
event::Event::WindowEvent { event, .. } => match event {
@ -102,22 +106,8 @@ fn handle_create_window_events(
let mut window_created_events = resources.get_mut::<Event<WindowCreated>>().unwrap();
for create_window_event in create_window_events.iter(create_window_event_handle) {
let window = Window::new(&create_window_event.descriptor);
create_window(
&event_loop,
&mut window_created_events,
&mut winit_windows,
&window,
);
winit_windows.create_window(event_loop, &window);
window_created_events.send(WindowCreated { id: window.id });
windows.add(window);
}
}
pub fn create_window(
event_loop: &EventLoopWindowTarget<()>,
window_created_events: &mut Event<WindowCreated>,
winit_windows: &mut WinitWindows,
window: &Window,
) {
winit_windows.create_window(event_loop, &window);
window_created_events.send(WindowCreated { id: window.id });
}

View file

@ -14,14 +14,7 @@ use super::{
AssetBatchers, EntityRenderResourceAssignments, RenderResourceAssignments,
},
};
use crate::{
app::{plugin::AppPlugin, AppBuilder},
asset::AssetStorage,
prelude::{
LocalToWorld, Mesh, PipelineDescriptor, Shader, StandardMaterial, Texture,
UniformResourceProvider,
},
};
use crate::{core::WindowResized, prelude::*};
#[derive(Default)]
pub struct RenderPlugin;
@ -40,8 +33,12 @@ impl RenderPlugin {
.add_draw_target(AssignedBatchesDrawTarget::default())
.add_draw_target(AssignedMeshesDrawTarget::default())
.add_draw_target(UiDrawTarget::default())
.add_resource_provider(CameraResourceProvider::default())
.add_resource_provider(Camera2dResourceProvider::default())
.add_resource_provider(CameraResourceProvider::new(
app.resources.get_event_handle::<WindowResized>(),
))
.add_resource_provider(Camera2dResourceProvider::new(
app.resources.get_event_handle::<WindowResized>(),
))
.add_resource_provider(LightResourceProvider::new(10))
.add_resource_provider(UiResourceProvider::new())
.add_resource_provider(MeshResourceProvider::new())

View file

@ -18,13 +18,4 @@ pub trait ResourceProvider {
_resources: &Resources,
) {
}
fn resize(
&mut self,
_renderer: &mut dyn Renderer,
_world: &mut World,
_resources: &Resources,
_width: u32,
_height: u32,
) {
}
}

View file

@ -1,18 +1,29 @@
use crate::render::{
use crate::{
core::WindowResized,
prelude::*,
render::{
render_resource::{
resource_name, BufferInfo, BufferUsage, RenderResource, RenderResourceAssignments,
ResourceProvider,
BufferInfo, BufferUsage, RenderResource, RenderResourceAssignments, ResourceProvider,
},
renderer::Renderer,
ActiveCamera2d, Camera,
},
};
use legion::prelude::*;
use zerocopy::AsBytes;
#[derive(Default)]
pub struct Camera2dResourceProvider {
pub camera_buffer: Option<RenderResource>,
pub tmp_buffer: Option<RenderResource>,
pub window_resized_event_handle: EventHandle<WindowResized>,
}
impl Camera2dResourceProvider {
pub fn new(window_resized_event_handle: EventHandle<WindowResized>) -> Self {
Camera2dResourceProvider {
camera_buffer: None,
tmp_buffer: None,
window_resized_event_handle,
}
}
}
impl ResourceProvider for Camera2dResourceProvider {
@ -34,17 +45,22 @@ impl ResourceProvider for Camera2dResourceProvider {
self.camera_buffer = Some(buffer);
}
fn resize(
&mut self,
renderer: &mut dyn Renderer,
world: &mut World,
_resources: &Resources,
width: u32,
height: u32,
) {
fn update(&mut self, renderer: &mut dyn Renderer, world: &mut World, resources: &Resources) {
let window_resized_events = resources.get::<Event<WindowResized>>().unwrap();
let primary_window_resized_event = window_resized_events
.iter(&mut self.window_resized_event_handle)
.rev()
.filter(|event| event.is_primary)
.next();
if let Some(primary_window_resized_event) = primary_window_resized_event {
let matrix_size = std::mem::size_of::<[[f32; 4]; 4]>();
for (mut camera, _) in <(Write<Camera>, Read<ActiveCamera2d>)>::query().iter_mut(world) {
camera.update(width, height);
for (mut camera, _) in <(Write<Camera>, Read<ActiveCamera2d>)>::query().iter_mut(world)
{
camera.update(
primary_window_resized_event.width,
primary_window_resized_event.height,
);
let camera_matrix: [[f32; 4]; 4] = camera.view_matrix.to_cols_array_2d();
if let Some(old_tmp_buffer) = self.tmp_buffer {
@ -71,4 +87,5 @@ impl ResourceProvider for Camera2dResourceProvider {
);
}
}
}
}

View file

@ -1,19 +1,31 @@
use crate::render::{
use crate::{
core::WindowResized,
prelude::*,
render::{
render_resource::{
resource_name, BufferInfo, BufferUsage, RenderResource, RenderResourceAssignments,
ResourceProvider,
},
renderer::Renderer,
ActiveCamera, Camera,
},
};
use bevy_transform::prelude::LocalToWorld;
use legion::prelude::*;
use zerocopy::AsBytes;
#[derive(Default)]
pub struct CameraResourceProvider {
pub camera_buffer: Option<RenderResource>,
pub tmp_buffer: Option<RenderResource>,
pub window_resized_event_handle: EventHandle<WindowResized>,
}
impl CameraResourceProvider {
pub fn new(window_resized_event_handle: EventHandle<WindowResized>) -> Self {
CameraResourceProvider {
camera_buffer: None,
tmp_buffer: None,
window_resized_event_handle,
}
}
}
impl ResourceProvider for CameraResourceProvider {
@ -35,19 +47,22 @@ impl ResourceProvider for CameraResourceProvider {
self.camera_buffer = Some(buffer);
}
fn resize(
&mut self,
renderer: &mut dyn Renderer,
world: &mut World,
_resources: &Resources,
width: u32,
height: u32,
) {
fn update(&mut self, renderer: &mut dyn Renderer, world: &mut World, resources: &Resources) {
let window_resized_events = resources.get::<Event<WindowResized>>().unwrap();
let primary_window_resized_event = window_resized_events
.iter(&mut self.window_resized_event_handle)
.rev()
.filter(|event| event.is_primary)
.next();
if let Some(primary_window_resized_event) = primary_window_resized_event {
let matrix_size = std::mem::size_of::<[[f32; 4]; 4]>();
for (mut camera, local_to_world, _) in
<(Write<Camera>, Read<LocalToWorld>, Read<ActiveCamera>)>::query().iter_mut(world)
{
camera.update(width, height);
camera.update(
primary_window_resized_event.width,
primary_window_resized_event.height,
);
let camera_matrix: [[f32; 4]; 4] =
(camera.view_matrix * local_to_world.0).to_cols_array_2d();
@ -75,4 +90,5 @@ impl ResourceProvider for CameraResourceProvider {
);
}
}
}
}

View file

@ -12,6 +12,8 @@ use legion::prelude::Resources;
pub struct FrameTextureResourceProvider {
pub name: String,
pub descriptor: TextureDescriptor,
pub width: u32,
pub height: u32,
}
impl FrameTextureResourceProvider {
@ -19,12 +21,18 @@ impl FrameTextureResourceProvider {
FrameTextureResourceProvider {
name: name.to_string(),
descriptor,
width: 0,
height: 0,
}
}
pub fn update(&mut self, renderer: &mut dyn Renderer, _world: &World, resources: &Resources) {
pub fn update(&mut self, renderer: &mut dyn Renderer, resources: &Resources) {
let windows = resources.get::<Windows>().unwrap();
let window = windows.get_primary().unwrap();
if self.descriptor.size.width != window.width
|| self.descriptor.size.height != window.height
{
self.descriptor.size.width = window.width;
self.descriptor.size.height = window.height;
@ -37,26 +45,11 @@ impl FrameTextureResourceProvider {
let texture_resource = renderer.create_texture(&self.descriptor, None);
render_resource_assignments.set(&self.name, texture_resource);
}
}
}
impl ResourceProvider for FrameTextureResourceProvider {
fn initialize(
&mut self,
renderer: &mut dyn Renderer,
world: &mut World,
resources: &Resources,
) {
self.update(renderer, world, resources);
}
fn resize(
&mut self,
renderer: &mut dyn Renderer,
world: &mut World,
resources: &Resources,
_width: u32,
_height: u32,
) {
self.update(renderer, world, resources);
fn update(&mut self, renderer: &mut dyn Renderer, _world: &mut World, resources: &Resources) {
self.update(renderer, resources)
}
}

View file

@ -11,7 +11,6 @@ use crate::{
use std::ops::Range;
pub trait Renderer {
fn resize(&mut self, world: &mut World, resources: &mut Resources);
fn update(&mut self, world: &mut World, resources: &mut Resources);
fn create_buffer_with_data(&mut self, buffer_info: BufferInfo, data: &[u8]) -> RenderResource;
fn create_sampler(&mut self, sampler_descriptor: &SamplerDescriptor) -> RenderResource;

View file

@ -8,8 +8,8 @@ pub use wgpu_renderer::*;
pub use wgpu_resources::*;
use crate::{
app::{plugin::AppPlugin, AppBuilder, system_stage},
core::{Event, WindowResize},
app::{plugin::AppPlugin, system_stage, AppBuilder},
core::{Event, WindowCreated, WindowResized},
render::renderer::Renderer,
};
@ -29,8 +29,12 @@ impl AppPlugin for WgpuRendererPlugin {
}
pub fn wgpu_render_system(resources: &Resources) -> impl FnMut(&mut World, &mut Resources) {
let window_resize_event = resources.get::<Event<WindowResize>>().unwrap();
let mut wgpu_renderer = futures::executor::block_on(WgpuRenderer::new(window_resize_event.get_handle()));
let window_resized_event = resources.get::<Event<WindowResized>>().unwrap();
let window_created_event = resources.get::<Event<WindowCreated>>().unwrap();
let mut wgpu_renderer = futures::executor::block_on(WgpuRenderer::new(
window_resized_event.get_handle(),
window_created_event.get_handle(),
));
move |world, resources| {
wgpu_renderer.update(world, resources);
}

View file

@ -1,7 +1,7 @@
use super::{wgpu_type_converter::OwnedWgpuVertexBufferDescriptor, WgpuRenderPass, WgpuResources};
use crate::{
asset::{AssetStorage, Handle},
core::{Event, EventHandle, WindowResize, winit::WinitWindows, Windows},
core::{winit::WinitWindows, Event, EventHandle, WindowCreated, WindowResized, Windows, Window},
legion::prelude::*,
render::{
pass::{
@ -19,21 +19,29 @@ use crate::{
texture::{SamplerDescriptor, TextureDescriptor},
},
};
use std::{cell::RefCell, collections::HashMap, ops::Deref, rc::Rc};
use std::{
cell::RefCell,
collections::{HashMap, HashSet},
ops::Deref,
rc::Rc,
};
pub struct WgpuRenderer {
pub device: Rc<RefCell<wgpu::Device>>,
pub queue: wgpu::Queue,
pub surface: Option<wgpu::Surface>,
pub encoder: Option<wgpu::CommandEncoder>,
pub render_pipelines: HashMap<Handle<PipelineDescriptor>, wgpu::RenderPipeline>,
pub wgpu_resources: WgpuResources,
pub window_resize_handle: EventHandle<WindowResize>,
pub window_resized_event_handle: EventHandle<WindowResized>,
pub window_created_event_handle: EventHandle<WindowCreated>,
pub intialized: bool,
}
impl WgpuRenderer {
pub async fn new(window_resize_event: EventHandle<WindowResize>) -> Self {
pub async fn new(
window_resized_event_handle: EventHandle<WindowResized>,
window_created_event_handle: EventHandle<WindowCreated>,
) -> Self {
let adapter = wgpu::Adapter::request(
&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::Default,
@ -43,19 +51,21 @@ impl WgpuRenderer {
.await
.unwrap();
let (device, queue) = adapter.request_device(&wgpu::DeviceDescriptor {
let (device, queue) = adapter
.request_device(&wgpu::DeviceDescriptor {
extensions: wgpu::Extensions {
anisotropic_filtering: false,
},
limits: wgpu::Limits::default(),
}).await;
})
.await;
WgpuRenderer {
device: Rc::new(RefCell::new(device)),
queue,
surface: None,
encoder: None,
window_resize_handle: window_resize_event,
window_resized_event_handle: window_resized_event_handle,
window_created_event_handle,
intialized: false,
wgpu_resources: WgpuResources::default(),
render_pipelines: HashMap::new(),
@ -67,10 +77,7 @@ impl WgpuRenderer {
return;
}
self.create_surface(resources);
self.initialize_resource_providers(world, resources);
self.resize(world, resources);
self.intialized = true;
}
@ -342,75 +349,76 @@ impl WgpuRenderer {
}
}
pub fn create_surface(&mut self, resources: &Resources) {
pub fn handle_window_resized_events(&mut self, resources: &mut Resources) {
let windows = resources.get::<Windows>().unwrap();
let window_resized_events = resources.get::<Event<WindowResized>>().unwrap();
let mut handled_windows = HashSet::new();
// iterate in reverse order so we can handle the latest window resize event first for each window.
// we skip earlier events for the same window because it results in redundant work
for window_resized_event in window_resized_events
.iter(&mut self.window_resized_event_handle)
.rev()
{
if handled_windows.contains(&window_resized_event.id) {
continue;
}
let window = windows
.get(window_resized_event.id)
.expect("Received window resized event for non-existent window");
self.setup_swap_chain(window);
handled_windows.insert(window_resized_event.id);
}
}
pub fn handle_window_created_events(&mut self, resources: &mut Resources) {
let windows = resources.get::<Windows>().unwrap();
let winit_windows = resources.get::<WinitWindows>().unwrap();
let window_created_events = resources.get::<Event<WindowCreated>>().unwrap();
for window_created_event in
window_created_events.iter(&mut self.window_created_event_handle)
{
let window = windows
.get(window_created_event.id)
.expect("Received window created event for non-existent window");
#[cfg(feature = "winit")]
{
let winit_windows = resources.get::<WinitWindows>().unwrap();
let windows = resources.get::<Windows>().unwrap();
let primary_window = windows.get_primary().unwrap();
let primary_winit_window = winit_windows.get_window(primary_window.id).unwrap();
let primary_winit_window = winit_windows.get_window(window.id).unwrap();
let surface = wgpu::Surface::create(primary_winit_window.deref());
self.surface = Some(surface);
self.wgpu_resources
.window_surfaces
.insert(window.id, surface);
self.setup_swap_chain(window);
}
}
}
}
impl Renderer for WgpuRenderer {
fn resize(&mut self, world: &mut World, resources: &mut Resources) {
if let Some(surface) = self.surface.as_ref() {
self.encoder = Some(
self.device
.borrow()
.create_command_encoder(&wgpu::CommandEncoderDescriptor { todo: 0 }),
);
let swap_chain_descriptor: wgpu::SwapChainDescriptor = {
let windows = resources.get::<Windows>().unwrap();
let window = windows.get_primary().unwrap();
window.into()
};
fn setup_swap_chain(&mut self, window: &Window) {
let surface = self
.wgpu_resources
.window_surfaces
.get(&window.id)
.expect("Received window resized event for window without a wgpu surface");
let swap_chain_descriptor: wgpu::SwapChainDescriptor = window.into();
let swap_chain = self
.device
.borrow()
.create_swap_chain(surface, &swap_chain_descriptor);
// WgpuRenderer can't own swap_chain without creating lifetime ergonomics issues, so lets just store it in World.
resources.insert(swap_chain);
let mut render_graph = resources.get_mut::<RenderGraph>().unwrap();
for resource_provider in render_graph.resource_providers.iter_mut() {
resource_provider.resize(
self,
world,
resources,
swap_chain_descriptor.width,
swap_chain_descriptor.height,
);
}
// consume current encoder
let command_buffer = self.encoder.take().unwrap().finish();
self.queue.submit(&[command_buffer]);
} else {
// TODO: remove this warning if this case is not a problem
println!("warning: attempted to resize renderer before surface was ready");
}
self.wgpu_resources
.window_swap_chains
.insert(window.id, swap_chain);
}
}
impl Renderer for WgpuRenderer {
fn update(&mut self, world: &mut World, resources: &mut Resources) {
self.initialize(world, resources);
self.handle_window_created_events(resources);
self.handle_window_resized_events(resources);
let resized =
resources
.get::<Event<WindowResize>>()
.unwrap()
.iter(&mut self.window_resize_handle)
.last()
.map(|_| ())
.is_some();
if resized {
self.resize(world, resources);
}
// TODO: this self.encoder handoff is a bit gross, but its here to give resource providers access to buffer copies without
// exposing the wgpu renderer internals to ResourceProvider traits. if this can be made cleaner that would be pretty cool.
self.encoder = Some(
@ -425,13 +433,17 @@ impl Renderer for WgpuRenderer {
let mut encoder = self.encoder.take().unwrap();
let mut swap_chain = resources.get_mut::<wgpu::SwapChain>().unwrap();
// TODO: create swap chain outputs for every swap chain
let swap_chain = self
.wgpu_resources
.window_swap_chains
.values_mut()
.next()
.unwrap();
let frame = swap_chain
.get_next_texture()
.expect("Timeout when acquiring next swap chain texture");
// self.setup_dynamic_entity_shader_uniforms(world, render_graph, &mut encoder);
// setup, pipelines, bind groups, and resources
let mut pipeline_storage = resources
.get_mut::<AssetStorage<PipelineDescriptor>>()

View file

@ -10,7 +10,7 @@ use crate::{
},
renderer::Renderer,
texture::{SamplerDescriptor, TextureDescriptor},
},
}, core::WindowId,
};
use std::collections::HashMap;
@ -22,6 +22,8 @@ pub struct WgpuBindGroupInfo {
#[derive(Default)]
pub struct WgpuResources {
pub render_resources: RenderResources,
pub window_surfaces: HashMap<WindowId, wgpu::Surface>,
pub window_swap_chains: HashMap<WindowId, wgpu::SwapChain>,
pub buffers: HashMap<RenderResource, wgpu::Buffer>,
pub textures: HashMap<RenderResource, wgpu::TextureView>,
pub samplers: HashMap<RenderResource, wgpu::Sampler>,