Implement toggleable gpu profiling that saves the timings from a recent frame with the screenshot key, rebase fixes

This commit is contained in:
Imbris 2021-02-27 04:21:23 -05:00 committed by Avi Weinstock
parent 5cdce0635d
commit d2e2580df4
15 changed files with 1134 additions and 790 deletions

544
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -110,3 +110,5 @@ nativeBuildInputs = ["pkg-config"]
# macos CI fix isn't merged yet
winit = { git = "https://gitlab.com/veloren/winit.git", branch = "macos-test-spiffed" }
vek = { git = "https://gitlab.com/veloren/vek.git", branch = "fix_intrinsics2" }
# patch wgpu so we can use wgpu-profiler crate
wgpu = { git = "https://github.com/gfx-rs/wgpu-rs.git", rev = "1f1a7e5dd47a1610733bbc3989414acb62395359" }

View File

@ -46,6 +46,7 @@ i18n = {package = "veloren-i18n", path = "i18n"}
# Graphics
winit = {version = "0.24.0", features = ["serde"]}
wgpu = { git = "https://github.com/gfx-rs/wgpu-rs.git", rev = "1f1a7e5dd47a1610733bbc3989414acb62395359" }
wgpu-profiler = "0.2.1"
bytemuck = { version="1.4", features=["derive"] }
shaderc = "0.6.2"
@ -95,8 +96,7 @@ rand = "0.8"
rodio = {version = "0.13", default-features = false, features = ["vorbis"]}
ron = {version = "0.6", default-features = false}
serde = {version = "1.0", features = [ "rc", "derive" ]}
# strum = "0.20"
strum = { version = "0.20.0", features = ["derive"] }
strum = "0.20"
strum_macros = "0.20"
treeculler = "0.2"
tokio = { version = "1", default-features = false, features = ["rt-multi-thread"] }

View File

@ -82,6 +82,9 @@ widget_ids! {
refresh_rate,
refresh_rate_label,
//
gpu_profiler_button,
gpu_profiler_label,
//
particles_button,
particles_label,
lossy_terrain_compression_button,
@ -902,11 +905,37 @@ impl<'a> Widget for Video<'a> {
.set(state.ids.shadow_mode_map_resolution_value, ui);
}
// GPU Profiler
Text::new(&self.localized_strings.get("hud.settings.gpu_profiler"))
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
.down_from(state.ids.shadow_mode_list, 8.0)
.color(TEXT_COLOR)
.set(state.ids.gpu_profiler_label, ui);
let gpu_profiler_enabled = ToggleButton::new(
render_mode.profiler_enabled,
self.imgs.checkbox,
self.imgs.checkbox_checked,
)
.w_h(18.0, 18.0)
.right_from(state.ids.gpu_profiler_label, 10.0)
.hover_images(self.imgs.checkbox_mo, self.imgs.checkbox_checked_mo)
.press_images(self.imgs.checkbox_press, self.imgs.checkbox_checked)
.set(state.ids.gpu_profiler_button, ui);
if render_mode.profiler_enabled != gpu_profiler_enabled {
events.push(GraphicsChange::ChangeRenderMode(Box::new(RenderMode {
profiler_enabled: gpu_profiler_enabled,
..render_mode.clone()
})));
}
// Particles
Text::new(&self.localized_strings.get("hud.settings.particles"))
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
.down_from(state.ids.shadow_mode_list, 8.0)
.down_from(state.ids.gpu_profiler_label, 8.0)
.color(TEXT_COLOR)
.set(state.ids.particles_label, ui);

View File

@ -8,7 +8,8 @@
const_generics,
drain_filter,
once_cell,
trait_alias
trait_alias,
or_patterns
)]
#![recursion_limit = "2048"]

View File

