par_term_render/renderer/shaders/
background.rs1use super::super::Renderer;
8use super::{CustomShaderEnableParams, CustomShaderInitParams};
9use crate::cell_renderer::CellRenderer;
10use crate::custom_shader_renderer::CustomShaderRenderer;
11
12pub(super) fn init_custom_shader(
16 cell_renderer: &CellRenderer,
17 params: CustomShaderInitParams<'_>,
18) -> (Option<CustomShaderRenderer>, Option<String>) {
19 let CustomShaderInitParams {
20 size_width,
21 size_height,
22 window_padding,
23 path: custom_shader_path,
24 enabled: custom_shader_enabled,
25 animation: custom_shader_animation,
26 animation_speed: custom_shader_animation_speed,
27 window_opacity,
28 full_content: custom_shader_full_content,
29 brightness: custom_shader_brightness,
30 channel_paths: custom_shader_channel_paths,
31 cubemap_path: custom_shader_cubemap_path,
32 use_background_as_channel0,
33 } = params;
34 log::info!(
35 "[shader-init] init_custom_shader: enabled={}, path={:?}",
36 custom_shader_enabled,
37 custom_shader_path
38 );
39 if !custom_shader_enabled {
40 log::info!("[shader-init] Skipping: custom_shader_enabled=false");
41 return (None, None);
42 }
43
44 let Some(shader_path) = custom_shader_path else {
45 log::info!("[shader-init] Skipping: custom_shader_path is None");
46 return (None, None);
47 };
48
49 let path = par_term_config::Config::shader_path(shader_path);
50 match CustomShaderRenderer::new(
51 cell_renderer.device(),
52 cell_renderer.queue(),
53 crate::custom_shader_renderer::CustomShaderRendererConfig {
54 surface_format: cell_renderer.surface_format(),
55 shader_path: &path,
56 width: size_width,
57 height: size_height,
58 animation_enabled: custom_shader_animation,
59 animation_speed: custom_shader_animation_speed,
60 window_opacity,
61 full_content_mode: custom_shader_full_content,
62 channel_paths: custom_shader_channel_paths,
63 cubemap_path: custom_shader_cubemap_path,
64 },
65 ) {
66 Ok(mut renderer) => {
67 renderer.update_cell_dimensions(
68 cell_renderer.cell_width(),
69 cell_renderer.cell_height(),
70 window_padding,
71 );
72 renderer.set_scale_factor(cell_renderer.scale_factor);
73 renderer.set_brightness(custom_shader_brightness);
74
75 if use_background_as_channel0 {
77 let bg_texture = cell_renderer.get_background_as_channel_texture();
79 renderer.set_background_texture(cell_renderer.device(), bg_texture);
80 renderer.update_use_background_as_channel0(
81 cell_renderer.device(),
82 use_background_as_channel0,
83 );
84 }
85
86 log::info!(
87 "[SHADER] Custom shader renderer initialized from: {} (use_bg_as_ch0={})",
88 path.display(),
89 use_background_as_channel0
90 );
91 (Some(renderer), Some(shader_path.to_string()))
92 }
93 Err(e) => {
94 log::info!(
95 "[SHADER] ERROR: Failed to load custom shader '{}': {}",
96 path.display(),
97 e
98 );
99 (None, None)
100 }
101 }
102}
103
104impl Renderer {
109 pub fn set_custom_shader_animation(&mut self, enabled: bool) {
111 if let Some(ref mut custom_shader) = self.custom_shader_renderer {
112 custom_shader.set_animation_enabled(enabled);
113 self.dirty = true;
114 }
115 }
116
117 pub fn reload_shader_from_source(
122 &mut self,
123 source: &str,
124 ) -> Result<(), crate::error::RenderError> {
125 if let Some(ref mut custom_shader) = self.custom_shader_renderer {
126 custom_shader
127 .reload_from_source(self.cell_renderer.device(), source, "editor")
128 .map_err(|e| crate::error::RenderError::NoActiveShader(format!("{:#}", e)))?;
129 self.dirty = true;
130 Ok(())
131 } else {
132 Err(crate::error::RenderError::NoActiveShader(
133 "No custom shader is currently loaded. Enable a custom shader first.".to_string(),
134 ))
135 }
136 }
137
138 pub fn set_custom_shader_enabled(
143 &mut self,
144 params: CustomShaderEnableParams<'_>,
145 ) -> Result<(), String> {
146 let CustomShaderEnableParams {
147 enabled,
148 shader_path,
149 window_opacity,
150 animation_enabled,
151 animation_speed,
152 full_content,
153 brightness,
154 channel_paths,
155 cubemap_path,
156 } = params;
157 match (enabled, shader_path) {
158 (true, Some(path)) => {
159 let path_changed = self.custom_shader_path.as_deref() != Some(path);
161
162 if let Some(renderer) = &mut self.custom_shader_renderer
164 && !path_changed
165 {
166 renderer.set_animation_enabled(animation_enabled);
167 renderer.set_animation_speed(animation_speed);
168 renderer.set_opacity(window_opacity);
169 renderer.set_full_content_mode(full_content);
170 renderer.set_brightness(brightness);
171
172 for (i, path) in channel_paths.iter().enumerate() {
174 if let Err(e) = renderer.update_channel_texture(
175 self.cell_renderer.device(),
176 self.cell_renderer.queue(),
177 (i + 1) as u8, path.as_deref(),
179 ) {
180 log::warn!("Failed to update channel {} texture: {}", i, e);
181 }
182 }
183
184 if let Some(cubemap) = cubemap_path
186 && let Err(e) = renderer.update_cubemap(
187 self.cell_renderer.device(),
188 self.cell_renderer.queue(),
189 Some(cubemap),
190 )
191 {
192 log::warn!("Failed to update cubemap: {}", e);
193 }
194
195 return Ok(());
196 }
197
198 let shader_path_full = par_term_config::Config::shader_path(path);
199 match CustomShaderRenderer::new(
200 self.cell_renderer.device(),
201 self.cell_renderer.queue(),
202 crate::custom_shader_renderer::CustomShaderRendererConfig {
203 surface_format: self.cell_renderer.surface_format(),
204 shader_path: &shader_path_full,
205 width: self.size.width,
206 height: self.size.height,
207 animation_enabled,
208 animation_speed,
209 window_opacity,
210 full_content_mode: full_content,
211 channel_paths,
212 cubemap_path,
213 },
214 ) {
215 Ok(mut renderer) => {
216 renderer.update_cell_dimensions(
218 self.cell_renderer.cell_width(),
219 self.cell_renderer.cell_height(),
220 self.cell_renderer.window_padding(),
221 );
222 renderer.set_scale_factor(self.cell_renderer.scale_factor);
224 renderer.set_brightness(brightness);
226 renderer.set_keep_text_opaque(self.cell_renderer.keep_text_opaque());
228 renderer.set_background_color(
231 self.cell_renderer.solid_background_color(),
232 false,
233 );
234 log::info!(
235 "[SHADER] Custom shader enabled at runtime: {}",
236 shader_path_full.display()
237 );
238 self.custom_shader_renderer = Some(renderer);
239 self.custom_shader_path = Some(path.to_string());
240
241 self.sync_cursor_shader_background_state();
243
244 self.dirty = true;
245 Ok(())
246 }
247 Err(e) => {
248 let error_msg = format!(
249 "Failed to load shader '{}': {}",
250 shader_path_full.display(),
251 e
252 );
253 log::info!("[SHADER] ERROR: {}", error_msg);
254 Err(error_msg)
255 }
256 }
257 }
258 _ => {
259 if self.custom_shader_renderer.is_some() {
260 log::info!("[SHADER] Custom shader disabled at runtime");
261 }
262 self.custom_shader_renderer = None;
263 self.custom_shader_path = None;
264
265 self.sync_cursor_shader_background_state();
267
268 self.dirty = true;
269 Ok(())
270 }
271 }
272 }
273
274 pub fn set_use_background_as_channel0(&mut self, use_background: bool) {
276 if let Some(ref mut custom_shader) = self.custom_shader_renderer {
277 custom_shader
278 .update_use_background_as_channel0(self.cell_renderer.device(), use_background);
279 self.dirty = true;
280 }
281 }
282
283 pub fn sync_background_texture_to_shader(&mut self) {
288 if let Some(ref mut custom_shader) = self.custom_shader_renderer {
289 let bg_texture = self.cell_renderer.get_background_as_channel_texture();
290 custom_shader.set_background_texture(self.cell_renderer.device(), bg_texture);
291 self.dirty = true;
292 }
293 }
294
295 pub fn update_background_as_channel0(&mut self, use_background: bool) {
297 if let Some(ref mut custom_shader) = self.custom_shader_renderer {
298 let bg_texture = self.cell_renderer.get_background_as_channel_texture();
300 custom_shader.set_background_texture(self.cell_renderer.device(), bg_texture);
301
302 custom_shader
304 .update_use_background_as_channel0(self.cell_renderer.device(), use_background);
305
306 self.dirty = true;
307 }
308 }
309
310 pub fn update_background_as_channel0_with_mode(
315 &mut self,
316 use_background: bool,
317 background_mode: par_term_config::BackgroundMode,
318 color: [u8; 3],
319 ) {
320 if let Some(ref mut custom_shader) = self.custom_shader_renderer {
321 let bg_texture = match background_mode {
322 par_term_config::BackgroundMode::Default => {
323 log::info!("update_background_as_channel0_with_mode: Default mode, no texture");
324 None
325 }
326 par_term_config::BackgroundMode::Color => {
327 log::info!(
328 "update_background_as_channel0_with_mode: Color mode, creating solid color texture RGB({},{},{})",
329 color[0],
330 color[1],
331 color[2]
332 );
333 Some(self.cell_renderer.get_solid_color_as_channel_texture(color))
334 }
335 par_term_config::BackgroundMode::Image => {
336 let tex = self.cell_renderer.get_background_as_channel_texture();
337 log::info!(
338 "update_background_as_channel0_with_mode: Image mode, texture={}",
339 if tex.is_some() { "Some" } else { "None" }
340 );
341 tex
342 }
343 };
344
345 let has_texture = bg_texture.is_some();
346 custom_shader.set_background_texture(self.cell_renderer.device(), bg_texture);
347 custom_shader
348 .update_use_background_as_channel0(self.cell_renderer.device(), use_background);
349
350 log::info!(
351 "update_background_as_channel0_with_mode: use_background={}, has_texture={}",
352 use_background,
353 has_texture
354 );
355
356 self.dirty = true;
357 }
358 }
359}