1use image::{ImageError, RgbaImage};
8use librashader_preprocess::{PreprocessError, ShaderSource};
9use librashader_presets::{ParameterMeta, PassMeta, ShaderFeatures, ShaderPreset, TextureMeta};
10use std::path::Path;
11
12#[cfg(not(target_arch = "wasm32"))]
13use rayon::prelude::*;
14
15#[derive(Debug, Clone)]
17#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
18pub struct TextureBuffer {
19 #[cfg_attr(feature = "serde", serde(with = "serde_base64_or_bytes"))]
20 image: Vec<u8>,
21 width: u32,
22 height: u32,
23}
24
25impl From<TextureBuffer> for Option<RgbaImage> {
26 fn from(value: TextureBuffer) -> Self {
27 RgbaImage::from_raw(value.width, value.height, value.image)
28 }
29}
30
31impl AsRef<[u8]> for TextureBuffer {
32 fn as_ref(&self) -> &[u8] {
33 self.image.as_ref()
34 }
35}
36
37impl From<RgbaImage> for TextureBuffer {
38 fn from(value: RgbaImage) -> Self {
39 let width = value.width();
40 let height = value.height();
41 TextureBuffer {
42 image: value.into_raw(),
43 width,
44 height,
45 }
46 }
47}
48
49#[derive(Debug, Clone)]
51#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
52pub struct LoadedResource<M: LoadableResource> {
53 pub data: M::ResourceType,
55 pub meta: M,
57}
58
59pub trait LoadableResource {
61 type ResourceType;
63 type Error;
65 type Options;
67 fn load(path: &Path, options: Self::Options) -> Result<Self::ResourceType, Self::Error>;
69}
70
71impl LoadableResource for PassMeta {
72 type ResourceType = ShaderSource;
73 type Error = PreprocessError;
74 type Options = ShaderFeatures;
75
76 fn load(path: &Path, options: Self::Options) -> Result<Self::ResourceType, Self::Error> {
77 ShaderSource::load(path, options)
78 }
79}
80
81impl LoadableResource for TextureMeta {
82 type ResourceType = TextureBuffer;
83 type Error = ImageError;
84 type Options = ();
85
86 fn load(path: &Path, _options: Self::Options) -> Result<Self::ResourceType, Self::Error> {
87 image::open(path).map(|img| TextureBuffer::from(img.to_rgba8()))
88 }
89}
90
91pub type PassResource = LoadedResource<PassMeta>;
93
94pub type TextureResource = LoadedResource<TextureMeta>;
96
97#[derive(Debug, Clone)]
99#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
100pub struct ShaderPresetPack {
101 #[cfg(feature = "parse_legacy_glsl")]
104 pub feedback_pass: i32,
105
106 pub pass_count: i32,
108 pub passes: Vec<PassResource>,
111
112 pub textures: Vec<TextureResource>,
114
115 pub parameters: Vec<ParameterMeta>,
117}
118
119impl ShaderPresetPack {
120 pub fn load_from_preset<E>(preset: ShaderPreset) -> Result<ShaderPresetPack, E>
122 where
123 E: From<PreprocessError>,
124 E: From<ImageError>,
125 E: Send,
126 {
127 #[cfg(not(target_arch = "wasm32"))]
128 let shaders_iter = preset.passes.into_par_iter();
129
130 #[cfg(target_arch = "wasm32")]
131 let shaders_iter = preset.shaders.into_iter();
132
133 #[cfg(not(target_arch = "wasm32"))]
134 let textures_iter = preset.textures.into_par_iter();
135
136 #[cfg(target_arch = "wasm32")]
137 let textures_iter = preset.textures.into_iter();
138
139 Ok(ShaderPresetPack {
140 #[cfg(feature = "parse_legacy_glsl")]
141 feedback_pass: preset.feedback_pass,
142
143 pass_count: preset.pass_count,
144 passes: shaders_iter
145 .map(|v| {
146 Ok::<_, E>(PassResource {
147 data: PassMeta::load(v.path.as_path(), preset.features)?,
148 meta: v.meta,
149 })
150 })
151 .collect::<Result<Vec<_>, _>>()?,
152 textures: textures_iter
153 .into_par_iter()
154 .map(|t| {
155 Ok::<_, E>(TextureResource {
156 data: TextureMeta::load(t.path.as_path(), ())?,
157 meta: t.meta,
158 })
159 })
160 .collect::<Result<Vec<_>, _>>()?,
161 parameters: preset.parameters,
162 })
163 }
164}
165
166#[cfg(feature = "serde")]
167mod serde_base64_or_bytes {
168 use base64::display::Base64Display;
169 use base64::engine::general_purpose::STANDARD;
170 use base64::Engine;
171 use serde::{Deserializer, Serializer};
172
173 #[allow(clippy::ptr_arg)]
174 pub fn serialize<S: Serializer>(v: &Vec<u8>, s: S) -> Result<S::Ok, S::Error> {
175 if s.is_human_readable() {
176 s.collect_str(&Base64Display::new(v, &STANDARD))
177 } else {
178 serde_bytes::serialize(v, s)
179 }
180 }
181
182 pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<u8>, D::Error> {
183 if d.is_human_readable() {
184 struct Base64Visitor;
185 impl<'de> serde::de::Visitor<'de> for Base64Visitor {
186 type Value = Vec<u8>;
187
188 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
189 formatter.write_str("a base64 encoded string")
190 }
191
192 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
193 where
194 E: serde::de::Error,
195 {
196 self.visit_bytes(v.as_ref())
197 }
198
199 fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
200 where
201 E: serde::de::Error,
202 {
203 self.visit_bytes(v.as_ref())
204 }
205
206 fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
207 where
208 E: serde::de::Error,
209 {
210 STANDARD.decode(v).map_err(serde::de::Error::custom)
211 }
212
213 fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
214 where
215 E: serde::de::Error,
216 {
217 self.visit_bytes(v.as_ref())
218 }
219 }
220
221 d.deserialize_str(Base64Visitor)
222 } else {
223 serde_bytes::deserialize(d)
224 }
225 }
226}
227
228#[cfg(test)]
229mod test {
230 use crate::ShaderPresetPack;
231 use librashader_presets::{ShaderFeatures, ShaderPreset};
232 use std::fs::File;
233 use std::io::Write;
234
235 #[test]
236 fn test() {
237 let preset = ShaderPreset::try_parse(
238 "../test/shaders_slang/crt/crt-royale.slangp",
239 ShaderFeatures::NONE,
240 )
241 .unwrap();
242 let resolved = ShaderPresetPack::load_from_preset::<anyhow::Error>(preset).unwrap();
243 let mut file = File::create("crt-royale.slangpack.json").unwrap();
244 file.write_all(serde_json::to_vec_pretty(&resolved).unwrap().as_ref())
245 .unwrap();
246 }
247
248 #[test]
249 fn test_rmp() {
250 let preset = ShaderPreset::try_parse(
251 "../test/shaders_slang/crt/crt-royale.slangp",
252 ShaderFeatures::NONE,
253 )
254 .unwrap();
255 let resolved = ShaderPresetPack::load_from_preset::<anyhow::Error>(preset).unwrap();
256 let mut file = File::create("crt-royale.slangpack").unwrap();
257 file.write_all(rmp_serde::to_vec(&resolved).unwrap().as_ref())
258 .unwrap();
259 }
260}