@ -8,8 +8,8 @@ pub mod mesh;
pub mod model;
pub mod pipelines;
pub mod renderer;
mod scope;
pub mod texture;
mod time;
// Reexports
pub use self::{
@ -271,4 +271,5 @@ pub struct RenderMode {
pub shadow: ShadowMode,
pub upscale_mode: UpscaleMode,
pub present_mode: PresentMode,
pub profiler_enabled: bool,
}

View File

@ -1,6 +1,13 @@
mod binding;
pub(super) mod drawer;
mod spans;
// Consts and bind groups for post-process and clouds
mod locals;
mod shaders;
mod shadow_map;
use locals::Locals;
use shaders::Shaders;
use shadow_map::{ShadowMap, ShadowMapRenderer};
use super::{
consts::Consts,
@ -18,141 +25,15 @@ use super::{
use common::assets::{self, AssetExt, AssetHandle};
use common_base::span;
use core::convert::TryFrom;
use hashbrown::HashMap;
use tracing::{error, info, warn};
use vek::*;
// TODO: yeet this somewhere else
/// A type representing data that can be converted to an immutable texture map
/// of ColLight data (used for texture atlases created during greedy meshing).
// TODO: revert to u16
pub type ColLightInfo = (Vec<[u8; 4]>, Vec2<u32>);
/// Load from a GLSL file.
pub struct Glsl(String);
impl From<String> for Glsl {
fn from(s: String) -> Glsl { Glsl(s) }
}
impl assets::Asset for Glsl {
type Loader = assets::LoadFrom<String, assets::StringLoader>;
const EXTENSION: &'static str = "glsl";
}
struct Shaders {
shaders: HashMap<String, AssetHandle<Glsl>>,
}
impl assets::Compound for Shaders {
// TODO: Taking the specifier argument as a base for shaders specifiers
// would allow to use several shaders groups easily
fn load<S: assets::source::Source>(
_: &assets::AssetCache<S>,
_: &str,
) -> Result<Shaders, assets::Error> {
let shaders = [
"include.constants",
"include.globals",
"include.sky",
"include.light",
"include.srgb",
"include.random",
"include.lod",
"include.shadows",
"antialias.none",
"antialias.fxaa",
"antialias.msaa-x4",
"antialias.msaa-x8",
"antialias.msaa-x16",
"include.cloud.none",
"include.cloud.regular",
"figure-vert",
"light-shadows-figure-vert",
"light-shadows-directed-vert",
"light-shadows-directed-frag",
"point-light-shadows-vert",
"skybox-vert",
"skybox-frag",
"figure-frag",
"terrain-vert",
"terrain-frag",
"fluid-vert",
"fluid-frag.cheap",
"fluid-frag.shiny",
"sprite-vert",
"sprite-frag",
"particle-vert",
"particle-frag",
"ui-vert",
"ui-frag",
"lod-terrain-vert",
"lod-terrain-frag",
"clouds-vert",
"clouds-frag",
"postprocess-vert",
"postprocess-frag",
"player-shadow-frag",
"light-shadows-geom",
"light-shadows-frag",
];
let shaders = shaders
.iter()
.map(|shader| {
let full_specifier = ["voxygen.shaders.", shader].concat();
let asset = AssetExt::load(&full_specifier)?;
Ok((String::from(*shader), asset))
})
.collect::<Result<HashMap<_, _>, assets::Error>>()?;
Ok(Self { shaders })
}
}
impl Shaders {
fn get(&self, shader: &str) -> Option<impl std::ops::Deref<Target = Glsl>> {
self.shaders.get(shader).map(|a| a.read())
}
}
/// A type that holds shadow map data. Since shadow mapping may not be
/// supported on all platforms, we try to keep it separate.
struct ShadowMapRenderer {
// directed_encoder: gfx::Encoder<gfx_backend::Resources, gfx_backend::CommandBuffer>,
// point_encoder: gfx::Encoder<gfx_backend::Resources, gfx_backend::CommandBuffer>,
directed_depth: Texture,
point_depth: Texture,
point_pipeline: shadow::PointShadowPipeline,
terrain_directed_pipeline: shadow::ShadowPipeline,
figure_directed_pipeline: shadow::ShadowFigurePipeline,
layout: shadow::ShadowLayout,
}
enum ShadowMap {
Enabled(ShadowMapRenderer),
Disabled {
dummy_point: Texture, // Cube texture
dummy_directed: Texture,
},
}
impl ShadowMap {
fn textures(&self) -> (&Texture, &Texture) {
match self {
Self::Enabled(renderer) => (&renderer.point_depth, &renderer.directed_depth),
Self::Disabled {
dummy_point,
dummy_directed,
} => (dummy_point, dummy_directed),
}
}
fn is_enabled(&self) -> bool { matches!(self, Self::Enabled(_)) }
}
/// A type that stores all the layouts associated with this renderer.
struct Layouts {
global: GlobalsLayouts,
@ -167,72 +48,37 @@ struct Layouts {
ui: ui::UiLayout,
}
struct Locals {
clouds: Consts<clouds::Locals>,
clouds_bind: clouds::BindGroup,
postprocess: Consts<postprocess::Locals>,
postprocess_bind: postprocess::BindGroup,
/// A type that stores all the pipelines associated with this renderer.
struct Pipelines {
figure: figure::FigurePipeline,
fluid: fluid::FluidPipeline,
lod_terrain: lod_terrain::LodTerrainPipeline,
particle: particle::ParticlePipeline,
clouds: clouds::CloudsPipeline,
postprocess: postprocess::PostProcessPipeline,
// Consider reenabling at some time
// player_shadow: figure::FigurePipeline,
skybox: skybox::SkyboxPipeline,
sprite: sprite::SpritePipeline,
terrain: terrain::TerrainPipeline,
ui: ui::UiPipeline,
}
impl Locals {
fn new(
device: &wgpu::Device,
layouts: &Layouts,
clouds_locals: Consts<clouds::Locals>,
postprocess_locals: Consts<postprocess::Locals>,
tgt_color_view: &wgpu::TextureView,
tgt_depth_view: &wgpu::TextureView,
tgt_color_pp_view: &wgpu::TextureView,
sampler: &wgpu::Sampler,
depth_sampler: &wgpu::Sampler,
) -> Self {
let clouds_bind = layouts.clouds.bind(
device,
tgt_color_view,
tgt_depth_view,
sampler,
depth_sampler,
&clouds_locals,
);
let postprocess_bind =
layouts
.postprocess
.bind(device, tgt_color_pp_view, sampler, &postprocess_locals);
/// Render target views
struct Views {
// NOTE: unused for now
win_depth: wgpu::TextureView,
Self {
clouds: clouds_locals,
clouds_bind,
postprocess: postprocess_locals,
postprocess_bind,
}
}
tgt_color: wgpu::TextureView,
tgt_depth: wgpu::TextureView,
// TODO: rename
tgt_color_pp: wgpu::TextureView,
}
fn rebind(
&mut self,
device: &wgpu::Device,
layouts: &Layouts,
// Call when these are recreated and need to be rebound
// e.g. resizing
tgt_color_view: &wgpu::TextureView,
tgt_depth_view: &wgpu::TextureView,
tgt_color_pp_view: &wgpu::TextureView,
sampler: &wgpu::Sampler,
depth_sampler: &wgpu::Sampler,
) {
self.clouds_bind = layouts.clouds.bind(
device,
tgt_color_view,
tgt_depth_view,
sampler,
depth_sampler,
&self.clouds,
);
self.postprocess_bind =
layouts
.postprocess
.bind(device, tgt_color_pp_view, sampler, &self.postprocess);
}
/// Shadow rendering textures, layouts, pipelines, and bind groups
struct Shadow {
map: ShadowMap,
bind: ShadowTexturesBindGroup,
}
/// A type that encapsulates rendering state. `Renderer` is central to Voxygen's
@ -242,51 +88,28 @@ impl Locals {
pub struct Renderer {
device: wgpu::Device,
queue: wgpu::Queue,
surface: wgpu::Surface,
swap_chain: wgpu::SwapChain,
sc_desc: wgpu::SwapChainDescriptor,
surface: wgpu::Surface,
win_depth_view: wgpu::TextureView,
tgt_color_view: wgpu::TextureView,
tgt_depth_view: wgpu::TextureView,
// TODO: rename
tgt_color_pp_view: wgpu::TextureView,
sampler: wgpu::Sampler,
depth_sampler: wgpu::Sampler,
shadow_map: ShadowMap,
shadow_bind: ShadowTexturesBindGroup,
layouts: Layouts,
figure_pipeline: figure::FigurePipeline,
fluid_pipeline: fluid::FluidPipeline,
lod_terrain_pipeline: lod_terrain::LodTerrainPipeline,
particle_pipeline: particle::ParticlePipeline,
clouds_pipeline: clouds::CloudsPipeline,
postprocess_pipeline: postprocess::PostProcessPipeline,
// Consider reenabling at some time
// player_shadow_pipeline: figure::FigurePipeline,
skybox_pipeline: skybox::SkyboxPipeline,
sprite_pipeline: sprite::SpritePipeline,
terrain_pipeline: terrain::TerrainPipeline,
ui_pipeline: ui::UiPipeline,
shaders: AssetHandle<Shaders>,
pipelines: Pipelines,
shadow: Shadow,
// Note: we keep these here since their bind groups need to be updated if we resize the
// color/depth textures
locals: Locals,
views: Views,
noise_tex: Texture,
mode: RenderMode,
shaders: AssetHandle<Shaders>,
mode: RenderMode,
resolution: Vec2<u32>,
tracer: super::time::GpuTracer<spans::Id>,
profiler: wgpu_profiler::GpuProfiler,
}
impl Renderer {
@ -334,7 +157,10 @@ impl Renderer {
features: wgpu::Features::DEPTH_CLAMPING
| wgpu::Features::ADDRESS_MODE_CLAMP_TO_BORDER
| wgpu::Features::PUSH_CONSTANTS
| super::time::required_features(),
// TODO: make optional based on enabling profiling
// NOTE: requires recreating the device/queue is this setting changes
// alternatively it could be a compile time feature toggle
| super::scope::required_features(),
limits,
},
None,
@ -399,16 +225,7 @@ impl Renderer {
};
let (
skybox_pipeline,
figure_pipeline,
terrain_pipeline,
fluid_pipeline,
sprite_pipeline,
particle_pipeline,
ui_pipeline,
lod_terrain_pipeline,
clouds_pipeline,
postprocess_pipeline,
pipelines,
//player_shadow_pipeline,
point_shadow_pipeline,
terrain_directed_shadow_pipeline,
@ -422,8 +239,7 @@ impl Renderer {
shadow_views.is_some(),
)?;
let (tgt_color_view, tgt_depth_view, tgt_color_pp_view, win_depth_view) =
Self::create_rt_views(&device, (dims.width, dims.height), &mode)?;
let views = Self::create_rt_views(&device, (dims.width, dims.height), &mode)?;
let shadow_map = if let (
Some(point_pipeline),
@ -467,6 +283,11 @@ impl Renderer {
.bind_shadow_textures(&device, point, directed)
};
let shadow = Shadow {
map: shadow_map,
bind: shadow_bind,
};
let create_sampler = |filter| {
device.create_sampler(&wgpu::SamplerDescriptor {
label: None,
@ -502,78 +323,52 @@ impl Renderer {
&layouts,
clouds_locals,
postprocess_locals,
&tgt_color_view,
&tgt_depth_view,
&tgt_color_pp_view,
&views.tgt_color,
&views.tgt_depth,
&views.tgt_color_pp,
&sampler,
&depth_sampler,
);
let tracer =
super::time::GpuTracer::new(&device, &queue, "voxygen_gpu_chrome_trace.json").unwrap();
let mut profiler = wgpu_profiler::GpuProfiler::new(4, queue.get_timestamp_period());
profiler.enable_timer = mode.profiler_enabled;
profiler.enable_debug_marker = mode.profiler_enabled;
Ok(Self {
device,
queue,
surface,
swap_chain,
sc_desc,
surface,
win_depth_view,
tgt_color_view,
tgt_depth_view,
tgt_color_pp_view,
layouts,
pipelines,
shadow,
locals,
views,
sampler,
depth_sampler,
shadow_map,
shadow_bind,
layouts,
skybox_pipeline,
figure_pipeline,
terrain_pipeline,
fluid_pipeline,
sprite_pipeline,
particle_pipeline,
ui_pipeline,
lod_terrain_pipeline,
clouds_pipeline,
postprocess_pipeline,
shaders,
//player_shadow_pipeline,
locals,
noise_tex,
mode,
shaders,
mode,
resolution: Vec2::new(dims.width, dims.height),
tracer,
profiler,
})
}
/// Get references to the internal render target views that get rendered to
/// before post-processing.
#[allow(dead_code)]
pub fn tgt_views(&self) -> (&wgpu::TextureView, &wgpu::TextureView) {
(&self.tgt_color_view, &self.tgt_depth_view)
}
/// Get references to the internal render target views that get displayed
/// directly by the window.
#[allow(dead_code)]
pub fn win_views(&self) -> &wgpu::TextureView { &self.win_depth_view }
/// Change the render mode.
pub fn set_render_mode(&mut self, mode: RenderMode) -> Result<(), RenderError> {
self.mode = mode;
self.sc_desc.present_mode = self.mode.present_mode.into();
// Enable/disable profiler
self.profiler.enable_timer = self.mode.profiler_enabled;
self.profiler.enable_debug_marker = self.mode.profiler_enabled;
// Recreate render target
self.on_resize(self.resolution)?;
@ -597,31 +392,26 @@ impl Renderer {
self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc);
// Resize other render targets
let (tgt_color_view, tgt_depth_view, tgt_color_pp_view, win_depth_view) =
Self::create_rt_views(&mut self.device, (dims.x, dims.y), &self.mode)?;
self.win_depth_view = win_depth_view;
self.tgt_color_view = tgt_color_view;
self.tgt_depth_view = tgt_depth_view;
self.tgt_color_pp_view = tgt_color_pp_view;
self.views = Self::create_rt_views(&mut self.device, (dims.x, dims.y), &self.mode)?;
// Rebind views to clouds/postprocess bind groups
self.locals.rebind(
&self.device,
&self.layouts,
&self.tgt_color_view,
&self.tgt_depth_view,
&self.tgt_color_pp_view,
&self.views.tgt_color,
&self.views.tgt_depth,
&self.views.tgt_color_pp,
&self.sampler,
&self.depth_sampler,
);
if let (ShadowMap::Enabled(shadow_map), ShadowMode::Map(mode)) =
(&mut self.shadow_map, self.mode.shadow)
(&mut self.shadow.map, self.mode.shadow)
{
match Self::create_shadow_views(&mut self.device, (dims.x, dims.y), &mode) {
Ok((point_depth, directed_depth)) => {
shadow_map.point_depth = point_depth;
shadow_map.directed_depth = directed_depth;
self.shadow_bind = self.layouts.global.bind_shadow_textures(
self.shadow.bind = self.layouts.global.bind_shadow_textures(
&self.device,
&shadow_map.point_depth,
&shadow_map.directed_depth,
@ -637,19 +427,12 @@ impl Renderer {
Ok(())
}
/// Create render target views
fn create_rt_views(
device: &wgpu::Device,
size: (u32, u32),
mode: &RenderMode,
) -> Result<
(
wgpu::TextureView,
wgpu::TextureView,
wgpu::TextureView,
wgpu::TextureView,
),
RenderError,
> {
) -> Result<Views, RenderError> {
let upscaled = Vec2::<u32>::from(size)
.map(|e| (e as f32 * mode.upscale_mode.factor) as u32)
.into_tuple();
@ -743,12 +526,12 @@ impl Renderer {
array_layer_count: None,
});
Ok((
tgt_color_view,
tgt_depth_view,
tgt_color_pp_view,
win_depth_view,
))
Ok(Views {
tgt_color: tgt_color_view,
tgt_depth: tgt_depth_view,
tgt_color_pp: tgt_color_pp_view,
win_depth: win_depth_view,
})
}
fn create_dummy_shadow_tex(device: &wgpu::Device, queue: &wgpu::Queue) -> (Texture, Texture) {
@ -959,7 +742,7 @@ impl Renderer {
/// Get the resolution of the shadow render target.
pub fn get_shadow_resolution(&self) -> (Vec2<u32>, Vec2<u32>) {
if let ShadowMap::Enabled(shadow_map) = &self.shadow_map {
if let ShadowMap::Enabled(shadow_map) = &self.shadow.map {
(
shadow_map.point_depth.get_dimensions().xy(),
shadow_map.directed_depth.get_dimensions().xy(),
@ -993,7 +776,6 @@ impl Renderer {
/// there may be some GPUs that don't quite support it correctly, the
/// impact is relatively small, so there is no reason not to enable it where
/// available.
#[allow(unsafe_code)]
fn enable_seamless_cube_maps() {
todo!()
// unsafe {
@ -1072,34 +854,16 @@ impl Renderer {
&self.shaders.read(),
&self.mode,
&self.sc_desc,
self.shadow_map.is_enabled(),
self.shadow.map.is_enabled(),
) {
Ok((
skybox_pipeline,
figure_pipeline,
terrain_pipeline,
fluid_pipeline,
sprite_pipeline,
particle_pipeline,
ui_pipeline,
lod_terrain_pipeline,
clouds_pipeline,
postprocess_pipeline,
pipelines,
//player_shadow_pipeline,
point_shadow_pipeline,
terrain_directed_shadow_pipeline,
figure_directed_shadow_pipeline,
)) => {
self.skybox_pipeline = skybox_pipeline;
self.figure_pipeline = figure_pipeline;
self.terrain_pipeline = terrain_pipeline;
self.fluid_pipeline = fluid_pipeline;
self.sprite_pipeline = sprite_pipeline;
self.particle_pipeline = particle_pipeline;
self.ui_pipeline = ui_pipeline;
self.lod_terrain_pipeline = lod_terrain_pipeline;
self.clouds_pipeline = clouds_pipeline;
self.postprocess_pipeline = postprocess_pipeline;
self.pipelines = pipelines;
//self.player_shadow_pipeline = player_shadow_pipeline;
if let (
Some(point_pipeline),
@ -1110,7 +874,7 @@ impl Renderer {
point_shadow_pipeline,
terrain_directed_shadow_pipeline,
figure_directed_shadow_pipeline,
&mut self.shadow_map,
&mut self.shadow.map,
) {
shadow_map.point_pipeline = point_pipeline;
shadow_map.terrain_directed_pipeline = terrain_directed_pipeline;
@ -1275,9 +1039,34 @@ impl Renderer {
/// Creates a download buffer, downloads the win_color_view, and converts to
/// a image::DynamicImage.
#[allow(clippy::map_clone)] // TODO: Pending review in #587
pub fn create_screenshot(&mut self) -> Result<image::DynamicImage, RenderError> {
todo!()
//pub fn create_screenshot(&mut self) -> Result<image::DynamicImage,
// RenderError> {
pub fn create_screenshot(&mut self) {
// TODO: check if enabled
// TODO: save alongside a screenshot
// Take profiler snapshot
let profiling_data = if let Some(data) = self.profiler.process_finished_frame() {
data
} else {
error!("Failed to retrieve profiling data");
return;
};
let file_name = format!(
"frame-trace_{}.json",
std::time::SystemTime::now()
.duration_since(std::time::SystemTime::UNIX_EPOCH)
.map(|d| d.as_millis())
.unwrap_or(0)
);
wgpu_profiler::chrometrace::write_chrometrace(
std::path::Path::new(&file_name),
&profiling_data,
);
println!("{}", file_name);
//todo!()
// let (width, height) = self.get_resolution().into_tuple();
// let download_buf = self
@ -2042,7 +1831,6 @@ impl Renderer {
}
/// Creates all the pipelines used to render.
#[allow(clippy::type_complexity)] // TODO: Pending review in #587
fn create_pipelines(
device: &wgpu::Device,
layouts: &Layouts,
@ -2052,16 +1840,7 @@ fn create_pipelines(
has_shadow_views: bool,
) -> Result<
(
skybox::SkyboxPipeline,
figure::FigurePipeline,
terrain::TerrainPipeline,
fluid::FluidPipeline,
sprite::SpritePipeline,
particle::ParticlePipeline,
ui::UiPipeline,
lod_terrain::LodTerrainPipeline,
clouds::CloudsPipeline,
postprocess::PostProcessPipeline,
Pipelines,
//figure::FigurePipeline,
Option<shadow::PointShadowPipeline>,
Option<shadow::ShadowPipeline>,
@ -2351,16 +2130,18 @@ fn create_pipelines(
);
Ok((
skybox_pipeline,
figure_pipeline,
terrain_pipeline,
fluid_pipeline,
sprite_pipeline,
particle_pipeline,
ui_pipeline,
lod_terrain_pipeline,
clouds_pipeline,
postprocess_pipeline,
Pipelines {
skybox: skybox_pipeline,
figure: figure_pipeline,
terrain: terrain_pipeline,
fluid: fluid_pipeline,
sprite: sprite_pipeline,
particle: particle_pipeline,
ui: ui_pipeline,
lod_terrain: lod_terrain_pipeline,
clouds: clouds_pipeline,
postprocess: postprocess_pipeline,
},
// player_shadow_pipeline,
Some(point_shadow_pipeline),
Some(terrain_directed_shadow_pipeline),

View File

@ -8,69 +8,89 @@ use super::{
clouds, figure, fluid, lod_terrain, particle, postprocess, shadow, skybox, sprite,
terrain, ui, ColLights, GlobalsBindGroup, Light, Shadow,
},
scope::{ManualOwningScope, OwningScope, Scope},
},
spans::{self, OwningSpan, Span},
Renderer, ShadowMap, ShadowMapRenderer,
};
use core::{num::NonZeroU32, ops::Range};
use std::sync::Arc;
use vek::Aabr;
// Borrow the fields we need from the renderer so that the GpuProfiler can be
// dijointly borrowed mutably
struct RendererBorrow<'frame> {
queue: &'frame wgpu::Queue,
device: &'frame wgpu::Device,
shadow: &'frame super::Shadow,
pipelines: &'frame super::Pipelines,
locals: &'frame super::locals::Locals,
views: &'frame super::Views,
mode: &'frame super::super::RenderMode,
}
pub struct Drawer<'frame> {
encoder: Option<wgpu::CommandEncoder>,
pub renderer: &'frame mut Renderer,
tex: wgpu::SwapChainTexture,
encoder: Option<ManualOwningScope<'frame, wgpu::CommandEncoder>>,
borrow: RendererBorrow<'frame>,
swap_tex: wgpu::SwapChainTexture,
globals: &'frame GlobalsBindGroup,
}
impl<'frame> Drawer<'frame> {
pub fn new(
mut encoder: wgpu::CommandEncoder,
encoder: wgpu::CommandEncoder,
renderer: &'frame mut Renderer,
tex: wgpu::SwapChainTexture,
swap_tex: wgpu::SwapChainTexture,
globals: &'frame GlobalsBindGroup,
) -> Self {
renderer.tracer.start_span(&mut encoder, &spans::Id::Frame);
let borrow = RendererBorrow {
queue: &renderer.queue,
device: &renderer.device,
shadow: &renderer.shadow,
pipelines: &renderer.pipelines,
locals: &renderer.locals,
views: &renderer.views,
mode: &renderer.mode,
};
let mut encoder =
ManualOwningScope::start(&mut renderer.profiler, encoder, borrow.device, "frame");
Self {
encoder: Some(encoder),
renderer,
tex,
borrow,
swap_tex,
globals,
}
}
pub fn shadow_pass(&mut self) -> Option<ShadowPassDrawer> {
if let ShadowMap::Enabled(ref shadow_renderer) = self.renderer.shadow_map {
let mut render_pass =
self.encoder
.as_mut()
.unwrap()
.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("shadow pass"),
color_attachments: &[],
depth_stencil_attachment: Some(
wgpu::RenderPassDepthStencilAttachmentDescriptor {
attachment: &shadow_renderer.directed_depth.view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(1.0),
store: true,
}),
stencil_ops: None,
},
),
});
/// Get the render mode.
pub fn render_mode(&self) -> &super::super::RenderMode { self.borrow.mode }
pub fn shadow_pass(&mut self) -> Option<ShadowPassDrawer> {
if let ShadowMap::Enabled(ref shadow_renderer) = self.borrow.shadow.map {
let encoder = self.encoder.as_mut().unwrap();
let device = self.borrow.device;
let mut render_pass =
encoder.scoped_render_pass(device, "shadow_pass", &wgpu::RenderPassDescriptor {
label: Some("shadow pass"),
color_attachments: &[],
depth_stencil_attachment: Some(
wgpu::RenderPassDepthStencilAttachmentDescriptor {
attachment: &shadow_renderer.directed_depth.view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(1.0),
store: true,
}),
stencil_ops: None,
},
),
});
let mut render_pass = OwningSpan::start(
&self.renderer.tracer,
render_pass,
spans::Id::DirectedShadows,
);
render_pass.set_bind_group(0, &self.globals.bind_group, &[]);
Some(ShadowPassDrawer {
render_pass,
renderer: &self.renderer,
borrow: &self.borrow,
shadow_renderer,
})
} else {
@ -79,101 +99,86 @@ impl<'frame> Drawer<'frame> {
}
pub fn first_pass(&mut self) -> FirstPassDrawer {
let render_pass =
self.encoder
.as_mut()
.unwrap()
.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("first pass"),
color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
attachment: &self.renderer.tgt_color_view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
store: true,
},
}],
depth_stencil_attachment: Some(
wgpu::RenderPassDepthStencilAttachmentDescriptor {
attachment: &self.renderer.tgt_depth_view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(0.0),
store: true,
}),
stencil_ops: None,
},
),
});
let encoder = self.encoder.as_mut().unwrap();
let device = self.borrow.device;
let mut render_pass =
OwningSpan::start(&self.renderer.tracer, render_pass, spans::Id::PassOne);
encoder.scoped_render_pass(device, "first_pass", &wgpu::RenderPassDescriptor {
label: Some("first pass"),
color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
attachment: &self.borrow.views.tgt_color,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
store: true,
},
}],
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachmentDescriptor {
attachment: &self.borrow.views.tgt_depth,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(0.0),
store: true,
}),
stencil_ops: None,
}),
});
render_pass.set_bind_group(0, &self.globals.bind_group, &[]);
render_pass.set_bind_group(1, &self.renderer.shadow_bind.bind_group, &[]);
render_pass.set_bind_group(1, &self.borrow.shadow.bind.bind_group, &[]);
FirstPassDrawer {
render_pass,
renderer: &self.renderer,
figures_called: false,
borrow: &self.borrow,
}
}
pub fn second_pass(&mut self) -> SecondPassDrawer {
let render_pass =
self.encoder
.as_mut()
.unwrap()
.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("second pass (clouds)"),
color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
attachment: &self.renderer.tgt_color_pp_view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
store: true,
},
}],
depth_stencil_attachment: None,
});
let encoder = self.encoder.as_mut().unwrap();
let device = self.borrow.device;
let mut render_pass =
OwningSpan::start(&self.renderer.tracer, render_pass, spans::Id::PassTwo);
encoder.scoped_render_pass(device, "second_pass", &wgpu::RenderPassDescriptor {
label: Some("second pass (clouds)"),
color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
attachment: &self.borrow.views.tgt_color_pp,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
store: true,
},
}],
depth_stencil_attachment: None,
});
render_pass.set_bind_group(0, &self.globals.bind_group, &[]);
render_pass.set_bind_group(1, &self.renderer.shadow_bind.bind_group, &[]);
render_pass.set_bind_group(1, &self.borrow.shadow.bind.bind_group, &[]);
SecondPassDrawer {
render_pass,
renderer: &self.renderer,
borrow: &self.borrow,
}
}
pub fn third_pass(&mut self) -> ThirdPassDrawer {
let render_pass =
self.encoder
.as_mut()
.unwrap()
.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("third pass (postprocess + ui)"),
color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
attachment: &self.tex.view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
store: true,
},
}],
depth_stencil_attachment: None,
});
let encoder = self.encoder.as_mut().unwrap();
let device = self.borrow.device;
let mut render_pass =
OwningSpan::start(&self.renderer.tracer, render_pass, spans::Id::PassThree);
encoder.scoped_render_pass(device, "third_pass", &wgpu::RenderPassDescriptor {
label: Some("third pass (postprocess + ui)"),
color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
attachment: &self.swap_tex.view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
store: true,
},
}],
depth_stencil_attachment: None,
});
render_pass.set_bind_group(0, &self.globals.bind_group, &[]);
ThirdPassDrawer {
render_pass,
renderer: &self.renderer,
borrow: &self.borrow,
}
}
@ -183,10 +188,13 @@ impl<'frame> Drawer<'frame> {
chunks: impl Clone
+ Iterator<Item = (&'data Model<terrain::Vertex>, &'data terrain::BoundLocals)>,
) {
if let ShadowMap::Enabled(ref shadow_renderer) = self.renderer.shadow_map {
self.renderer
.tracer
.start_span(self.encoder.as_mut().unwrap(), &spans::Id::PointShadows);
if let ShadowMap::Enabled(ref shadow_renderer) = self.borrow.shadow.map {
let device = self.borrow.device;
let mut encoder = self
.encoder
.as_mut()
.unwrap()
.scope(device, "point shadows");
const STRIDE: usize = std::mem::size_of::<shadow::PointLightMatrix>();
let data = bytemuck::cast_slice(matrices);
@ -209,23 +217,20 @@ impl<'frame> Drawer<'frame> {
let label = format!("point shadow face-{} pass", face);
let mut render_pass =
self.encoder
.as_mut()
.unwrap()
.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some(&label),
color_attachments: &[],
depth_stencil_attachment: Some(
wgpu::RenderPassDepthStencilAttachmentDescriptor {
attachment: &view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(1.0),
store: true,
}),
stencil_ops: None,
},
),
});
encoder.scoped_render_pass(device, &label, &wgpu::RenderPassDescriptor {
label: Some(&label),
color_attachments: &[],
depth_stencil_attachment: Some(
wgpu::RenderPassDepthStencilAttachmentDescriptor {
attachment: &view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(1.0),
store: true,
}),
stencil_ops: None,
},
),
});
render_pass.set_pipeline(&shadow_renderer.point_pipeline.pipeline);
render_pass.set_bind_group(0, &self.globals.bind_group, &[]);
@ -244,9 +249,6 @@ impl<'frame> Drawer<'frame> {
});
});
}
self.renderer
.tracer
.end_span(self.encoder.as_mut().unwrap(), &spans::Id::PointShadows);
}
}
@ -258,11 +260,13 @@ impl<'frame> Drawer<'frame> {
/// requires an array of matrices that could be a pain to construct
/// simply for clearing
pub fn clear_shadows(&mut self) {
if let ShadowMap::Enabled(ref shadow_renderer) = self.renderer.shadow_map {
self.encoder
.as_mut()
.unwrap()
.begin_render_pass(&wgpu::RenderPassDescriptor {
if let ShadowMap::Enabled(ref shadow_renderer) = self.borrow.shadow.map {
let device = self.borrow.device;
let encoder = self.encoder.as_mut().unwrap();
encoder.scoped_render_pass(
device,
"clear_directed_shadow",
&wgpu::RenderPassDescriptor {
label: Some("clear directed shadow pass"),
color_attachments: &[],
depth_stencil_attachment: Some(
@ -275,7 +279,8 @@ impl<'frame> Drawer<'frame> {
stencil_ops: None,
},
),
});
},
);
for face in 0..6 {
// TODO: view creation cost?
@ -295,23 +300,20 @@ impl<'frame> Drawer<'frame> {
});
let label = format!("clear point shadow face-{} pass", face);
self.encoder
.as_mut()
.unwrap()
.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some(&label),
color_attachments: &[],
depth_stencil_attachment: Some(
wgpu::RenderPassDepthStencilAttachmentDescriptor {
attachment: &view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(1.0),
store: true,
}),
stencil_ops: None,
},
),
});
encoder.scoped_render_pass(device, &label, &wgpu::RenderPassDescriptor {
label: Some(&label),
color_attachments: &[],
depth_stencil_attachment: Some(
wgpu::RenderPassDepthStencilAttachmentDescriptor {
attachment: &view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(1.0),
store: true,
}),
stencil_ops: None,
},
),
});
}
}
}
@ -321,47 +323,38 @@ impl<'frame> Drop for Drawer<'frame> {
fn drop(&mut self) {
// TODO: submitting things to the queue can let the gpu start on them sooner
// maybe we should submit each render pass to the queue as they are produced?
self.renderer
.tracer
.end_span(self.encoder.as_mut().unwrap(), &spans::Id::Frame);
self.renderer
.tracer
.resolve_timestamps(self.encoder.as_mut().unwrap());
self.renderer
.queue
.submit(std::iter::once(self.encoder.take().unwrap().finish()));
// NOTE: this introduces blocking on GPU work
self.renderer
.tracer
.record_timestamps(&self.renderer.device)
let (mut encoder, profiler) = self.encoder.take().unwrap().end_scope();
profiler.resolve_queries(&mut encoder);
self.borrow.queue.submit(std::iter::once(encoder.finish()));
profiler
.end_frame()
.expect("Gpu profiler error! Maybe there was an unclosed scope?");
}
}
// Shadow pass
pub struct ShadowPassDrawer<'pass> {
render_pass: OwningSpan<'pass, wgpu::RenderPass<'pass>>,
pub renderer: &'pass Renderer,
render_pass: OwningScope<'pass, wgpu::RenderPass<'pass>>,
borrow: &'pass RendererBorrow<'pass>,
shadow_renderer: &'pass ShadowMapRenderer,
}
impl<'pass> ShadowPassDrawer<'pass> {
pub fn draw_figure_shadows(&mut self) -> FigureShadowDrawer<'_, 'pass> {
let mut render_pass = Span::start(
&self.renderer.tracer,
&mut *self.render_pass,
spans::Id::DirectedFigureShadows,
);
let mut render_pass = self
.render_pass
.scope(self.borrow.device, "direcred_figure_shadows");
render_pass.set_pipeline(&self.shadow_renderer.figure_directed_pipeline.pipeline);
FigureShadowDrawer { render_pass }
}
pub fn draw_terrain_shadows(&mut self) -> TerrainShadowDrawer<'_, 'pass> {
let mut render_pass = Span::start(
&self.renderer.tracer,
&mut *self.render_pass,
spans::Id::DirectedTerrainShadows,
);
let mut render_pass = self
.render_pass
.scope(self.borrow.device, "direcred_terrain_shadows");
render_pass.set_pipeline(&self.shadow_renderer.terrain_directed_pipeline.pipeline);
TerrainShadowDrawer { render_pass }
@ -369,7 +362,7 @@ impl<'pass> ShadowPassDrawer<'pass> {
}
pub struct FigureShadowDrawer<'pass_ref, 'pass: 'pass_ref> {
render_pass: Span<'pass_ref, wgpu::RenderPass<'pass>>,
render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
}
impl<'pass_ref, 'pass: 'pass_ref> FigureShadowDrawer<'pass_ref, 'pass> {
@ -385,7 +378,7 @@ impl<'pass_ref, 'pass: 'pass_ref> FigureShadowDrawer<'pass_ref, 'pass> {
}
pub struct TerrainShadowDrawer<'pass_ref, 'pass: 'pass_ref> {
render_pass: Span<'pass_ref, wgpu::RenderPass<'pass>>,
render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
}
impl<'pass_ref, 'pass: 'pass_ref> TerrainShadowDrawer<'pass_ref, 'pass> {
@ -402,73 +395,50 @@ impl<'pass_ref, 'pass: 'pass_ref> TerrainShadowDrawer<'pass_ref, 'pass> {
// First pass
pub struct FirstPassDrawer<'pass> {
pub(super) render_pass: OwningSpan<'pass, wgpu::RenderPass<'pass>>,
pub renderer: &'pass Renderer,
// TODO: hack
figures_called: bool,
pub(super) render_pass: OwningScope<'pass, wgpu::RenderPass<'pass>>,
borrow: &'pass RendererBorrow<'pass>,
}
impl<'pass> FirstPassDrawer<'pass> {
pub fn draw_skybox<'data: 'pass>(&mut self, model: &'data Model<skybox::Vertex>) {
let mut render_pass = Span::start(
&self.renderer.tracer,
&mut *self.render_pass,
spans::Id::Skybox,
);
render_pass.set_pipeline(&self.renderer.skybox_pipeline.pipeline);
let mut render_pass = self.render_pass.scope(self.borrow.device, "skybox");
render_pass.set_pipeline(&self.borrow.pipelines.skybox.pipeline);
render_pass.set_vertex_buffer(0, model.buf().slice(..));
render_pass.draw(0..model.len() as u32, 0..1);
}
pub fn draw_lod_terrain<'data: 'pass>(&mut self, model: &'data Model<lod_terrain::Vertex>) {
let mut render_pass = Span::start(
&self.renderer.tracer,
&mut *self.render_pass,
spans::Id::Lod,
);
render_pass.set_pipeline(&self.renderer.lod_terrain_pipeline.pipeline);
let mut render_pass = self.render_pass.scope(self.borrow.device, "lod_terrain");
render_pass.set_pipeline(&self.borrow.pipelines.lod_terrain.pipeline);
render_pass.set_vertex_buffer(0, model.buf().slice(..));
render_pass.draw(0..model.len() as u32, 0..1);
}
pub fn draw_figures(&mut self) -> FigureDrawer<'_, 'pass> {
let mut render_pass = Span::start(
&self.renderer.tracer,
&mut *self.render_pass,
if !self.figures_called {
spans::Id::Figures1
} else {
spans::Id::Figures2
},
);
self.figures_called = true;
render_pass.set_pipeline(&self.renderer.figure_pipeline.pipeline);
let mut render_pass = self.render_pass.scope(self.borrow.device, "figures");
render_pass.set_pipeline(&self.borrow.pipelines.figure.pipeline);
FigureDrawer { render_pass }
}
pub fn draw_terrain<'data: 'pass>(&mut self) -> TerrainDrawer<'_, 'pass> {
let mut render_pass = Span::start(
&self.renderer.tracer,
&mut *self.render_pass,
spans::Id::Terrain,
);
render_pass.set_pipeline(&self.renderer.terrain_pipeline.pipeline);
let mut render_pass = self.render_pass.scope(self.borrow.device, "terrain");
render_pass.set_pipeline(&self.borrow.pipelines.terrain.pipeline);
TerrainDrawer {
render_pass,
col_lights: None,
}
}
pub fn draw_particles(&mut self) -> ParticleDrawer<'_, 'pass> {
let mut render_pass = Span::start(
&self.renderer.tracer,
&mut *self.render_pass,
spans::Id::Particles,
);
render_pass.set_pipeline(&self.renderer.particle_pipeline.pipeline);
let mut render_pass = self.render_pass.scope(self.borrow.device, "particles");
render_pass.set_pipeline(&self.borrow.pipelines.particle.pipeline);
ParticleDrawer { render_pass }
}
@ -477,15 +447,10 @@ impl<'pass> FirstPassDrawer<'pass> {
&mut self,
col_lights: &'data ColLights<sprite::Locals>,
) -> SpriteDrawer<'_, 'pass> {
let mut render_pass = Span::start(
&self.renderer.tracer,
&mut *self.render_pass,
spans::Id::Sprites,
);
self.render_pass
.set_pipeline(&self.renderer.sprite_pipeline.pipeline);
self.render_pass
.set_bind_group(4, &col_lights.bind_group, &[]);
let mut render_pass = self.render_pass.scope(self.borrow.device, "sprites");
render_pass.set_pipeline(&self.borrow.pipelines.sprite.pipeline);
render_pass.set_bind_group(4, &col_lights.bind_group, &[]);
SpriteDrawer { render_pass }
}
@ -494,12 +459,9 @@ impl<'pass> FirstPassDrawer<'pass> {
&mut self,
waves: &'data fluid::BindGroup,
) -> FluidDrawer<'_, 'pass> {
let mut render_pass = Span::start(
&self.renderer.tracer,
&mut *self.render_pass,
spans::Id::Fluid,
);
render_pass.set_pipeline(&self.renderer.fluid_pipeline.pipeline);
let mut render_pass = self.render_pass.scope(self.borrow.device, "fluid");
render_pass.set_pipeline(&self.borrow.pipelines.fluid.pipeline);
render_pass.set_bind_group(2, &waves.bind_group, &[]);
FluidDrawer { render_pass }
@ -507,7 +469,7 @@ impl<'pass> FirstPassDrawer<'pass> {
}
pub struct FigureDrawer<'pass_ref, 'pass: 'pass_ref> {
render_pass: Span<'pass_ref, wgpu::RenderPass<'pass>>,
render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
}
impl<'pass_ref, 'pass: 'pass_ref> FigureDrawer<'pass_ref, 'pass> {
@ -527,7 +489,7 @@ impl<'pass_ref, 'pass: 'pass_ref> FigureDrawer<'pass_ref, 'pass> {
}
pub struct TerrainDrawer<'pass_ref, 'pass: 'pass_ref> {
render_pass: Span<'pass_ref, wgpu::RenderPass<'pass>>,
render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
col_lights: Option<&'pass_ref Arc<ColLights<terrain::Locals>>>,
}
@ -559,7 +521,7 @@ impl<'pass_ref, 'pass: 'pass_ref> TerrainDrawer<'pass_ref, 'pass> {
}
pub struct ParticleDrawer<'pass_ref, 'pass: 'pass_ref> {
render_pass: Span<'pass_ref, wgpu::RenderPass<'pass>>,
render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
}
impl<'pass_ref, 'pass: 'pass_ref> ParticleDrawer<'pass_ref, 'pass> {
@ -580,7 +542,7 @@ impl<'pass_ref, 'pass: 'pass_ref> ParticleDrawer<'pass_ref, 'pass> {
}
pub struct SpriteDrawer<'pass_ref, 'pass: 'pass_ref> {
render_pass: Span<'pass_ref, wgpu::RenderPass<'pass>>,
render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
}
impl<'pass_ref, 'pass: 'pass_ref> SpriteDrawer<'pass_ref, 'pass> {
@ -617,7 +579,7 @@ impl<'pass_ref, 'pass: 'pass_ref> ChunkSpriteDrawer<'pass_ref, 'pass> {
}
pub struct FluidDrawer<'pass_ref, 'pass: 'pass_ref> {
render_pass: Span<'pass_ref, wgpu::RenderPass<'pass>>,
render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
}
impl<'pass_ref, 'pass: 'pass_ref> FluidDrawer<'pass_ref, 'pass> {
@ -634,49 +596,44 @@ impl<'pass_ref, 'pass: 'pass_ref> FluidDrawer<'pass_ref, 'pass> {
// Second pass: clouds
pub struct SecondPassDrawer<'pass> {
render_pass: OwningSpan<'pass, wgpu::RenderPass<'pass>>,
renderer: &'pass Renderer,
render_pass: OwningScope<'pass, wgpu::RenderPass<'pass>>,
borrow: &'pass RendererBorrow<'pass>,
}
impl<'pass> SecondPassDrawer<'pass> {
pub fn draw_clouds(&mut self) {
self.render_pass
.set_pipeline(&self.renderer.clouds_pipeline.pipeline);
.set_pipeline(&self.borrow.pipelines.clouds.pipeline);
self.render_pass
.set_bind_group(2, &self.renderer.locals.clouds_bind.bind_group, &[]);
.set_bind_group(2, &self.borrow.locals.clouds_bind.bind_group, &[]);
self.render_pass.draw(0..3, 0..1);
}
}
// Third pass: postprocess + ui
pub struct ThirdPassDrawer<'pass> {
render_pass: OwningSpan<'pass, wgpu::RenderPass<'pass>>,
renderer: &'pass Renderer,
render_pass: OwningScope<'pass, wgpu::RenderPass<'pass>>,
borrow: &'pass RendererBorrow<'pass>,
}
impl<'pass> ThirdPassDrawer<'pass> {
pub fn draw_post_process(&mut self) {
let mut render_pass = Span::start(
&self.renderer.tracer,
&mut *self.render_pass,
spans::Id::Postprocess,
);
render_pass.set_pipeline(&self.renderer.postprocess_pipeline.pipeline);
render_pass.set_bind_group(1, &self.renderer.locals.postprocess_bind.bind_group, &[]);
let mut render_pass = self.render_pass.scope(self.borrow.device, "postprocess");
render_pass.set_pipeline(&self.borrow.pipelines.postprocess.pipeline);
render_pass.set_bind_group(1, &self.borrow.locals.postprocess_bind.bind_group, &[]);
render_pass.draw(0..3, 0..1);
}
pub fn draw_ui(&mut self) -> UiDrawer<'_, 'pass> {
let mut render_pass =
Span::start(&self.renderer.tracer, &mut *self.render_pass, spans::Id::Ui);
render_pass.set_pipeline(&self.renderer.ui_pipeline.pipeline);
let mut render_pass = self.render_pass.scope(self.borrow.device, "ui");
render_pass.set_pipeline(&self.borrow.pipelines.ui.pipeline);
UiDrawer { render_pass }
}
}
pub struct UiDrawer<'pass_ref, 'pass: 'pass_ref> {
render_pass: Span<'pass_ref, wgpu::RenderPass<'pass>>,
render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
}
pub struct PreparedUiDrawer<'pass_ref, 'pass: 'pass_ref> {

