rafx_assets/assets/image/
assets.rs1use rafx_api::{RafxResourceType, RafxResult};
2use rafx_framework::upload::GpuImageDataColorSpace;
3use rafx_framework::{ImageResource, ImageViewResource, ResourceArc};
4use serde::{Deserialize, Serialize};
5use type_uuid::*;
6
7#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
9pub enum ImageAssetColorSpaceConfig {
10 Srgb,
11 Linear,
12}
13
14#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq)]
16pub enum ImageAssetMipGeneration {
17 NoMips,
18 Precomupted,
19 Runtime,
20}
21
22impl Into<GpuImageDataColorSpace> for ImageAssetColorSpaceConfig {
23 fn into(self) -> GpuImageDataColorSpace {
24 match self {
25 ImageAssetColorSpaceConfig::Srgb => GpuImageDataColorSpace::Srgb,
26 ImageAssetColorSpaceConfig::Linear => GpuImageDataColorSpace::Linear,
27 }
28 }
29}
30
31#[derive(Serialize, Deserialize, Clone, Copy, Debug)]
33pub enum ImageAssetBasisCompressionType {
34 Etc1S,
35 Uastc,
36}
37
38#[cfg(feature = "basis-universal")]
39impl Into<basis_universal::BasisTextureFormat> for ImageAssetBasisCompressionType {
40 fn into(self) -> basis_universal::BasisTextureFormat {
41 match self {
42 ImageAssetBasisCompressionType::Etc1S => basis_universal::BasisTextureFormat::ETC1S,
43 ImageAssetBasisCompressionType::Uastc => basis_universal::BasisTextureFormat::UASTC4x4,
44 }
45 }
46}
47
48#[derive(Serialize, Deserialize, Clone, Copy, Debug)]
50pub struct ImageAssetBasisCompressionSettings {
51 pub compression_type: ImageAssetBasisCompressionType,
52 pub quality: u32,
53}
54
55#[cfg(feature = "basis-universal")]
56impl ImageAssetBasisCompressionSettings {
57 pub fn default_uastc() -> Self {
58 ImageAssetBasisCompressionSettings {
59 compression_type: ImageAssetBasisCompressionType::Uastc,
60 quality: basis_universal::UASTC_QUALITY_DEFAULT,
61 }
62 }
63
64 pub fn default_etc1s() -> Self {
65 ImageAssetBasisCompressionSettings {
66 compression_type: ImageAssetBasisCompressionType::Etc1S,
67 quality: basis_universal::ETC1S_QUALITY_DEFAULT,
68 }
69 }
70}
71
72#[derive(Serialize, Deserialize, Clone, Copy, Debug)]
73#[allow(non_camel_case_types)]
74pub enum ImageAssetDataFormat {
75 RGBA32_Linear,
76 RGBA32_Srgb,
77 Basis_Linear,
78 Basis_Srgb,
79 BC1_UNorm_Linear,
80 BC1_UNorm_Srgb,
81 BC2_UNorm_Linear,
82 BC2_UNorm_Srgb,
83 BC3_UNorm_Linear,
84 BC3_UNorm_Srgb,
85 BC4_UNorm,
86 BC4_SNorm,
87 BC5_UNorm,
88 BC5_SNorm,
89 BC6H_UFloat,
90 BC6H_SFloat,
91 BC7_Unorm_Linear,
92 BC7_Unorm_Srgb,
93}
94
95#[derive(Serialize, Deserialize, Clone, Copy, Debug)]
97pub enum ImageAssetDataFormatConfig {
98 Uncompressed,
99 BasisCompressed(ImageAssetBasisCompressionSettings),
100}
101
102#[derive(Serialize, Deserialize, Clone)]
103pub struct ImageAssetDataMipLevel {
104 pub width: u32,
105 pub height: u32,
106 #[serde(with = "serde_bytes")]
107 pub bytes: Vec<u8>,
108}
109
110#[derive(Serialize, Deserialize, Clone)]
111pub struct ImageAssetDataLayer {
112 pub mip_levels: Vec<ImageAssetDataMipLevel>,
113}
114
115#[derive(Serialize, Deserialize, Clone)]
116pub struct ImageAssetDataPayloadSubresources {
117 pub layers: Vec<ImageAssetDataLayer>,
118}
119
120#[derive(Serialize, Deserialize, Clone)]
121pub struct ImageAssetDataPayloadSingleBuffer {
122 #[serde(with = "serde_bytes")]
123 pub buffer: Vec<u8>,
124}
125
126#[derive(Serialize, Deserialize, Clone)]
127pub enum ImageAssetDataPayload {
128 Subresources(ImageAssetDataPayloadSubresources),
129 SingleBuffer(ImageAssetDataPayloadSingleBuffer),
131}
132
133#[derive(TypeUuid, Serialize, Deserialize, Clone)]
134#[uuid = "e6166902-8716-401b-9d2e-8b01701c5626"]
135pub struct ImageAssetData {
136 pub width: u32,
137 pub height: u32,
138 pub format: ImageAssetDataFormat,
139 pub resource_type: RafxResourceType,
140 pub generate_mips_at_runtime: bool,
141 pub data: ImageAssetDataPayload,
142}
143
144impl std::fmt::Debug for ImageAssetData {
145 fn fmt(
146 &self,
147 f: &mut std::fmt::Formatter<'_>,
148 ) -> std::fmt::Result {
149 f.debug_struct("Point")
150 .field("width", &self.width)
151 .field("width", &self.height)
152 .field("format", &self.format)
153 .finish()
154 }
155}
156
157impl ImageAssetData {
158 pub fn default_format_and_mip_generation(
160 ) -> (ImageAssetDataFormatConfig, ImageAssetMipGeneration) {
161 let compress_textures = false;
162 if compress_textures {
163 #[cfg(feature = "basis-universal")]
164 {
165 let basis_settings = ImageAssetBasisCompressionSettings::default_uastc();
166 let format_config = ImageAssetDataFormatConfig::BasisCompressed(basis_settings);
167 let mipmap_generation = ImageAssetMipGeneration::Precomupted;
168 (format_config, mipmap_generation)
169 }
170
171 #[cfg(not(feature = "basis-universal"))]
172 {
173 unimplemented!("Not built with basis-universal feature")
174 }
175 } else {
176 let format_config = ImageAssetDataFormatConfig::Uncompressed;
177 let mipmap_generation = ImageAssetMipGeneration::Runtime;
178 (format_config, mipmap_generation)
179 }
180 }
181
182 pub fn from_raw_rgba32(
183 width: u32,
184 height: u32,
185 color_space: ImageAssetColorSpaceConfig,
186 format_config: ImageAssetDataFormatConfig,
187 mip_generation: ImageAssetMipGeneration,
188 resource_type: RafxResourceType,
189 raw_rgba32: &[u8],
190 ) -> RafxResult<ImageAssetData> {
191 match format_config {
192 ImageAssetDataFormatConfig::Uncompressed => {
193 let generate_mips_at_runtime = match mip_generation {
194 ImageAssetMipGeneration::NoMips => false,
195 ImageAssetMipGeneration::Precomupted => Err(
196 "Uncompressed ImageAssetDataFormatConfig cannot store precomputed mipmaps",
197 )?,
198 ImageAssetMipGeneration::Runtime => true,
199 };
200
201 let mip = ImageAssetDataMipLevel {
202 width,
203 height,
204 bytes: raw_rgba32.to_vec(),
205 };
206
207 let layer = ImageAssetDataLayer {
208 mip_levels: vec![mip],
209 };
210
211 let format = match color_space {
212 ImageAssetColorSpaceConfig::Linear => ImageAssetDataFormat::RGBA32_Linear,
213 ImageAssetColorSpaceConfig::Srgb => ImageAssetDataFormat::RGBA32_Srgb,
214 };
215
216 Ok(ImageAssetData {
217 width,
218 height,
219 format,
220 generate_mips_at_runtime,
221 resource_type,
222 data: ImageAssetDataPayload::Subresources(ImageAssetDataPayloadSubresources {
223 layers: vec![layer],
224 }),
225 })
226 }
227 #[cfg(not(feature = "basis-universal"))]
228 ImageAssetDataFormatConfig::BasisCompressed(_) => {
229 unimplemented!("crate not built with basis-universal feature");
230 }
231 #[cfg(feature = "basis-universal")]
232 ImageAssetDataFormatConfig::BasisCompressed(settings) => {
233 let generate_mips_at_runtime = match mip_generation {
234 ImageAssetMipGeneration::NoMips => false,
235 ImageAssetMipGeneration::Precomupted => false,
236 ImageAssetMipGeneration::Runtime => true,
237 };
238
239 let basis_color_space = match color_space {
240 ImageAssetColorSpaceConfig::Srgb => basis_universal::ColorSpace::Srgb,
241 ImageAssetColorSpaceConfig::Linear => basis_universal::ColorSpace::Linear,
242 };
243
244 let mut compressor_params = basis_universal::CompressorParams::new();
245 compressor_params.set_basis_format(settings.compression_type.into());
246 compressor_params
247 .set_generate_mipmaps(mip_generation == ImageAssetMipGeneration::Precomupted);
248 compressor_params.set_color_space(basis_color_space);
249
250 match settings.compression_type {
251 ImageAssetBasisCompressionType::Etc1S => {
252 let quality_level = settings.quality.clamp(
253 basis_universal::ETC1S_QUALITY_MIN,
254 basis_universal::ETC1S_QUALITY_MAX,
255 );
256 compressor_params.set_etc1s_quality_level(quality_level)
257 }
258 ImageAssetBasisCompressionType::Uastc => {
259 let quality_level = settings.quality.clamp(
260 basis_universal::ETC1S_QUALITY_MIN,
261 basis_universal::ETC1S_QUALITY_MAX,
262 );
263 compressor_params.set_uastc_quality_level(quality_level)
264 }
265 }
266
267 let mut source_image = compressor_params.source_image_mut(0);
268 source_image.init(raw_rgba32, width, height, 4);
269
270 let mut compressor = basis_universal::Compressor::new(4);
271 unsafe {
272 compressor.init(&compressor_params);
273 log::debug!("Compressing texture");
274 compressor.process().unwrap();
275 log::debug!("Compressed texture");
276 }
277 let compressed_basis_data = compressor.basis_file();
278
279 let format = match color_space {
280 ImageAssetColorSpaceConfig::Linear => ImageAssetDataFormat::Basis_Linear,
281 ImageAssetColorSpaceConfig::Srgb => ImageAssetDataFormat::Basis_Srgb,
282 };
283
284 Ok(ImageAssetData {
285 width,
286 height,
287 format,
288 generate_mips_at_runtime,
289 resource_type,
290 data: ImageAssetDataPayload::SingleBuffer(ImageAssetDataPayloadSingleBuffer {
291 buffer: compressed_basis_data.to_vec(),
292 }),
293 })
294 }
295 }
296 }
297}
298
299#[derive(TypeUuid, Clone)]
300#[uuid = "7a67b850-17f9-4877-8a6e-293a1589bbd8"]
301pub struct ImageAsset {
302 pub image: ResourceArc<ImageResource>,
303 pub image_view: ResourceArc<ImageViewResource>,
304}