mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'imbris/ui_changes' into 'master'
UI Cache Updates and Warning Cleanup See merge request veloren/veloren!296
This commit is contained in:
commit
fc73f71e60
@ -282,6 +282,11 @@ impl Renderer {
|
|||||||
model.update(&mut self.encoder, mesh, offset)
|
model.update(&mut self.encoder, mesh, offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the maximum supported texture size.
|
||||||
|
pub fn max_texture_size(&self) -> usize {
|
||||||
|
self.factory.get_capabilities().max_texture_size
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new texture from the provided image.
|
/// Create a new texture from the provided image.
|
||||||
pub fn create_texture<P: Pipeline>(
|
pub fn create_texture<P: Pipeline>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -6,6 +6,13 @@ use crate::{
|
|||||||
use conrod_core::text::GlyphCache;
|
use conrod_core::text::GlyphCache;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
|
// Multiplied by current window size
|
||||||
|
const GLYPH_CACHE_SIZE: u16 = 1;
|
||||||
|
const GRAPHIC_CACHE_SIZE: u16 = 2;
|
||||||
|
// Glyph cache tolerances
|
||||||
|
const SCALE_TOLERANCE: f32 = 0.1;
|
||||||
|
const POSITION_TOLERANCE: f32 = 0.1;
|
||||||
|
|
||||||
pub struct Cache {
|
pub struct Cache {
|
||||||
glyph_cache: GlyphCache<'static>,
|
glyph_cache: GlyphCache<'static>,
|
||||||
glyph_cache_tex: Texture<UiPipeline>,
|
glyph_cache_tex: Texture<UiPipeline>,
|
||||||
@ -17,17 +24,21 @@ pub struct Cache {
|
|||||||
impl Cache {
|
impl Cache {
|
||||||
pub fn new(renderer: &mut Renderer) -> Result<Self, Error> {
|
pub fn new(renderer: &mut Renderer) -> Result<Self, Error> {
|
||||||
let (w, h) = renderer.get_resolution().into_tuple();
|
let (w, h) = renderer.get_resolution().into_tuple();
|
||||||
const SCALE_TOLERANCE: f32 = 0.1;
|
|
||||||
const POSITION_TOLERANCE: f32 = 0.1;
|
|
||||||
|
|
||||||
let graphic_cache_dims = Vec2::new(w * 4, h * 4);
|
let max_texture_size = renderer.max_texture_size();
|
||||||
|
|
||||||
|
let graphic_cache_dims =
|
||||||
|
Vec2::new(w, h).map(|e| (e * GRAPHIC_CACHE_SIZE).min(max_texture_size as u16));
|
||||||
|
let glyph_cache_dims =
|
||||||
|
Vec2::new(w, h).map(|e| (e * GLYPH_CACHE_SIZE).min(max_texture_size as u16));
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
glyph_cache: GlyphCache::builder()
|
glyph_cache: GlyphCache::builder()
|
||||||
.dimensions(w as u32, h as u32)
|
.dimensions(glyph_cache_dims.x as u32, glyph_cache_dims.y as u32)
|
||||||
.scale_tolerance(SCALE_TOLERANCE)
|
.scale_tolerance(SCALE_TOLERANCE)
|
||||||
.position_tolerance(POSITION_TOLERANCE)
|
.position_tolerance(POSITION_TOLERANCE)
|
||||||
.build(),
|
.build(),
|
||||||
glyph_cache_tex: renderer.create_dynamic_texture((w, h).into())?,
|
glyph_cache_tex: renderer.create_dynamic_texture(glyph_cache_dims.map(|e| e as u16))?,
|
||||||
graphic_cache: GraphicCache::new(graphic_cache_dims),
|
graphic_cache: GraphicCache::new(graphic_cache_dims),
|
||||||
graphic_cache_tex: renderer.create_dynamic_texture(graphic_cache_dims)?,
|
graphic_cache_tex: renderer.create_dynamic_texture(graphic_cache_dims)?,
|
||||||
})
|
})
|
||||||
@ -47,8 +58,28 @@ impl Cache {
|
|||||||
pub fn add_graphic(&mut self, graphic: Graphic) -> GraphicId {
|
pub fn add_graphic(&mut self, graphic: Graphic) -> GraphicId {
|
||||||
self.graphic_cache.add_graphic(graphic)
|
self.graphic_cache.add_graphic(graphic)
|
||||||
}
|
}
|
||||||
pub fn clear_graphic_cache(&mut self, renderer: &mut Renderer, new_size: Vec2<u16>) {
|
// Resizes and clears the GraphicCache
|
||||||
self.graphic_cache.clear_cache(new_size);
|
pub fn resize_graphic_cache(&mut self, renderer: &mut Renderer) -> Result<(), Error> {
|
||||||
self.graphic_cache_tex = renderer.create_dynamic_texture(new_size).unwrap();
|
let max_texture_size = renderer.max_texture_size();
|
||||||
|
let cache_dims = renderer
|
||||||
|
.get_resolution()
|
||||||
|
.map(|e| (e * GRAPHIC_CACHE_SIZE).min(max_texture_size as u16));
|
||||||
|
self.graphic_cache.clear_cache(cache_dims);
|
||||||
|
self.graphic_cache_tex = renderer.create_dynamic_texture(cache_dims)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
// Resizes and clears the GlyphCache
|
||||||
|
pub fn resize_glyph_cache(&mut self, renderer: &mut Renderer) -> Result<(), Error> {
|
||||||
|
let max_texture_size = renderer.max_texture_size();
|
||||||
|
let cache_dims = renderer
|
||||||
|
.get_resolution()
|
||||||
|
.map(|e| (e * GLYPH_CACHE_SIZE).min(max_texture_size as u16));
|
||||||
|
self.glyph_cache = GlyphCache::builder()
|
||||||
|
.dimensions(cache_dims.x as u32, cache_dims.y as u32)
|
||||||
|
.scale_tolerance(SCALE_TOLERANCE)
|
||||||
|
.position_tolerance(POSITION_TOLERANCE)
|
||||||
|
.build();
|
||||||
|
self.glyph_cache_tex = renderer.create_dynamic_texture(cache_dims.map(|e| e as u16))?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
use dot_vox::DotVoxData;
|
use dot_vox::DotVoxData;
|
||||||
use fnv::FnvHashMap;
|
use fnv::FnvHashMap;
|
||||||
use guillotiere::{size2, Allocation, AtlasAllocator};
|
use guillotiere::{size2, AllocId, Allocation, AtlasAllocator};
|
||||||
use image::DynamicImage;
|
use image::{DynamicImage, RgbaImage};
|
||||||
|
use log::{error, warn};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub enum Graphic {
|
pub enum Graphic {
|
||||||
Image(Arc<DynamicImage>),
|
Image(Arc<DynamicImage>),
|
||||||
Voxel(Arc<DotVoxData>, Option<u8>),
|
Voxel(Arc<DotVoxData>, Option<u8>),
|
||||||
@ -16,19 +18,39 @@ pub struct Id(u32);
|
|||||||
|
|
||||||
type Parameters = (Id, Vec2<u16>, Aabr<u64>);
|
type Parameters = (Id, Vec2<u16>, Aabr<u64>);
|
||||||
|
|
||||||
|
pub struct CachedDetails {
|
||||||
|
// Id used by AtlasAllocator
|
||||||
|
alloc_id: AllocId,
|
||||||
|
// Last frame this was used on
|
||||||
|
frame: u32,
|
||||||
|
// Where in the cache texture this is
|
||||||
|
aabr: Aabr<u16>,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct GraphicCache {
|
pub struct GraphicCache {
|
||||||
atlas: AtlasAllocator,
|
|
||||||
graphic_map: FnvHashMap<Id, Graphic>,
|
graphic_map: FnvHashMap<Id, Graphic>,
|
||||||
rect_map: FnvHashMap<Parameters, Aabr<u16>>,
|
|
||||||
next_id: u32,
|
next_id: u32,
|
||||||
|
|
||||||
|
atlas: AtlasAllocator,
|
||||||
|
cache_map: FnvHashMap<Parameters, CachedDetails>,
|
||||||
|
// The current frame
|
||||||
|
current_frame: u32,
|
||||||
|
unused_entries_this_frame: Option<Vec<Option<(u32, Parameters)>>>,
|
||||||
|
|
||||||
|
soft_cache: FnvHashMap<Parameters, RgbaImage>,
|
||||||
|
transfer_ready: Vec<(Parameters, Aabr<u16>)>,
|
||||||
}
|
}
|
||||||
impl GraphicCache {
|
impl GraphicCache {
|
||||||
pub fn new(size: Vec2<u16>) -> Self {
|
pub fn new(size: Vec2<u16>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
atlas: AtlasAllocator::new(size2(i32::from(size.x), i32::from(size.y))),
|
|
||||||
graphic_map: FnvHashMap::default(),
|
graphic_map: FnvHashMap::default(),
|
||||||
rect_map: FnvHashMap::default(),
|
|
||||||
next_id: 0,
|
next_id: 0,
|
||||||
|
atlas: AtlasAllocator::new(size2(i32::from(size.x), i32::from(size.y))),
|
||||||
|
cache_map: FnvHashMap::default(),
|
||||||
|
current_frame: 0,
|
||||||
|
unused_entries_this_frame: None,
|
||||||
|
soft_cache: FnvHashMap::default(),
|
||||||
|
transfer_ready: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn add_graphic(&mut self, graphic: Graphic) -> Id {
|
pub fn add_graphic(&mut self, graphic: Graphic) -> Id {
|
||||||
@ -44,77 +66,177 @@ impl GraphicCache {
|
|||||||
self.graphic_map.get(&id)
|
self.graphic_map.get(&id)
|
||||||
}
|
}
|
||||||
pub fn clear_cache(&mut self, new_size: Vec2<u16>) {
|
pub fn clear_cache(&mut self, new_size: Vec2<u16>) {
|
||||||
self.rect_map.clear();
|
self.soft_cache.clear();
|
||||||
|
self.transfer_ready.clear();
|
||||||
|
self.cache_map.clear();
|
||||||
self.atlas = AtlasAllocator::new(size2(i32::from(new_size.x), i32::from(new_size.y)));
|
self.atlas = AtlasAllocator::new(size2(i32::from(new_size.x), i32::from(new_size.y)));
|
||||||
}
|
}
|
||||||
pub fn cache_res<F>(
|
|
||||||
|
pub fn queue_res(
|
||||||
&mut self,
|
&mut self,
|
||||||
graphic_id: Id,
|
graphic_id: Id,
|
||||||
dims: Vec2<u16>,
|
dims: Vec2<u16>,
|
||||||
source: Aabr<f64>,
|
source: Aabr<f64>,
|
||||||
mut cacher: F,
|
) -> Option<Aabr<u16>> {
|
||||||
) -> Option<Aabr<u16>>
|
let key = (graphic_id, dims, source.map(|e| e.to_bits())); // TODO: Replace this with rounded representation of source
|
||||||
|
|
||||||
|
if let Some(details) = self.cache_map.get_mut(&key) {
|
||||||
|
// Update frame
|
||||||
|
details.frame = self.current_frame;
|
||||||
|
|
||||||
|
Some(details.aabr)
|
||||||
|
} else {
|
||||||
|
// Create image if it doesn't already exist
|
||||||
|
if !self.soft_cache.contains_key(&key) {
|
||||||
|
self.soft_cache.insert(
|
||||||
|
key,
|
||||||
|
match self.graphic_map.get(&graphic_id) {
|
||||||
|
Some(Graphic::Blank) => return None,
|
||||||
|
// Render image at requested resolution
|
||||||
|
// TODO: Use source aabr.
|
||||||
|
Some(Graphic::Image(ref image)) => image
|
||||||
|
.resize_exact(
|
||||||
|
u32::from(dims.x),
|
||||||
|
u32::from(dims.y),
|
||||||
|
image::FilterType::Nearest,
|
||||||
|
)
|
||||||
|
.to_rgba(),
|
||||||
|
Some(Graphic::Voxel(ref vox, min_samples)) => {
|
||||||
|
super::renderer::draw_vox(&vox.as_ref().into(), dims, *min_samples)
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
warn!("A graphic was requested via an id which is not in use");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let aabr_from_alloc_rect = |rect: guillotiere::Rectangle| {
|
||||||
|
let (min, max) = (rect.min, rect.max);
|
||||||
|
Aabr {
|
||||||
|
min: Vec2::new(min.x as u16, min.y as u16),
|
||||||
|
max: Vec2::new(max.x as u16, max.y as u16),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Allocate rectangle.
|
||||||
|
let (alloc_id, aabr) = match self
|
||||||
|
.atlas
|
||||||
|
.allocate(size2(i32::from(dims.x), i32::from(dims.y)))
|
||||||
|
{
|
||||||
|
Some(Allocation { id, rectangle }) => (id, aabr_from_alloc_rect(rectangle)),
|
||||||
|
// Out of room.
|
||||||
|
// 1) Remove unused allocations
|
||||||
|
// TODO: Make more room.
|
||||||
|
// 2) Rearrange rectangles (see comments below)
|
||||||
|
// 3) Expand cache size
|
||||||
|
None => {
|
||||||
|
// 1) Remove unused allocations
|
||||||
|
if self.unused_entries_this_frame.is_none() {
|
||||||
|
self.unused_entries_this_frame = {
|
||||||
|
let mut unused = self
|
||||||
|
.cache_map
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(key, details)| {
|
||||||
|
if details.frame < self.current_frame - 1 {
|
||||||
|
Some(Some((details.frame, *key)))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
unused
|
||||||
|
.sort_unstable_by(|a, b| a.map(|(f, _)| f).cmp(&b.map(|(f, _)| f)));
|
||||||
|
Some(unused)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut allocation = None;
|
||||||
|
// Fight the checker!
|
||||||
|
let current_frame = self.current_frame;
|
||||||
|
// Will always be Some
|
||||||
|
if let Some(ref mut unused_entries) = self.unused_entries_this_frame {
|
||||||
|
// Deallocate from oldest to newest
|
||||||
|
for key in unused_entries
|
||||||
|
.iter_mut()
|
||||||
|
.filter_map(|e| e.take().map(|(_, key)| key))
|
||||||
|
{
|
||||||
|
// Check if still in cache map and it has not been used since the vec was built
|
||||||
|
if self
|
||||||
|
.cache_map
|
||||||
|
.get(&key)
|
||||||
|
.filter(|d| d.frame != current_frame)
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
if let Some(alloc_id) =
|
||||||
|
self.cache_map.remove(&key).map(|d| d.alloc_id)
|
||||||
|
{
|
||||||
|
// Deallocate
|
||||||
|
self.atlas.deallocate(alloc_id);
|
||||||
|
// Try to allocate
|
||||||
|
allocation = self
|
||||||
|
.atlas
|
||||||
|
.allocate(size2(i32::from(dims.x), i32::from(dims.y)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 2) Rearrange rectangles
|
||||||
|
// This needs to be done infrequently and be based on whether rectangles have been removed
|
||||||
|
// Maybe find a way to calculate whether there is a significant amount of fragmentation
|
||||||
|
// Or consider dropping the use of an atlas and moving to a hashmap of individual textures :/
|
||||||
|
// if allocation.is_none() {
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
match allocation {
|
||||||
|
Some(Allocation { id, rectangle }) => (id, aabr_from_alloc_rect(rectangle)),
|
||||||
|
None => {
|
||||||
|
warn!("Can't find space for an image in the graphic cache");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.transfer_ready.push((key, aabr));
|
||||||
|
|
||||||
|
// Insert area into map for retrieval.
|
||||||
|
self.cache_map.insert(
|
||||||
|
key,
|
||||||
|
CachedDetails {
|
||||||
|
alloc_id,
|
||||||
|
frame: self.current_frame,
|
||||||
|
aabr,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
Some(aabr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Anything not queued since the last call to this will be removed if there is not enough space in the cache
|
||||||
|
pub fn cache_queued<F>(&mut self, mut cacher: F)
|
||||||
where
|
where
|
||||||
F: FnMut(Aabr<u16>, &[[u8; 4]]),
|
F: FnMut(Aabr<u16>, &[[u8; 4]]),
|
||||||
{
|
{
|
||||||
match self
|
// Cached queued
|
||||||
.rect_map
|
// TODO: combine nearby transfers
|
||||||
.get(&(graphic_id, dims, source.map(|e| e.to_bits()))) // TODO: Replace this with rounded representation of source
|
for (key, target_aarb) in self.transfer_ready.drain(..) {
|
||||||
{
|
if let Some(image) = self.soft_cache.get(&key) {
|
||||||
Some(aabr) => Some(*aabr),
|
cacher(
|
||||||
None => match self.graphic_map.get(&graphic_id) {
|
target_aarb,
|
||||||
Some(graphic) => {
|
&image.pixels().map(|p| p.data).collect::<Vec<[u8; 4]>>(),
|
||||||
// Allocate rectangle.
|
);
|
||||||
let aabr = match self
|
} else {
|
||||||
.atlas
|
error!("Image queued for transfer to gpu cache but it doesn't exist (this should never occur)");
|
||||||
.allocate(size2(i32::from(dims.x), i32::from(dims.y)))
|
}
|
||||||
{
|
|
||||||
Some(Allocation { id: _, rectangle }) => {
|
|
||||||
let (min, max) = (rectangle.min, rectangle.max);
|
|
||||||
Aabr {
|
|
||||||
min: Vec2::new(min.x as u16, min.y as u16),
|
|
||||||
max: Vec2::new(max.x as u16, max.y as u16),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Out of room.
|
|
||||||
// TODO: Make more room.
|
|
||||||
// 1) Expand cache size
|
|
||||||
// 2) Remove unused allocations
|
|
||||||
// 3) Rearrange rectangles
|
|
||||||
None => return None,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Render image.
|
|
||||||
// TODO: Use source.
|
|
||||||
let data = match graphic {
|
|
||||||
Graphic::Image(ref image) => image
|
|
||||||
.resize_exact(
|
|
||||||
u32::from(aabr.size().w),
|
|
||||||
u32::from(aabr.size().h),
|
|
||||||
image::FilterType::Nearest,
|
|
||||||
)
|
|
||||||
.to_rgba()
|
|
||||||
// TODO: might be a better way to do this
|
|
||||||
.pixels()
|
|
||||||
.map(|p| p.data)
|
|
||||||
.collect::<Vec<[u8; 4]>>(),
|
|
||||||
Graphic::Voxel(ref vox, min_samples) =>
|
|
||||||
super::renderer::draw_vox(&vox.as_ref().into(), aabr.size().into(), *min_samples),
|
|
||||||
Graphic::Blank => return None,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Draw to allocated area.
|
|
||||||
cacher(aabr, &data);
|
|
||||||
|
|
||||||
// Insert area into map for retrieval.
|
|
||||||
self.rect_map
|
|
||||||
.insert((graphic_id, dims, source.map(|e| e.to_bits())), aabr);
|
|
||||||
|
|
||||||
// Return area.
|
|
||||||
Some(aabr)
|
|
||||||
}
|
|
||||||
None => None,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Increment frame
|
||||||
|
self.current_frame += 1;
|
||||||
|
|
||||||
|
// Reset unused entries
|
||||||
|
self.unused_entries_this_frame = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,19 +11,20 @@ struct Voxel {
|
|||||||
mvp: Mat4<f32>,
|
mvp: Mat4<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: use norm or remove it
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
struct Vert {
|
struct Vert {
|
||||||
pos: Vec3<f32>,
|
pos: Vec3<f32>,
|
||||||
col: Rgb<f32>,
|
col: Rgb<f32>,
|
||||||
norm: Vec3<f32>,
|
//norm: Vec3<f32>,
|
||||||
ao_level: u8,
|
ao_level: u8,
|
||||||
}
|
}
|
||||||
impl Vert {
|
impl Vert {
|
||||||
fn new(pos: Vec3<f32>, col: Rgb<f32>, norm: Vec3<f32>, ao_level: u8) -> Self {
|
fn new(pos: Vec3<f32>, col: Rgb<f32>, _norm: Vec3<f32>, ao_level: u8) -> Self {
|
||||||
Vert {
|
Vert {
|
||||||
pos,
|
pos,
|
||||||
col,
|
col,
|
||||||
norm,
|
//norm,
|
||||||
ao_level,
|
ao_level,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -40,7 +41,7 @@ impl<'a> Pipeline for Voxel {
|
|||||||
Vert {
|
Vert {
|
||||||
pos,
|
pos,
|
||||||
col,
|
col,
|
||||||
norm: _,
|
//norm: _,
|
||||||
ao_level,
|
ao_level,
|
||||||
}: &Self::Vertex,
|
}: &Self::Vertex,
|
||||||
) -> ([f32; 3], Self::VsOut) {
|
) -> ([f32; 3], Self::VsOut) {
|
||||||
@ -57,11 +58,7 @@ impl<'a> Pipeline for Voxel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_vox(
|
pub fn draw_vox(segment: &Segment, output_size: Vec2<u16>, min_samples: Option<u8>) -> RgbaImage {
|
||||||
segment: &Segment,
|
|
||||||
output_size: Vec2<u16>,
|
|
||||||
min_samples: Option<u8>,
|
|
||||||
) -> Vec<[u8; 4]> {
|
|
||||||
let scale = min_samples.map_or(1.0, |s| s as f32).sqrt().ceil() as usize;
|
let scale = min_samples.map_or(1.0, |s| s as f32).sqrt().ceil() as usize;
|
||||||
let dims = output_size.map(|e| e as usize * scale).into_array();
|
let dims = output_size.map(|e| e as usize * scale).into_array();
|
||||||
let mut color = Buffer2d::new(dims, [0; 4]);
|
let mut color = Buffer2d::new(dims, [0; 4]);
|
||||||
@ -85,33 +82,29 @@ pub fn draw_vox(
|
|||||||
&mut depth,
|
&mut depth,
|
||||||
);
|
);
|
||||||
|
|
||||||
if scale > 1 {
|
let image = DynamicImage::ImageRgba8(
|
||||||
DynamicImage::ImageRgba8(
|
RgbaImage::from_vec(
|
||||||
RgbaImage::from_vec(
|
dims[0] as u32,
|
||||||
dims[0] as u32,
|
dims[1] as u32,
|
||||||
dims[1] as u32,
|
color
|
||||||
color
|
.as_ref()
|
||||||
.as_ref()
|
.iter()
|
||||||
.iter()
|
.flatten()
|
||||||
.flatten()
|
.cloned()
|
||||||
.cloned()
|
.collect::<Vec<u8>>(),
|
||||||
.collect::<Vec<u8>>(),
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
)
|
)
|
||||||
.resize_exact(
|
.unwrap(),
|
||||||
|
);
|
||||||
|
if scale > 1 {
|
||||||
|
image.resize_exact(
|
||||||
output_size.x as u32,
|
output_size.x as u32,
|
||||||
output_size.y as u32,
|
output_size.y as u32,
|
||||||
image::FilterType::Triangle,
|
image::FilterType::Triangle,
|
||||||
)
|
)
|
||||||
.to_rgba()
|
|
||||||
.pixels()
|
|
||||||
.map(|p| p.data)
|
|
||||||
.collect::<Vec<[u8; 4]>>()
|
|
||||||
} else {
|
} else {
|
||||||
// TODO: Remove clone.
|
image
|
||||||
color.as_ref().to_vec()
|
|
||||||
}
|
}
|
||||||
|
.to_rgba()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ao_level(side1: bool, corner: bool, side2: bool) -> u8 {
|
fn ao_level(side1: bool, corner: bool, side2: bool) -> u8 {
|
||||||
|
@ -101,6 +101,8 @@ pub struct Ui {
|
|||||||
ingame_locals: Vec<Consts<UiLocals>>,
|
ingame_locals: Vec<Consts<UiLocals>>,
|
||||||
// Window size for updating scaling
|
// Window size for updating scaling
|
||||||
window_resized: Option<Vec2<f64>>,
|
window_resized: Option<Vec2<f64>>,
|
||||||
|
// Used to delay cache resizing until after current frame is drawn
|
||||||
|
need_cache_resize: bool,
|
||||||
// Scaling of the ui
|
// Scaling of the ui
|
||||||
scale: Scale,
|
scale: Scale,
|
||||||
}
|
}
|
||||||
@ -122,6 +124,7 @@ impl Ui {
|
|||||||
default_globals: renderer.create_consts(&[Globals::default()])?,
|
default_globals: renderer.create_consts(&[Globals::default()])?,
|
||||||
ingame_locals: Vec::new(),
|
ingame_locals: Vec::new(),
|
||||||
window_resized: None,
|
window_resized: None,
|
||||||
|
need_cache_resize: false,
|
||||||
scale,
|
scale,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -216,6 +219,15 @@ impl Ui {
|
|||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if self.need_cache_resize {
|
||||||
|
// Resize graphic cache
|
||||||
|
self.cache.resize_graphic_cache(renderer).unwrap();
|
||||||
|
// Resize glyph cache
|
||||||
|
self.cache.resize_glyph_cache(renderer).unwrap();
|
||||||
|
|
||||||
|
self.need_cache_resize = false;
|
||||||
|
}
|
||||||
|
|
||||||
self.draw_commands.clear();
|
self.draw_commands.clear();
|
||||||
let mut mesh = Mesh::new();
|
let mut mesh = Mesh::new();
|
||||||
|
|
||||||
@ -344,7 +356,7 @@ impl Ui {
|
|||||||
PrimitiveKind::Image {
|
PrimitiveKind::Image {
|
||||||
image_id,
|
image_id,
|
||||||
color,
|
color,
|
||||||
source_rect,
|
source_rect: _, // TODO: <-- use this
|
||||||
} => {
|
} => {
|
||||||
let graphic_id = self
|
let graphic_id = self
|
||||||
.image_map
|
.image_map
|
||||||
@ -391,35 +403,25 @@ impl Ui {
|
|||||||
max: Vec2::new(uv_r, uv_t),
|
max: Vec2::new(uv_r, uv_t),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
// TODO: get dims from graphic_cache (or have it return floats directly)
|
||||||
let (cache_w, cache_h) =
|
let (cache_w, cache_h) =
|
||||||
cache_tex.get_dimensions().map(|e| e as f32).into_tuple();
|
cache_tex.get_dimensions().map(|e| e as f32).into_tuple();
|
||||||
|
|
||||||
// Cache graphic at particular resolution.
|
// Cache graphic at particular resolution.
|
||||||
let uv_aabr = match graphic_cache.cache_res(
|
let uv_aabr =
|
||||||
*graphic_id,
|
match graphic_cache.queue_res(*graphic_id, resolution, source_aabr) {
|
||||||
resolution,
|
Some(aabr) => Aabr {
|
||||||
source_aabr,
|
min: Vec2::new(
|
||||||
|aabr, data| {
|
aabr.min.x as f32 / cache_w,
|
||||||
let offset = aabr.min.into_array();
|
aabr.max.y as f32 / cache_h,
|
||||||
let size = aabr.size().into_array();
|
),
|
||||||
if let Err(err) = renderer.update_texture(cache_tex, offset, size, data)
|
max: Vec2::new(
|
||||||
{
|
aabr.max.x as f32 / cache_w,
|
||||||
warn!("Failed to update texture: {:?}", err);
|
aabr.min.y as f32 / cache_h,
|
||||||
}
|
),
|
||||||
},
|
},
|
||||||
) {
|
None => continue,
|
||||||
Some(aabr) => Aabr {
|
};
|
||||||
min: Vec2::new(
|
|
||||||
aabr.min.x as f32 / cache_w,
|
|
||||||
aabr.max.y as f32 / cache_h,
|
|
||||||
),
|
|
||||||
max: Vec2::new(
|
|
||||||
aabr.max.x as f32 / cache_w,
|
|
||||||
aabr.min.y as f32 / cache_h,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
None => continue,
|
|
||||||
};
|
|
||||||
|
|
||||||
mesh.push_quad(create_ui_quad(gl_aabr(rect), uv_aabr, color, UiMode::Image));
|
mesh.push_quad(create_ui_quad(gl_aabr(rect), uv_aabr, color, UiMode::Image));
|
||||||
}
|
}
|
||||||
@ -630,22 +632,31 @@ impl Ui {
|
|||||||
.create_dynamic_model(mesh.vertices().len() * 4 / 3)
|
.create_dynamic_model(mesh.vertices().len() * 4 / 3)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
renderer.update_model(&self.model, &mesh, 0).unwrap();
|
|
||||||
// Update model with new mesh.
|
// Update model with new mesh.
|
||||||
|
renderer.update_model(&self.model, &mesh, 0).unwrap();
|
||||||
|
|
||||||
|
// Move cached graphics to the gpu
|
||||||
|
let (graphic_cache, cache_tex) = self.cache.graphic_cache_mut_and_tex();
|
||||||
|
graphic_cache.cache_queued(|aabr, data| {
|
||||||
|
let offset = aabr.min.into_array();
|
||||||
|
let size = aabr.size().into_array();
|
||||||
|
if let Err(err) = renderer.update_texture(cache_tex, offset, size, data) {
|
||||||
|
warn!("Failed to update texture: {:?}", err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Handle window resizing.
|
// Handle window resizing.
|
||||||
if let Some(new_dims) = self.window_resized.take() {
|
if let Some(new_dims) = self.window_resized.take() {
|
||||||
|
let (old_w, old_h) = self.scale.scaled_window_size().into_tuple();
|
||||||
self.scale.window_resized(new_dims, renderer);
|
self.scale.window_resized(new_dims, renderer);
|
||||||
let (w, h) = self.scale.scaled_window_size().into_tuple();
|
let (w, h) = self.scale.scaled_window_size().into_tuple();
|
||||||
self.ui.handle_event(Input::Resize(w, h));
|
self.ui.handle_event(Input::Resize(w, h));
|
||||||
|
|
||||||
let res = renderer.get_resolution();
|
|
||||||
// Avoid panic in graphic cache when minimizing.
|
// Avoid panic in graphic cache when minimizing.
|
||||||
if res.x > 0 && res.y > 0 {
|
// Avoid resetting cache if window size didn't change
|
||||||
self.cache
|
// Somewhat inefficient for elements that won't change size after a window resize
|
||||||
.clear_graphic_cache(renderer, renderer.get_resolution().map(|e| e * 4));
|
let res = renderer.get_resolution();
|
||||||
}
|
self.need_cache_resize = res.x > 0 && res.y > 0 && !(old_w == w && old_h == h);
|
||||||
// TODO: Probably need to resize glyph cache, see conrod's gfx backend for reference.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,10 +48,6 @@ impl Scale {
|
|||||||
pub fn scale_factor_physical(&self) -> f64 {
|
pub fn scale_factor_physical(&self) -> f64 {
|
||||||
self.scale_factor_logical() * self.dpi_factor
|
self.scale_factor_logical() * self.dpi_factor
|
||||||
}
|
}
|
||||||
// Get the dpi factor (ratio between physical and logical coordinates)
|
|
||||||
pub fn dpi_factor(&self) -> f64 {
|
|
||||||
self.dpi_factor
|
|
||||||
}
|
|
||||||
// Updates internal window size (and/or dpi_factor).
|
// Updates internal window size (and/or dpi_factor).
|
||||||
pub fn window_resized(&mut self, new_dims: Vec2<f64>, renderer: &Renderer) {
|
pub fn window_resized(&mut self, new_dims: Vec2<f64>, renderer: &Renderer) {
|
||||||
self.dpi_factor = renderer.get_resolution().x as f64 / new_dims.x;
|
self.dpi_factor = renderer.get_resolution().x as f64 / new_dims.x;
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
use conrod_core::{
|
use conrod_core::{
|
||||||
builder_methods,
|
builder_methods, position::Dimension, widget, Position, Ui, UiCell, Widget, WidgetCommon,
|
||||||
position::Dimension,
|
|
||||||
widget::{self, Id},
|
|
||||||
Position, Ui, UiCell, Widget, WidgetCommon,
|
|
||||||
};
|
};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
@ -18,12 +15,11 @@ pub trait Ingameable: Widget + Sized {
|
|||||||
fn prim_count(&self) -> usize;
|
fn prim_count(&self) -> usize;
|
||||||
// Note this is not responsible for the 3d positioning
|
// Note this is not responsible for the 3d positioning
|
||||||
// Only call this directly if using IngameAnchor
|
// Only call this directly if using IngameAnchor
|
||||||
fn set_ingame(self, id: widget::Id, parent_id: Id, ui: &mut UiCell) -> Self::Event {
|
fn set_ingame(self, id: widget::Id, ui: &mut UiCell) -> Self::Event {
|
||||||
self
|
self
|
||||||
// should pass focus to the window if these are clicked
|
// should pass focus to the window if these are clicked
|
||||||
// (they are not displayed where conrod thinks they are)
|
// (they are not displayed where conrod thinks they are)
|
||||||
.graphics_for(ui.window)
|
.graphics_for(ui.window)
|
||||||
//.parent(parent_id) // TODO: Is this needed?
|
|
||||||
.set(id, ui)
|
.set(id, ui)
|
||||||
}
|
}
|
||||||
fn position_ingame(self, pos: Vec3<f32>) -> Ingame<Self> {
|
fn position_ingame(self, pos: Vec3<f32>) -> Ingame<Self> {
|
||||||
@ -104,7 +100,7 @@ impl<W: Ingameable> Widget for Ingame<W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
|
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
|
||||||
let widget::UpdateArgs { id, state, ui, .. } = args;
|
let widget::UpdateArgs { state, ui, .. } = args;
|
||||||
let Ingame {
|
let Ingame {
|
||||||
widget, parameters, ..
|
widget, parameters, ..
|
||||||
} = self;
|
} = self;
|
||||||
@ -116,19 +112,19 @@ impl<W: Ingameable> Widget for Ingame<W> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
widget.set_ingame(state.id.unwrap(), id, ui)
|
widget.set_ingame(state.id.unwrap(), ui)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_x_position(&self, ui: &Ui) -> Position {
|
fn default_x_position(&self, _: &Ui) -> Position {
|
||||||
Position::Absolute(0.0)
|
Position::Absolute(0.0)
|
||||||
}
|
}
|
||||||
fn default_y_position(&self, ui: &Ui) -> Position {
|
fn default_y_position(&self, _: &Ui) -> Position {
|
||||||
Position::Absolute(0.0)
|
Position::Absolute(0.0)
|
||||||
}
|
}
|
||||||
fn default_x_dimension(&self, ui: &Ui) -> Dimension {
|
fn default_x_dimension(&self, _: &Ui) -> Dimension {
|
||||||
Dimension::Absolute(1.0)
|
Dimension::Absolute(1.0)
|
||||||
}
|
}
|
||||||
fn default_y_dimension(&self, ui: &Ui) -> Dimension {
|
fn default_y_dimension(&self, _: &Ui) -> Dimension {
|
||||||
Dimension::Absolute(1.0)
|
Dimension::Absolute(1.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user