WIP: cross-platform
This commit is contained in:
+79
-89
@@ -4,7 +4,7 @@ use std::rc::Rc;
|
||||
use wasm_bindgen::prelude::wasm_bindgen;
|
||||
|
||||
use crate::gpu::Wgpu;
|
||||
use crate::gpu_resource::{GpuPass, ProfileMap};
|
||||
use crate::gpu_resource::{profiler, GpuPass, ProfileMap};
|
||||
use crate::graph::{ColorAttachment, DepthAttachment};
|
||||
use crate::render_data::RenderData;
|
||||
use crate::store::{Loadout, Store};
|
||||
@@ -39,10 +39,8 @@ pub struct RenderLoop {
|
||||
frame: Cell<u32>,
|
||||
elapsed: Cell<f64>,
|
||||
last: Cell<f64>,
|
||||
profiling: Cell<bool>,
|
||||
profile_pending: Cell<bool>,
|
||||
profile_after: Cell<f64>,
|
||||
profile: RefCell<Option<String>>,
|
||||
active_loadout: Loadout,
|
||||
profile_state: RefCell<profiler::ProfileState>,
|
||||
}
|
||||
|
||||
impl RenderLoop {
|
||||
@@ -54,10 +52,8 @@ impl RenderLoop {
|
||||
frame: Cell::new(0),
|
||||
elapsed: Cell::new(0.0),
|
||||
last: Cell::new(js_sys::Date::now()),
|
||||
profiling: Cell::new(false),
|
||||
profile_pending: Cell::new(false),
|
||||
profile_after: Cell::new(0.0),
|
||||
profile: RefCell::new(None),
|
||||
active_loadout: todo!(),
|
||||
profile_state: RefCell::new(profiler::ProfileState::Detached),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,24 +74,19 @@ impl RenderLoop {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_profiling(&self, enabled: bool) {
|
||||
self.profiling.set(enabled);
|
||||
self.profile_after.set(0.0);
|
||||
if !enabled {
|
||||
self.profile.borrow_mut().take();
|
||||
}
|
||||
pub fn set_profiling(&mut self) {
|
||||
self.profile_state = RefCell::new(profiler::ProfileState::Attached);
|
||||
}
|
||||
|
||||
pub fn take_profile(&self) -> Option<String> {
|
||||
self.profile.borrow_mut().take()
|
||||
if let profiler::ProfileState::Ready(data) = self.profile_state.get_mut().take() {
|
||||
Some(data)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start(
|
||||
self: &Rc<Self>,
|
||||
gpu: Rc<RefCell<Option<Wgpu>>>,
|
||||
store: Rc<RefCell<Store>>,
|
||||
data: Rc<RefCell<RenderData>>,
|
||||
) {
|
||||
pub fn start(self: &Rc<Self>, gpu: &Wgpu, data: Rc<RefCell<RenderData>>) {
|
||||
if self.started.replace(true) {
|
||||
return;
|
||||
}
|
||||
@@ -106,6 +97,7 @@ impl RenderLoop {
|
||||
if !control.playing.get() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let started = js_sys::Date::now();
|
||||
let fps = control.fps.get();
|
||||
if fps != 0 && started - control.last.get() < 900.0 / f64::from(fps) {
|
||||
@@ -120,36 +112,25 @@ impl RenderLoop {
|
||||
let mut data = data.borrow_mut();
|
||||
data.update_signals(delta as f32, frame, elapsed as f32, control.fps.get());
|
||||
}
|
||||
let submission = if data.borrow().render_pending() {
|
||||
if let (Some(gpu), Some(loadout)) =
|
||||
(gpu.borrow_mut().as_mut(), store.borrow_mut().active_mut())
|
||||
{
|
||||
if data.borrow_mut().begin_render() {
|
||||
let profile = control.profiling.get()
|
||||
&& !control.profile_pending.get()
|
||||
&& started >= control.profile_after.get();
|
||||
let result = {
|
||||
let data = data.borrow();
|
||||
gpu.render(loadout, &data, profile)
|
||||
};
|
||||
match result {
|
||||
Ok(profile) => profile.map(|profile| {
|
||||
(profile, gpu.adapter.clone(), gpu.width, gpu.height)
|
||||
}),
|
||||
Err(_) => {
|
||||
data.borrow_mut().mark_dirty();
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// returns false when no render is pending
|
||||
if !data.borrow_mut().begin_render() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let should_profile = matches!(
|
||||
*control.profile_state.borrow(),
|
||||
profiler::ProfileState::Attached
|
||||
);
|
||||
|
||||
gpu.render(&mut self.active_loadout, &data.borrow(), should_profile)
|
||||
.and_then(|profile_metadata| {
|
||||
// we are done with render loop but we got a profile data for this
|
||||
// frame, so we need to process that
|
||||
|
||||
control.profile_state
|
||||
});
|
||||
|
||||
if let Some((profile, adapter, width, height)) = submission {
|
||||
control.profile_pending.set(true);
|
||||
control.profile_after.set(started + 250.0);
|
||||
@@ -162,27 +143,20 @@ impl RenderLoop {
|
||||
let passes = profile
|
||||
.read()
|
||||
.into_iter()
|
||||
.map(|(name, milliseconds)| {
|
||||
serde_json::json!({
|
||||
"name": name,
|
||||
"milliseconds": milliseconds,
|
||||
})
|
||||
})
|
||||
.map(|(name, milliseconds)| profiler::Pass { name, milliseconds })
|
||||
.collect::<Vec<_>>();
|
||||
let milliseconds = passes
|
||||
.iter()
|
||||
.filter_map(|pass| pass["milliseconds"].as_f64())
|
||||
.sum::<f64>();
|
||||
let milliseconds =
|
||||
passes.iter().map(|pass| pass.milliseconds).sum::<f64>();
|
||||
if control.profiling.get() {
|
||||
*control.profile.borrow_mut() = Some(
|
||||
serde_json::json!({
|
||||
"frame": frame,
|
||||
"milliseconds": milliseconds,
|
||||
"readbackMilliseconds": js_sys::Date::now() - started,
|
||||
"adapter": adapter,
|
||||
"canvas": { "width": width, "height": height },
|
||||
"passes": passes,
|
||||
})
|
||||
*control.profile_state.borrow_mut() = Some(
|
||||
profiler::FrameData {
|
||||
frame,
|
||||
milliseconds,
|
||||
readback_milliseconds: js_sys::Date::now() - started,
|
||||
adapter,
|
||||
canvas: profiler::Canvas { width, height },
|
||||
passes,
|
||||
}
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
@@ -201,7 +175,7 @@ impl Wgpu {
|
||||
loadout: &mut Loadout,
|
||||
data: &RenderData,
|
||||
profile: bool,
|
||||
) -> Result<Option<ProfileMap>, String> {
|
||||
) -> Result<ProfileMap, String> {
|
||||
for buffer in loadout.resources.buffers.values() {
|
||||
if !buffer.sync_each_frame {
|
||||
continue;
|
||||
@@ -212,6 +186,7 @@ impl Wgpu {
|
||||
data.bytes(&buffer.source).ok_or("ROWS_UNKNOWN")?,
|
||||
);
|
||||
}
|
||||
|
||||
let output = match self.surface.get_current_texture() {
|
||||
Ok(output) => output,
|
||||
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
|
||||
@@ -221,9 +196,21 @@ impl Wgpu {
|
||||
Err(wgpu::SurfaceError::Timeout) => return Err("SURFACE".into()),
|
||||
Err(_) => return Err("SURFACE".into()),
|
||||
};
|
||||
|
||||
let surface_view = output
|
||||
.texture
|
||||
.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
|
||||
// profiler is used only when enabled
|
||||
// the presence of a profiler means:
|
||||
// - profiler is available and
|
||||
// - we want to profile this frame
|
||||
let profiler = if profile {
|
||||
loadout.resources.profiler
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut encoder = self
|
||||
.device
|
||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
@@ -238,9 +225,12 @@ impl Wgpu {
|
||||
..
|
||||
} => {
|
||||
let pass = &loadout.graph.passes[*pass];
|
||||
let timestamp_writes = profile
|
||||
.then(|| loadout.resources.compute_timestamps(pass_index))
|
||||
.flatten();
|
||||
let timestamp_writes = profiler.and(|p| wgpu::ComputePassTimestampWrites {
|
||||
query_set: &p.query_set,
|
||||
beginning_of_pass_write_index: Some(pass_index as u32 * 2),
|
||||
end_of_pass_write_index: Some(pass_index as u32 + 1),
|
||||
});
|
||||
|
||||
let mut command = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
|
||||
label: Some(&pass.id),
|
||||
timestamp_writes,
|
||||
@@ -296,9 +286,13 @@ impl Wgpu {
|
||||
})
|
||||
})
|
||||
.transpose()?;
|
||||
let timestamp_writes = profile
|
||||
.then(|| loadout.resources.render_timestamps(pass_index))
|
||||
.flatten();
|
||||
|
||||
let timestamp_writes = profiler.and(|p| wgpu::RenderPassTimestampWrites {
|
||||
query_set: &p.query_set,
|
||||
beginning_of_pass_write_index: Some(pass_index as u32 * 2),
|
||||
end_of_pass_write_index: Some(pass_index as u32 * 2 + 1),
|
||||
});
|
||||
|
||||
let mut command = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: Some(&pass.id),
|
||||
color_attachments: &colors,
|
||||
@@ -310,19 +304,15 @@ impl Wgpu {
|
||||
}
|
||||
}
|
||||
}
|
||||
if profile {
|
||||
loadout.resources.resolve_profile(&mut encoder);
|
||||
}
|
||||
|
||||
profiler.as_ref().map(|p| p.encode_query_set(&mut encoder));
|
||||
self.queue.submit([encoder.finish()]);
|
||||
let profile = profile
|
||||
.then(|| {
|
||||
loadout
|
||||
.resources
|
||||
.map_profile(self.queue.get_timestamp_period())
|
||||
})
|
||||
.flatten();
|
||||
|
||||
output.present();
|
||||
Ok(profile)
|
||||
|
||||
profiler
|
||||
.and_then(|p| p.map_profile(self.queue.get_timestamp_period()))
|
||||
.flatten();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user