use crate::{
asset::manager::ResourceManager,
core::err_once,
core::log::{Log, MessageKind},
graphics::{
error::FrameworkError,
gpu_texture::{GpuTexture, GpuTextureDescriptor, GpuTextureKind, PixelKind},
sampler::{
GpuSampler, GpuSamplerDescriptor, MagnificationFilter, MinificationFilter, WrapMode,
},
server::GraphicsServer,
},
renderer::cache::{TemporaryCache, TimeToLive},
resource::texture::{Texture, TextureResource},
};
use fyrox_texture::{
TextureKind, TextureMagnificationFilter, TextureMinificationFilter, TexturePixelKind,
TextureWrapMode,
};
use std::{borrow::Cow, time::Duration};
use uuid::Uuid;
#[derive(Clone)]
pub struct TextureRenderData {
pub gpu_texture: GpuTexture,
pub gpu_sampler: GpuSampler,
modifications_counter: u64,
sampler_modifications_counter: u64,
}
#[derive(Default)]
pub struct TextureCache {
cache: TemporaryCache<TextureRenderData>,
}
fn convert_texture_kind(v: TextureKind) -> GpuTextureKind {
match v {
TextureKind::Line { length } => GpuTextureKind::Line {
length: length as usize,
},
TextureKind::Rectangle { width, height } => GpuTextureKind::Rectangle {
width: width as usize,
height: height as usize,
},
TextureKind::Cube { size } => GpuTextureKind::Cube {
size: size as usize,
},
TextureKind::Volume {
width,
height,
depth,
} => GpuTextureKind::Volume {
width: width as usize,
height: height as usize,
depth: depth as usize,
},
}
}
pub fn convert_pixel_kind(texture_kind: TexturePixelKind) -> PixelKind {
match texture_kind {
TexturePixelKind::R8 => PixelKind::R8,
TexturePixelKind::RGB8 => PixelKind::RGB8,
TexturePixelKind::RGBA8 => PixelKind::RGBA8,
TexturePixelKind::RG8 => PixelKind::RG8,
TexturePixelKind::R16 => PixelKind::R16,
TexturePixelKind::RG16 => PixelKind::RG16,
TexturePixelKind::BGR8 => PixelKind::BGR8,
TexturePixelKind::BGRA8 => PixelKind::BGRA8,
TexturePixelKind::RGB16 => PixelKind::RGB16,
TexturePixelKind::RGBA16 => PixelKind::RGBA16,
TexturePixelKind::RGB16F => PixelKind::RGB16F,
TexturePixelKind::DXT1RGB => PixelKind::DXT1RGB,
TexturePixelKind::DXT1RGBA => PixelKind::DXT1RGBA,
TexturePixelKind::DXT3RGBA => PixelKind::DXT3RGBA,
TexturePixelKind::DXT5RGBA => PixelKind::DXT5RGBA,
TexturePixelKind::R8RGTC => PixelKind::R8RGTC,
TexturePixelKind::RG8RGTC => PixelKind::RG8RGTC,
TexturePixelKind::RGB32F => PixelKind::RGB32F,
TexturePixelKind::RGBA32F => PixelKind::RGBA32F,
TexturePixelKind::Luminance8 => PixelKind::L8,
TexturePixelKind::LuminanceAlpha8 => PixelKind::LA8,
TexturePixelKind::Luminance16 => PixelKind::L16,
TexturePixelKind::LuminanceAlpha16 => PixelKind::LA16,
TexturePixelKind::R32F => PixelKind::R32F,
TexturePixelKind::R16F => PixelKind::R16F,
TexturePixelKind::SRGBA8 => PixelKind::SRGBA8,
TexturePixelKind::SRGB8 => PixelKind::SRGB8,
}
}
pub fn convert_magnification_filter(v: TextureMagnificationFilter) -> MagnificationFilter {
match v {
TextureMagnificationFilter::Nearest => MagnificationFilter::Nearest,
TextureMagnificationFilter::Linear => MagnificationFilter::Linear,
}
}
pub fn convert_minification_filter(v: TextureMinificationFilter) -> MinificationFilter {
match v {
TextureMinificationFilter::Nearest => MinificationFilter::Nearest,
TextureMinificationFilter::NearestMipMapNearest => MinificationFilter::NearestMipMapNearest,
TextureMinificationFilter::NearestMipMapLinear => MinificationFilter::NearestMipMapLinear,
TextureMinificationFilter::Linear => MinificationFilter::Linear,
TextureMinificationFilter::LinearMipMapNearest => MinificationFilter::LinearMipMapNearest,
TextureMinificationFilter::LinearMipMapLinear => MinificationFilter::LinearMipMapLinear,
}
}
pub fn convert_wrap_mode(v: TextureWrapMode) -> WrapMode {
match v {
TextureWrapMode::Repeat => WrapMode::Repeat,
TextureWrapMode::ClampToEdge => WrapMode::ClampToEdge,
TextureWrapMode::ClampToBorder => WrapMode::ClampToBorder,
TextureWrapMode::MirroredRepeat => WrapMode::MirroredRepeat,
TextureWrapMode::MirrorClampToEdge => WrapMode::MirrorClampToEdge,
}
}
fn create_sampler(
server: &dyn GraphicsServer,
texture: &Texture,
) -> Result<GpuSampler, FrameworkError> {
server.create_sampler(GpuSamplerDescriptor {
mag_filter: convert_magnification_filter(texture.magnification_filter()),
min_filter: convert_minification_filter(texture.minification_filter()),
s_wrap_mode: convert_wrap_mode(texture.s_wrap_mode()),
t_wrap_mode: convert_wrap_mode(texture.t_wrap_mode()),
r_wrap_mode: convert_wrap_mode(texture.r_wrap_mode()),
anisotropy: texture.anisotropy_level(),
min_lod: texture.min_lod(),
max_lod: texture.max_lod(),
lod_bias: texture.lod_bias(),
})
}
fn create_gpu_texture(
server: &dyn GraphicsServer,
resource_manager: &ResourceManager,
uuid: &Uuid,
texture: &Texture,
) -> Result<TextureRenderData, FrameworkError> {
let path = resource_manager
.try_get_state(Duration::from_millis(1))
.and_then(|state| state.uuid_to_resource_path(*uuid));
let name = path
.as_ref()
.map(|path| path.to_string_lossy())
.unwrap_or_else(|| Cow::Borrowed(""));
let gpu_texture = server.create_texture(GpuTextureDescriptor {
name: &name,
kind: convert_texture_kind(texture.kind()),
pixel_kind: convert_pixel_kind(texture.pixel_kind()),
mip_count: texture.mip_count() as usize,
data: Some(texture.data()),
base_level: texture.base_level(),
max_level: texture.max_level(),
})?;
Ok(TextureRenderData {
gpu_texture,
gpu_sampler: create_sampler(server, texture)?,
modifications_counter: texture.modifications_count(),
sampler_modifications_counter: texture.sampler_modifications_count(),
})
}
impl TextureCache {
pub fn upload(
&mut self,
server: &dyn GraphicsServer,
resource_manager: &ResourceManager,
texture: &TextureResource,
) -> Result<(), FrameworkError> {
let uuid = texture.resource_uuid();
let texture = texture.state();
if let Some(texture) = texture.data_ref() {
self.cache.get_entry_mut_or_insert_with(
&texture.cache_index,
Default::default(),
|| create_gpu_texture(server, resource_manager, &uuid, texture),
)?;
Ok(())
} else {
Err(FrameworkError::Custom(
"Texture is not loaded yet!".to_string(),
))
}
}
pub fn get(
&mut self,
server: &dyn GraphicsServer,
resource_manager: &ResourceManager,
texture_resource: &TextureResource,
) -> Option<&TextureRenderData> {
let uuid = texture_resource.resource_uuid();
let texture_data_guard = texture_resource.state();
if let Some(texture) = texture_data_guard.data_ref() {
match self.cache.get_mut_or_insert_with(
&texture.cache_index,
Default::default(),
|| create_gpu_texture(server, resource_manager, &uuid, texture),
) {
Ok(entry) => {
let modifications_count = texture.modifications_count();
if entry.modifications_counter != modifications_count {
if let Err(e) = entry.gpu_texture.set_data(
convert_texture_kind(texture.kind()),
convert_pixel_kind(texture.pixel_kind()),
texture.mip_count() as usize,
Some(texture.data()),
) {
Log::writeln(
MessageKind::Error,
format!("Unable to upload new texture data to GPU. Reason: {e:?}"),
)
} else {
entry.modifications_counter = modifications_count;
}
}
if entry.sampler_modifications_counter != texture.sampler_modifications_count()
{
entry.gpu_sampler = create_sampler(server, texture).unwrap();
}
return Some(entry);
}
Err(e) => {
drop(texture_data_guard);
err_once!(
texture_resource.key() as usize,
"Failed to create GPU texture from texture. Reason: {:?}",
e,
);
}
}
}
None
}
pub fn update(&mut self, dt: f32) {
self.cache.update(dt)
}
pub fn clear(&mut self) {
self.cache.clear();
}
pub fn unload(&mut self, texture: &TextureResource) {
if let Some(texture) = texture.state().data() {
self.cache.remove(&texture.cache_index);
}
}
pub fn alive_count(&self) -> usize {
self.cache.alive_count()
}
pub fn try_register(
&mut self,
server: &dyn GraphicsServer,
texture: &TextureResource,
gpu_texture: GpuTexture,
) -> Result<(), FrameworkError> {
let data = texture.data_ref();
let index = data.cache_index.clone();
let entry = self.cache.get_mut(&index);
if entry.is_none() {
self.cache.spawn(
TextureRenderData {
gpu_texture,
gpu_sampler: create_sampler(server, &data)?,
modifications_counter: data.modifications_count(),
sampler_modifications_counter: data.sampler_modifications_count(),
},
index,
TimeToLive::default(),
);
}
Ok(())
}
}