View File

@ -0,0 +1,75 @@
use super::{
super::{
consts::Consts,
pipelines::{clouds, postprocess},
},
Layouts,
};
pub struct Locals {
pub clouds: Consts<clouds::Locals>,
pub clouds_bind: clouds::BindGroup,
pub postprocess: Consts<postprocess::Locals>,
pub postprocess_bind: postprocess::BindGroup,
}
impl Locals {
pub(super) fn new(
device: &wgpu::Device,
layouts: &Layouts,
clouds_locals: Consts<clouds::Locals>,
postprocess_locals: Consts<postprocess::Locals>,
tgt_color_view: &wgpu::TextureView,
tgt_depth_view: &wgpu::TextureView,
tgt_color_pp_view: &wgpu::TextureView,
sampler: &wgpu::Sampler,
depth_sampler: &wgpu::Sampler,
) -> Self {
let clouds_bind = layouts.clouds.bind(
device,
tgt_color_view,
tgt_depth_view,
sampler,
depth_sampler,
&clouds_locals,
);
let postprocess_bind =
layouts
.postprocess
.bind(device, tgt_color_pp_view, sampler, &postprocess_locals);
Self {
clouds: clouds_locals,
clouds_bind,
postprocess: postprocess_locals,
postprocess_bind,
}
}
pub(super) fn rebind(
&mut self,
device: &wgpu::Device,
layouts: &Layouts,
// Call when these are recreated and need to be rebound
// e.g. resizing
tgt_color_view: &wgpu::TextureView,
tgt_depth_view: &wgpu::TextureView,
tgt_color_pp_view: &wgpu::TextureView,
sampler: &wgpu::Sampler,
depth_sampler: &wgpu::Sampler,
) {
self.clouds_bind = layouts.clouds.bind(
device,
tgt_color_view,
tgt_depth_view,
sampler,
depth_sampler,
&self.clouds,
);
self.postprocess_bind =
layouts
.postprocess
.bind(device, tgt_color_pp_view, sampler, &self.postprocess);
}
}

