use rinecs::{
EntitiesThreadLocal, ResourcesThreadLocal, system_thread_local, EntitiesExt, Read
};
use rin_graphics::CameraExt;
use rin_gl as gl;
use densevec::KeyedDenseVec;
use crate::{
light::LightInfo,
renderer::resources::{
ScreenRenderBuffer, LightingTextures, OpaqueSortedGeometry, TranslucentSortedGeometry,
DynamicShadowsSortedGeometry, StaticShadowsSortedGeometry, AllShadowsSortedGeometry
},
renderer::components::RenderPlane,
transformation::Camera,
};
use rin_material::{MaterialRef, ShadowMaterialRef, MaterialMultiRef};
#[cfg(feature="debug_geometry")]
use crate::geometry::DebugGeometryRef;
#[cfg(feature="debug_geometry")]
use crate::renderer::resources::DebugSortedGeometry;
use super::{
FullMaterial, MaterialOffsets, ShadowMaterialOffsets,
ProgramCache, std140_data::Data, Material, TexturesPool,
PropertyChanged, Materials, CameraType, ProgramRef, ProgramSettings, MaterialTransparency,
AlphayTypeToGlProperties
};
use monotonic_clock::Clock;
use std::any::TypeId;
use std::iter;
use std::ops::{Index, IndexMut};
use std::mem;
use std::cell::UnsafeCell;
use std::hash::{Hash, Hasher};
#[cfg(feature="gui")]
use rin_events::Property;
struct MaterialMeta {
name: String,
material: Box<dyn FullMaterial>,
}
pub type ShadowMaterialPool = MaterialPool<ShadowMaterialRef>;
pub struct MaterialPool<M: densevec::Key = MaterialRef>{
typed_materials: hashbrown::HashMap<TypeId, Vec<M>>,
materials: UnsafeCell<KeyedDenseVec<M, MaterialMeta>>,
names_index: hashbrown::HashMap<String, M>,
reverse_name_index: KeyedDenseVec<M, String>,
last_camera_type: CameraType,
#[cfg(feature="gui")]
active_materials: Property<'static, usize>,
removed: Vec<M>,
}
impl MaterialPool<MaterialRef>{
pub fn new(
unified_material_ubo: bool,
world: &mut crate::DeferredScene,
gl: &gl::Renderer,
#[cfg(feature="gui")]
active_materials: Property<'static, usize>,
) -> MaterialPool<MaterialRef>
{
if unified_material_ubo {
let material_offsets = MaterialOffsets{
offsets_data: vec![],
offsets_buffer: gl.new_shared_buffer().empty_target(gl::ARRAY_BUFFER).unwrap(),
};
world.add_resource_thread_local(material_offsets);
let ubos = Buffer::<MaterialRef, UnifiedMaterialOffset>::new(gl);
world.add_resource_thread_local(ubos);
}else{
let ubos = Buffer::<MaterialRef, PerMaterialOffset>::new(gl);
world.add_resource_thread_local(ubos);
}
MaterialPool{
typed_materials: hashbrown::HashMap::new(),
materials: UnsafeCell::new(KeyedDenseVec::new()),
names_index: hashbrown::HashMap::new(),
reverse_name_index: KeyedDenseVec::new(),
last_camera_type: CameraType::empty(),
#[cfg(feature="gui")]
active_materials,
removed: vec![],
}
}
}
impl MaterialPool<ShadowMaterialRef>{
pub fn new_shadows(
unified_material_ubo: bool,
world: &mut crate::DeferredScene,
gl: &gl::Renderer,
#[cfg(feature="gui")]
active_materials: Property<'static, usize>,
) -> MaterialPool<ShadowMaterialRef>
{
if unified_material_ubo {
let material_offsets = ShadowMaterialOffsets{
all_offsets_data: vec![],
dynamic_offsets_data: vec![],
static_offsets_data: vec![],
all_offsets_buffer: Some(gl.new_shared_buffer().empty_target(gl::ARRAY_BUFFER).unwrap()),
dynamic_offsets_buffer: Some(gl.new_shared_buffer().empty_target(gl::ARRAY_BUFFER).unwrap()),
static_offsets_buffer: Some(gl.new_shared_buffer().empty_target(gl::ARRAY_BUFFER).unwrap()),
};
world.add_resource_thread_local(material_offsets);
let ubos = Buffer::<ShadowMaterialRef, UnifiedMaterialOffset>::new(gl);
world.add_resource_thread_local(ubos);
}else{
let ubos = Buffer::<ShadowMaterialRef, PerMaterialOffset>::new(gl);
world.add_resource_thread_local(ubos);
}
MaterialPool{
typed_materials: hashbrown::HashMap::new(),
materials: UnsafeCell::new(KeyedDenseVec::new()),
names_index: hashbrown::HashMap::new(),
reverse_name_index: KeyedDenseVec::new(),
last_camera_type: CameraType::empty(),
#[cfg(feature="gui")]
active_materials,
removed: vec![],
}
}
}
impl<M: densevec::Key + Copy + 'static> MaterialPool<M>{
pub fn register_material<F: FullMaterial + 'static>(&mut self, name: &str, material: F) -> M{
log::trace!("Registering material {}", name);
let materials = unsafe{ &mut *self.materials.get() };
let id = materials.insert_key_gen(MaterialMeta{
name: name.to_owned(),
material: Box::new(material),
});
self.names_index.insert(name.to_owned(), id);
self.reverse_name_index.insert(id, name.to_owned());
self.typed_materials.entry(TypeId::of::<F>())
.or_insert_with(|| vec![])
.push(id);
id
}
pub fn remove(&mut self, material: M) {
for (_, typed_materials) in self.typed_materials.iter_mut() {
if let Some(index) = typed_materials
.iter()
.position(|typed_material| *typed_material == material)
{
typed_materials.remove(index);
break;
}
}
unsafe{ (*self.materials.get()).remove(material) };
if let Some(name) = self.reverse_name_index.remove(material) {
self.names_index.remove(&name);
}
#[cfg(feature="gui")]
self.active_materials.set(unsafe{ (*self.materials.get()).len() });
self.removed.push(material);
}
pub fn find_material(&self, name: &str) -> Option<M>{
self.names_index.get(name).cloned()
}
pub fn material_name(&self, materialref: M) -> Option<&str>{
self.reverse_name_index.get(materialref).map(|name| name.as_str())
}
pub fn dyn_materials(&self) -> impl Iterator<Item = &dyn Material>{
let materials = unsafe{ &*self.materials.get() };
materials.values().map(|mat| mat.material.material())
}
pub fn iter_dyn(&self) -> impl Iterator<Item = (M, &dyn Material)>{
let materials = unsafe{ &*self.materials.get() };
materials.iter().map(|(id, mat)| (id, mat.material.material()))
}
pub fn materials<F: FullMaterial>(&self) -> impl Iterator<Item = &F>{
let type_id = TypeId::of::<F>();
let materials = unsafe{ &*self.materials.get() };
self.typed_materials.get(&type_id).into_iter().flat_map(move |mats| {
mats.iter().map(move |mat| materials[*mat].material.downcast_ref().unwrap())
})
}
pub fn named_materials<F: FullMaterial>(&self) -> impl Iterator<Item = (String, &F)>{
let type_id = TypeId::of::<F>();
let materials = unsafe{ &*self.materials.get() };
let reverse_name_index = &self.reverse_name_index;
self.typed_materials.get(&type_id).into_iter().flat_map(move |mats| {
mats.iter().map(move |mat| {
let name = reverse_name_index[*mat].clone();
(name, materials[*mat].material.downcast_ref().unwrap())
})
})
}
pub fn iter<F: FullMaterial>(&self) -> impl Iterator<Item = (M, &F)>{
let type_id = TypeId::of::<F>();
let materials = unsafe{ &*self.materials.get() };
self.typed_materials.get(&type_id).into_iter().flat_map(move |mats| {
mats.iter().map(move |mat| (*mat, materials[*mat].material.downcast_ref().unwrap()))
})
}
pub fn named_iter<F: FullMaterial>(&self) -> impl Iterator<Item = (String, M, &F)>{
let type_id = TypeId::of::<F>();
let materials = unsafe{ &*self.materials.get() };
let reverse_name_index = &self.reverse_name_index;
self.typed_materials.get(&type_id).into_iter().flat_map(move |mats| {
mats.iter().map(move |mat| {
let name = reverse_name_index[*mat].clone();
(name, *mat, materials[*mat].material.downcast_ref().unwrap())
})
})
}
pub fn materials_mut<F: FullMaterial>(&mut self) -> impl Iterator<Item = &mut F>{
let type_id = TypeId::of::<F>();
let materials = &self.materials;
self.typed_materials.get(&type_id).into_iter().flat_map(move |mats| {
mats.iter().map(move |mat| {
let materials = unsafe{ &mut *materials.get() };
materials.get_mut(*mat).unwrap().material.downcast_mut().unwrap()
})
})
}
pub fn named_materials_mut<F: FullMaterial>(&mut self) -> impl Iterator<Item = (String, &mut F)>{
let type_id = TypeId::of::<F>();
let materials = &self.materials;
let reverse_name_index = &self.reverse_name_index;
self.typed_materials.get(&type_id).into_iter().flat_map(move |mats| {
mats.iter().map(move |mat| {
let name = reverse_name_index[*mat].clone();
let materials = unsafe{ &mut *materials.get() };
(name, materials[*mat].material.downcast_mut().unwrap())
})
})
}
pub fn iter_mut<F: FullMaterial>(&mut self) -> impl Iterator<Item = (M, &mut F)>{
let type_id = TypeId::of::<F>();
let materials = &self.materials;
self.typed_materials.get(&type_id).into_iter().flat_map(move |mats| {
mats.iter().map(move |mat| {
let materials = unsafe{ &mut *materials.get() };
let material: &mut F = materials.get_mut(*mat).unwrap()
.material
.downcast_mut()
.unwrap();
(*mat, material)
})
})
}
pub fn named_iter_mut<F: FullMaterial>(&mut self) -> impl Iterator<Item = (String, M, &mut F)>{
let type_id = TypeId::of::<F>();
let materials = &self.materials;
let reverse_name_index = &self.reverse_name_index;
self.typed_materials.get(&type_id).into_iter().flat_map(move |mats| {
mats.iter().map(move |mat| {
let name = reverse_name_index[*mat].clone();
let materials = unsafe{ &mut *materials.get() };
(name, *mat, materials[*mat].material.downcast_mut().unwrap())
})
})
}
pub fn material<F: FullMaterial>(&self, materialref: M) -> Option<&F>{
let materials = unsafe{ &*self.materials.get() };
materials.get(materialref).and_then(|m| m.material.downcast_ref())
}
pub fn material_mut<F: FullMaterial>(&mut self, materialref: M) -> Option<&mut F>{
let materials = unsafe{ &mut *self.materials.get() };
materials.get_mut(materialref).and_then(|m| m.material.downcast_mut())
}
pub fn dyn_material(&self, materialref: M) -> Option<&dyn Material> {
let materials = unsafe{ &*self.materials.get() };
materials.get(materialref).map(|m| m.material.material())
}
pub fn dyn_material_mut(&mut self, materialref: M) -> Option<&mut dyn Material>{
let materials = unsafe{ &mut *self.materials.get() };
materials.get_mut(materialref).map(|m| m.material.material_mut())
}
fn update(&mut self, mut entities: EntitiesThreadLocal, resources: ResourcesThreadLocal){
let mut materials_cache = resources.get_mut::<MaterialCache<M>>().unwrap();
let glin = resources.get::<gl::Renderer<'static>>().unwrap();
let textures_pool = resources.get::<TexturesPool>().unwrap();
let lighting_textures = resources.get::<LightingTextures>().unwrap();
let mut ubos_unified = resources.get_mut::<Buffer<M, UnifiedMaterialOffset>>();
let mut ubos_per = resources.get_mut::<Buffer<M, PerMaterialOffset>>();
let ubos: &mut dyn BufferAdd<M> = if let Some(ubos) = ubos_unified.as_mut() {
&mut **ubos
}else if let Some(ubos) = ubos_per.as_mut() {
&mut **ubos
}else{
unreachable!()
};
let mut program_cache = resources.get_mut::<ProgramCache>().unwrap();
let camera = resources.as_trait::<dyn CameraExt + Send>().unwrap();
let screen_renderbuffer = resources.get::<ScreenRenderBuffer>();
let materials = unsafe{ &mut *self.materials.get() };
let light_info = resources.get::<LightInfo>().unwrap();
let lights_changed = light_info.has_changed() || lighting_textures.num_shadow_maps_changed;
let new_camera_type = CameraType::from_camera(&*camera);
let camera_type_changed = new_camera_type != self.last_camera_type;
let mut any_material_changed = false;
let mut any_material_transparency_changed = false;
let mut any_texture_or_uniform_changed = false;
for toremove in self.removed.drain(..) {
if let Some(material_data) = materials_cache.remove(toremove){
if !materials_cache.any_material_uses(material_data.programref) {
program_cache.remove(material_data.programref)
}
}
}
for (materialref, meta) in materials.iter_mut(){
let material = &mut meta.material;
material.material_mut().update(entities.clone(), resources.clone());
if let Some(material_data) = materials_cache.get_mut(materialref){
let program_settings = if material_data.program_settings_changes{
Some(material.material().program_settings())
}else{
None
};
let shaders = if material_data.shaders_change{
Some(material.material().shaders())
}else{
None
};
let post_fragment = material.post_fragment();
let is_translucent;
let transparency;
if material_data.transparency_changes {
transparency = material.material().transparency();
if let PropertyChanged::New(transparency) = &transparency{
is_translucent = transparency.is_translucent
}else{
is_translucent = material_data.translucent
}
}else{
transparency = PropertyChanged::Old;
is_translucent = material_data.translucent
};
if (material_data.needs_light_info && lights_changed)
|| camera_type_changed
|| program_settings.as_ref().map(|p| p.is_new()).unwrap_or(false)
|| shaders.as_ref().map(|s| s.is_new()).unwrap_or(false)
|| post_fragment.is_new()
|| material_data.translucent != is_translucent
{
any_material_changed = true;
let prev_program_id = &program_cache.reverse_index()[&material_data.programref];
let program_settings = if let Some(program_settings) = program_settings{
program_settings
.unwrap_or_else(|| material_data.program_settings.clone())
}else{
material_data.program_settings.clone()
};
let shaders = if let Some(shaders) = shaders {
shaders.unwrap_or_else(|| prev_program_id.shaders.clone())
}else{
prev_program_id.shaders.clone()
};
let post_fragment = post_fragment
.unwrap_or_else(|| prev_program_id.post_fragment.as_ref())
.cloned();
let needs_data = material_data.needs_data;
let (programref, needs_light_info) = program_cache.program_for(
&glin,
&meta.name,
program_settings,
is_translucent,
shaders,
post_fragment,
needs_data,
&light_info,
new_camera_type,
&lighting_textures,
screen_renderbuffer.as_ref(),
);
material_data.programref = programref;
material_data.needs_light_info = needs_light_info;
let program = program_cache.program_info_mut(programref).unwrap();
material_data.update_uniforms_locations(program.program());
material_data.update_textures_locations(program.program());
material_data.update_render_planes_locations(program.program());
if needs_light_info {
program.set_lighting_textures(&lighting_textures);
}
}
let program = program_cache.program_info_mut(material_data.programref).unwrap();
material_data.update_parts(
&mut **material,
&meta.name,
&entities,
program.program(),
&textures_pool,
ubos,
materialref,
transparency,
&mut any_material_changed,
&mut any_material_transparency_changed,
&mut any_texture_or_uniform_changed);
}else{
log::trace!("creating material {}", meta.name);
any_material_changed = true;
let screen_renderbuffer = resources.get::<ScreenRenderBuffer>();
let program_settings = material.material().program_settings();
let mat_program_settings = program_settings.clone()
.expect("Material without program settings on creation");
let shaders = material.material().shaders();
let shaders_change = !shaders.is_always();
let shaders = shaders.expect("Material without shaders on creation");
let post_fragment = material.post_fragment()
.expect("Material without post render on creation");
let data = material.material().data();
let needs_data = data.as_ref().map(|d| d.is_some()).unwrap_or(false);
let transparency = material.material().transparency();
let translucent = transparency
.as_ref()
.expect("Material without transparency on creation")
.is_translucent;
let (programref, needs_light_info) = program_cache.program_for(
&glin,
&meta.name,
mat_program_settings,
translucent,
shaders,
post_fragment.cloned(),
needs_data,
&light_info,
new_camera_type,
&lighting_textures,
screen_renderbuffer.as_ref(),
);
let program = program_cache.program_info_mut(programref).unwrap();
let material_data = MaterialData::new(&mut **material,
shaders_change,
needs_light_info,
&entities,
program.program(),
&textures_pool,
ubos,
data,
program_settings,
materialref,
programref,
transparency);
materials_cache.insert(materialref, material_data);
if needs_light_info {
program.set_lighting_textures(&lighting_textures);
}
}
material.full_reset_changed();
}
materials_cache.any_material_changed = any_material_changed;
materials_cache.any_material_transparency_changed = any_material_transparency_changed;
materials_cache.any_texture_or_uniform_changed = any_texture_or_uniform_changed;
self.last_camera_type = new_camera_type;
#[cfg(feature = "gui")]
{
self.active_materials.set(materials_cache.len());
}
}
}
impl MaterialPool<MaterialRef> {
pub fn unused<'a, E: EntitiesExt<'a>>(&self, entities: &E) -> Vec<MaterialRef> {
let used_materials = entities.iter_for::<Read<MaterialRef>>()
.flatten()
.copied();
let used_multimaterials = entities.iter_for::<Read<MaterialMultiRef>>()
.flatten()
.flat_map(|m| m.iter())
.copied();
#[cfg(feature="debug_geometry")]
let debug_materials = entities.iter_for::<Read<DebugGeometryRef>>()
.map(|m| m.material())
.copied();
let used_materials = used_materials
.chain(used_multimaterials);
#[cfg(feature="debug_geometry")]
let used_materials = used_materials.chain(debug_materials);
let used_materials = used_materials.collect::<hashbrown::HashSet<_>>();
let materials = unsafe{ &*self.materials.get() };
materials.keys().filter(|k| !used_materials.contains(k)).collect::<Vec<_>>()
}
pub fn remove_unused<'a, E: EntitiesExt<'a>>(&mut self, entities: &E){
for non_used in self.unused(entities) {
self.remove(non_used)
}
}
}
impl<M: densevec::Key> Index<M> for MaterialPool<M>{
type Output = Box<dyn FullMaterial>;
fn index(&self, index: M) -> &Box<dyn FullMaterial>{
let materials = unsafe{ &*self.materials.get() };
&materials[index].material
}
}
impl<M: densevec::Key> IndexMut<M> for MaterialPool<M>{
fn index_mut(&mut self, index: M) -> &mut Box<dyn FullMaterial>{
let materials = unsafe{ &mut *self.materials.get() };
&mut materials[index].material
}
}
#[system_thread_local(name = "material pool")]
#[gpu_stats]
#[needs("LightInfo", "Clock", "TexturesPool", "RenderPlane", "Materials", "LightingTextures", "Camera")]
#[updates("MaterialPool<MaterialRef>", "MaterialCache<MaterialRef>")]
#[reads(
gl::Renderer,
TexturesPool,
LightingTextures,
ScreenRenderBuffer,
LightInfo,
)]
#[writes(
MaterialPool<MaterialRef>,
MaterialCache<MaterialRef>,
Buffer<MaterialRef, UnifiedMaterialOffset>,
Buffer<MaterialRef, PerMaterialOffset>,
ProgramCache,
)]
pub fn material_pool_updater(entities: EntitiesThreadLocal, resources: ResourcesThreadLocal){
let mut pool = resources.get_mut::<MaterialPool<MaterialRef>>().unwrap();
pool.update(entities, resources);
}
#[system_thread_local(name = "material offsets update")]
#[needs(
OpaqueSortedGeometry,
TranslucentSortedGeometry,
Buffer<MaterialRef, UnifiedMaterialOffset>
)]
#[cfg_attr(feature="debug_geometry", needs("DebugSortedGeometry"))]
#[after("material_pool_updater")]
#[updates("MaterialPool<MaterialRef>")]
#[writes(MaterialOffsets)]
#[gpu_stats]
pub fn material_offsets_updater(_: EntitiesThreadLocal, resources: ResourcesThreadLocal){
let mut material_offsets = resources.get_mut::<MaterialOffsets>().unwrap();
let ubos = resources.get::<Buffer<MaterialRef, UnifiedMaterialOffset>>().unwrap();
let opaque_geometry_sorted = resources
.get::<OpaqueSortedGeometry>()
.unwrap();
let translucent_geometry_sorted = resources
.get::<TranslucentSortedGeometry>()
.unwrap();
#[cfg(feature="debug_geometry")]
let debug_geometry_sorted = resources
.get::<DebugSortedGeometry>()
.unwrap();
let opaque_changed = opaque_geometry_sorted.has_changed();
let translucent_changed = translucent_geometry_sorted.has_changed();
#[cfg(feature="debug_geometry")]
let debug_changed = debug_geometry_sorted.has_changed();
#[cfg(not(feature="debug_geometry"))]
let debug_changed = false;
let was_empty = material_offsets.offsets_data.is_empty();
if opaque_changed || translucent_changed || debug_changed || was_empty {
let geometries = opaque_geometry_sorted.iter()
.chain(translucent_geometry_sorted.iter());
#[cfg(feature="debug_geometry")]
let geometries = geometries.chain(debug_geometry_sorted.iter());
material_offsets.offsets_data.clear();
let offset_index = &ubos.offset_index;
let material_offsets_it = geometries.flat_map(|geom| {
let offset = offset_index.get(geom.materialref)
.map(|offset| offset.index as u32)
.unwrap_or(0);
iter::repeat(offset).take(geom.entities.len())
});
material_offsets.offsets_data.extend(material_offsets_it);
if !(was_empty && material_offsets.offsets_data.is_empty()){
material_offsets.load_data();
}
}
}
#[system_thread_local(name = "shadow materials pool")]
#[gpu_stats]
#[needs("LightInfo", "Clock", "TexturesPool", "RenderPlane", "Materials", "LightingTextures", "Camera")]
#[updates("ShadowMaterialPool", "ShadowMaterialCache")]
#[reads(
gl::Renderer,
TexturesPool,
LightingTextures,
ScreenRenderBuffer,
LightInfo
)]
#[writes(
Buffer<ShadowMaterialRef, UnifiedMaterialOffset>,
Buffer<ShadowMaterialRef, PerMaterialOffset>,
ProgramCache,
ShadowMaterialCache,
)]
pub fn shadow_material_pool_updater(entities: EntitiesThreadLocal, resources: ResourcesThreadLocal){
let mut pool = resources.get_mut::<ShadowMaterialPool>().unwrap();
pool.update(entities, resources);
}
#[system_thread_local(name = "shadow material offsets update")]
#[needs("DynamicShadowsSortedGeometry", "StaticShadowsSortedGeometry", "AllShadowsSortedGeometry")]
#[after("shadow_material_pool_updater")]
#[updates("ShadowMaterialPool")]
#[writes(ShadowMaterialOffsets)]
#[reads(
Buffer<ShadowMaterialRef,
UnifiedMaterialOffset>,
StaticShadowsSortedGeometry,
DynamicShadowsSortedGeometry,
AllShadowsSortedGeometry,
Buffer<ShadowMaterialRef, UnifiedMaterialOffset>
)]
#[gpu_stats]
pub fn shadow_material_offsets_updater(_: EntitiesThreadLocal, resources: ResourcesThreadLocal){
let mut material_offsets = resources.get_mut::<ShadowMaterialOffsets>().unwrap();
let ubos = resources.get::<Buffer<ShadowMaterialRef, UnifiedMaterialOffset>>().unwrap();
let dynamic_geometry_sorted = resources
.get::<DynamicShadowsSortedGeometry>()
.unwrap();
let dynamic_changed = dynamic_geometry_sorted.has_changed();
let dynamic_was_empty = material_offsets.dynamic_offsets_data.is_empty();
if dynamic_changed || (dynamic_was_empty != dynamic_geometry_sorted.is_empty()) {
material_offsets.dynamic_offsets_data.clear();
let offset_index = &ubos.offset_index;
let material_offsets_it = dynamic_geometry_sorted.iter().flat_map(|geom| {
let offset = if let Some(materialref) = geom.materialref {
offset_index.get(materialref)
.map(|offset| offset.index as u32)
.unwrap_or(0)
}else{
0
};
iter::repeat(offset).take(geom.entities.len())
});
material_offsets.dynamic_offsets_data.extend(material_offsets_it);
material_offsets.load_dynamic_data();
}
let static_geometry_sorted = resources
.get::<StaticShadowsSortedGeometry>()
.unwrap();
let static_changed = static_geometry_sorted.has_changed();
let static_was_empty = material_offsets.static_offsets_data.is_empty();
if static_changed || (static_was_empty != static_geometry_sorted.is_empty()) {
material_offsets.static_offsets_data.clear();
let offset_index = &ubos.offset_index;
let material_offsets_it = static_geometry_sorted.iter().flat_map(|geom| {
let offset = if let Some(materialref) = geom.materialref {
offset_index.get(materialref)
.map(|offset| offset.index as u32)
.unwrap_or(0)
}else{
0
};
iter::repeat(offset).take(geom.entities.len())
});
material_offsets.static_offsets_data.extend(material_offsets_it);
material_offsets.load_static_data();
}
let all_geometry_sorted = resources
.get::<AllShadowsSortedGeometry>()
.unwrap();
let all_changed = all_geometry_sorted.has_changed();
let all_was_empty = material_offsets.all_offsets_data.is_empty();
if all_changed || (all_was_empty != all_geometry_sorted.is_empty()) {
material_offsets.all_offsets_data.clear();
let offset_index = &ubos.offset_index;
let material_offsets_it = all_geometry_sorted.iter().flat_map(|geom| {
let offset = if let Some(materialref) = geom.materialref {
offset_index.get(materialref)
.map(|offset| offset.index as u32)
.unwrap_or(0)
}else{
0
};
iter::repeat(offset).take(geom.entities.len())
});
material_offsets.all_offsets_data.extend(material_offsets_it);
material_offsets.load_all_data();
}
}
#[derive(Debug)]
pub struct PerMaterialOffset{
start: usize,
end: usize,
}
#[derive(Debug)]
pub struct UnifiedMaterialOffset{
start: usize,
end: usize,
index: usize,
}
type MaterialUBO<'a> = glin::buffer::Range<u8, glin::Buffer<u8>, &'a glin::Buffer<u8>>;
pub struct Buffer<M, O>{
data: Vec<u8>,
buffer: glin::Buffer<u8>,
offset_index: KeyedDenseVec<M, O>,
last_offset: usize,
ubo_align_size: usize,
}
pub trait BufferAdd<M>{
fn add(
&mut self,
materialref: M,
data: Data) -> MaterialUBO;
fn per_material_ubo(&self, material_ref: M) -> Option<MaterialUBO>;
}
impl<M: densevec::Key + Copy> BufferAdd<M> for Buffer<M, PerMaterialOffset> {
fn add(
&mut self,
materialref: M,
data: Data) -> MaterialUBO
{
if let Some(offset) = self.offset_index.get(materialref){
if data.len() == offset.end - offset.start{
self.data[offset.start .. offset.end].copy_from_slice(&data);
self.buffer.range_mut(offset.start .. offset.end).update(&data);
return self.buffer.range(offset.start .. offset.end);
}
}
let begin = self.last_offset;
let end = begin + data.len();
let offset = PerMaterialOffset{
start: begin,
end,
};
self.offset_index.insert(materialref, offset);
self.data.extend_from_slice(&data);
let rem = data.len() % self.ubo_align_size;
if rem != 0 {
let add = self.ubo_align_size - rem;
self.data.reserve(add);
unsafe{ self.data.set_len(self.data.len() + add) };
self.last_offset = end + add;
}else{
self.last_offset = end;
}
self.buffer.load(&self.data, gl::DYNAMIC_DRAW);
self.buffer.range(begin .. end)
}
fn per_material_ubo(&self, material_ref: M) -> Option<MaterialUBO>{
self.offset_index.get(material_ref)
.map(|range| self.buffer.range(range.start .. range.end))
}
}
impl<M: densevec::Key + Copy> BufferAdd<M> for Buffer<M, UnifiedMaterialOffset> {
fn add(
&mut self,
materialref: M,
data: Data) -> MaterialUBO
{
if let Some(offset) = self.offset_index.get(materialref){
if data.len() == offset.end - offset.start{
self.data[offset.start .. offset.end].copy_from_slice(&data);
self.buffer.range_mut(offset.start .. offset.end).update(&data);
return self.buffer.range(offset.start .. offset.end);
}
}
let mut begin = self.last_offset;
let rem = begin % data.len();
if rem != 0 {
let add = data.len() - rem;
self.data.reserve(add);
unsafe{ self.data.set_len(self.data.len() + add) };
begin += add;
}
let end = begin + data.len();
self.offset_index.insert(materialref, UnifiedMaterialOffset{
start: begin,
end: end,
index: begin / data.len(),
});
self.data.extend_from_slice(&data);
self.last_offset = end;
self.buffer.load(&self.data, gl::DYNAMIC_DRAW);
self.buffer.range(begin .. end)
}
fn per_material_ubo(&self, _: M) -> Option<MaterialUBO>{
None
}
}
impl<M: densevec::Key, O> Buffer<M, O> {
fn new<R: glin::RenderSurface>(gl: &gl::Renderer<R>) -> Buffer<M, O>{
let ubo_align_size = unsafe{
gl.context().state().get_int(gl::UNIFORM_BUFFER_OFFSET_ALIGNMENT) as usize
};
Buffer{
data: vec![],
buffer: gl.new_buffer().empty_target(gl::UNIFORM_BUFFER).unwrap(),
offset_index: KeyedDenseVec::default(),
last_offset: 0,
ubo_align_size,
}
}
pub fn buffer(&self) -> &glin::Buffer<u8>{
&self.buffer
}
fn _offset(&self, material_ref: M) -> Option<&O>{
self.offset_index.get(material_ref)
}
}
fn hash_uniforms(uniforms: &[gl::program::Uniform]) -> u64{
let mut hasher = fxhash::FxHasher::default();
for uniform in uniforms{
match uniform{
gl::program::Uniform::Location(loc, value) => {
loc.hash(&mut hasher);
mem::discriminant(value).hash(&mut hasher);
match value{
gl::program::UniformValue::Float(v) => {
let v: &i32 = unsafe{ mem::transmute(v) };
v.hash(&mut hasher);
},
gl::program::UniformValue::Int(v) => {
v.hash(&mut hasher);
},
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
gl::program::UniformValue::UInt(v) => {
v.hash(&mut hasher);
},
gl::program::UniformValue::Bool(v) => {
v.hash(&mut hasher);
},
gl::program::UniformValue::Mat2(v) => {
let v: &[i32;4] = unsafe{ mem::transmute(v) };
v.hash(&mut hasher);
},
gl::program::UniformValue::Mat3(v) => {
let v: &[i32;9] = unsafe{ mem::transmute(v) };
v.hash(&mut hasher);
},
gl::program::UniformValue::Mat4(v) => {
let v: &[i32;16] = unsafe{ mem::transmute(v) };
v.hash(&mut hasher);
},
&gl::program::UniformValue::Vec2(x,y) => {
let v: [i32;2] = unsafe{ mem::transmute((x,y)) };
v.hash(&mut hasher);
},
&gl::program::UniformValue::Vec3(x,y,z) => {
let v: [i32;3] = unsafe{ mem::transmute((x,y,z)) };
v.hash(&mut hasher);
},
&gl::program::UniformValue::Vec4(x,y,z,w) => {
let v: [i32;4] = unsafe{ mem::transmute((x,y,z,w)) };
v.hash(&mut hasher);
},
gl::program::UniformValue::IVec2(x,y) => {
(x,y).hash(&mut hasher);
},
gl::program::UniformValue::IVec3(x,y,z) => {
(x,y,z).hash(&mut hasher);
},
gl::program::UniformValue::IVec4(x,y,z,w) => {
(x,y,z,w).hash(&mut hasher);
},
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
gl::program::UniformValue::UVec2(x,y) => {
(x,y).hash(&mut hasher);
},
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
gl::program::UniformValue::UVec3(x,y,z) => {
(x,y,z).hash(&mut hasher);
},
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
gl::program::UniformValue::UVec4(x,y,z,w) => {
(x,y,z,w).hash(&mut hasher);
},
gl::program::UniformValue::ArrayFloat(v) => {
let v: &Vec<i32> = unsafe{ mem::transmute(v) };
v.hash(&mut hasher);
},
gl::program::UniformValue::ArrayVec2(v) => {
let v: &Vec<[i32;2]> = unsafe{ mem::transmute(v) };
v.hash(&mut hasher);
},
gl::program::UniformValue::ArrayVec3(v) => {
let v: &Vec<[i32;3]> = unsafe{ mem::transmute(v) };
v.hash(&mut hasher);
},
gl::program::UniformValue::ArrayVec4(v) => {
let v: &Vec<[i32;4]> = unsafe{ mem::transmute(v) };
v.hash(&mut hasher);
},
gl::program::UniformValue::ArrayInt(v) => {
v.hash(&mut hasher);
},
gl::program::UniformValue::ArrayIVec2(v) => {
v.hash(&mut hasher);
},
gl::program::UniformValue::ArrayIVec3(v) => {
v.hash(&mut hasher);
},
gl::program::UniformValue::ArrayIVec4(v) => {
v.hash(&mut hasher);
},
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
gl::program::UniformValue::ArrayUInt(v) => {
v.hash(&mut hasher);
},
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
gl::program::UniformValue::ArrayUVec2(v) => {
v.hash(&mut hasher);
},
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
gl::program::UniformValue::ArrayUVec3(v) => {
v.hash(&mut hasher);
},
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
gl::program::UniformValue::ArrayUVec4(v) => {
v.hash(&mut hasher);
},
gl::program::UniformValue::Texture(tex, tex_location, target) => {
(tex, tex_location, target).hash(&mut hasher);
},
gl::program::UniformValue::TextureSampler(tex, tex_location, target, sampler) => {
(tex, tex_location, target, sampler).hash(&mut hasher);
},
gl::program::UniformValue::UniformBlockBinding(v) => {
v.hash(&mut hasher);
},
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
gl::program::UniformValue::ShaderStorageBlockBinding(v) => {
v.hash(&mut hasher);
},
}
},
_ => unreachable!(),
}
}
hasher.finish()
}
#[derive(Clone)]
pub struct MaterialData<M>{
pub materialref: M,
pub programref: ProgramRef,
pub uniforms: Vec<glin::program::Uniform>,
pub textures: Vec<glin::program::Uniform>,
pub render_planes: Vec<glin::program::Uniform>,
pub properties: Vec<gl::Property>,
pub alpha_type_properties: Vec<gl::Property>,
pub translucent: bool,
pub visible: bool,
pub priority: i32,
pub textures_hash: u64,
pub uniforms_hash: u64,
named_uniforms: Vec<glin::program::Uniform>,
named_textures: Vec<glin::program::Uniform>,
named_cubemaps: Vec<glin::program::Uniform>,
named_render_planes: Vec<glin::program::Uniform>,
program_settings: ProgramSettings,
textures_offset: u32,
data_changes: bool,
program_settings_changes: bool,
shaders_change: bool,
uniforms_change: bool,
textures_change: bool,
cubemaps_change: bool,
render_planes_change: bool,
properties_change: bool,
transparency_changes: bool,
needs_data: bool,
needs_light_info: bool,
only_textures_hash: u64,
only_cubemaps_hash: u64,
only_renderplanes_hash: Option<u64>,
}
unsafe impl<M> Send for MaterialData<M>{}
impl<M: Copy> MaterialData<M>{
fn new(material: &mut dyn FullMaterial,
shaders_change: bool,
needs_light_info: bool,
entities: &EntitiesThreadLocal,
program: &glin::Program,
textures_pool: &TexturesPool,
ubos: &mut dyn BufferAdd<M>,
material_data: PropertyChanged<Option<Data>>,
program_settings: PropertyChanged<ProgramSettings>,
materialref: M,
programref: ProgramRef,
transparency: PropertyChanged<MaterialTransparency>) -> MaterialData<M>
{
let data_changes = !material_data.is_always();
let needs_data = if let Some(data) = material_data.unwrap_or(None){
ubos.add(materialref, data);
true
}else{
false
};
let named_uniforms = material.full_uniforms();
let uniforms_change = !named_uniforms.is_always();
let named_uniforms = named_uniforms.expect("Material without uniforms on creation");
let uniforms = named_uniforms.iter()
.filter_map(|uniform| uniform.to_location(program))
.collect::<Vec<_>>();
let uniforms_hash = hash_uniforms(&uniforms);
let mut textures_offset = 0;
let named_textures = material.full_textures(&textures_pool, &mut textures_offset);
let textures_change = !named_textures.is_always();
let (named_textures, textures_hash) = named_textures
.expect("Material without textures on creation");
let named_cubemaps = material.full_cubemaps(&textures_pool, &mut textures_offset);
let cubemaps_change = !named_cubemaps.is_always();
let (named_cubemaps, cubemaps_hash) = named_cubemaps
.expect("Material without cubemaps on creation");
let textures = named_textures.iter().chain(named_cubemaps.iter())
.filter_map(|uniform| uniform.to_location(program))
.collect();
let named_opt_render_planes = material.full_renderplanes(&entities, textures_offset);
let render_planes_change = !named_opt_render_planes.is_always();
let render_planes;
let render_planes_hash;
let named_render_planes;
if let Some((named_opt_render_planes, hash)) = named_opt_render_planes
.expect("Material without render planes on creation")
{
render_planes = named_opt_render_planes.iter()
.filter_map(|uniform| uniform.to_location(program))
.collect();
render_planes_hash = Some(hash);
named_render_planes = named_opt_render_planes;
}else{
render_planes = vec![];
render_planes_hash = None;
named_render_planes = vec![];
}
let mut hasher = fxhash::FxHasher::default();
textures_hash.hash(&mut hasher);
cubemaps_hash.hash(&mut hasher);
render_planes_hash.hash(&mut hasher);
let combined_hash = hasher.finish();
let properties = material.material().properties();
let properties_change = !properties.is_always();
let mut properties = properties.expect("Material without properties on creation");
if let Some(ubo) = ubos.per_material_ubo(materialref) {
properties.push(super::UBOBindingPoints::Material.to_buffer_binding(ubo));
}
let transparency_changes = !transparency.is_always();
let transparency = transparency.expect("Material without transparency on creation");
let alpha_type_properties = transparency.alpha_type.into_gl_properties();
MaterialData{
materialref,
programref,
data_changes,
program_settings_changes: !program_settings.is_always(),
program_settings: program_settings.unwrap_or_else(|| ProgramSettings::default()),
uniforms,
uniforms_hash,
named_uniforms,
uniforms_change,
textures,
named_textures,
textures_change,
named_cubemaps,
cubemaps_change,
textures_offset,
render_planes,
named_render_planes,
render_planes_change,
properties,
properties_change,
translucent: transparency.is_translucent,
visible: transparency.is_visible,
priority: transparency.priority.unwrap_or(0),
transparency_changes,
alpha_type_properties,
textures_hash: combined_hash,
only_textures_hash: textures_hash,
only_cubemaps_hash: cubemaps_hash,
only_renderplanes_hash: render_planes_hash,
shaders_change,
needs_data,
needs_light_info,
}
}
fn update_parts(&mut self,
material: &mut dyn FullMaterial,
mat_name: &str,
entities: &EntitiesThreadLocal,
program: &glin::Program,
textures_pool: &TexturesPool,
ubos: &mut dyn BufferAdd<M>,
materialref: M,
transparency: PropertyChanged<MaterialTransparency>,
changed: &mut bool,
transparency_changed: &mut bool,
any_texture_or_uniform_changed: &mut bool)
{
if self.data_changes {
if let PropertyChanged::New(Some(data)) = material.material().data(){
ubos.add(materialref, data);
*changed = true;
}
}
if self.uniforms_change {
if let PropertyChanged::New(named_uniforms) = material.full_uniforms() {
self.named_uniforms = named_uniforms;
self.update_uniforms_locations(program);
self.uniforms_hash = hash_uniforms(&self.uniforms);
log::trace!("Uniforms changed {}", mat_name);
*changed = true;
*any_texture_or_uniform_changed = true;
}
}
let mut textures_offset = self.named_textures.len() as u32;
let mut needs_textures_refresh = false;
let mut needs_textures_hash_refresh = false;
if self.textures_change {
if let PropertyChanged::New((named_textures, textures_hash)) = material
.full_textures(textures_pool, &mut textures_offset)
{
self.named_textures = named_textures;
self.only_textures_hash = textures_hash;
self.textures_offset = textures_offset;
log::trace!("Textures changed {}", mat_name);
*changed = true;
needs_textures_refresh = true;
needs_textures_hash_refresh = true;
*any_texture_or_uniform_changed = true;
}
}
if self.cubemaps_change {
if let PropertyChanged::New((named_cubemaps, cubemaps_hash)) = material
.full_cubemaps(textures_pool, &mut textures_offset)
{
self.named_cubemaps = named_cubemaps;
self.only_cubemaps_hash = cubemaps_hash;
self.textures_offset = textures_offset;
log::trace!("Cubemaps changed {}", mat_name);
*changed = true;
needs_textures_refresh = true;
needs_textures_hash_refresh = true;
*any_texture_or_uniform_changed = true;
}
}
if needs_textures_refresh {
self.update_textures_locations(program);
}
if self.render_planes_change {
if let PropertyChanged::New(named_render_planes) = material
.full_renderplanes(&entities, self.textures_offset)
{
if let Some((named_render_planes, render_planes_hash)) = named_render_planes {
self.named_render_planes = named_render_planes;
self.only_renderplanes_hash = Some(render_planes_hash);
}else{
self.named_render_planes.clear();
self.only_renderplanes_hash = None;
}
self.update_render_planes_locations(program);
log::trace!("Render planes changed {}", mat_name);
*changed = true;
needs_textures_hash_refresh = true;
*any_texture_or_uniform_changed = true;
}
}
if needs_textures_hash_refresh {
let mut hasher = fxhash::FxHasher::default();
self.only_textures_hash.hash(&mut hasher);
self.only_cubemaps_hash.hash(&mut hasher);
self.only_renderplanes_hash.hash(&mut hasher);
self.textures_hash = hasher.finish();
}
if self.properties_change {
if let PropertyChanged::New(properties) = material.material().properties(){
self.properties = properties;
if let Some(ubo) = ubos.per_material_ubo(materialref) {
let bind_ubo = super::UBOBindingPoints::Material.to_buffer_binding(ubo);
self.properties.push(bind_ubo);
}
*changed = true;
}
}
if self.transparency_changes {
if let PropertyChanged::New(transparency) = transparency {
self.translucent = transparency.is_translucent;
self.visible = transparency.is_visible;
self.priority = transparency.priority.unwrap_or(0);
self.alpha_type_properties = transparency.alpha_type.into_gl_properties();
*changed = true;
*transparency_changed = true;
}
}
}
fn update_uniforms_locations(&mut self, program: &glin::Program){
self.uniforms = self.named_uniforms.iter()
.filter_map(|uniform| uniform.to_location(program))
.collect();
self.uniforms.sort_unstable_by_key(|uniform| match uniform{
gl::program::Uniform::Location(loc, _) => *loc,
_ => unreachable!(),
});
}
fn update_textures_locations(&mut self, program: &glin::Program){
self.textures = self.named_textures.iter().chain(self.named_cubemaps.iter())
.filter_map(|uniform| uniform.to_location(program))
.collect();
}
fn update_render_planes_locations(&mut self, program: &glin::Program){
self.render_planes = self.named_render_planes.iter()
.filter_map(|uniform| uniform.to_location(program))
.collect();
}
pub fn all_uniforms(&self) -> impl Iterator<Item = &glin::program::Uniform>{
self.uniforms.iter().chain(self.textures.iter()).chain(self.render_planes.iter())
}
}
pub type ShadowMaterialCache = MaterialCache<ShadowMaterialRef>;
pub struct MaterialCache<M: densevec::Key = MaterialRef>{
cache: KeyedDenseVec<M, MaterialData<M>>,
any_material_changed: bool,
any_material_transparency_changed: bool,
any_texture_or_uniform_changed: bool,
}
impl MaterialCache<MaterialRef>{
pub fn new() -> MaterialCache<MaterialRef>{
MaterialCache{
cache: KeyedDenseVec::new(),
any_material_changed: false,
any_material_transparency_changed: false,
any_texture_or_uniform_changed: false,
}
}
}
impl ShadowMaterialCache{
pub fn new_shadows() -> ShadowMaterialCache{
ShadowMaterialCache{
cache: KeyedDenseVec::new(),
any_material_changed: false,
any_material_transparency_changed: false,
any_texture_or_uniform_changed: false,
}
}
}
impl<M: densevec::Key> MaterialCache<M>{
fn insert(&mut self, materialref: M, data: MaterialData<M>) -> Option<MaterialData<M>>{
self.cache.insert(materialref, data)
}
fn contains(&self, materialref: M) -> bool{
self.cache.contains_key(materialref)
}
fn get_mut(&mut self, materialref: M) -> Option<&mut MaterialData<M>>{
self.cache.get_mut(materialref)
}
fn is_empty(&self) -> bool{
self.cache.is_empty()
}
fn len(&self) -> usize {
self.cache.len()
}
fn remove(&mut self, materialref: M) -> Option<MaterialData<M>> {
self.cache.remove(materialref)
}
pub fn any_material_changed(&self) -> bool{
self.any_material_changed
}
pub fn any_material_transparency_changed(&self) -> bool{
self.any_material_transparency_changed
}
pub fn any_texture_or_uniform_changed(&self) -> bool{
self.any_texture_or_uniform_changed
}
pub fn any_material_uses(&self, programref: ProgramRef) -> bool {
self.cache.values().any(|mat| mat.programref == programref)
}
}
impl<M: densevec::Key> Index<M> for MaterialCache<M>{
type Output = MaterialData<M>;
fn index(&self, materialref: M) -> &MaterialData<M>{
&self.cache[materialref]
}
}
impl<M: densevec::Key> IndexMut<M> for MaterialCache<M>{
fn index_mut(&mut self, materialref: M) -> &mut MaterialData<M>{
&mut self.cache[materialref]
}
}