summaryrefslogtreecommitdiff
path: root/client/src
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2025-01-10 17:49:37 +0100
committermetamuffin <metamuffin@disroot.org>2025-01-10 17:49:37 +0100
commit9335f7193ae666c378f5011e1eb90db5721e43fd (patch)
tree0e45add1360a74012c5aec8f665f2bde94c51d1d /client/src
parentc4e99ec50b3ac45ff01f902655cad07118835d1c (diff)
downloadweareserver-9335f7193ae666c378f5011e1eb90db5721e43fd.tar
weareserver-9335f7193ae666c378f5011e1eb90db5721e43fd.tar.bz2
weareserver-9335f7193ae666c378f5011e1eb90db5721e43fd.tar.zst
multithreaded world loading
Diffstat (limited to 'client/src')
-rw-r--r--client/src/download.rs45
-rw-r--r--client/src/renderer.rs31
-rw-r--r--client/src/scene_prepare.rs46
-rw-r--r--client/src/scene_render.rs2
-rw-r--r--client/src/state.rs45
5 files changed, 114 insertions, 55 deletions
diff --git a/client/src/download.rs b/client/src/download.rs
index 24dbe15..bc3de09 100644
--- a/client/src/download.rs
+++ b/client/src/download.rs
@@ -17,7 +17,7 @@
use crate::network::Network;
use anyhow::Result;
use log::debug;
-use std::{collections::HashSet, marker::PhantomData};
+use std::{collections::HashSet, marker::PhantomData, sync::RwLock};
use weareshared::{
helper::ReadWrite,
packets::{Packet, Resource},
@@ -25,6 +25,9 @@ use weareshared::{
};
pub struct Downloader {
+ inner: RwLock<DownloaderState>,
+}
+struct DownloaderState {
have: HashSet<Resource>,
need: HashSet<Resource>,
pending: HashSet<Resource>,
@@ -34,33 +37,38 @@ pub struct Downloader {
impl Downloader {
pub fn new(store: ResourceStore) -> Self {
Self {
- have: HashSet::new(),
- need: HashSet::new(),
- pending: HashSet::new(),
- store,
+ inner: DownloaderState {
+ have: HashSet::new(),
+ need: HashSet::new(),
+ pending: HashSet::new(),
+ store,
+ }
+ .into(),
}
}
- pub fn try_get<T: ReadWrite>(&mut self, hash: Resource<T>) -> Result<Option<T>> {
+ pub fn try_get<T: ReadWrite>(&self, hash: Resource<T>) -> Result<Option<T>> {
self.try_get_raw(Resource(hash.0, PhantomData))?
.map(|x| T::read(&mut x.as_slice()))
.transpose()
}
- pub fn try_get_raw(&mut self, hash: Resource) -> Result<Option<Vec<u8>>> {
- if self.have.contains(&hash) {
- self.store.get_raw(hash)
+ pub fn try_get_raw(&self, hash: Resource) -> Result<Option<Vec<u8>>> {
+ let mut state = self.inner.write().unwrap();
+ if state.have.contains(&hash) {
+ state.store.get_raw(hash)
} else {
- self.need.insert(hash);
+ state.need.insert(hash);
Ok(None)
}
}
- pub fn packet(&mut self, p: &Packet) -> Result<()> {
+ pub fn packet(&self, p: &Packet) -> Result<()> {
+ let mut state = self.inner.write().unwrap();
match p {
Packet::RespondResource(d) => {
let key = Resource(sha256(&d.0), PhantomData);
- self.store.set_raw(&d.0)?;
- self.need.remove(&key);
- self.pending.remove(&key);
- if self.have.insert(key) {
+ state.store.set_raw(&d.0)?;
+ state.need.remove(&key);
+ state.pending.remove(&key);
+ if state.have.insert(key) {
debug!("have {key}");
}
}
@@ -68,9 +76,10 @@ impl Downloader {
}
Ok(())
}
- pub fn update(&mut self, network: &mut Network) -> Result<()> {
+ pub fn update(&self, network: &Network) -> Result<()> {
+ let mut state = self.inner.write().unwrap();
let mut new_pending = Vec::new();
- for n in self.need.difference(&self.pending) {
+ for n in state.need.difference(&state.pending) {
network
.packet_send
.send(Packet::RequestResource(*n))
@@ -78,7 +87,7 @@ impl Downloader {
debug!("need {n}");
new_pending.push(*n);
}
- self.pending.extend(new_pending);
+ state.pending.extend(new_pending);
Ok(())
}
}
diff --git a/client/src/renderer.rs b/client/src/renderer.rs
index bb87b6c..a9a2332 100644
--- a/client/src/renderer.rs
+++ b/client/src/renderer.rs
@@ -15,12 +15,17 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use crate::{
- camera::Camera, scene_prepare::ScenePreparer, scene_render::ScenePipeline, ui::UiRenderer,
+ camera::Camera, download::Downloader, scene_prepare::ScenePreparer,
+ scene_render::ScenePipeline, ui::UiRenderer,
};
use anyhow::{Result, anyhow};
use log::{info, warn};
use pollster::FutureExt;
-use std::sync::Arc;
+use std::{
+ sync::Arc,
+ thread::{sleep, spawn},
+ time::Duration,
+};
use weareshared::tree::SceneTree;
use wgpu::{
Backends, CommandEncoderDescriptor, Device, DeviceDescriptor, Extent3d, Features, Instance,
@@ -37,12 +42,12 @@ pub struct Renderer<'a> {
surface_configuration: SurfaceConfiguration,
scene_pipeline: ScenePipeline,
pub ui_renderer: UiRenderer,
- pub scene_prepare: ScenePreparer,
+ pub scene_prepare: Arc<ScenePreparer>,
surface_needs_reconfigure: bool,
depth: TextureView,
}
impl<'a> Renderer<'a> {
- pub fn new(window: &'a Window) -> Result<Self> {
+ pub fn new(window: &'a Window, downloader: Arc<Downloader>) -> Result<Self> {
info!("wgpu init");
let instance = Instance::new(InstanceDescriptor {
backends: Backends::all(),
@@ -85,7 +90,11 @@ impl<'a> Renderer<'a> {
let (scene_pipeline, texture_bgl) =
ScenePipeline::new(&device, surface_configuration.format);
- let scene_prepare = ScenePreparer::new(device.clone(), queue.clone(), texture_bgl);
+ let scene_prepare = Arc::new(ScenePreparer::new(
+ device.clone(),
+ queue.clone(),
+ texture_bgl,
+ ));
let ui_renderer =
UiRenderer::new(device.clone(), queue.clone(), surface_configuration.format);
@@ -106,6 +115,16 @@ impl<'a> Renderer<'a> {
});
let depth = depth.create_view(&TextureViewDescriptor::default());
+ {
+ let scene_prepare = scene_prepare.clone();
+ spawn(move || {
+ loop {
+ scene_prepare.update(&downloader).unwrap();
+ sleep(Duration::from_millis(50));
+ }
+ });
+ }
+
Ok(Self {
scene_pipeline,
scene_prepare,
@@ -170,7 +189,7 @@ impl<'a> Renderer<'a> {
&target_view,
&self.depth,
scene,
- &mut self.scene_prepare.prefabs,
+ &self.scene_prepare.prefabs,
projection,
);
diff --git a/client/src/scene_prepare.rs b/client/src/scene_prepare.rs
index f74277c..ae7e599 100644
--- a/client/src/scene_prepare.rs
+++ b/client/src/scene_prepare.rs
@@ -22,7 +22,7 @@ use std::{
collections::{HashMap, HashSet},
hash::Hash,
io::Cursor,
- sync::Arc,
+ sync::{Arc, RwLock},
};
use weareshared::{
Affine3A,
@@ -37,25 +37,36 @@ use wgpu::{
};
pub struct DemandMap<K, V> {
+ inner: RwLock<DemandMapState<K, V>>,
+}
+struct DemandMapState<K, V> {
values: HashMap<K, V>,
needed: HashSet<K>,
}
-impl<K: Hash + Eq, V: Clone> DemandMap<K, V> {
+impl<K: Hash + Eq + Clone, V: Clone> DemandMap<K, V> {
pub fn new() -> Self {
Self {
- needed: HashSet::new(),
- values: HashMap::new(),
+ inner: DemandMapState {
+ needed: HashSet::new(),
+ values: HashMap::new(),
+ }
+ .into(),
}
}
- pub fn insert(&mut self, key: K, value: V) {
- self.needed.remove(&key);
- self.values.insert(key, value);
+ pub fn needed(&self) -> Vec<K> {
+ self.inner.read().unwrap().needed.iter().cloned().collect()
+ }
+ pub fn insert(&self, key: K, value: V) {
+ let mut s = self.inner.write().unwrap();
+ s.needed.remove(&key);
+ s.values.insert(key, value);
}
- pub fn try_get(&mut self, key: K) -> Option<V> {
- if let Some(k) = self.values.get(&key) {
+ pub fn try_get(&self, key: K) -> Option<V> {
+ let mut s = self.inner.write().unwrap();
+ if let Some(k) = s.values.get(&key) {
Some(k.to_owned())
} else {
- self.needed.insert(key);
+ s.needed.insert(key);
None
}
}
@@ -100,8 +111,8 @@ impl ScenePreparer {
queue,
}
}
- pub fn update(&mut self, dls: &mut Downloader) -> Result<()> {
- for pres in self.prefabs.needed.clone() {
+ pub fn update(&self, dls: &Downloader) -> Result<()> {
+ for pres in self.prefabs.needed() {
if let Some(prefab) = dls.try_get(pres.clone())? {
let mut rprefab = RPrefab(Vec::new());
for (aff, partres) in &prefab.mesh {
@@ -115,7 +126,7 @@ impl ScenePreparer {
}
}
}
- for pres in self.index_buffers.needed.clone() {
+ for pres in self.index_buffers.needed() {
if let Some(buf) = dls.try_get(pres.clone())? {
let buf = buf
.0
@@ -133,7 +144,7 @@ impl ScenePreparer {
debug!("index buffer created (len={}) {pres}", buf.len() / 2);
}
}
- for pres in self.vertex_buffers.needed.clone() {
+ for pres in self.vertex_buffers.needed() {
if let Some(buf) = dls.try_get(pres.clone())? {
let buf = buf
.0
@@ -154,7 +165,7 @@ impl ScenePreparer {
);
}
}
- for pres in self.textures.needed.clone() {
+ for pres in self.textures.needed() {
if let Some(buf) = dls.try_get(pres.clone())? {
let image = ImageReader::new(Cursor::new(buf.0)).with_guessed_format()?;
let image = image.decode()?;
@@ -171,7 +182,7 @@ impl ScenePreparer {
self.textures.insert(pres.clone(), tex_bg);
}
}
- for pres in self.placeholder_textures.needed.clone() {
+ for pres in self.placeholder_textures.needed() {
let tex_bg = create_texture(
&self.device,
&self.queue,
@@ -182,12 +193,11 @@ impl ScenePreparer {
);
self.placeholder_textures.insert(pres, tex_bg);
}
- for pres in self.mesh_parts.needed.clone() {
+ for pres in self.mesh_parts.needed() {
if let Some(part) = dls.try_get(pres.clone())? {
if let (Some(indexres), Some(positionres)) = (part.index, part.va_position) {
let Some((index, index_count)) = self.index_buffers.try_get(indexres.clone())
else {
- self.index_buffers.needed.insert(indexres);
continue;
};
let mut position = Vec::new();
diff --git a/client/src/scene_render.rs b/client/src/scene_render.rs
index 9aceaef..047cee4 100644
--- a/client/src/scene_render.rs
+++ b/client/src/scene_render.rs
@@ -128,7 +128,7 @@ impl ScenePipeline {
target: &TextureView,
depth: &TextureView,
scene: &SceneTree,
- prefabs: &mut DemandMap<Resource<Prefab>, Arc<RPrefab>>,
+ prefabs: &DemandMap<Resource<Prefab>, Arc<RPrefab>>,
projection: Mat4,
) {
let mut rpass = commands.begin_render_pass(&RenderPassDescriptor {
diff --git a/client/src/state.rs b/client/src/state.rs
index 00a8c0b..cd11d4a 100644
--- a/client/src/state.rs
+++ b/client/src/state.rs
@@ -18,17 +18,26 @@ use crate::{camera::Camera, download::Downloader, network::Network, renderer::Re
use anyhow::{Context, Result};
use glam::{Vec2, Vec3};
use log::{info, warn};
-use std::{net::TcpStream, time::Instant};
-use weareshared::{store::ResourceStore, tree::SceneTree};
+use std::{net::TcpStream, sync::Arc, time::Instant};
+use weareshared::{
+ packets::{Packet, Resource},
+ resources::PrefabIndex,
+ store::ResourceStore,
+ tree::SceneTree,
+};
use winit::event::MouseButton;
pub struct State<'a> {
- pub network: Network,
- pub downloader: Downloader,
+ pub network: Arc<Network>,
+ pub downloader: Arc<Downloader>,
pub renderer: Renderer<'a>,
pub tree: SceneTree,
pub camera: Camera,
pub delta: DeltaState,
+
+ pub prefab_index: PrefabIndex,
+ pub prefab_index_res_loaded: Option<Resource<PrefabIndex>>,
+ pub prefab_index_res: Option<Resource<PrefabIndex>>,
}
pub struct DeltaState {
@@ -41,18 +50,22 @@ pub struct DeltaState {
impl<'a> State<'a> {
pub fn new(conn: TcpStream, window: &'a winit::window::Window) -> Result<State<'a>> {
info!("new state");
+ let downloader = Arc::new(Downloader::new(ResourceStore::new_memory()));
Ok(Self {
camera: Camera::new(),
- network: Network::new(conn),
+ network: Network::new(conn).into(),
tree: SceneTree::default(),
- renderer: Renderer::new(window)?,
- downloader: Downloader::new(ResourceStore::new_memory()),
+ renderer: Renderer::new(window, downloader.clone())?,
+ downloader,
delta: DeltaState {
time: Instant::now(),
move_dir: Vec3::ZERO,
mouse_acc: Vec2::ZERO,
cursor_pos: Vec2::ZERO,
},
+ prefab_index_res: None,
+ prefab_index_res_loaded: None,
+ prefab_index: PrefabIndex::default(),
})
}
pub fn draw(&mut self) {
@@ -92,15 +105,23 @@ impl<'a> State<'a> {
for p in self.network.packet_recv.try_iter() {
self.downloader.packet(&p)?;
self.tree.packet(&p);
+ if let Packet::PrefabIndex(res) = &p {
+ self.prefab_index_res = Some(res.to_owned());
+ }
}
self.downloader
- .update(&mut self.network)
+ .update(&self.network)
.context("downloader state")?;
- self.renderer
- .scene_prepare
- .update(&mut self.downloader)
- .context("scene preparation")?;
+ if self.prefab_index_res != self.prefab_index_res_loaded {
+ if let Some(res) = &self.prefab_index_res {
+ if let Some(index) = self.downloader.try_get(res.to_owned())? {
+ info!("prefab index loaded");
+ self.prefab_index = index;
+ self.prefab_index_res_loaded = Some(res.to_owned());
+ }
+ }
+ }
Ok(())
}
}