View File

@ -0,0 +1,91 @@
use common::assets::{self, AssetExt, AssetHandle};
use hashbrown::HashMap;
/// Load from a GLSL file.
pub struct Glsl(pub String);
impl From<String> for Glsl {
fn from(s: String) -> Glsl { Glsl(s) }
}
impl assets::Asset for Glsl {
type Loader = assets::LoadFrom<String, assets::StringLoader>;
const EXTENSION: &'static str = "glsl";
}
pub struct Shaders {
shaders: HashMap<String, AssetHandle<Glsl>>,
}
impl assets::Compound for Shaders {
// TODO: Taking the specifier argument as a base for shaders specifiers
// would allow to use several shaders groups easily
fn load<S: assets::source::Source>(
_: &assets::AssetCache<S>,
_: &str,
) -> Result<Shaders, assets::Error> {
let shaders = [
"include.constants",
"include.globals",
"include.sky",
"include.light",
"include.srgb",
"include.random",
"include.lod",
"include.shadows",
"antialias.none",
"antialias.fxaa",
"antialias.msaa-x4",
"antialias.msaa-x8",
"antialias.msaa-x16",
"include.cloud.none",
"include.cloud.regular",
"figure-vert",
"light-shadows-figure-vert",
"light-shadows-directed-vert",
"light-shadows-directed-frag",
"point-light-shadows-vert",
"skybox-vert",
"skybox-frag",
"figure-frag",
"terrain-vert",
"terrain-frag",
"fluid-vert",
"fluid-frag.cheap",
"fluid-frag.shiny",
"sprite-vert",
"sprite-frag",
"particle-vert",
"particle-frag",
"ui-vert",
"ui-frag",
"lod-terrain-vert",
"lod-terrain-frag",
"clouds-vert",
"clouds-frag",
"postprocess-vert",
"postprocess-frag",
"player-shadow-frag",
"light-shadows-geom",
"light-shadows-frag",
];
let shaders = shaders
.iter()
.map(|shader| {
let full_specifier = ["voxygen.shaders.", shader].concat();
let asset = AssetExt::load(&full_specifier)?;
Ok((String::from(*shader), asset))
})
.collect::<Result<HashMap<_, _>, assets::Error>>()?;
Ok(Self { shaders })
}
}
impl Shaders {
pub fn get(&self, shader: &str) -> Option<impl core::ops::Deref<Target = Glsl>> {
self.shaders.get(shader).map(|a| a.read())
}
}

