1use crate::{
22 asset::manager::ResourceManager,
23 core::err_once,
24 core::log::{Log, MessageKind},
25 graphics::{
26 error::FrameworkError,
27 gpu_texture::{GpuTexture, GpuTextureDescriptor, GpuTextureKind, PixelKind},
28 sampler::{
29 GpuSampler, GpuSamplerDescriptor, MagnificationFilter, MinificationFilter, WrapMode,
30 },
31 server::GraphicsServer,
32 },
33 renderer::cache::{TemporaryCache, TimeToLive},
34 resource::texture::{Texture, TextureResource},
35};
36use fyrox_texture::{
37 TextureKind, TextureMagnificationFilter, TextureMinificationFilter, TexturePixelKind,
38 TextureWrapMode,
39};
40use std::{borrow::Cow, time::Duration};
41use uuid::Uuid;
42
43#[derive(Clone)]
44pub struct TextureRenderData {
45 pub gpu_texture: GpuTexture,
46 pub gpu_sampler: GpuSampler,
47 modifications_counter: u64,
48 sampler_modifications_counter: u64,
49}
50
51#[derive(Default)]
52pub struct TextureCache {
53 cache: TemporaryCache<TextureRenderData>,
54}
55
56fn convert_texture_kind(v: TextureKind) -> GpuTextureKind {
57 match v {
58 TextureKind::Line { length } => GpuTextureKind::Line {
59 length: length as usize,
60 },
61 TextureKind::Rectangle { width, height } => GpuTextureKind::Rectangle {
62 width: width as usize,
63 height: height as usize,
64 },
65 TextureKind::Cube { size } => GpuTextureKind::Cube {
66 size: size as usize,
67 },
68 TextureKind::Volume {
69 width,
70 height,
71 depth,
72 } => GpuTextureKind::Volume {
73 width: width as usize,
74 height: height as usize,
75 depth: depth as usize,
76 },
77 }
78}
79
80pub fn convert_pixel_kind(texture_kind: TexturePixelKind) -> PixelKind {
81 match texture_kind {
82 TexturePixelKind::R8 => PixelKind::R8,
83 TexturePixelKind::RGB8 => PixelKind::RGB8,
84 TexturePixelKind::RGBA8 => PixelKind::RGBA8,
85 TexturePixelKind::RG8 => PixelKind::RG8,
86 TexturePixelKind::R16 => PixelKind::R16,
87 TexturePixelKind::RG16 => PixelKind::RG16,
88 TexturePixelKind::BGR8 => PixelKind::BGR8,
89 TexturePixelKind::BGRA8 => PixelKind::BGRA8,
90 TexturePixelKind::RGB16 => PixelKind::RGB16,
91 TexturePixelKind::RGBA16 => PixelKind::RGBA16,
92 TexturePixelKind::RGB16F => PixelKind::RGB16F,
93 TexturePixelKind::DXT1RGB => PixelKind::DXT1RGB,
94 TexturePixelKind::DXT1RGBA => PixelKind::DXT1RGBA,
95 TexturePixelKind::DXT3RGBA => PixelKind::DXT3RGBA,
96 TexturePixelKind::DXT5RGBA => PixelKind::DXT5RGBA,
97 TexturePixelKind::R8RGTC => PixelKind::R8RGTC,
98 TexturePixelKind::RG8RGTC => PixelKind::RG8RGTC,
99 TexturePixelKind::RGB32F => PixelKind::RGB32F,
100 TexturePixelKind::RGBA32F => PixelKind::RGBA32F,
101 TexturePixelKind::Luminance8 => PixelKind::L8,
102 TexturePixelKind::LuminanceAlpha8 => PixelKind::LA8,
103 TexturePixelKind::Luminance16 => PixelKind::L16,
104 TexturePixelKind::LuminanceAlpha16 => PixelKind::LA16,
105 TexturePixelKind::R32F => PixelKind::R32F,
106 TexturePixelKind::R16F => PixelKind::R16F,
107 TexturePixelKind::SRGBA8 => PixelKind::SRGBA8,
108 TexturePixelKind::SRGB8 => PixelKind::SRGB8,
109 }
110}
111
112pub fn convert_magnification_filter(v: TextureMagnificationFilter) -> MagnificationFilter {
113 match v {
114 TextureMagnificationFilter::Nearest => MagnificationFilter::Nearest,
115 TextureMagnificationFilter::Linear => MagnificationFilter::Linear,
116 }
117}
118
119pub fn convert_minification_filter(v: TextureMinificationFilter) -> MinificationFilter {
120 match v {
121 TextureMinificationFilter::Nearest => MinificationFilter::Nearest,
122 TextureMinificationFilter::NearestMipMapNearest => MinificationFilter::NearestMipMapNearest,
123 TextureMinificationFilter::NearestMipMapLinear => MinificationFilter::NearestMipMapLinear,
124 TextureMinificationFilter::Linear => MinificationFilter::Linear,
125 TextureMinificationFilter::LinearMipMapNearest => MinificationFilter::LinearMipMapNearest,
126 TextureMinificationFilter::LinearMipMapLinear => MinificationFilter::LinearMipMapLinear,
127 }
128}
129
130pub fn convert_wrap_mode(v: TextureWrapMode) -> WrapMode {
131 match v {
132 TextureWrapMode::Repeat => WrapMode::Repeat,
133 TextureWrapMode::ClampToEdge => WrapMode::ClampToEdge,
134 TextureWrapMode::ClampToBorder => WrapMode::ClampToBorder,
135 TextureWrapMode::MirroredRepeat => WrapMode::MirroredRepeat,
136 TextureWrapMode::MirrorClampToEdge => WrapMode::MirrorClampToEdge,
137 }
138}
139
140fn create_sampler(
141 server: &dyn GraphicsServer,
142 texture: &Texture,
143) -> Result<GpuSampler, FrameworkError> {
144 server.create_sampler(GpuSamplerDescriptor {
145 mag_filter: convert_magnification_filter(texture.magnification_filter()),
146 min_filter: convert_minification_filter(texture.minification_filter()),
147 s_wrap_mode: convert_wrap_mode(texture.s_wrap_mode()),
148 t_wrap_mode: convert_wrap_mode(texture.t_wrap_mode()),
149 r_wrap_mode: convert_wrap_mode(texture.r_wrap_mode()),
150 anisotropy: texture.anisotropy_level(),
151 min_lod: texture.min_lod(),
152 max_lod: texture.max_lod(),
153 lod_bias: texture.lod_bias(),
154 })
155}
156
157fn create_gpu_texture(
158 server: &dyn GraphicsServer,
159 resource_manager: &ResourceManager,
160 uuid: &Uuid,
161 texture: &Texture,
162) -> Result<TextureRenderData, FrameworkError> {
163 let path = resource_manager
164 .try_get_state(Duration::from_millis(1))
165 .and_then(|state| state.uuid_to_resource_path(*uuid));
166 let name = path
167 .as_ref()
168 .map(|path| path.to_string_lossy())
169 .unwrap_or_else(|| Cow::Borrowed(""));
170
171 let gpu_texture = server.create_texture(GpuTextureDescriptor {
172 name: &name,
173 kind: convert_texture_kind(texture.kind()),
174 pixel_kind: convert_pixel_kind(texture.pixel_kind()),
175 mip_count: texture.mip_count() as usize,
176 data: Some(texture.data()),
177 base_level: texture.base_level(),
178 max_level: texture.max_level(),
179 })?;
180
181 Ok(TextureRenderData {
182 gpu_texture,
183 gpu_sampler: create_sampler(server, texture)?,
184 modifications_counter: texture.modifications_count(),
185 sampler_modifications_counter: texture.sampler_modifications_count(),
186 })
187}
188
189impl TextureCache {
190 pub fn upload(
193 &mut self,
194 server: &dyn GraphicsServer,
195 resource_manager: &ResourceManager,
196 texture: &TextureResource,
197 ) -> Result<(), FrameworkError> {
198 let uuid = texture.resource_uuid();
199 let texture = texture.state();
200 if let Some(texture) = texture.data_ref() {
201 self.cache.get_entry_mut_or_insert_with(
202 &texture.cache_index,
203 Default::default(),
204 || create_gpu_texture(server, resource_manager, &uuid, texture),
205 )?;
206 Ok(())
207 } else {
208 Err(FrameworkError::Custom(
209 "Texture is not loaded yet!".to_string(),
210 ))
211 }
212 }
213
214 pub fn get(
215 &mut self,
216 server: &dyn GraphicsServer,
217 resource_manager: &ResourceManager,
218 texture_resource: &TextureResource,
219 ) -> Option<&TextureRenderData> {
220 let uuid = texture_resource.resource_uuid();
221 let texture_data_guard = texture_resource.state();
222 if let Some(texture) = texture_data_guard.data_ref() {
223 match self.cache.get_mut_or_insert_with(
224 &texture.cache_index,
225 Default::default(),
226 || create_gpu_texture(server, resource_manager, &uuid, texture),
227 ) {
228 Ok(entry) => {
229 let modifications_count = texture.modifications_count();
233 if entry.modifications_counter != modifications_count {
234 if let Err(e) = entry.gpu_texture.set_data(
235 convert_texture_kind(texture.kind()),
236 convert_pixel_kind(texture.pixel_kind()),
237 texture.mip_count() as usize,
238 Some(texture.data()),
239 ) {
240 Log::writeln(
241 MessageKind::Error,
242 format!("Unable to upload new texture data to GPU. Reason: {e:?}"),
243 )
244 } else {
245 entry.modifications_counter = modifications_count;
246 }
247 }
248
249 if entry.sampler_modifications_counter != texture.sampler_modifications_count()
250 {
251 entry.gpu_sampler = create_sampler(server, texture).unwrap();
252 }
253
254 return Some(entry);
255 }
256 Err(e) => {
257 drop(texture_data_guard);
258 err_once!(
259 texture_resource.key() as usize,
260 "Failed to create GPU texture from texture. Reason: {:?}",
261 e,
262 );
263 }
264 }
265 }
266 None
267 }
268
269 pub fn update(&mut self, dt: f32) {
270 self.cache.update(dt)
271 }
272
273 pub fn clear(&mut self) {
274 self.cache.clear();
275 }
276
277 pub fn unload(&mut self, texture: &TextureResource) {
278 if let Some(texture) = texture.state().data() {
279 self.cache.remove(&texture.cache_index);
280 }
281 }
282
283 pub fn alive_count(&self) -> usize {
284 self.cache.alive_count()
285 }
286
287 pub fn try_register(
291 &mut self,
292 server: &dyn GraphicsServer,
293 texture: &TextureResource,
294 gpu_texture: GpuTexture,
295 ) -> Result<(), FrameworkError> {
296 let data = texture.data_ref();
297 let index = data.cache_index.clone();
298 let entry = self.cache.get_mut(&index);
299 if entry.is_none() {
300 self.cache.spawn(
301 TextureRenderData {
302 gpu_texture,
303 gpu_sampler: create_sampler(server, &data)?,
304 modifications_counter: data.modifications_count(),
305 sampler_modifications_counter: data.sampler_modifications_count(),
306 },
307 index,
308 TimeToLive::default(),
309 );
310 }
311 Ok(())
312 }
313}