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
parent 8c21023500
commit 7a67ce87e7
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

@ -113,3 +113,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"
@ -96,8 +97,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?
@ -1343,7 +1345,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();
@ -1378,7 +1382,7 @@ impl Window {
.unwrap();
},
Err(e) => error!(?e, "Couldn't create screenshot due to renderer error"),
}
}*/
}
fn is_pressed(