View File

@ -0,0 +1,38 @@
use super::super::{pipelines::shadow, texture::Texture};
/// A type that holds shadow map data. Since shadow mapping may not be
/// supported on all platforms, we try to keep it separate.
pub struct ShadowMapRenderer {
// directed_encoder: gfx::Encoder<gfx_backend::Resources, gfx_backend::CommandBuffer>,
// point_encoder: gfx::Encoder<gfx_backend::Resources, gfx_backend::CommandBuffer>,
pub directed_depth: Texture,
pub point_depth: Texture,
pub point_pipeline: shadow::PointShadowPipeline,
pub terrain_directed_pipeline: shadow::ShadowPipeline,
pub figure_directed_pipeline: shadow::ShadowFigurePipeline,
pub layout: shadow::ShadowLayout,
}
pub enum ShadowMap {
Enabled(ShadowMapRenderer),
Disabled {
dummy_point: Texture, // Cube texture
dummy_directed: Texture,
},
}
impl ShadowMap {
pub fn textures(&self) -> (&Texture, &Texture) {
match self {
Self::Enabled(renderer) => (&renderer.point_depth, &renderer.directed_depth),
Self::Disabled {
dummy_point,
dummy_directed,
} => (dummy_point, dummy_directed),
}
}
pub fn is_enabled(&self) -> bool { matches!(self, Self::Enabled(_)) }
}

