mirror of
https://github.com/bevyengine/bevy
synced 2024-11-21 20:23:28 +00:00
more texture work
This commit is contained in:
parent
9e5f4aeefc
commit
8d3026899d
12 changed files with 243 additions and 44 deletions
|
@ -12,6 +12,7 @@ Here is the current list of planned features. All items are sorted in approximat
|
|||
* Macro to produce vertex buffer attributes (and maybe descriptors) from structs
|
||||
* Add runtime type safety to uniform bindings (and maybe compile time)
|
||||
* Inject layout set/bindings into shader source so they don't need to be defined in-shader. Specify set / binding indices in resource providers?
|
||||
* Pull as much logic as possible from wgpu_renderer into a "render orchestrator" struct/trait
|
||||
* Error Handling
|
||||
* Custom error type?
|
||||
* Remove as many panics / unwraps as possible
|
||||
|
|
|
@ -16,7 +16,7 @@ pub fn ui_draw_target(
|
|||
let mut current_mesh_index_buffer = None;
|
||||
let ui_instances_buffer = {
|
||||
let renderer = render_pass.get_renderer();
|
||||
match renderer.get_named_resource(resource_name::buffer::UI_INSTANCES) {
|
||||
match renderer.get_render_resources().get_named_resource(resource_name::buffer::UI_INSTANCES) {
|
||||
Some(buffer) => buffer,
|
||||
None => return,
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::render::shader_reflect::ShaderLayout;
|
||||
use crate::{asset::Texture, render::shader_reflect::ShaderLayout};
|
||||
use std::{
|
||||
collections::{hash_map::DefaultHasher, BTreeSet, HashMap},
|
||||
hash::{Hash, Hasher},
|
||||
|
@ -270,3 +270,19 @@ impl From<SamplerDescriptor> for wgpu::SamplerDescriptor {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Texture> for SamplerDescriptor {
|
||||
fn from(_texture: &Texture) -> Self {
|
||||
SamplerDescriptor {
|
||||
address_mode_u: wgpu::AddressMode::ClampToEdge,
|
||||
address_mode_v: wgpu::AddressMode::ClampToEdge,
|
||||
address_mode_w: wgpu::AddressMode::ClampToEdge,
|
||||
mag_filter: wgpu::FilterMode::Nearest,
|
||||
min_filter: wgpu::FilterMode::Linear,
|
||||
mipmap_filter: wgpu::FilterMode::Nearest,
|
||||
lod_min_clamp: -100.0,
|
||||
lod_max_clamp: 100.0,
|
||||
compare_function: wgpu::CompareFunction::Always,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,10 +4,13 @@ use std::collections::HashMap;
|
|||
#[derive(Copy, Clone, Hash, Debug, Eq, PartialEq)]
|
||||
pub struct RenderResource(pub u64);
|
||||
|
||||
// TODO: consider scoping breaking these mappings up by type: Texture, Sampler, etc
|
||||
// the overlap could cause accidents.
|
||||
#[derive(Default)]
|
||||
pub struct RenderResources {
|
||||
pub name_to_resource: HashMap<String, RenderResource>,
|
||||
pub texture_to_resource: HashMap<Handle<Texture>, RenderResource>,
|
||||
pub texture_to_sampler_resource: HashMap<Handle<Texture>, RenderResource>,
|
||||
pub resource_index: u64,
|
||||
}
|
||||
|
||||
|
@ -28,6 +31,14 @@ impl RenderResources {
|
|||
self.texture_to_resource.get(&texture).cloned()
|
||||
}
|
||||
|
||||
pub fn set_texture_sampler_resource(&mut self, texture: Handle<Texture>, resource: RenderResource) {
|
||||
self.texture_to_sampler_resource.insert(texture, resource);
|
||||
}
|
||||
|
||||
pub fn get_texture_sampler_resource(&self, texture: Handle<Texture>) -> Option<RenderResource> {
|
||||
self.texture_to_sampler_resource.get(&texture).cloned()
|
||||
}
|
||||
|
||||
pub fn get_next_resource(&mut self) -> RenderResource {
|
||||
let resource = self.resource_index;
|
||||
self.resource_index += 1;
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use crate::{
|
||||
legion::prelude::*,
|
||||
render::render_graph::{
|
||||
render_resource::RenderResource, DynamicUniformBufferInfo, PipelineDescriptor, RenderGraph,
|
||||
ResourceInfo, TextureDescriptor, SamplerDescriptor
|
||||
}, asset::{Handle, Texture},
|
||||
render_resource::{RenderResource, RenderResources},
|
||||
DynamicUniformBufferInfo, PipelineDescriptor, RenderGraph, ResourceInfo, SamplerDescriptor,
|
||||
TextureDescriptor,
|
||||
},
|
||||
};
|
||||
use std::ops::Range;
|
||||
|
||||
|
@ -77,10 +78,10 @@ pub trait Renderer {
|
|||
destination_offset: u64,
|
||||
size: u64,
|
||||
);
|
||||
fn get_named_resource(&self, name: &str) -> Option<RenderResource>;
|
||||
fn set_named_resource(&mut self, name: &str, resource: RenderResource);
|
||||
fn get_texture_resource(&self, texture: Handle<Texture>) -> Option<RenderResource>;
|
||||
fn set_texture_resource(&mut self, texture: Handle<Texture>, resource: RenderResource);
|
||||
fn get_render_resources(&self) -> &RenderResources;
|
||||
fn get_render_resources_mut(&mut self) -> &mut RenderResources;
|
||||
fn set_entity_uniform_resource(&mut self, entity: Entity, uniform_name: &str, resource: RenderResource);
|
||||
fn get_entity_uniform_resource(&mut self, entity: Entity, uniform_name: &str) -> Option<RenderResource>;
|
||||
}
|
||||
|
||||
pub trait RenderPass {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
asset::{AssetStorage, Handle, Texture},
|
||||
asset::{AssetStorage, Handle},
|
||||
legion::prelude::*,
|
||||
render::{
|
||||
render_graph::{
|
||||
|
@ -12,7 +12,7 @@ use crate::{
|
|||
Shader,
|
||||
},
|
||||
};
|
||||
use std::{collections::HashMap, ops::Deref};
|
||||
use std::{collections::HashMap, ops::Deref, borrow::Cow};
|
||||
|
||||
pub struct WgpuRenderer {
|
||||
pub device: wgpu::Device,
|
||||
|
@ -27,6 +27,8 @@ pub struct WgpuRenderer {
|
|||
pub resource_info: HashMap<RenderResource, ResourceInfo>,
|
||||
pub bind_groups: HashMap<u64, BindGroupInfo>,
|
||||
pub bind_group_layouts: HashMap<u64, wgpu::BindGroupLayout>,
|
||||
pub entity_bind_groups: HashMap<(Entity, u64), BindGroupInfo>,
|
||||
pub entity_uniform_resources: HashMap<(Cow<'static, Entity>, Cow<'static, str>), RenderResource>,
|
||||
pub dynamic_uniform_buffer_info: HashMap<RenderResource, DynamicUniformBufferInfo>,
|
||||
pub render_resources: RenderResources,
|
||||
}
|
||||
|
@ -70,6 +72,8 @@ impl WgpuRenderer {
|
|||
bind_groups: HashMap::new(),
|
||||
bind_group_layouts: HashMap::new(),
|
||||
dynamic_uniform_buffer_info: HashMap::new(),
|
||||
entity_bind_groups: HashMap::new(),
|
||||
entity_uniform_resources: HashMap::new(),
|
||||
render_resources: RenderResources::default(),
|
||||
}
|
||||
}
|
||||
|
@ -300,7 +304,7 @@ impl WgpuRenderer {
|
|||
}
|
||||
|
||||
// TODO: consider moving this to a resource provider
|
||||
fn setup_bind_group(&mut self, bind_group: &BindGroup) -> u64 {
|
||||
fn setup_bind_group(&mut self, bind_group: &BindGroup) {
|
||||
let bind_group_id = bind_group.get_hash().unwrap();
|
||||
|
||||
if let None = self.bind_groups.get(&bind_group_id) {
|
||||
|
@ -331,7 +335,7 @@ impl WgpuRenderer {
|
|||
},
|
||||
BindType::Sampler | BindType::SampledTexture { .. } => {
|
||||
// textures and samplers are handled per-entity
|
||||
None
|
||||
return;
|
||||
},
|
||||
_ => panic!("unsupported bind type: {:?}", binding),
|
||||
}
|
||||
|
@ -392,8 +396,6 @@ impl WgpuRenderer {
|
|||
},
|
||||
);
|
||||
}
|
||||
|
||||
bind_group_id
|
||||
}
|
||||
|
||||
pub fn create_shader_module(
|
||||
|
@ -421,6 +423,139 @@ impl WgpuRenderer {
|
|||
let command_buffer = self.encoder.take().unwrap().finish();
|
||||
self.queue.submit(&[command_buffer]);
|
||||
}
|
||||
|
||||
pub fn get_entity_bind_group(&self, entity: Entity, bind_group_id: u64) -> Option<&BindGroupInfo> {
|
||||
self.entity_bind_groups.get(&(entity, bind_group_id))
|
||||
}
|
||||
|
||||
pub fn create_entity_bind_group(&mut self, bind_group: &BindGroup, entity: Entity) {
|
||||
let bind_group_id = bind_group.get_hash().unwrap();
|
||||
let mut binding_resources = Vec::new();
|
||||
for binding in bind_group.bindings.iter() {
|
||||
if let Some(resource) = self.get_entity_uniform_resource(entity, &binding.name) {
|
||||
let resource_info = self.resource_info.get(&resource).unwrap();
|
||||
match binding.bind_type {
|
||||
BindType::SampledTexture { .. } => {
|
||||
},
|
||||
BindType::Sampler => {
|
||||
|
||||
},
|
||||
_ => panic!("Attempted to create an unsupported BindType for BindGroup. Binding: {:?}, BindGroup: {:?}, Entity: {:?}", binding, bind_group, entity),
|
||||
}
|
||||
wgpu::Binding {
|
||||
binding: binding.index,
|
||||
resource: match &binding.bind_type {
|
||||
BindType::SampledTexture {
|
||||
} => {
|
||||
if let ResourceInfo::Texture = resource_info {
|
||||
let texture = self.textures.get(&resource).unwrap();
|
||||
wgpu::BindingResource::TextureView(texture)
|
||||
} else {
|
||||
panic!("expected a Texture resource");
|
||||
}
|
||||
},
|
||||
_ => panic!("unsupported bind type"),
|
||||
},
|
||||
}
|
||||
}
|
||||
else {
|
||||
panic!("No resource assigned to uniform {} for entity {}", binding.name, entity),
|
||||
}
|
||||
}
|
||||
}
|
||||
fn setup_bind_group(&mut self, bind_group: &BindGroup) {
|
||||
let bind_group_id = bind_group.get_hash().unwrap();
|
||||
|
||||
if let None = self.bind_groups.get(&bind_group_id) {
|
||||
let mut unset_uniforms = Vec::new();
|
||||
|
||||
let mut binding_resources = Vec::new();
|
||||
// if a uniform resource buffer doesn't exist, create a new empty one
|
||||
for binding in bind_group.bindings.iter() {
|
||||
let resource = match self.render_resources.get_named_resource(&binding.name) {
|
||||
resource @ Some(_) => resource,
|
||||
None => {
|
||||
println!(
|
||||
"Warning: creating new empty buffer for binding {} {:?}",
|
||||
binding.name, binding
|
||||
);
|
||||
unset_uniforms.push(binding.name.to_string());
|
||||
match binding.bind_type {
|
||||
BindType::Uniform { .. } => {
|
||||
let size = binding.bind_type.get_uniform_size().unwrap();
|
||||
let resource = self.create_buffer(
|
||||
size,
|
||||
wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST,
|
||||
);
|
||||
|
||||
self.render_resources
|
||||
.set_named_resource(&binding.name, resource);
|
||||
Some(resource)
|
||||
},
|
||||
BindType::Sampler | BindType::SampledTexture { .. } => {
|
||||
// textures and samplers are handled per-entity
|
||||
return;
|
||||
},
|
||||
_ => panic!("unsupported bind type: {:?}", binding),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(resource) = resource {
|
||||
binding_resources.push(resource);
|
||||
}
|
||||
}
|
||||
|
||||
// create wgpu Bindings
|
||||
let bindings = bind_group
|
||||
.bindings
|
||||
.iter()
|
||||
.zip(binding_resources)
|
||||
.map(|(binding, resource)| {
|
||||
let resource_info = self.resource_info.get(&resource).unwrap();
|
||||
wgpu::Binding {
|
||||
binding: binding.index,
|
||||
resource: match &binding.bind_type {
|
||||
BindType::Uniform {
|
||||
dynamic: _,
|
||||
properties: _,
|
||||
} => {
|
||||
if let ResourceInfo::Buffer {
|
||||
size,
|
||||
buffer_usage: _,
|
||||
} = resource_info
|
||||
{
|
||||
let buffer = self.buffers.get(&resource).unwrap();
|
||||
wgpu::BindingResource::Buffer {
|
||||
buffer,
|
||||
range: 0..*size,
|
||||
}
|
||||
} else {
|
||||
panic!("expected a Buffer resource");
|
||||
}
|
||||
}
|
||||
_ => panic!("unsupported bind type"),
|
||||
},
|
||||
}
|
||||
})
|
||||
.collect::<Vec<wgpu::Binding>>();
|
||||
|
||||
let bind_group_layout = self.bind_group_layouts.get(&bind_group_id).unwrap();
|
||||
let bind_group_descriptor = wgpu::BindGroupDescriptor {
|
||||
layout: bind_group_layout,
|
||||
bindings: bindings.as_slice(),
|
||||
};
|
||||
|
||||
let bind_group = self.device.create_bind_group(&bind_group_descriptor);
|
||||
self.bind_groups.insert(
|
||||
bind_group_id,
|
||||
BindGroupInfo {
|
||||
bind_group,
|
||||
unset_uniforms,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Renderer for WgpuRenderer {
|
||||
|
@ -762,14 +897,6 @@ impl Renderer for WgpuRenderer {
|
|||
resource
|
||||
}
|
||||
|
||||
fn get_named_resource(&self, name: &str) -> Option<RenderResource> {
|
||||
self.render_resources.get_named_resource(name)
|
||||
}
|
||||
|
||||
fn set_named_resource(&mut self, name: &str, resource: RenderResource) {
|
||||
self.render_resources.set_named_resource(name, resource);
|
||||
}
|
||||
|
||||
fn remove_texture(&mut self, resource: RenderResource) {
|
||||
self.textures.remove(&resource);
|
||||
self.resource_info.remove(&resource);
|
||||
|
@ -780,12 +907,19 @@ impl Renderer for WgpuRenderer {
|
|||
self.resource_info.remove(&resource);
|
||||
}
|
||||
|
||||
fn get_texture_resource(&self, texture: Handle<Texture>) -> Option<RenderResource> {
|
||||
self.render_resources.get_texture_resource(texture)
|
||||
fn get_render_resources(&self) -> &RenderResources {
|
||||
&self.render_resources
|
||||
}
|
||||
|
||||
fn set_texture_resource(&mut self, texture: Handle<Texture>, resource: RenderResource) {
|
||||
self.render_resources.set_texture_resource(texture, resource);
|
||||
fn get_render_resources_mut(&mut self) -> &mut RenderResources {
|
||||
&mut self.render_resources
|
||||
}
|
||||
|
||||
fn set_entity_uniform_resource(&mut self, entity: Entity, uniform_name: &str, resource: RenderResource) {
|
||||
self.entity_uniform_resources.insert((Cow::Owned(entity), Cow::Owned(uniform_name.to_string())), resource);
|
||||
}
|
||||
fn get_entity_uniform_resource(&mut self, entity: Entity, uniform_name: &str) -> Option<RenderResource> {
|
||||
self.entity_uniform_resources.get(&(Cow::Owned(entity), Cow::Borrowed(uniform_name))).cloned()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -829,7 +963,20 @@ impl<'a, 'b, 'c, 'd> RenderPass for WgpuRenderPass<'a, 'b, 'c, 'd> {
|
|||
let pipeline_layout = self.pipeline_descriptor.get_layout().unwrap();
|
||||
for bind_group in pipeline_layout.bind_groups.iter() {
|
||||
let bind_group_id = bind_group.get_hash().unwrap();
|
||||
let bind_group_info = self.renderer.bind_groups.get(&bind_group_id).unwrap();
|
||||
let bind_group_info = match self.renderer.bind_groups.get(&bind_group_id) {
|
||||
Some(bind_group_info) => bind_group_info,
|
||||
None => {
|
||||
if let Some(entity) = entity {
|
||||
if let None = self.renderer.get_entity_bind_group(*entity, bind_group_id) {
|
||||
self.renderer.create_entity_bind_group(bind_group, *entity);
|
||||
}
|
||||
|
||||
self.renderer.get_entity_bind_group(*entity, bind_group_id).unwrap()
|
||||
} else {
|
||||
panic!("No bind group exists that matches: {:?}");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut dynamic_uniform_indices = Vec::new();
|
||||
for binding in bind_group.bindings.iter() {
|
||||
|
|
|
@ -18,7 +18,7 @@ impl ResourceProvider for Camera2dResourceProvider {
|
|||
wgpu::BufferUsage::COPY_DST | wgpu::BufferUsage::UNIFORM,
|
||||
);
|
||||
|
||||
renderer.set_named_resource(resource_name::uniform::CAMERA2D, buffer);
|
||||
renderer.get_render_resources_mut().set_named_resource(resource_name::uniform::CAMERA2D, buffer);
|
||||
self.camera_buffer = Some(buffer);
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ impl ResourceProvider for CameraResourceProvider {
|
|||
wgpu::BufferUsage::COPY_DST | wgpu::BufferUsage::UNIFORM,
|
||||
);
|
||||
|
||||
renderer.set_named_resource(resource_name::uniform::CAMERA, buffer);
|
||||
renderer.get_render_resources_mut().set_named_resource(resource_name::uniform::CAMERA, buffer);
|
||||
self.camera_buffer = Some(buffer);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,12 +22,12 @@ impl FrameTextureResourceProvider {
|
|||
self.descriptor.size.width = window_size.width;
|
||||
self.descriptor.size.height = window_size.height;
|
||||
|
||||
if let Some(old_resource) = renderer.get_named_resource(&self.name) {
|
||||
if let Some(old_resource) = renderer.get_render_resources().get_named_resource(&self.name) {
|
||||
renderer.remove_texture(old_resource);
|
||||
}
|
||||
|
||||
let texture_resource = renderer.create_texture(&self.descriptor, None);
|
||||
renderer.set_named_resource(&self.name, texture_resource);
|
||||
renderer.get_render_resources_mut().set_named_resource(&self.name, texture_resource);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ impl ResourceProvider for LightResourceProvider {
|
|||
light_uniform_size,
|
||||
wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_SRC | wgpu::BufferUsage::COPY_DST,
|
||||
);
|
||||
renderer.set_named_resource(resource_name::uniform::LIGHTS, buffer);
|
||||
renderer.get_render_resources_mut().set_named_resource(resource_name::uniform::LIGHTS, buffer);
|
||||
self.light_buffer = Some(buffer);
|
||||
}
|
||||
|
||||
|
|
|
@ -82,7 +82,7 @@ impl UiResourceProvider {
|
|||
wgpu::BufferUsage::COPY_SRC | wgpu::BufferUsage::VERTEX,
|
||||
);
|
||||
|
||||
renderer.set_named_resource(resource_name::buffer::UI_INSTANCES, buffer);
|
||||
renderer.get_render_resources_mut().set_named_resource(resource_name::buffer::UI_INSTANCES, buffer);
|
||||
self.instance_buffer = Some(buffer);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use crate::{
|
|||
asset::{AssetStorage, Texture},
|
||||
render::render_graph::{
|
||||
render_resource::RenderResource, AsUniforms, BindType, DynamicUniformBufferInfo,
|
||||
Renderable, Renderer, ResourceProvider, TextureDescriptor, UniformInfoIter,
|
||||
Renderable, Renderer, ResourceProvider, TextureDescriptor, UniformInfoIter, SamplerDescriptor,
|
||||
},
|
||||
};
|
||||
use legion::prelude::*;
|
||||
|
@ -51,7 +51,7 @@ where
|
|||
}
|
||||
|
||||
let mut counts = Vec::new();
|
||||
for (uniforms, _renderable) in query.iter(world) {
|
||||
for (entity, (uniforms, _renderable)) in query.iter_entities(world) {
|
||||
let mut uniform_index = 0;
|
||||
let field_uniform_names = uniforms.get_field_uniform_names();
|
||||
for uniform_info in UniformInfoIter::new(field_uniform_names, uniforms.deref()) {
|
||||
|
@ -75,14 +75,37 @@ where
|
|||
uniforms.get_uniform_texture(&uniform_info.name).unwrap();
|
||||
let storage = world.resources.get::<AssetStorage<Texture>>().unwrap();
|
||||
let texture = storage.get(&texture_handle).unwrap();
|
||||
if let None = renderer.get_texture_resource(texture_handle) {
|
||||
let descriptor: TextureDescriptor = texture.into();
|
||||
let resource =
|
||||
renderer.create_texture(&descriptor, Some(&texture.data));
|
||||
renderer.set_texture_resource(texture_handle, resource);
|
||||
}
|
||||
let resource = match renderer.get_render_resources().get_texture_resource(texture_handle) {
|
||||
Some(resource) => resource,
|
||||
None => {
|
||||
let descriptor: TextureDescriptor = texture.into();
|
||||
let resource =
|
||||
renderer.create_texture(&descriptor, Some(&texture.data));
|
||||
renderer.get_render_resources_mut().set_texture_resource(texture_handle, resource);
|
||||
resource
|
||||
}
|
||||
};
|
||||
|
||||
renderer.assign_entity_uniform_resource(*entity, uniform_info.name, resource);
|
||||
}
|
||||
BindType::Sampler { .. } => {
|
||||
let texture_handle =
|
||||
uniforms.get_uniform_texture(&uniform_info.name).unwrap();
|
||||
let storage = world.resources.get::<AssetStorage<Texture>>().unwrap();
|
||||
let texture = storage.get(&texture_handle).unwrap();
|
||||
let resource = match renderer.get_render_resources().get_texture_sampler_resource(texture_handle) {
|
||||
Some(resource) => resource,
|
||||
None => {
|
||||
let descriptor: SamplerDescriptor = texture.into();
|
||||
let resource =
|
||||
renderer.create_sampler(&descriptor);
|
||||
renderer.get_render_resources_mut().set_texture_sampler_resource(texture_handle, resource);
|
||||
resource
|
||||
}
|
||||
};
|
||||
|
||||
renderer.assign_entity_uniform_resource(*entity, uniform_info.name, resource);
|
||||
}
|
||||
BindType::Sampler { .. } => {}
|
||||
_ => panic!(
|
||||
"encountered unsupported bind_type {:?}",
|
||||
uniform_info.bind_type
|
||||
|
@ -114,7 +137,7 @@ where
|
|||
info.capacity = capacity;
|
||||
renderer.add_dynamic_uniform_buffer_info(created_resource, info);
|
||||
*resource = Some(created_resource);
|
||||
renderer.set_named_resource(name, created_resource);
|
||||
renderer.get_render_resources_mut().set_named_resource(name, created_resource);
|
||||
}
|
||||
|
||||
// copy entity uniform data to buffers
|
||||
|
|
Loading…
Reference in a new issue