wde_terrain/render/
renderer_gpu.rs1use std::collections::HashMap;
2
3use bevy::{ecs::system::SystemParamItem, prelude::*};
4use wde_renderer::prelude::*;
5
6use crate::{
7 manager::{ChunkPos, SPLAT_MAP_COUNT, TerrainDirtyTile},
8 prelude::TerrainExtractor,
9 render::{extractor, renderer::TerrainRenderer}
10};
11
12#[derive(Asset, Clone, TypePath, Default)]
13pub struct TerrainTileBgRender {
14 tile_position: ChunkPos,
15 heightmap: Handle<Texture>,
16 splatmaps: Vec<Handle<Texture>>
17}
18impl RenderBinding for TerrainTileBgRender {
19 type Params = ();
20
21 fn describe(
22 &mut self,
23 _params: &SystemParamItem<Self::Params>,
24 builder: &mut RenderBindingBuilder
25 ) {
26 builder.add_texture_view_from_id(Some(self.heightmap.id()));
27 builder.add_texture_sampler_from_id(Some(self.heightmap.id()));
28 for i in 0..SPLAT_MAP_COUNT / 4 {
29 if i as usize >= self.splatmaps.len() {
30 continue;
31 }
32 builder.add_texture_view_from_id(Some(self.splatmaps[i as usize].id()));
33 builder.add_texture_sampler_from_id(Some(self.splatmaps[i as usize].id()));
34 }
35 }
36
37 fn label(&self) -> &str {
38 Box::leak(
39 format!(
40 "terrain-tile-{}-{}-render",
41 self.tile_position.x, self.tile_position.y
42 )
43 .into_boxed_str()
44 )
45 }
46}
47
48#[derive(Asset, Clone, TypePath, Default)]
49pub struct TerrainTileBgCompute {
50 tile_position: ChunkPos,
51 heightmap: Handle<Texture>,
52 splatmaps: Vec<Handle<Texture>>
53}
54impl RenderBinding for TerrainTileBgCompute {
55 type Params = ();
56
57 fn describe(
58 &mut self,
59 _params: &SystemParamItem<Self::Params>,
60 builder: &mut RenderBindingBuilder
61 ) {
62 builder.add_storage_texture_from_id(Some(self.heightmap.id()));
63 for i in 0..SPLAT_MAP_COUNT / 4 {
64 if i as usize >= self.splatmaps.len() {
65 continue;
66 }
67 builder.add_storage_texture_from_id(Some(self.splatmaps[i as usize].id()));
68 }
69 }
70
71 fn label(&self) -> &str {
72 Box::leak(
73 format!(
74 "terrain-tile-{}-{}-compute",
75 self.tile_position.x, self.tile_position.y
76 )
77 .into_boxed_str()
78 )
79 }
80}
81
82#[derive(Default, Clone)]
84pub struct TerrainRenderTileGPU {
85 pub position: ChunkPos,
87
88 pub heightmap: Handle<Texture>,
90 pub splatmaps: Vec<Handle<Texture>>,
91
92 pub render_bind_group: Option<Handle<TerrainTileBgRender>>,
94 pub compute_bind_group: Option<Handle<TerrainTileBgCompute>>
95}
96
97#[derive(Resource, Default)]
99pub struct TerrainRendererGPU {
100 pub ready: bool,
102 pub pos_to_tile: HashMap<ChunkPos, usize>,
104 pub tiles: Vec<TerrainRenderTileGPU>,
106 pub dirty: Vec<Option<TerrainDirtyTile>>
108}
109impl TerrainRendererGPU {
110 pub fn extract_dirty(
112 main_terrain_extractor: &TerrainExtractor,
113 render_terrain_extractor: &mut TerrainExtractor,
114 terrain_renderer_query: Query<&TerrainRenderer>,
115 gpu_terrain_renderer: &mut TerrainRendererGPU
116 ) {
117 extractor::extract_dirty(main_terrain_extractor, render_terrain_extractor);
119
120 let terrain_renderer = match terrain_renderer_query.iter().next() {
122 Some(terrain) => terrain,
123 None => return
124 };
125
126 if gpu_terrain_renderer.tiles.is_empty() {
128 gpu_terrain_renderer.tiles = terrain_renderer
129 .tiles
130 .iter()
131 .map(|tile| TerrainRenderTileGPU {
132 position: tile.position,
133 heightmap: tile.heightmap.clone(),
134 splatmaps: tile.splatmaps.to_vec(),
135 render_bind_group: None,
136 compute_bind_group: None
137 })
138 .collect();
139 gpu_terrain_renderer.pos_to_tile = terrain_renderer.pos_to_tile.clone();
140 }
141
142 for i in 0..terrain_renderer.dirty.len() {
144 if let Some(dirty_tile) = &terrain_renderer.dirty[i] {
145 gpu_terrain_renderer.dirty.push(Some(dirty_tile.clone()));
146 }
147 }
148 }
149
150 pub fn upload_dirty(
152 mut gpu_terrain_renderer: ResMut<TerrainRendererGPU>,
153 textures: Res<RenderAssets<GpuTexture>>,
154 render_instance: Res<RenderInstance>
155 ) {
156 let render_instance = render_instance.0.read().unwrap();
157
158 for i in 0..gpu_terrain_renderer.dirty.len() {
160 if let Some(dirty_tile) = gpu_terrain_renderer.dirty[i].take() {
161 let (pos, map_type, splat_map_index, tile_data) = dirty_tile;
162 let tile_index = match gpu_terrain_renderer.pos_to_tile.get(&pos) {
163 Some(index) => *index,
164 None => continue
165 };
166 let tile = &mut gpu_terrain_renderer.tiles[tile_index];
167 match map_type {
168 0 => {
169 let heightmap = match textures.get(&tile.heightmap) {
171 Some(texture) => texture,
172 None => continue
173 };
174 heightmap.texture.copy_from_buffer(
175 &render_instance,
176 heightmap.texture.format,
177 bytemuck::cast_slice(&tile_data)
178 );
179 }
180 1 => {
181 let splatmap = match textures.get(&tile.splatmaps[splat_map_index as usize])
183 {
184 Some(texture) => texture,
185 None => continue
186 };
187 splatmap.texture.copy_from_buffer(
188 &render_instance,
189 splatmap.texture.format,
190 bytemuck::cast_slice(&tile_data)
191 );
192 }
193 _ => unreachable!()
194 };
195 }
196 }
197 }
198
199 pub fn prepare_bind_groups(
201 asset_server: Res<AssetServer>,
202 mut gpu_terrain_renderer: ResMut<TerrainRendererGPU>
203 ) {
204 if gpu_terrain_renderer.ready {
206 return;
207 }
208
209 for tile in &mut gpu_terrain_renderer.tiles {
211 if tile.render_bind_group.is_some() {
213 continue;
214 }
215
216 tile.render_bind_group = Some(asset_server.add(TerrainTileBgRender {
218 tile_position: tile.position,
219 heightmap: tile.heightmap.clone(),
220 splatmaps: tile.splatmaps.to_vec()
221 }));
222
223 tile.compute_bind_group = Some(asset_server.add(TerrainTileBgCompute {
225 tile_position: tile.position,
226 heightmap: tile.heightmap.clone(),
227 splatmaps: tile.splatmaps.to_vec()
228 }));
229 }
230
231 gpu_terrain_renderer.ready = gpu_terrain_renderer
233 .tiles
234 .iter()
235 .all(|tile| tile.render_bind_group.is_some());
236 }
237}