Log (wip), parallelisation
This commit is contained in:
+124
-55
@@ -5,6 +5,7 @@ use bevy::asset::RenderAssetUsages;
|
||||
use bevy::prelude::*;
|
||||
use bevy::render::render_resource;
|
||||
use bevy_platform::collections::hash_map::HashMap;
|
||||
use rayon::prelude::*;
|
||||
|
||||
#[derive(Resource)]
|
||||
pub struct Textures {
|
||||
@@ -29,6 +30,7 @@ const SKY_PATH: &str = "sky.png";
|
||||
const DIRT_WALL_PATH: &str = "dirt_wall.png";
|
||||
const ROCK_WALL_PATH: &str = "rock_wall.png";
|
||||
const BEDROCK_WALL_PATH: &str = "bedrock_wall.png";
|
||||
const LOG_PATH: &str = "log.png";
|
||||
|
||||
pub fn initialize_textures(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
let mut textures: HashMap<String, Handle<Image>> = HashMap::new();
|
||||
@@ -73,6 +75,8 @@ pub fn initialize_textures(mut commands: Commands, asset_server: Res<AssetServer
|
||||
BEDROCK_WALL_PATH.to_string(),
|
||||
asset_server.load(BEDROCK_WALL_PATH),
|
||||
);
|
||||
texture_ids.insert(FIXTURE_ID_OFFSET + 4, LOG_PATH.to_string());
|
||||
textures.insert(LOG_PATH.to_string(), asset_server.load(LOG_PATH));
|
||||
|
||||
commands.insert_resource(Textures { handles: textures });
|
||||
commands.insert_resource(TextureIDs { refs: texture_ids });
|
||||
@@ -91,6 +95,7 @@ pub struct CurrentWorldSpriteState {
|
||||
pub state: TerrainSpriteState,
|
||||
}
|
||||
|
||||
use std::sync::Mutex;
|
||||
use std::time::Instant;
|
||||
|
||||
#[derive(Component)]
|
||||
@@ -116,7 +121,7 @@ impl Default for QuiltCache {
|
||||
// here be dragons :(
|
||||
pub fn build_quilted_terrain_sprites(
|
||||
query: Query<(&FloorTile, &Transform)>,
|
||||
mut commands: Commands,
|
||||
commands: ParallelCommands<'_, '_>,
|
||||
mut cwss: ResMut<CurrentWorldSpriteState>,
|
||||
textures: Res<Textures>,
|
||||
texture_ids: Res<TextureIDs>,
|
||||
@@ -130,18 +135,25 @@ pub fn build_quilted_terrain_sprites(
|
||||
let now = Instant::now();
|
||||
cwss.state = TerrainSpriteState::InProgress;
|
||||
|
||||
for entity in query_sprites.iter() {
|
||||
commands.entity(entity).despawn();
|
||||
// Despawn existing terrain sprites
|
||||
let despawn_entities: Vec<Entity> = query_sprites.iter().collect();
|
||||
for entity in despawn_entities {
|
||||
commands.command_scope(|mut cmd| {
|
||||
cmd.entity(entity).despawn();
|
||||
});
|
||||
}
|
||||
|
||||
// Collect tiles by z-index first
|
||||
let mut tiles_by_z: HashMap<usize, Vec<(Vec2, &FloorTile)>> = HashMap::new();
|
||||
|
||||
// Calculate bounds for all visible tiles
|
||||
for z_index in 0..=tilemap::Z_TOTAL as usize {
|
||||
for (floortile, transform) in query.iter() {
|
||||
for (floortile, transform) in query.iter() {
|
||||
let position = Vec2::new(transform.translation.x, transform.translation.y);
|
||||
|
||||
for z_index in 0..=tilemap::Z_TOTAL as usize {
|
||||
let is_visible =
|
||||
(floortile.visible_range[z_index / 32] & (1 << (z_index % 32) as u32)) != 0;
|
||||
if is_visible {
|
||||
let position = Vec2::new(transform.translation.x, transform.translation.y);
|
||||
tiles_by_z
|
||||
.entry(z_index)
|
||||
.or_default()
|
||||
@@ -150,12 +162,25 @@ pub fn build_quilted_terrain_sprites(
|
||||
}
|
||||
}
|
||||
|
||||
// For each z-index, create a new quilted texture, baked and composited.
|
||||
for (z_index, tiles) in tiles_by_z.iter() {
|
||||
// Thread-safe collections to store results
|
||||
let dimensions_mutex = Mutex::new(HashMap::new());
|
||||
let texture_handles_mutex = Mutex::new(HashMap::new());
|
||||
|
||||
// Process each z-level in parallel
|
||||
let z_indices: Vec<usize> = tiles_by_z.keys().cloned().collect();
|
||||
|
||||
z_indices.into_par_iter().for_each(|z_index| {
|
||||
let tiles = if let Some(tiles) = tiles_by_z.get(&z_index) {
|
||||
tiles
|
||||
} else {
|
||||
return; // Skip empty z-levels
|
||||
};
|
||||
|
||||
if tiles.is_empty() {
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate bounds
|
||||
let min_x_aligned: f32 = ((tiles.iter().map(|(pos, _)| pos.x).reduce(f32::min).unwrap()
|
||||
- TILE_SIZE / 2.0)
|
||||
/ TILE_SIZE)
|
||||
@@ -183,49 +208,56 @@ pub fn build_quilted_terrain_sprites(
|
||||
let width_px = width_tiles * TILE_PIXELS;
|
||||
let height_px = height_tiles * TILE_PIXELS;
|
||||
|
||||
quilt_cache
|
||||
.dimensions
|
||||
.insert(*z_index, (width_px, height_px));
|
||||
// Store dimensions in our thread-safe map
|
||||
dimensions_mutex
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(z_index, (width_px, height_px));
|
||||
|
||||
let mut texture_data = vec![0u8; (width_px * height_px * 4) as usize];
|
||||
|
||||
// Process tiles for this z-level
|
||||
// We can't parallelize this inner loop without more complex locking on texture_data
|
||||
for (pos, floortile) in tiles {
|
||||
let texture = textures
|
||||
.handles
|
||||
.get(texture_ids.refs.get(&floortile.id).unwrap())
|
||||
.unwrap();
|
||||
if let Some(texture_id) = texture_ids.refs.get(&floortile.id) {
|
||||
if let Some(texture) = textures.handles.get(texture_id) {
|
||||
let rel_x = pos.x - min_x_aligned;
|
||||
let rel_y = pos.y - min_y_aligned;
|
||||
|
||||
let rel_x = pos.x - min_x_aligned;
|
||||
let rel_y = pos.y - min_y_aligned;
|
||||
let tile_x = (rel_x / TILE_SIZE).round() as u32;
|
||||
let tile_y = (height_tiles as f32 - 1.0 - (rel_y / TILE_SIZE).round()) as u32;
|
||||
|
||||
let tile_x = (rel_x / TILE_SIZE).round() as u32;
|
||||
let tile_y = (height_tiles as f32 - 1.0 - (rel_y / TILE_SIZE).round()) as u32;
|
||||
let target_x = tile_x * TILE_PIXELS;
|
||||
let target_y = tile_y * TILE_PIXELS;
|
||||
let mut data: Vec<&[u8]> = vec![];
|
||||
|
||||
let target_x = tile_x * TILE_PIXELS;
|
||||
let target_y = tile_y * TILE_PIXELS;
|
||||
let mut data: Vec<&[u8]> = vec![];
|
||||
let mut base_texture: &Image = &Default::default();
|
||||
|
||||
let mut base_texture: &Image = &Default::default();
|
||||
// Use a thread-safe approach to access images
|
||||
// In a full implementation, this would require a more sophisticated
|
||||
// thread-safe access pattern to Assets<Image>
|
||||
if let Some(_base_texture) = images.get(texture) {
|
||||
if let Some(_data) = &_base_texture.data {
|
||||
data.push(_data);
|
||||
base_texture = _base_texture;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(_base_texture) = images.get(texture) {
|
||||
if let Some(_data) = &_base_texture.data {
|
||||
data.push(_data);
|
||||
base_texture = _base_texture;
|
||||
blit_texture_with_alpha(
|
||||
data, // tile
|
||||
&mut texture_data, // terrain
|
||||
base_texture.size().x as u32,
|
||||
base_texture.size().y as u32,
|
||||
width_px,
|
||||
height_px,
|
||||
target_x,
|
||||
target_y,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
blit_texture_with_alpha(
|
||||
data, // tile
|
||||
&mut texture_data, // terrain
|
||||
base_texture.size().x as u32,
|
||||
base_texture.size().y as u32,
|
||||
width_px,
|
||||
height_px,
|
||||
target_x,
|
||||
target_y,
|
||||
);
|
||||
}
|
||||
|
||||
// Create the quilted texture
|
||||
let quilted_texture = Image::new_fill(
|
||||
render_resource::Extent3d {
|
||||
width: width_px,
|
||||
@@ -237,25 +269,48 @@ pub fn build_quilted_terrain_sprites(
|
||||
render_resource::TextureFormat::Rgba8UnormSrgb,
|
||||
RenderAssetUsages::RENDER_WORLD,
|
||||
);
|
||||
let texture_handle = images.add(quilted_texture);
|
||||
|
||||
// In a real implementation, we would need thread-safe access to images
|
||||
// For now, we'll collect the textures and add them after parallel processing
|
||||
let center_x = min_x_aligned + (max_x_aligned - min_x_aligned) / 2.0;
|
||||
let center_y = min_y_aligned + (max_y_aligned - min_y_aligned) / 2.0;
|
||||
|
||||
commands.spawn((
|
||||
Sprite {
|
||||
image: texture_handle,
|
||||
..Default::default()
|
||||
},
|
||||
Transform::from_xyz(
|
||||
center_x - TILE_SIZE / 2.0,
|
||||
center_y - TILE_SIZE / 2.0,
|
||||
-10.0 * TILE_SIZE,
|
||||
)
|
||||
.with_scale(Vec3::splat(PIXEL_RATIO)),
|
||||
Visibility::Hidden,
|
||||
TerrainSprite { z_index: *z_index },
|
||||
));
|
||||
// Store texture and position data for later spawning
|
||||
texture_handles_mutex
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(z_index, (quilted_texture, center_x, center_y));
|
||||
});
|
||||
|
||||
// Process collected textures and spawn entities
|
||||
let collected_dimensions = dimensions_mutex.into_inner().unwrap();
|
||||
let collected_textures = texture_handles_mutex.into_inner().unwrap();
|
||||
|
||||
// Update quilt_cache dimensions
|
||||
for (z_index, dimensions) in collected_dimensions {
|
||||
quilt_cache.dimensions.insert(z_index, dimensions);
|
||||
}
|
||||
|
||||
// Add images and spawn entities with the collected data
|
||||
for (z_index, (quilted_texture, center_x, center_y)) in collected_textures {
|
||||
let texture_handle = images.add(quilted_texture);
|
||||
|
||||
commands.command_scope(|mut cmd| {
|
||||
cmd.spawn((
|
||||
Sprite {
|
||||
image: texture_handle,
|
||||
..Default::default()
|
||||
},
|
||||
Transform::from_xyz(
|
||||
center_x - TILE_SIZE / 2.0,
|
||||
center_y - TILE_SIZE / 2.0,
|
||||
-10.0 * TILE_SIZE,
|
||||
)
|
||||
.with_scale(Vec3::splat(PIXEL_RATIO)),
|
||||
Visibility::Hidden,
|
||||
TerrainSprite { z_index },
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
quilt_cache.dirty_indices.clear();
|
||||
@@ -488,7 +543,21 @@ impl FixtureTilePrefab {
|
||||
FixtureTilePrefab {
|
||||
transform: Transform::from_translation(position),
|
||||
tile: FixtureTile {
|
||||
id: 2,
|
||||
id: FIXTURE_ID_OFFSET + 2,
|
||||
..Default::default()
|
||||
},
|
||||
visibility: Visibility::Hidden,
|
||||
needs_occluded: NeedsOccluded {
|
||||
has_been_occluded: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn log(position: Vec3) -> Self {
|
||||
FixtureTilePrefab {
|
||||
transform: Transform::from_translation(position),
|
||||
tile: FixtureTile {
|
||||
id: FIXTURE_ID_OFFSET + 3,
|
||||
..Default::default()
|
||||
},
|
||||
visibility: Visibility::Hidden,
|
||||
|
||||
Reference in New Issue
Block a user