165
voxygen/src/render/scope.rs Normal file
View File

@ -0,0 +1,165 @@
use wgpu_profiler::{GpuProfiler, ProfilerCommandRecorder};
pub fn required_features() -> wgpu::Features { wgpu::Features::TIMESTAMP_QUERY }
pub struct Scope<'a, W: ProfilerCommandRecorder> {
profiler: &'a mut GpuProfiler,
wgpu_thing: &'a mut W,
}
pub struct OwningScope<'a, W: ProfilerCommandRecorder> {
profiler: &'a mut GpuProfiler,
wgpu_thing: W,
}
// Separate type since we can't destructure types that impl Drop :/
pub struct ManualOwningScope<'a, W: ProfilerCommandRecorder> {
profiler: &'a mut GpuProfiler,
wgpu_thing: W,
}
impl<'a, W: ProfilerCommandRecorder> Scope<'a, W> {
pub fn start(
profiler: &'a mut GpuProfiler,
wgpu_thing: &'a mut W,
device: &wgpu::Device,
label: &str,
) -> Self {
profiler.begin_scope(label, wgpu_thing, device);
Self {
profiler,
wgpu_thing,
}
}
/// Starts a scope nested within this one
pub fn scope(&mut self, device: &wgpu::Device, label: &str) -> Scope<'_, W> {
Scope::start(self.profiler, self.wgpu_thing, device, label)
}
}
impl<'a, W: ProfilerCommandRecorder> OwningScope<'a, W> {
pub fn start(
profiler: &'a mut GpuProfiler,
mut wgpu_thing: W,
device: &wgpu::Device,
label: &str,
) -> Self {
profiler.begin_scope(label, &mut wgpu_thing, device);
Self {
profiler,
wgpu_thing,
}
}
/// Starts a scope nested within this one
pub fn scope(&mut self, device: &wgpu::Device, label: &str) -> Scope<'_, W> {
Scope::start(self.profiler, &mut self.wgpu_thing, device, label)
}
}
impl<'a, W: ProfilerCommandRecorder> ManualOwningScope<'a, W> {
pub fn start(
profiler: &'a mut GpuProfiler,
mut wgpu_thing: W,
device: &wgpu::Device,
label: &str,
) -> Self {
profiler.begin_scope(label, &mut wgpu_thing, device);
Self {
profiler,
wgpu_thing,
}
}
/// Starts a scope nested within this one
pub fn scope(&mut self, device: &wgpu::Device, label: &str) -> Scope<'_, W> {
Scope::start(self.profiler, &mut self.wgpu_thing, device, label)
}
/// Ends the scope allowing the extraction of owned the wgpu thing
/// and the mutable reference to the GpuProfiler
pub fn end_scope(mut self) -> (W, &'a mut GpuProfiler) {
self.profiler.end_scope(&mut self.wgpu_thing);
(self.wgpu_thing, self.profiler)
}
}
impl<'a> Scope<'a, wgpu::CommandEncoder> {
/// Start a render pass wrapped in an OwnedScope
pub fn scoped_render_pass<'b>(
&'b mut self,
device: &wgpu::Device,
label: &str,
pass_descriptor: &wgpu::RenderPassDescriptor<'b, '_>,
) -> OwningScope<'b, wgpu::RenderPass> {
let render_pass = self.wgpu_thing.begin_render_pass(pass_descriptor);
OwningScope::start(self.profiler, render_pass, device, label)
}
}
impl<'a> OwningScope<'a, wgpu::CommandEncoder> {
/// Start a render pass wrapped in an OwnedScope
pub fn scoped_render_pass<'b>(
&'b mut self,
device: &wgpu::Device,
label: &str,
pass_descriptor: &wgpu::RenderPassDescriptor<'b, '_>,
) -> OwningScope<'b, wgpu::RenderPass> {
let render_pass = self.wgpu_thing.begin_render_pass(pass_descriptor);
OwningScope::start(self.profiler, render_pass, device, label)
}
}
impl<'a> ManualOwningScope<'a, wgpu::CommandEncoder> {
/// Start a render pass wrapped in an OwnedScope
pub fn scoped_render_pass<'b>(
&'b mut self,
device: &wgpu::Device,
label: &str,
pass_descriptor: &wgpu::RenderPassDescriptor<'b, '_>,
) -> OwningScope<'b, wgpu::RenderPass> {
let render_pass = self.wgpu_thing.begin_render_pass(pass_descriptor);
OwningScope::start(self.profiler, render_pass, device, label)
}
}
// Scope
impl<'a, W: ProfilerCommandRecorder> std::ops::Deref for Scope<'a, W> {
type Target = W;
fn deref(&self) -> &Self::Target { self.wgpu_thing }
}
impl<'a, W: ProfilerCommandRecorder> std::ops::DerefMut for Scope<'a, W> {
fn deref_mut(&mut self) -> &mut Self::Target { self.wgpu_thing }
}
impl<'a, W: ProfilerCommandRecorder> Drop for Scope<'a, W> {
fn drop(&mut self) { self.profiler.end_scope(self.wgpu_thing); }
}
// OwningScope
impl<'a, W: ProfilerCommandRecorder> std::ops::Deref for OwningScope<'a, W> {
type Target = W;
fn deref(&self) -> &Self::Target { &self.wgpu_thing }
}
impl<'a, W: ProfilerCommandRecorder> std::ops::DerefMut for OwningScope<'a, W> {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.wgpu_thing }
}
impl<'a, W: ProfilerCommandRecorder> Drop for OwningScope<'a, W> {
fn drop(&mut self) { self.profiler.end_scope(&mut self.wgpu_thing); }
}
// ManualOwningScope
impl<'a, W: ProfilerCommandRecorder> std::ops::Deref for ManualOwningScope<'a, W> {
type Target = W;
fn deref(&self) -> &Self::Target { &self.wgpu_thing }
}
impl<'a, W: ProfilerCommandRecorder> std::ops::DerefMut for ManualOwningScope<'a, W> {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.wgpu_thing }
}

