mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Use a threadpool to speed up minimap chunk rendering. Also fix ceiling height calculations and color water blue.
This commit is contained in:
parent
59e93d23c1
commit
1214715c21
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -5825,6 +5825,7 @@ dependencies = [
|
||||
"cpal",
|
||||
"criterion",
|
||||
"crossbeam",
|
||||
"crossbeam-channel",
|
||||
"directories-next",
|
||||
"dispatch 0.1.4",
|
||||
"dot_vox",
|
||||
|
@ -106,6 +106,7 @@ num_cpus = "1.0"
|
||||
# vec_map = { version = "0.8.2" }
|
||||
inline_tweak = "1.0.2"
|
||||
itertools = "0.10.0"
|
||||
crossbeam-channel = "0.5"
|
||||
|
||||
# Tracy
|
||||
tracing = "0.1"
|
||||
|
@ -11,6 +11,7 @@ pub fn init(world: &mut World) {
|
||||
|
||||
{
|
||||
let pool = world.read_resource::<SlowJobPool>();
|
||||
pool.configure("IMAGE_PROCESSING", |_| 1);
|
||||
pool.configure("FIGURE_MESHING", |n| n / 2);
|
||||
pool.configure("TERRAIN_MESHING", |n| n / 2);
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ use super::{
|
||||
use crate::{
|
||||
hud::{Graphic, Ui},
|
||||
session::settings_change::{Interface as InterfaceChange, Interface::*},
|
||||
ui::{fonts::Fonts, img_ids},
|
||||
ui::{fonts::Fonts, img_ids, KeyedJobs},
|
||||
GlobalState,
|
||||
};
|
||||
use client::{self, Client};
|
||||
@ -14,7 +14,8 @@ use common::{
|
||||
comp,
|
||||
comp::group::Role,
|
||||
grid::Grid,
|
||||
terrain::{Block, TerrainChunk, TerrainChunkSize},
|
||||
slowjob::SlowJobPool,
|
||||
terrain::{Block, BlockKind, TerrainChunk, TerrainChunkSize},
|
||||
vol::{ReadVol, RectVolSize},
|
||||
};
|
||||
use common_net::msg::world_msg::SiteKind;
|
||||
@ -46,6 +47,9 @@ pub struct VoxelMinimap {
|
||||
image_id: img_ids::Rotations,
|
||||
last_pos: Vec3<i32>,
|
||||
last_ceiling: i32,
|
||||
/// Maximum z of the top of the tallest loaded chunk (for ceiling pruning)
|
||||
max_chunk_z: i32,
|
||||
keyed_jobs: KeyedJobs<Vec2<i32>, MinimapColumn>,
|
||||
}
|
||||
|
||||
const VOXEL_MINIMAP_SIDELENGTH: u32 = 512;
|
||||
@ -66,13 +70,22 @@ impl VoxelMinimap {
|
||||
composited,
|
||||
last_pos: Vec3::zero(),
|
||||
last_ceiling: 0,
|
||||
max_chunk_z: 0,
|
||||
keyed_jobs: KeyedJobs::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn block_color(block: &Block) -> Option<Vec4<u8>> {
|
||||
block
|
||||
.get_color()
|
||||
.map(|rgb| Vec4::new(rgb.r, rgb.g, rgb.b, 192))
|
||||
.map(|rgb| Vec4::new(rgb.r, rgb.g, rgb.b, 255))
|
||||
.or_else(|| {
|
||||
if matches!(block.kind(), BlockKind::Water) {
|
||||
Some(Vec4::new(107, 165, 220, 255))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Each layer is a slice of the terrain near that z-level
|
||||
@ -86,7 +99,7 @@ impl VoxelMinimap {
|
||||
.get(Vec3::new(v.x, v.y, dz as i32 + z + zoff))
|
||||
.ok()
|
||||
.and_then(Self::block_color)
|
||||
.unwrap_or(Vec4::zero());
|
||||
.unwrap_or_else(Vec4::zero);
|
||||
rgba += color.as_() * weights[dz as usize] as f32;
|
||||
}
|
||||
let rgba: Vec4<u8> = (rgba / weights.iter().map(|x| *x as f32).sum::<f32>()).as_();
|
||||
@ -111,9 +124,10 @@ impl VoxelMinimap {
|
||||
.and_then(Self::block_color)
|
||||
{
|
||||
if seen_air > 0 {
|
||||
rgba = Some(color.map(|j| {
|
||||
/*rgba = Some(color.map(|j| {
|
||||
(j as u32).saturating_sub(if seen_air > 2 { 4 } else { 0 }) as u8
|
||||
}));
|
||||
}));*/
|
||||
rgba = Some(color);
|
||||
break;
|
||||
}
|
||||
seen_solids += 1;
|
||||
@ -137,40 +151,47 @@ impl VoxelMinimap {
|
||||
}
|
||||
|
||||
pub fn maintain(&mut self, client: &Client, ui: &mut Ui) {
|
||||
let pool = client.state().ecs().read_resource::<SlowJobPool>();
|
||||
let terrain = client.state().terrain();
|
||||
let mut new_chunk = false;
|
||||
for (key, chunk) in terrain.iter() {
|
||||
if !self.chunk_minimaps.contains_key(&key) {
|
||||
new_chunk = true;
|
||||
let mut layers = Vec::new();
|
||||
const MODE_OVERHEAD: bool = true;
|
||||
if MODE_OVERHEAD {
|
||||
Self::composite_layer_overhead(chunk, &mut layers);
|
||||
} else {
|
||||
Self::composite_layer_slice(chunk, &mut layers);
|
||||
let arc_chunk = Arc::clone(chunk);
|
||||
if let Some((_, column)) = self.keyed_jobs.spawn(Some(&pool), key, move |_| {
|
||||
let mut layers = Vec::new();
|
||||
const MODE_OVERHEAD: bool = true;
|
||||
if MODE_OVERHEAD {
|
||||
Self::composite_layer_overhead(&arc_chunk, &mut layers);
|
||||
} else {
|
||||
Self::composite_layer_slice(&arc_chunk, &mut layers);
|
||||
}
|
||||
let above = arc_chunk
|
||||
.get(Vec3::new(0, 0, arc_chunk.get_max_z() + 1))
|
||||
.ok()
|
||||
.cloned()
|
||||
.unwrap_or_else(Block::empty);
|
||||
let below = arc_chunk
|
||||
.get(Vec3::new(0, 0, arc_chunk.get_min_z() - 1))
|
||||
.ok()
|
||||
.cloned()
|
||||
.unwrap_or_else(Block::empty);
|
||||
MinimapColumn {
|
||||
zlo: arc_chunk.get_min_z(),
|
||||
layers,
|
||||
above: (
|
||||
Self::block_color(&above).unwrap_or_else(Vec4::zero),
|
||||
above.is_filled(),
|
||||
),
|
||||
below: (
|
||||
Self::block_color(&below).unwrap_or_else(Vec4::zero),
|
||||
below.is_filled(),
|
||||
),
|
||||
}
|
||||
}) {
|
||||
self.chunk_minimaps.insert(key, column);
|
||||
new_chunk = true;
|
||||
self.max_chunk_z = self.max_chunk_z.max(chunk.get_max_z());
|
||||
}
|
||||
let above = chunk
|
||||
.get(Vec3::new(0, 0, chunk.get_max_z() + 1))
|
||||
.ok()
|
||||
.cloned()
|
||||
.unwrap_or_else(Block::empty);
|
||||
let below = chunk
|
||||
.get(Vec3::new(0, 0, chunk.get_min_z() - 1))
|
||||
.ok()
|
||||
.cloned()
|
||||
.unwrap_or_else(Block::empty);
|
||||
self.chunk_minimaps.insert(key, MinimapColumn {
|
||||
zlo: chunk.get_min_z(),
|
||||
layers,
|
||||
above: (
|
||||
Self::block_color(&above).unwrap_or_else(Vec4::zero),
|
||||
above.is_filled(),
|
||||
),
|
||||
below: (
|
||||
Self::block_color(&below).unwrap_or_else(Vec4::zero),
|
||||
below.is_filled(),
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
let player = client.entity();
|
||||
@ -208,7 +229,7 @@ impl VoxelMinimap {
|
||||
if above.1 {
|
||||
1
|
||||
} else {
|
||||
layers.len() as i32 - pos.z as i32 + zlo
|
||||
self.max_chunk_z - pos.z as i32
|
||||
}
|
||||
})
|
||||
},
|
||||
@ -220,7 +241,6 @@ impl VoxelMinimap {
|
||||
|| self.last_ceiling != ceiling_offset
|
||||
|| new_chunk
|
||||
{
|
||||
tracing::info!("{:?} {:?}", pos, ceiling_offset);
|
||||
self.last_pos = cpos.with_z(pos.z as i32);
|
||||
self.last_ceiling = ceiling_offset;
|
||||
for y in 0..VOXEL_MINIMAP_SIDELENGTH {
|
||||
@ -241,7 +261,7 @@ impl VoxelMinimap {
|
||||
layers
|
||||
.get(
|
||||
((pos.z as i32 - zlo + ceiling_offset) as usize)
|
||||
.min(layers.len() - 1),
|
||||
.min(layers.len().saturating_sub(1)),
|
||||
)
|
||||
.and_then(|grid| grid.get(cmod).map(|c| c.0.as_()))
|
||||
.or_else(|| {
|
||||
@ -253,7 +273,7 @@ impl VoxelMinimap {
|
||||
})
|
||||
},
|
||||
)
|
||||
.unwrap_or(Vec4::zero());
|
||||
.unwrap_or_else(Vec4::zero);
|
||||
self.composited.put_pixel(
|
||||
x,
|
||||
VOXEL_MINIMAP_SIDELENGTH - y - 1,
|
||||
|
@ -81,6 +81,7 @@ use common::{
|
||||
},
|
||||
consts::MAX_PICKUP_RANGE,
|
||||
outcome::Outcome,
|
||||
slowjob::SlowJobPool,
|
||||
terrain::{SpriteKind, TerrainChunk},
|
||||
trade::{ReducedInventory, TradeAction},
|
||||
uid::Uid,
|
||||
@ -3603,9 +3604,14 @@ impl Hud {
|
||||
|
||||
// Check if item images need to be reloaded
|
||||
self.item_imgs.reload_if_changed(&mut self.ui);
|
||||
|
||||
// TODO: using a thread pool in the obvious way for speeding up map zoom results
|
||||
// in flickering artifacts, figure out a better way to make use of the
|
||||
// thread pool
|
||||
let _pool = client.state().ecs().read_resource::<SlowJobPool>();
|
||||
self.ui.maintain(
|
||||
&mut global_state.window.renderer_mut(),
|
||||
None,
|
||||
//Some(&pool),
|
||||
Some(proj_mat * view_mat * Mat4::translation_3d(-focus_off)),
|
||||
);
|
||||
|
||||
|
@ -1563,6 +1563,7 @@ impl CharSelectionUi {
|
||||
self.controls
|
||||
.view(&global_state.settings, &client, &self.error, &i18n),
|
||||
global_state.window.renderer_mut(),
|
||||
None,
|
||||
global_state.clipboard.as_ref(),
|
||||
);
|
||||
|
||||
|
@ -571,6 +571,7 @@ impl<'a> MainMenuUi {
|
||||
let (messages, _) = self.ui.maintain(
|
||||
self.controls.view(&global_state.settings, dt.as_secs_f32()),
|
||||
global_state.window.renderer_mut(),
|
||||
None,
|
||||
global_state.clipboard.as_ref(),
|
||||
);
|
||||
|
||||
|
@ -3,13 +3,16 @@ mod renderer;
|
||||
|
||||
pub use renderer::{SampleStrat, Transform};
|
||||
|
||||
use crate::render::{RenderError, Renderer, Texture};
|
||||
use common::figure::Segment;
|
||||
use crate::{
|
||||
render::{RenderError, Renderer, Texture},
|
||||
ui::KeyedJobs,
|
||||
};
|
||||
use common::{figure::Segment, slowjob::SlowJobPool};
|
||||
use guillotiere::{size2, SimpleAtlasAllocator};
|
||||
use hashbrown::{hash_map::Entry, HashMap};
|
||||
use image::{DynamicImage, RgbaImage};
|
||||
use pixel_art::resize_pixel_art;
|
||||
use std::sync::Arc;
|
||||
use std::{hash::Hash, sync::Arc};
|
||||
use tracing::warn;
|
||||
use vek::*;
|
||||
|
||||
@ -142,6 +145,9 @@ pub struct GraphicCache {
|
||||
textures: Vec<Texture>,
|
||||
// Stores the location of graphics rendered at a particular resolution and cached on the cpu
|
||||
cache_map: HashMap<Parameters, CachedDetails>,
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
keyed_jobs: KeyedJobs<(Id, Vec2<u16>), Option<(RgbaImage, Option<Rgba<f32>>)>>,
|
||||
}
|
||||
impl GraphicCache {
|
||||
pub fn new(renderer: &mut Renderer) -> Self {
|
||||
@ -153,6 +159,7 @@ impl GraphicCache {
|
||||
atlases: vec![(atlas, 0)],
|
||||
textures: vec![texture],
|
||||
cache_map: HashMap::default(),
|
||||
keyed_jobs: KeyedJobs::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -222,6 +229,7 @@ impl GraphicCache {
|
||||
pub fn cache_res(
|
||||
&mut self,
|
||||
renderer: &mut Renderer,
|
||||
pool: Option<&SlowJobPool>,
|
||||
graphic_id: Id,
|
||||
dims: Vec2<u16>,
|
||||
source: Aabr<f64>,
|
||||
@ -277,7 +285,8 @@ impl GraphicCache {
|
||||
// graphic
|
||||
if !valid {
|
||||
// Create image
|
||||
let (image, border) = draw_graphic(graphic_map, graphic_id, dims)?;
|
||||
let (image, border) =
|
||||
draw_graphic(graphic_map, graphic_id, dims, &mut self.keyed_jobs, pool)?;
|
||||
// If the cache location is invalid, we know the underlying texture is mutable,
|
||||
// so we should be able to replace the graphic. However, we still want to make
|
||||
// sure that we are not reusing textures for images that specify a border
|
||||
@ -292,8 +301,10 @@ impl GraphicCache {
|
||||
Entry::Vacant(details) => details,
|
||||
};
|
||||
|
||||
// Construct image
|
||||
let (image, border_color) = draw_graphic(graphic_map, graphic_id, dims)?;
|
||||
// Construct image in a threadpool
|
||||
|
||||
let (image, border_color) =
|
||||
draw_graphic(graphic_map, graphic_id, dims, &mut self.keyed_jobs, pool)?;
|
||||
|
||||
// Upload
|
||||
let atlas_size = atlas_size(renderer);
|
||||
@ -382,23 +393,40 @@ impl GraphicCache {
|
||||
}
|
||||
|
||||
// Draw a graphic at the specified dimensions
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn draw_graphic(
|
||||
graphic_map: &GraphicMap,
|
||||
graphic_id: Id,
|
||||
dims: Vec2<u16>,
|
||||
keyed_jobs: &mut KeyedJobs<(Id, Vec2<u16>), Option<(RgbaImage, Option<Rgba<f32>>)>>,
|
||||
pool: Option<&SlowJobPool>,
|
||||
) -> Option<(RgbaImage, Option<Rgba<f32>>)> {
|
||||
match graphic_map.get(&graphic_id) {
|
||||
Some(Graphic::Blank) => None,
|
||||
// Render image at requested resolution
|
||||
// TODO: Use source aabr.
|
||||
Some(&Graphic::Image(ref image, border_color)) => Some((
|
||||
resize_pixel_art(&image.to_rgba8(), u32::from(dims.x), u32::from(dims.y)),
|
||||
border_color,
|
||||
)),
|
||||
Some(Graphic::Voxel(ref segment, trans, sample_strat)) => Some((
|
||||
renderer::draw_vox(&segment, dims, trans.clone(), *sample_strat),
|
||||
None,
|
||||
)),
|
||||
Some(inner) => {
|
||||
let inner = inner.clone();
|
||||
keyed_jobs
|
||||
.spawn(pool, (graphic_id, dims), move |_| {
|
||||
match inner {
|
||||
// Render image at requested resolution
|
||||
// TODO: Use source aabr.
|
||||
Graphic::Image(ref image, border_color) => Some((
|
||||
resize_pixel_art(
|
||||
&image.to_rgba8(),
|
||||
u32::from(dims.x),
|
||||
u32::from(dims.y),
|
||||
),
|
||||
border_color,
|
||||
)),
|
||||
Graphic::Voxel(ref segment, trans, sample_strat) => Some((
|
||||
renderer::draw_vox(&segment, dims, trans, sample_strat),
|
||||
None,
|
||||
)),
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.and_then(|(_, v)| v)
|
||||
},
|
||||
None => {
|
||||
warn!(
|
||||
?graphic_id,
|
||||
|
@ -15,6 +15,7 @@ use super::{
|
||||
scale::{Scale, ScaleMode},
|
||||
};
|
||||
use crate::{render::Renderer, window::Window, Error};
|
||||
use common::slowjob::SlowJobPool;
|
||||
use common_base::span;
|
||||
use iced::{mouse, Cache, Size, UserInterface};
|
||||
use iced_winit::Clipboard;
|
||||
@ -142,6 +143,7 @@ impl IcedUi {
|
||||
&mut self,
|
||||
root: E,
|
||||
renderer: &mut Renderer,
|
||||
pool: Option<&SlowJobPool>,
|
||||
clipboard: Option<&Clipboard>,
|
||||
) -> (Vec<M>, mouse::Interaction) {
|
||||
span!(_guard, "maintain", "IcedUi::maintain");
|
||||
@ -207,7 +209,7 @@ impl IcedUi {
|
||||
|
||||
self.cache = Some(user_interface.into_cache());
|
||||
|
||||
self.renderer.draw(primitive, renderer);
|
||||
self.renderer.draw(primitive, renderer, pool);
|
||||
|
||||
(messages, mouse_interaction)
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ use crate::{
|
||||
},
|
||||
Error,
|
||||
};
|
||||
use common::util::srgba_to_linear;
|
||||
use common::{slowjob::SlowJobPool, util::srgba_to_linear};
|
||||
use common_base::span;
|
||||
use std::{convert::TryInto, ops::Range};
|
||||
use vek::*;
|
||||
@ -184,7 +184,12 @@ impl IcedRenderer {
|
||||
self.cache.resize_glyph_cache(renderer).unwrap();
|
||||
}
|
||||
|
||||
pub fn draw(&mut self, primitive: Primitive, renderer: &mut Renderer) {
|
||||
pub fn draw(
|
||||
&mut self,
|
||||
primitive: Primitive,
|
||||
renderer: &mut Renderer,
|
||||
pool: Option<&SlowJobPool>,
|
||||
) {
|
||||
span!(_guard, "draw", "IcedRenderer::draw");
|
||||
// Re-use memory
|
||||
self.draw_commands.clear();
|
||||
@ -194,7 +199,7 @@ impl IcedRenderer {
|
||||
self.current_state = State::Plain;
|
||||
self.start = 0;
|
||||
|
||||
self.draw_primitive(primitive, Vec2::zero(), 1.0, renderer);
|
||||
self.draw_primitive(primitive, Vec2::zero(), 1.0, renderer, pool);
|
||||
|
||||
// Enter the final command.
|
||||
self.draw_commands.push(match self.current_state {
|
||||
@ -426,12 +431,13 @@ impl IcedRenderer {
|
||||
offset: Vec2<u32>,
|
||||
alpha: f32,
|
||||
renderer: &mut Renderer,
|
||||
pool: Option<&SlowJobPool>,
|
||||
) {
|
||||
match primitive {
|
||||
Primitive::Group { primitives } => {
|
||||
primitives
|
||||
.into_iter()
|
||||
.for_each(|p| self.draw_primitive(p, offset, alpha, renderer));
|
||||
.for_each(|p| self.draw_primitive(p, offset, alpha, renderer, pool));
|
||||
},
|
||||
Primitive::Image {
|
||||
handle,
|
||||
@ -536,6 +542,7 @@ impl IcedRenderer {
|
||||
// Cache graphic at particular resolution.
|
||||
let (uv_aabr, tex_id) = match graphic_cache.cache_res(
|
||||
renderer,
|
||||
pool,
|
||||
graphic_id,
|
||||
resolution,
|
||||
// TODO: take f32 here
|
||||
@ -730,7 +737,7 @@ impl IcedRenderer {
|
||||
// TODO: cull primitives outside the current scissor
|
||||
|
||||
// Renderer child
|
||||
self.draw_primitive(*content, offset + clip_offset, alpha, renderer);
|
||||
self.draw_primitive(*content, offset + clip_offset, alpha, renderer, pool);
|
||||
|
||||
// Reset scissor
|
||||
self.draw_commands.push(match self.current_state {
|
||||
@ -745,7 +752,7 @@ impl IcedRenderer {
|
||||
.push(DrawCommand::Scissor(self.window_scissor));
|
||||
},
|
||||
Primitive::Opacity { alpha: a, content } => {
|
||||
self.draw_primitive(*content, offset, alpha * a, renderer);
|
||||
self.draw_primitive(*content, offset, alpha * a, renderer, pool);
|
||||
},
|
||||
Primitive::Nothing => {},
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ use crate::{
|
||||
#[rustfmt::skip]
|
||||
use ::image::GenericImageView;
|
||||
use cache::Cache;
|
||||
use common::util::srgba_to_linear;
|
||||
use common::{slowjob::SlowJobPool, util::srgba_to_linear};
|
||||
use common_base::span;
|
||||
use conrod_core::{
|
||||
event::Input,
|
||||
@ -48,8 +48,8 @@ use conrod_core::{
|
||||
};
|
||||
use core::{convert::TryInto, f32, f64, ops::Range};
|
||||
use graphic::TexId;
|
||||
use hashbrown::hash_map::Entry;
|
||||
use std::time::Duration;
|
||||
use hashbrown::{hash_map::Entry, HashMap};
|
||||
use std::{hash::Hash, time::Duration};
|
||||
use tracing::{error, warn};
|
||||
use vek::*;
|
||||
|
||||
@ -306,7 +306,12 @@ impl Ui {
|
||||
pub fn widget_input(&self, id: widget::Id) -> Widget { self.ui.widget_input(id) }
|
||||
|
||||
#[allow(clippy::float_cmp)] // TODO: Pending review in #587
|
||||
pub fn maintain(&mut self, renderer: &mut Renderer, view_projection_mat: Option<Mat4<f32>>) {
|
||||
pub fn maintain(
|
||||
&mut self,
|
||||
renderer: &mut Renderer,
|
||||
pool: Option<&SlowJobPool>,
|
||||
view_projection_mat: Option<Mat4<f32>>,
|
||||
) {
|
||||
span!(_guard, "maintain", "Ui::maintain");
|
||||
// Maintain tooltip manager
|
||||
self.tooltip_manager
|
||||
@ -353,16 +358,17 @@ impl Ui {
|
||||
}
|
||||
|
||||
let mut retry = false;
|
||||
self.maintain_internal(renderer, view_projection_mat, &mut retry);
|
||||
self.maintain_internal(renderer, pool, view_projection_mat, &mut retry);
|
||||
if retry {
|
||||
// Update the glyph cache and try again.
|
||||
self.maintain_internal(renderer, view_projection_mat, &mut retry);
|
||||
self.maintain_internal(renderer, pool, view_projection_mat, &mut retry);
|
||||
}
|
||||
}
|
||||
|
||||
fn maintain_internal(
|
||||
&mut self,
|
||||
renderer: &mut Renderer,
|
||||
pool: Option<&SlowJobPool>,
|
||||
view_projection_mat: Option<Mat4<f32>>,
|
||||
retry: &mut bool,
|
||||
) {
|
||||
@ -806,6 +812,7 @@ impl Ui {
|
||||
// Cache graphic at particular resolution.
|
||||
let (uv_aabr, tex_id) = match graphic_cache.cache_res(
|
||||
renderer,
|
||||
pool,
|
||||
*graphic_id,
|
||||
resolution,
|
||||
source_aabr,
|
||||
@ -1047,3 +1054,51 @@ fn default_scissor(renderer: &Renderer) -> Aabr<u16> {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub struct KeyedJobs<K, V> {
|
||||
tx: crossbeam_channel::Sender<(K, V)>,
|
||||
rx: crossbeam_channel::Receiver<(K, V)>,
|
||||
buf: HashMap<K, V>,
|
||||
}
|
||||
|
||||
impl<K: Hash + Eq + Send + Sync + 'static, V: Send + Sync + 'static> KeyedJobs<K, V> {
|
||||
#[allow(clippy::new_without_default)]
|
||||
pub fn new() -> Self {
|
||||
let (tx, rx) = crossbeam_channel::unbounded();
|
||||
Self {
|
||||
tx,
|
||||
rx,
|
||||
buf: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn spawn(
|
||||
&mut self,
|
||||
pool: Option<&SlowJobPool>,
|
||||
k: K,
|
||||
f: impl FnOnce(&K) -> V + Send + Sync + 'static,
|
||||
) -> Option<(K, V)> {
|
||||
if let Some(pool) = pool {
|
||||
if let Some(v) = self.buf.remove(&k) {
|
||||
Some((k, v))
|
||||
} else {
|
||||
while let Ok((k2, v)) = self.rx.try_recv() {
|
||||
if k == k2 {
|
||||
return Some((k, v));
|
||||
} else {
|
||||
self.buf.insert(k2, v);
|
||||
}
|
||||
}
|
||||
let tx = self.tx.clone();
|
||||
pool.spawn("IMAGE_PROCESSING", move || {
|
||||
let v = f(&k);
|
||||
let _ = tx.send((k, v));
|
||||
});
|
||||
None
|
||||
}
|
||||
} else {
|
||||
let v = f(&k);
|
||||
Some((k, v))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user