summaryrefslogtreecommitdiff
path: root/client/src/scene_prepare.rs
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/scene_prepare.rs')
-rw-r--r--client/src/scene_prepare.rs295
1 files changed, 227 insertions, 68 deletions
diff --git a/client/src/scene_prepare.rs b/client/src/scene_prepare.rs
index e7d3403..e50dd34 100644
--- a/client/src/scene_prepare.rs
+++ b/client/src/scene_prepare.rs
@@ -43,15 +43,16 @@ use weareshared::{
use wgpu::{
AddressMode, BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout,
BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingResource, BindingType, BlendState,
- Buffer, BufferBindingType, BufferUsages, ColorTargetState, ColorWrites, CompareFunction,
- DepthBiasState, DepthStencilState, Device, Extent3d, Face, FilterMode, FragmentState,
- FrontFace, MultisampleState, PipelineCompilationOptions, PipelineLayoutDescriptor, PolygonMode,
- PrimitiveState, PrimitiveTopology, PushConstantRange, Queue, RenderPipeline,
- RenderPipelineDescriptor, SamplerBindingType, SamplerDescriptor, ShaderStages, StencilState,
- Texture, TextureDescriptor, TextureDimension, TextureFormat, TextureSampleType, TextureUsages,
- TextureViewDescriptor, TextureViewDimension, VertexAttribute, VertexBufferLayout, VertexFormat,
- VertexState, VertexStepMode,
- util::{BufferInitDescriptor, DeviceExt, TextureDataOrder},
+ Buffer, BufferBindingType, BufferUsages, Color, ColorTargetState, ColorWrites,
+ CommandEncoderDescriptor, CompareFunction, DepthBiasState, DepthStencilState, Device, Extent3d,
+ Face, FilterMode, FragmentState, FrontFace, ImageDataLayout, LoadOp, MultisampleState,
+ Operations, PipelineCompilationOptions, PipelineLayoutDescriptor, PolygonMode, PrimitiveState,
+ PrimitiveTopology, PushConstantRange, Queue, RenderPassColorAttachment, RenderPassDescriptor,
+ RenderPipeline, RenderPipelineDescriptor, SamplerBindingType, SamplerDescriptor, ShaderStages,
+ StencilState, StoreOp, Texture, TextureDescriptor, TextureDimension, TextureFormat,
+ TextureSampleType, TextureUsages, TextureViewDescriptor, TextureViewDimension, VertexAttribute,
+ VertexBufferLayout, VertexFormat, VertexState, VertexStepMode, include_wgsl,
+ util::{BufferInitDescriptor, DeviceExt},
};
pub struct DemandMap<K, V> {
@@ -100,7 +101,7 @@ pub struct ScenePreparer {
shaders: SceneShaders,
render_format: TextureFormat,
- textures: DemandMap<(Resource<Image<'static>>, bool), (Arc<Texture>, Arc<BindGroup>)>,
+ textures: DemandMap<TextureSpec, (Arc<Texture>, Arc<BindGroup>)>,
placeholder_textures: DemandMap<TextureIdentityKind, (Arc<Texture>, Arc<BindGroup>)>,
index_buffers: DemandMap<Resource<Vec<[u32; 3]>>, (Arc<Buffer>, u32)>,
vertex_buffers: DemandMap<Resource<Vec<f32>>, Arc<Buffer>>,
@@ -109,12 +110,13 @@ pub struct ScenePreparer {
generated_texcoord_buffers: DemandMap<TexcoordBufferSpec, Arc<Buffer>>,
mesh_parts: DemandMap<Resource<MeshPart>, Arc<RMeshPart>>,
materials: DemandMap<Material, Arc<BindGroup>>,
- pipelines: DemandMap<PipelineConfig, Arc<RenderPipeline>>,
+ pipelines: DemandMap<PipelineSpec, Arc<RenderPipeline>>,
+ mip_generation_pipelines: DemandMap<TextureFormat, Arc<MipGenerationPipeline>>,
pub prefabs: DemandMap<Resource<Prefab>, Arc<RPrefab>>,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
-pub struct PipelineConfig {
+pub struct PipelineSpec {
format: TextureFormat,
skin: bool,
backface_culling: bool,
@@ -140,6 +142,12 @@ pub struct RMeshPart {
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
+struct TextureSpec {
+ data: Resource<Image<'static>>,
+ linear: bool,
+}
+
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
struct TangentBufferSpec {
index: Resource<Vec<[u32; 3]>>,
position: Resource<Vec<Vec3>>,
@@ -177,6 +185,7 @@ struct Material {
impl ScenePreparer {
pub fn new(device: Arc<Device>, queue: Arc<Queue>, render_format: TextureFormat) -> Self {
Self {
+ // TODO normal mipmap requires linear texture, also demand map?
render_format,
layouts: SceneBgLayouts::load(&device),
shaders: SceneShaders::load(&device),
@@ -193,6 +202,7 @@ impl ScenePreparer {
generated_texcoord_buffers: DemandMap::new(),
materials: DemandMap::new(),
pipelines: DemandMap::new(),
+ mip_generation_pipelines: DemandMap::new(),
}
}
pub fn update(&self, dls: &Downloader) -> Result<usize> {
@@ -254,32 +264,47 @@ impl ScenePreparer {
num_done += 1;
}
}
- for (pres, linear) in self.textures.needed() {
+ for format in self.mip_generation_pipelines.needed() {
+ self.mip_generation_pipelines.insert(
+ format,
+ Arc::new(MipGenerationPipeline::load(&self.device, format)),
+ 0,
+ );
+ }
+ for spec in self.textures.needed() {
let start = Instant::now();
- if let Some(buf) = dls.try_get(pres.clone())? {
- let image = ImageReader::new(Cursor::new(buf.0)).with_guessed_format()?;
- let image = image.decode()?;
- let dims = (image.width(), image.height());
- let image = image.into_rgba8();
- let image = image.into_vec();
- let tex_bg = create_texture(
- &self.device,
- &self.queue,
- &self.layouts.texture,
- &image,
- dims.0,
- dims.1,
- linear,
- );
- self.textures
- .insert((pres.clone(), linear), tex_bg, image.len());
- debug!(
- "texture created (res={}x{}, took {:?})",
- dims.0,
- dims.1,
- start.elapsed()
- );
- num_done += 1;
+ let format = if spec.linear {
+ TextureFormat::Rgba8Unorm
+ } else {
+ TextureFormat::Rgba8UnormSrgb
+ };
+ if let Some(mipgen) = self.mip_generation_pipelines.try_get(format) {
+ if let Some(buf) = dls.try_get(spec.data.clone())? {
+ let image = ImageReader::new(Cursor::new(buf.0)).with_guessed_format()?;
+ let image = image.decode()?;
+ let dims = (image.width(), image.height());
+ let image = image.into_rgba8();
+ let image = image.into_vec();
+ let tex_bg = create_texture(
+ &self.device,
+ &self.queue,
+ &self.layouts.texture,
+ &image,
+ dims.0,
+ dims.1,
+ format,
+ dims.0.ilog2().max(4) - 3,
+ Some(&mipgen),
+ );
+ self.textures.insert(spec, tex_bg, image.len());
+ debug!(
+ "texture created (res={}x{}, took {:?})",
+ dims.0,
+ dims.1,
+ start.elapsed()
+ );
+ num_done += 1;
+ }
}
}
for kind in self.placeholder_textures.needed() {
@@ -294,7 +319,13 @@ impl ScenePreparer {
&color,
1,
1,
- linear,
+ if linear {
+ TextureFormat::Rgba8Unorm
+ } else {
+ TextureFormat::Rgba8UnormSrgb
+ },
+ 1,
+ None,
);
self.placeholder_textures.insert(kind, tex_bg, 4);
num_done += 1;
@@ -436,7 +467,10 @@ impl ScenePreparer {
let mut tex_albedo = None;
if let Some(albedores) = part.tex_albedo {
- if let Some((_tex, bg)) = self.textures.try_get((albedores, false)) {
+ if let Some((_tex, bg)) = self.textures.try_get(TextureSpec {
+ data: albedores,
+ linear: false,
+ }) {
tex_albedo = Some(bg)
}
} else {
@@ -449,7 +483,10 @@ impl ScenePreparer {
}
let mut tex_normal = None;
if let Some(normalres) = part.tex_normal {
- if let Some((_tex, bg)) = self.textures.try_get((normalres, true)) {
+ if let Some((_tex, bg)) = self.textures.try_get(TextureSpec {
+ data: normalres,
+ linear: true,
+ }) {
tex_normal = Some(bg)
}
} else {
@@ -489,7 +526,7 @@ impl ScenePreparer {
Some(None)
};
- let pipeline = self.pipelines.try_get(PipelineConfig {
+ let pipeline = self.pipelines.try_get(PipelineSpec {
format: self.render_format,
skin: false,
backface_culling: part.g_double_sided.is_none(),
@@ -562,6 +599,44 @@ impl ScenePreparer {
}
}
+struct MipGenerationPipeline {
+ pipeline: RenderPipeline,
+}
+impl MipGenerationPipeline {
+ pub fn load(device: &Device, format: TextureFormat) -> Self {
+ let shader = device.create_shader_module(include_wgsl!("shaders/texture_copy.wgsl"));
+ let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
+ label: Some("map generator"),
+ layout: None,
+ vertex: wgpu::VertexState {
+ module: &shader,
+ entry_point: Some("vs_main"),
+ compilation_options: Default::default(),
+ buffers: &[],
+ },
+ fragment: Some(wgpu::FragmentState {
+ module: &shader,
+ entry_point: Some("fs_main"),
+ compilation_options: Default::default(),
+ targets: &[Some(ColorTargetState {
+ format,
+ blend: None,
+ write_mask: ColorWrites::ALL,
+ })],
+ }),
+ primitive: wgpu::PrimitiveState {
+ topology: wgpu::PrimitiveTopology::TriangleList,
+ ..Default::default()
+ },
+ depth_stencil: None,
+ multisample: wgpu::MultisampleState::default(),
+ multiview: None,
+ cache: None,
+ });
+ Self { pipeline }
+ }
+}
+
fn create_texture(
device: &Device,
queue: &Queue,
@@ -569,40 +644,37 @@ fn create_texture(
data: &[u8],
width: u32,
height: u32,
- linear: bool,
+ format: TextureFormat,
+ mip_level_count: u32,
+ mipgen: Option<&MipGenerationPipeline>,
) -> (Arc<Texture>, Arc<BindGroup>) {
- let texture = device.create_texture_with_data(
- &queue,
- &TextureDescriptor {
- label: None,
- size: Extent3d {
- depth_or_array_layers: 1,
- width,
- height,
- },
- mip_level_count: 1,
- sample_count: 1,
- dimension: TextureDimension::D2,
- format: if linear {
- TextureFormat::Rgba8Unorm
- } else {
- TextureFormat::Rgba8UnormSrgb
- },
- usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST,
- view_formats: &[],
- },
- TextureDataOrder::LayerMajor,
- data,
- );
+ let extent = Extent3d {
+ depth_or_array_layers: 1,
+ width,
+ height,
+ };
+ let texture = device.create_texture(&TextureDescriptor {
+ label: None,
+ size: extent,
+ mip_level_count,
+ sample_count: 1,
+ dimension: TextureDimension::D2,
+ format,
+ usage: TextureUsages::TEXTURE_BINDING
+ | TextureUsages::COPY_DST
+ | TextureUsages::RENDER_ATTACHMENT,
+ view_formats: &[],
+ });
let textureview = texture.create_view(&TextureViewDescriptor::default());
let sampler = device.create_sampler(&SamplerDescriptor {
address_mode_u: AddressMode::Repeat,
address_mode_v: AddressMode::Repeat,
mag_filter: FilterMode::Linear,
min_filter: FilterMode::Linear,
+ mipmap_filter: FilterMode::Linear,
..Default::default()
});
- let bindgroup = device.create_bind_group(&BindGroupDescriptor {
+ let bind_group = device.create_bind_group(&BindGroupDescriptor {
label: None,
layout: &bgl,
entries: &[
@@ -616,7 +688,94 @@ fn create_texture(
},
],
});
- (Arc::new(texture), Arc::new(bindgroup))
+
+ let level_views = (0..mip_level_count)
+ .map(|mip| {
+ texture.create_view(&TextureViewDescriptor {
+ label: Some("mip generation level view"),
+ format: None,
+ dimension: None,
+ aspect: wgpu::TextureAspect::All,
+ base_mip_level: mip,
+ mip_level_count: Some(1),
+ base_array_layer: 0,
+ array_layer_count: None,
+ })
+ })
+ .collect::<Vec<_>>();
+
+ let mut encoder = device.create_command_encoder(&CommandEncoderDescriptor { label: None });
+
+ // TODO why does copy_buffer_to_texture have more restrictive alignment requirements?!
+ // let upload_buffer = device.create_buffer_init(&BufferInitDescriptor {
+ // label: Some("texture upload"),
+ // contents: data,
+ // usage: BufferUsages::COPY_DST | BufferUsages::COPY_SRC,
+ // });
+ // encoder.copy_buffer_to_texture(
+ // ImageCopyBuffer {
+ // buffer: &upload_buffer,
+ // layout: ImageDataLayout {
+ // offset: 0,
+ // bytes_per_row: Some(width * 4),
+ // rows_per_image: None,
+ // },
+ // },
+ // texture.as_image_copy(),
+ // extent,
+ // );
+ queue.write_texture(
+ texture.as_image_copy(),
+ data,
+ ImageDataLayout {
+ bytes_per_row: Some(width * 4),
+ rows_per_image: None,
+ offset: 0,
+ },
+ extent,
+ );
+
+ for level in 1..mip_level_count {
+ let mip_pipeline = &mipgen.unwrap().pipeline;
+ let source_view = &level_views[level as usize - 1];
+ let target_view = &level_views[level as usize];
+ let mip_bind_group = device.create_bind_group(&BindGroupDescriptor {
+ layout: &mip_pipeline.get_bind_group_layout(0),
+ entries: &[
+ BindGroupEntry {
+ binding: 0,
+ resource: BindingResource::TextureView(source_view),
+ },
+ BindGroupEntry {
+ binding: 1,
+ resource: BindingResource::Sampler(&sampler),
+ },
+ ],
+ label: None,
+ });
+ let mut rpass = encoder.begin_render_pass(&RenderPassDescriptor {
+ label: None,
+ color_attachments: &[Some(RenderPassColorAttachment {
+ view: target_view,
+ resolve_target: None,
+ ops: Operations {
+ load: LoadOp::Clear(Color::WHITE),
+ store: StoreOp::Store,
+ },
+ })],
+ depth_stencil_attachment: None,
+ timestamp_writes: None,
+ occlusion_query_set: None,
+ });
+
+ rpass.set_pipeline(&mip_pipeline);
+ rpass.set_bind_group(0, &mip_bind_group, &[]);
+ rpass.draw(0..3, 0..1);
+ }
+
+ queue.submit(Some(encoder.finish()));
+
+ (Arc::new(texture), Arc::new(bind_group))
}
pub struct SceneBgLayouts {
@@ -678,7 +837,7 @@ impl SceneBgLayouts {
}
}
-impl PipelineConfig {
+impl PipelineSpec {
pub fn create(
&self,
device: &Device,