begin rewrite of UniformResourceProvider (no instancing or dynamic uniform support yet)

This commit is contained in:
Carter Anderson 2020-03-24 13:50:40 -07:00
parent 55745b0812
commit c71b886165
7 changed files with 627 additions and 17 deletions

View File

@ -199,8 +199,8 @@ impl AppBuilder {
.add_resource_provider(LightResourceProvider::new(10))
.add_resource_provider(UiResourceProvider::new())
.add_resource_provider(MeshResourceProvider::new())
.add_resource_provider(UniformResourceProvider::<StandardMaterial>::new())
.add_resource_provider(UniformResourceProvider::<LocalToWorld>::new())
.add_resource_provider(UniformResourceProviderNew::<StandardMaterial>::new(false))
.add_resource_provider(UniformResourceProviderNew::<LocalToWorld>::new(false))
.add_forward_pass()
.add_forward_pipeline()
.add_ui_pipeline();

View File

@ -1,4 +1,8 @@
use crate::{asset::Handle, math::Vec4, render::texture::Texture};
use crate::{
asset::Handle,
math::{Mat4, Vec4},
render::texture::Texture,
};
use zerocopy::AsBytes;
pub trait GetBytes {
@ -53,6 +57,20 @@ impl GetBytes for Vec4 {
}
}
impl GetBytes for Mat4 {
fn get_bytes(&self) -> Vec<u8> {
self.as_ref()
.as_bytes()
.iter()
.cloned()
.collect::<Vec<u8>>()
}
fn get_bytes_ref(&self) -> Option<&[u8]> {
Some(self.as_ref().as_bytes())
}
}
impl GetBytes for Handle<Texture> {
fn get_bytes(&self) -> Vec<u8> {
Vec::new()

View File

@ -27,7 +27,7 @@ impl<'a> ForwardPassBuilder for RenderGraphBuilder<'a> {
mip_level_count: 1,
sample_count: 1,
dimension: TextureDimension::D2,
format: TextureFormat::Depth32Float,
format: TextureFormat::Depth32Float, // PERF: vulkan recommends using 24 bit depth for better performance
usage: TextureUsage::OUTPUT_ATTACHMENT,
},
))

View File

@ -5,6 +5,7 @@ mod light_resource_provider;
mod mesh_resource_provider;
mod ui_resource_provider;
mod uniform_resource_provider;
mod uniform_resource_provider_new;
pub use camera2d_resource_provider::*;
pub use camera_resource_provider::*;
@ -13,3 +14,4 @@ pub use light_resource_provider::*;
pub use mesh_resource_provider::*;
pub use ui_resource_provider::*;
pub use uniform_resource_provider::*;
pub use uniform_resource_provider_new::*;

View File

@ -0,0 +1,589 @@
use crate::{
asset::{AssetStorage, Handle},
render::{
render_graph::RenderGraph,
render_resource::{
AssetBatchers, BufferArrayInfo, BufferDynamicUniformInfo, BufferInfo, BufferUsage,
RenderResource, RenderResourceAssignments, RenderResourceAssignmentsProvider,
ResourceInfo, ResourceProvider,
},
renderer::Renderer,
shader::{AsUniforms, FieldBindType},
texture::{SamplerDescriptor, Texture, TextureDescriptor},
Renderable,
},
};
use legion::{filter::*, prelude::*};
use std::marker::PhantomData;
pub const BIND_BUFFER_ALIGNMENT: u64 = 256;
#[derive(Debug)]
struct BufferArrayStatus {
new_item_count: usize,
item_size: usize,
staging_buffer_offset: usize,
buffer: Option<RenderResource>,
}
pub struct UniformResourceProviderNew<T>
where
T: AsUniforms + Send + Sync + 'static,
{
_marker: PhantomData<T>,
use_dynamic_uniforms: bool,
is_instanceable: bool,
// PERF: somehow remove this HashSet
dynamic_uniform_buffer_status: Vec<Option<(String, BufferArrayStatus)>>,
instance_buffer_status: Option<BufferArrayStatus>,
query: Option<
Query<
(Read<T>, Write<Renderable>),
EntityFilterTuple<
And<(ComponentFilter<T>, ComponentFilter<Renderable>)>,
And<(Passthrough, Passthrough)>,
And<(Passthrough, Passthrough)>,
>,
>,
>,
query_finish: Option<
Query<
(Read<T>, Write<Renderable>),
EntityFilterTuple<
And<(ComponentFilter<T>, ComponentFilter<Renderable>)>,
And<(Passthrough, Passthrough)>,
And<(Passthrough, Passthrough)>,
>,
>,
>,
handle_query: Option<
Query<
(Read<Handle<T>>, Write<Renderable>),
EntityFilterTuple<
And<(ComponentFilter<Handle<T>>, ComponentFilter<Renderable>)>,
And<(Passthrough, Passthrough)>,
And<(Passthrough, Passthrough)>,
>,
>,
>,
handle_query_finish: Option<
Query<
(Read<Handle<T>>, Write<Renderable>),
EntityFilterTuple<
And<(ComponentFilter<Handle<T>>, ComponentFilter<Renderable>)>,
And<(Passthrough, Passthrough)>,
And<(Passthrough, Passthrough)>,
>,
>,
>,
}
impl<T> UniformResourceProviderNew<T>
where
T: AsUniforms + Send + Sync + 'static,
{
pub fn new(use_dynamic_uniforms: bool) -> Self {
let mut dynamic_uniform_buffer_status = Vec::new();
let field_infos = T::get_field_infos();
dynamic_uniform_buffer_status.resize_with(field_infos.len(), || None);
let is_instanceable = field_infos.iter().find(|f| f.is_instanceable).is_some();
UniformResourceProviderNew {
dynamic_uniform_buffer_status,
use_dynamic_uniforms,
instance_buffer_status: None,
is_instanceable,
query: Some(<(Read<T>, Write<Renderable>)>::query()),
query_finish: Some(<(Read<T>, Write<Renderable>)>::query()),
handle_query: Some(<(Read<Handle<T>>, Write<Renderable>)>::query()),
handle_query_finish: Some(<(Read<Handle<T>>, Write<Renderable>)>::query()),
_marker: PhantomData,
}
}
fn reset_buffer_array_status_counts(&mut self) {
for buffer_status in self.dynamic_uniform_buffer_status.iter_mut() {
if let Some((_name, buffer_status)) = buffer_status {
buffer_status.new_item_count = 0;
}
}
if let Some(ref mut buffer_status) = self.instance_buffer_status {
buffer_status.new_item_count = 0;
}
}
fn update_uniforms_info(&mut self, world: &mut World) {
if !self.use_dynamic_uniforms {
return;
}
let query = self.query.take().unwrap();
for (uniforms, mut renderable) in query.iter_mut(world) {
if !renderable.is_visible {
return;
}
if renderable.is_instanced {
if self.is_instanceable {
self.increment_instance_count(|| Self::get_instance_size(&uniforms));
} else {
panic!(
"Cannot instance uniforms of type {}",
std::any::type_name::<T>()
);
}
} else if self.use_dynamic_uniforms {
self.increment_dynamic_uniform_counts(&uniforms);
}
Self::update_shader_defs(
&uniforms,
renderable.render_resource_assignments.as_mut().unwrap(),
);
}
self.query = Some(query);
}
fn update_handles_info(&mut self, world: &mut World, resources: &Resources) {
let handle_query = self.handle_query.take().unwrap();
let assets = resources.get::<AssetStorage<T>>();
let mut asset_batchers = resources.get_mut::<AssetBatchers>().unwrap();
if let Some(assets) = assets {
for (entity, (handle, mut renderable)) in handle_query.iter_entities_mut(world) {
if !renderable.is_visible {
return;
}
if renderable.is_instanced {
if self.is_instanceable {
asset_batchers.set_entity_handle(entity, *handle);
self.increment_instance_count(|| {
let uniforms = assets.get(&handle).unwrap();
Self::get_instance_size(uniforms)
});
} else {
panic!(
"Cannot instance uniforms of type Handle<{}>",
std::any::type_name::<T>()
);
}
} else {
let uniforms = assets
.get(&handle)
.expect("Handle points to a non-existent resource");
Self::update_shader_defs(
uniforms,
renderable.render_resource_assignments.as_mut().unwrap(),
);
if self.use_dynamic_uniforms {
self.increment_dynamic_uniform_counts(&uniforms);
}
}
}
}
self.handle_query = Some(handle_query);
}
fn increment_instance_count(&mut self, f: impl Fn() -> usize) {
if let Some(ref mut buffer_array_status) = self.instance_buffer_status {
buffer_array_status.new_item_count += 1;
} else {
self.instance_buffer_status = Some(BufferArrayStatus {
new_item_count: 1,
item_size: f(),
staging_buffer_offset: 0,
buffer: None,
})
}
}
fn get_instance_size(uniforms: &T) -> usize {
let mut instance_buffer_size = 0;
for field_info in T::get_field_infos().iter() {
if field_info.is_instanceable {
if let Some(FieldBindType::Uniform { size }) =
uniforms.get_field_bind_type(field_info.name)
{
instance_buffer_size += size;
}
}
}
instance_buffer_size
}
fn increment_dynamic_uniform_counts(&mut self, uniforms: &T) {
for (i, field_info) in T::get_field_infos().iter().enumerate() {
if let Some(FieldBindType::Uniform { size }) =
uniforms.get_field_bind_type(&field_info.name)
{
if let Some((ref _name, ref mut buffer_array_status)) =
self.dynamic_uniform_buffer_status[i]
{
buffer_array_status.new_item_count += 1;
} else {
self.dynamic_uniform_buffer_status[i] = Some((
field_info.uniform_name.to_string(),
BufferArrayStatus {
new_item_count: 1,
item_size: size,
staging_buffer_offset: 0,
buffer: None,
},
))
}
}
}
}
fn update_shader_defs(
uniforms: &T,
render_resource_assignments: &mut RenderResourceAssignments,
) {
if let Some(shader_defs) = uniforms.get_shader_defs() {
for shader_def in shader_defs {
render_resource_assignments.shader_defs.insert(shader_def);
}
}
}
fn setup_uniform_resources(
&mut self,
uniforms: &T,
renderer: &mut dyn Renderer,
resources: &Resources,
render_resource_assignments: &mut RenderResourceAssignments,
) {
for field_info in T::get_field_infos() {
let bind_type = uniforms.get_field_bind_type(&field_info.name);
match bind_type {
Some(FieldBindType::Uniform { size }) => {
if self.use_dynamic_uniforms {
} else {
let buffer_resource = match render_resource_assignments
.get(field_info.uniform_name)
{
Some(render_resource) => render_resource,
None => {
let resource = renderer.create_buffer(BufferInfo {
size: size as u64,
buffer_usage: BufferUsage::COPY_DST | BufferUsage::UNIFORM,
..Default::default()
});
render_resource_assignments.set(&field_info.uniform_name, resource);
resource
}
};
let (tmp_buffer, tmp_buffer_size) = if let Some(uniform_bytes) =
uniforms.get_uniform_bytes_ref(&field_info.uniform_name)
{
if size != uniform_bytes.len() {
panic!("The number of bytes produced for {} do not match the expected count. Actual: {}. Expected: {}.", field_info.uniform_name, uniform_bytes.len(), size);
}
(
renderer.create_buffer_mapped(
BufferInfo {
size: uniform_bytes.len() as u64,
buffer_usage: BufferUsage::COPY_SRC,
..Default::default()
},
&mut |mapped| {
mapped.copy_from_slice(uniform_bytes);
},
),
uniform_bytes.len(),
)
} else if let Some(uniform_bytes) =
uniforms.get_uniform_bytes(field_info.uniform_name)
{
if size != uniform_bytes.len() {
panic!("The number of bytes produced for {} do not match the expected count. Actual: {}. Expected: {}.", field_info.uniform_name, uniform_bytes.len(), size);
}
(
renderer.create_buffer_mapped(
BufferInfo {
size: uniform_bytes.len() as u64,
buffer_usage: BufferUsage::COPY_SRC,
..Default::default()
},
&mut |mapped| {
mapped.copy_from_slice(&uniform_bytes);
},
),
uniform_bytes.len(),
)
} else {
panic!(
"failed to get data from uniform: {}",
field_info.uniform_name
);
};
renderer.copy_buffer_to_buffer(
tmp_buffer,
0,
buffer_resource,
0,
tmp_buffer_size as u64,
);
renderer.remove_buffer(tmp_buffer);
}
}
Some(FieldBindType::Texture) => {
let texture_handle = uniforms
.get_uniform_texture(&field_info.texture_name)
.unwrap();
let (texture_resource, sampler_resource) = match renderer
.get_render_resources()
.get_texture_resource(texture_handle)
{
Some(texture_resource) => (
texture_resource,
renderer
.get_render_resources()
.get_texture_sampler_resource(texture_handle)
.unwrap(),
),
None => {
let storage = resources.get::<AssetStorage<Texture>>().unwrap();
let texture = storage.get(&texture_handle).unwrap();
let texture_descriptor: TextureDescriptor = texture.into();
let texture_resource =
renderer.create_texture(&texture_descriptor, Some(&texture.data));
let sampler_descriptor: SamplerDescriptor = texture.into();
let sampler_resource = renderer.create_sampler(&sampler_descriptor);
let render_resources = renderer.get_render_resources_mut();
render_resources.set_texture_resource(texture_handle, texture_resource);
render_resources
.set_texture_sampler_resource(texture_handle, sampler_resource);
(texture_resource, sampler_resource)
}
};
render_resource_assignments.set(field_info.texture_name, texture_resource);
render_resource_assignments.set(field_info.sampler_name, sampler_resource);
}
None => {}
}
}
}
fn setup_uniforms_resources(
&mut self,
world: &mut World,
resources: &Resources,
renderer: &mut dyn Renderer,
) {
let query_finish = self.query_finish.take().unwrap();
for (uniforms, mut renderable) in query_finish.iter_mut(world) {
if !renderable.is_visible {
return;
}
if renderable.is_instanced {
panic!(
"Cannot instance uniforms of type {0}. Only Handle<{0}> can be instanced.",
std::any::type_name::<T>()
);
} else {
self.setup_uniform_resources(
&uniforms,
renderer,
resources,
renderable.render_resource_assignments.as_mut().unwrap(),
)
}
}
self.query_finish = Some(query_finish);
}
fn setup_handles_resources(
&mut self,
world: &mut World,
resources: &Resources,
renderer: &mut dyn Renderer,
) {
let assets = resources.get::<AssetStorage<T>>();
if let Some(assets) = assets {
let handle_query_finish = self.handle_query_finish.take().unwrap();
for (handle, mut renderable) in handle_query_finish.iter_mut(world) {
if !renderable.is_visible || renderable.is_instanced {
return;
}
let uniforms = assets
.get(&handle)
.expect("Handle points to a non-existent resource");
self.setup_uniform_resources(
&uniforms,
renderer,
resources,
renderable.render_resource_assignments.as_mut().unwrap(),
)
}
self.handle_query_finish = Some(handle_query_finish);
}
}
fn setup_batched_resources(
&mut self,
world: &mut World,
resources: &Resources,
renderer: &mut dyn Renderer,
) {
// update batch resources. this needs to run in "finish_update" because batches aren't finalized across
// all members of the batch until "UniformResourceProvider.update" has run for all members of the batch
if let Some(asset_storage) = resources.get::<AssetStorage<T>>() {
let mut asset_batchers = resources.get_mut::<AssetBatchers>().unwrap();
let mut render_resource_assignments_provider = resources
.get_mut::<RenderResourceAssignmentsProvider>()
.unwrap();
let handle_type = std::any::TypeId::of::<T>();
for batch in asset_batchers.get_handle_batches_mut::<T>().unwrap() {
let handle: Handle<T> = batch
.handles
.iter()
.find(|h| h.type_id == handle_type)
.map(|h| (*h).into())
.unwrap();
let render_resource_assignments = batch
.render_resource_assignments
.get_or_insert_with(|| render_resource_assignments_provider.next());
if let Some(uniforms) = asset_storage.get(&handle) {
self.setup_uniform_resources(
uniforms,
renderer,
resources,
render_resource_assignments,
);
Self::update_shader_defs(&uniforms, render_resource_assignments);
}
}
}
}
fn setup_buffer_arrays(&mut self, renderer: &mut dyn Renderer) {
for buffer_array_status in self.dynamic_uniform_buffer_status.iter_mut() {
if let Some((name, buffer_array_status)) = buffer_array_status {
println!("dynamic {} {:?}", name, buffer_array_status);
Self::setup_buffer_array(buffer_array_status, renderer);
}
}
if let Some(ref mut buffer_array_status) = self.instance_buffer_status {
println!("instance {}", std::any::type_name::<T>());
Self::setup_buffer_array(buffer_array_status, renderer);
}
}
fn setup_buffer_array(
buffer_array_status: &mut BufferArrayStatus,
renderer: &mut dyn Renderer,
) {
let new_capacity = if let Some(buffer) = buffer_array_status.buffer {
if let Some(ResourceInfo::Buffer(BufferInfo {
array_info: Some(array_info),
..
})) = renderer.get_resource_info_mut(buffer)
{
if array_info.item_capacity < buffer_array_status.new_item_count as u64 {
Some(
buffer_array_status.new_item_count + buffer_array_status.new_item_count / 2,
)
} else {
None
}
} else {
Some(buffer_array_status.new_item_count)
}
} else {
Some(buffer_array_status.new_item_count)
};
if let Some(new_capacity) = new_capacity {
println!("creating buffer {}", new_capacity);
let buffer = renderer.create_buffer(BufferInfo {
array_info: Some(BufferArrayInfo {
item_capacity: new_capacity as u64,
item_count: buffer_array_status.new_item_count as u64,
item_size: buffer_array_status.item_size as u64,
}),
size: (buffer_array_status.item_size * new_capacity) as u64,
buffer_usage: BufferUsage::COPY_DST | BufferUsage::UNIFORM,
dynamic_uniform_info: Some(BufferDynamicUniformInfo::default()),
});
buffer_array_status.buffer = Some(buffer);
}
}
fn initialize_vertex_buffer_descriptor(&self, render_graph: &mut RenderGraph) {
let vertex_buffer_descriptor = T::get_vertex_buffer_descriptor();
if let Some(vertex_buffer_descriptor) = vertex_buffer_descriptor {
if let None = render_graph.get_vertex_buffer_descriptor(&vertex_buffer_descriptor.name)
{
render_graph.set_vertex_buffer_descriptor(vertex_buffer_descriptor.clone());
}
}
}
fn update_staging_buffer_offsets(&mut self) -> usize {
let mut size = 0;
for dynamic_buffer_array_status in self.dynamic_uniform_buffer_status.iter_mut() {
if let Some((_name, ref mut buffer_array_status)) = dynamic_buffer_array_status {
buffer_array_status.staging_buffer_offset = size;
size += buffer_array_status.item_size * buffer_array_status.new_item_count;
}
}
size
}
}
impl<T> ResourceProvider for UniformResourceProviderNew<T>
where
T: AsUniforms + Send + Sync + 'static,
{
fn initialize(
&mut self,
renderer: &mut dyn Renderer,
world: &mut World,
resources: &Resources,
) {
let mut render_graph = resources.get_mut::<RenderGraph>().unwrap();
self.initialize_vertex_buffer_descriptor(&mut render_graph);
self.update(renderer, world, resources);
}
fn update(&mut self, renderer: &mut dyn Renderer, world: &mut World, resources: &Resources) {
self.reset_buffer_array_status_counts();
self.update_uniforms_info(world);
self.update_handles_info(world, resources);
}
fn finish_update(
&mut self,
renderer: &mut dyn Renderer,
world: &mut World,
resources: &Resources,
) {
let staging_buffer_size = self.update_staging_buffer_offsets();
// TODO: when setting batch shader_defs, add INSTANCING
self.setup_buffer_arrays(renderer);
self.setup_uniforms_resources(world, resources, renderer);
self.setup_handles_resources(world, resources, renderer);
self.setup_batched_resources(world, resources, renderer);
}
}

View File

@ -5,7 +5,7 @@ use crate::{
color::ColorSource,
pipeline::{BindType, VertexBufferDescriptor},
texture::{Texture, TextureViewDimension},
},
}, prelude::Color,
};
pub trait AsUniforms {
@ -32,7 +32,7 @@ impl ShaderDefSuffixProvider for bool {
}
pub enum FieldBindType {
Uniform,
Uniform { size: usize },
Texture,
}
@ -79,7 +79,7 @@ where
let bind_type = self.uniforms.get_field_bind_type(field_info.name);
if let Some(bind_type) = bind_type {
Some(match bind_type {
FieldBindType::Uniform => UniformInfo {
FieldBindType::Uniform { .. }=> UniformInfo {
bind_type: BindType::Uniform {
dynamic: false,
properties: Vec::new(),
@ -119,10 +119,10 @@ pub trait AsFieldBindType {
impl AsFieldBindType for ColorSource {
fn get_field_bind_type(&self) -> Option<FieldBindType> {
Some(match *self {
ColorSource::Texture(_) => FieldBindType::Texture,
ColorSource::Color(_) => FieldBindType::Uniform,
})
match *self {
ColorSource::Texture(_) => Some(FieldBindType::Texture),
ColorSource::Color(color) => color.get_field_bind_type(),
}
}
}
@ -145,8 +145,9 @@ impl<T> AsFieldBindType for T
where
T: GetBytes,
{
// TODO: this breaks if get_bytes_ref() isn't supported for a datatype
default fn get_field_bind_type(&self) -> Option<FieldBindType> {
Some(FieldBindType::Uniform)
Some(FieldBindType::Uniform { size: self.get_bytes_ref().unwrap().len() })
}
}

View File

@ -1,15 +1,15 @@
use crate::{
asset::Handle,
core::GetBytes,
render::{
pipeline::{
InputStepMode, VertexAttributeDescriptor, VertexBufferDescriptor, VertexFormat,
},
shader::{AsUniforms, FieldBindType, FieldInfo},
shader::{AsUniforms, FieldBindType, FieldInfo, AsFieldBindType},
texture::Texture,
},
};
use once_cell::sync::Lazy;
use zerocopy::AsBytes;
static LOCAL_TO_WORLD_FIELD_INFOS: &[FieldInfo] = &[FieldInfo {
name: "object",
@ -59,7 +59,7 @@ impl AsUniforms for bevy_transform::prelude::LocalToWorld {
fn get_uniform_bytes(&self, name: &str) -> Option<Vec<u8>> {
match name {
"Object" => Some(self.0.as_ref().as_bytes().into()),
"Object" => Some(self.0.get_bytes()),
_ => None,
}
}
@ -69,7 +69,7 @@ impl AsUniforms for bevy_transform::prelude::LocalToWorld {
}
fn get_field_bind_type(&self, name: &str) -> Option<FieldBindType> {
match name {
"object" => Some(FieldBindType::Uniform),
"object" => self.0.get_field_bind_type(),
_ => None,
}
}
@ -79,7 +79,7 @@ impl AsUniforms for bevy_transform::prelude::LocalToWorld {
fn get_uniform_bytes_ref(&self, name: &str) -> Option<&[u8]> {
match name {
"Object" => Some(self.0.as_ref().as_bytes()),
"Object" => self.0.get_bytes_ref(),
_ => None,
}
}