View File

@ -1048,9 +1048,7 @@ impl Scene {
let camera_data = (&self.camera, scene_data.figure_lod_render_distance);
// would instead have this as an extension.
if drawer.renderer.render_mode().shadow.is_map()
&& (is_daylight || !self.light_data.is_empty())
{
if drawer.render_mode().shadow.is_map() && (is_daylight || !self.light_data.is_empty()) {
if is_daylight {
if let Some(mut shadow_pass) = drawer.shadow_pass() {
// Render terrain directed shadows.

View File

@ -516,7 +516,7 @@ impl SpriteRenderContext {
.into_iter()
.map(
|SpriteDataResponse {
locals,
locals: locals_buffer,
model,
offset,
}| {

View File

@ -10,7 +10,6 @@ use gilrs::{EventType, Gilrs};
use hashbrown::HashMap;
use itertools::Itertools;
use keyboard_keynames::key_layout::KeyLayout;
use old_school_gfx_glutin_ext::{ContextBuilderExt, WindowInitExt, WindowUpdateExt};
use serde::{Deserialize, Serialize};
use tracing::{error, warn};
use vek::*;
@ -941,6 +940,9 @@ impl Window {
let winit::dpi::PhysicalSize { width, height } = physical;
self.events
.push(Event::Resize(Vec2::new(width as u32, height as u32)));
// TODO: can get invalid scissor rect
// panic with this + resize several times
// std::thread::sleep_ms(500);
},
WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
// TODO: is window resized event emitted? or do we need to handle that here?
@ -1342,7 +1344,9 @@ impl Window {
pub fn send_event(&mut self, event: Event) { self.events.push(event) }
pub fn take_screenshot(&mut self, settings: &Settings) {
match self.renderer.create_screenshot() {
let _ = self.renderer.create_screenshot();
// TODO
/*match self.renderer.create_screenshot() {
Ok(img) => {
let mut path = settings.screenshots_path.clone();
let sender = self.message_sender.clone();
@ -1377,7 +1381,7 @@ impl Window {
.unwrap();
},
Err(e) => error!(?e, "Couldn't create screenshot due to renderer error"),
}
}*/
}
fn is_pressed(