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 custom_uniforms,
33 use_background_as_channel0,
34 background_channel0_blend_mode,
35 auto_dim_under_text,
36 auto_dim_strength,
37 } = params;
38 log::info!(
39 "[shader-init] init_custom_shader: enabled={}, path={:?}",
40 custom_shader_enabled,
41 custom_shader_path
42 );
43 if !custom_shader_enabled {
44 log::info!("[shader-init] Skipping: custom_shader_enabled=false");
45 return (None, None);
46 }
47
48 let Some(shader_path) = custom_shader_path else {
49 log::info!("[shader-init] Skipping: custom_shader_path is None");
50 return (None, None);
51 };
52
53 let path = par_term_config::Config::shader_path(shader_path);
54 match CustomShaderRenderer::new(
55 cell_renderer.device(),
56 cell_renderer.queue(),
57 crate::custom_shader_renderer::CustomShaderRendererConfig {
58 surface_format: cell_renderer.surface_format(),
59 shader_path: &path,
60 width: size_width,
61 height: size_height,
62 animation_enabled: custom_shader_animation,
63 animation_speed: custom_shader_animation_speed,
64 window_opacity,
65 full_content_mode: custom_shader_full_content,
66 channel_paths: custom_shader_channel_paths,
67 cubemap_path: custom_shader_cubemap_path,
68 custom_uniforms,
69 background_channel0_blend_mode,
70 },
71 ) {
72 Ok(mut renderer) => {
73 renderer.update_cell_dimensions(
74 cell_renderer.cell_width(),
75 cell_renderer.cell_height(),
76 window_padding,
77 );
78 renderer.set_scale_factor(cell_renderer.scale_factor);
79 renderer.set_brightness(custom_shader_brightness);
80 renderer.set_auto_dim_under_text(auto_dim_under_text, auto_dim_strength);
81
82 if use_background_as_channel0 {
84 let bg_texture = cell_renderer.get_background_as_channel_texture();
86 renderer.set_background_texture(cell_renderer.device(), bg_texture);
87 renderer.update_use_background_as_channel0(
88 cell_renderer.device(),
89 use_background_as_channel0,
90 );
91 }
92
93 log::info!(
94 "[SHADER] Custom shader renderer initialized from: {} (use_bg_as_ch0={})",
95 path.display(),
96 use_background_as_channel0
97 );
98 (Some(renderer), Some(shader_path.to_string()))
99 }
100 Err(e) => {
101 log::info!(
102 "[SHADER] ERROR: Failed to load custom shader '{}': {}",
103 path.display(),
104 e
105 );
106 (None, None)
107 }
108 }
109}
110
111impl Renderer {
116 pub fn set_custom_shader_animation(&mut self, enabled: bool) {
118 if let Some(ref mut custom_shader) = self.custom_shader_renderer {
119 custom_shader.set_animation_enabled(enabled);
120 self.dirty = true;
121 }
122 }
123
124 pub fn set_custom_shader_uniform_values(
126 &mut self,
127 values: std::collections::BTreeMap<String, par_term_config::ShaderUniformValue>,
128 ) {
129 if let Some(ref mut custom_shader) = self.custom_shader_renderer {
130 custom_shader.set_custom_uniform_values(values);
131 self.dirty = true;
132 }
133 }
134
135 pub fn reload_shader_from_source(
140 &mut self,
141 source: &str,
142 ) -> Result<(), crate::error::RenderError> {
143 if let Some(ref mut custom_shader) = self.custom_shader_renderer {
144 custom_shader
145 .reload_from_source(self.cell_renderer.device(), source, "editor")
146 .map_err(|e| crate::error::RenderError::NoActiveShader(format!("{:#}", e)))?;
147 self.dirty = true;
148 Ok(())
149 } else {
150 Err(crate::error::RenderError::NoActiveShader(
151 "No custom shader is currently loaded. Enable a custom shader first.".to_string(),
152 ))
153 }
154 }
155
156 pub fn set_custom_shader_enabled(
161 &mut self,
162 params: CustomShaderEnableParams<'_>,
163 ) -> Result<(), String> {
164 let CustomShaderEnableParams {
165 enabled,
166 shader_path,
167 window_opacity,
168 animation_enabled,
169 animation_speed,
170 full_content,
171 brightness,
172 channel_paths,
173 cubemap_path,
174 custom_uniforms,
175 background_channel0_blend_mode,
176 auto_dim_under_text,
177 auto_dim_strength,
178 } = params;
179 match (enabled, shader_path) {
180 (true, Some(path)) => {
181 let path_changed = self.custom_shader_path.as_deref() != Some(path);
183
184 if let Some(renderer) = &mut self.custom_shader_renderer
186 && !path_changed
187 {
188 renderer.set_animation_enabled(animation_enabled);
189 renderer.set_animation_speed(animation_speed);
190 renderer.set_opacity(window_opacity);
191 renderer.set_full_content_mode(full_content);
192 renderer.set_brightness(brightness);
193 renderer.set_auto_dim_under_text(auto_dim_under_text, auto_dim_strength);
194 renderer.set_background_channel0_blend_mode(background_channel0_blend_mode);
195 renderer.set_custom_uniform_values(custom_uniforms.clone());
196
197 for (i, path) in channel_paths.iter().enumerate() {
199 if let Err(e) = renderer.update_channel_texture(
200 self.cell_renderer.device(),
201 self.cell_renderer.queue(),
202 (i + 1) as u8, path.as_deref(),
204 ) {
205 log::warn!("Failed to update channel {} texture: {}", i, e);
206 }
207 }
208
209 if let Some(cubemap) = cubemap_path
211 && let Err(e) = renderer.update_cubemap(
212 self.cell_renderer.device(),
213 self.cell_renderer.queue(),
214 Some(cubemap),
215 )
216 {
217 log::warn!("Failed to update cubemap: {}", e);
218 }
219
220 return Ok(());
221 }
222
223 let shader_path_full = par_term_config::Config::shader_path(path);
224 match CustomShaderRenderer::new(
225 self.cell_renderer.device(),
226 self.cell_renderer.queue(),
227 crate::custom_shader_renderer::CustomShaderRendererConfig {
228 surface_format: self.cell_renderer.surface_format(),
229 shader_path: &shader_path_full,
230 width: self.size.width,
231 height: self.size.height,
232 animation_enabled,
233 animation_speed,
234 window_opacity,
235 full_content_mode: full_content,
236 channel_paths,
237 cubemap_path,
238 custom_uniforms,
239 background_channel0_blend_mode,
240 },
241 ) {
242 Ok(mut renderer) => {
243 renderer.update_cell_dimensions(
245 self.cell_renderer.cell_width(),
246 self.cell_renderer.cell_height(),
247 self.cell_renderer.window_padding(),
248 );
249 renderer.set_scale_factor(self.cell_renderer.scale_factor);
251 renderer.set_brightness(brightness);
253 renderer.set_auto_dim_under_text(auto_dim_under_text, auto_dim_strength);
254 renderer.set_keep_text_opaque(self.cell_renderer.keep_text_opaque());
256 renderer.set_background_color(
259 self.cell_renderer.solid_background_color(),
260 false,
261 );
262 log::info!(
263 "[SHADER] Custom shader enabled at runtime: {}",
264 shader_path_full.display()
265 );
266 self.custom_shader_renderer = Some(renderer);
267 self.custom_shader_path = Some(path.to_string());
268
269 self.sync_cursor_shader_background_state();
271
272 self.dirty = true;
273 Ok(())
274 }
275 Err(e) => {
276 let error_msg = format!(
277 "Failed to load shader '{}': {}",
278 shader_path_full.display(),
279 e
280 );
281 log::info!("[SHADER] ERROR: {}", error_msg);
282 Err(error_msg)
283 }
284 }
285 }
286 _ => {
287 if self.custom_shader_renderer.is_some() {
288 log::info!("[SHADER] Custom shader disabled at runtime");
289 }
290 self.custom_shader_renderer = None;
291 self.custom_shader_path = None;
292
293 self.sync_cursor_shader_background_state();
295
296 self.dirty = true;
297 Ok(())
298 }
299 }
300 }
301
302 pub fn set_use_background_as_channel0(&mut self, use_background: bool) {
304 if let Some(ref mut custom_shader) = self.custom_shader_renderer {
305 custom_shader
306 .update_use_background_as_channel0(self.cell_renderer.device(), use_background);
307 self.dirty = true;
308 }
309 }
310
311 pub fn sync_background_texture_to_shader(&mut self) {
316 if let Some(ref mut custom_shader) = self.custom_shader_renderer {
317 let bg_texture = self.cell_renderer.get_background_as_channel_texture();
318 custom_shader.set_background_texture(self.cell_renderer.device(), bg_texture);
319 self.dirty = true;
320 }
321 }
322
323 pub fn update_background_as_channel0(&mut self, use_background: bool) {
325 if let Some(ref mut custom_shader) = self.custom_shader_renderer {
326 let bg_texture = self.cell_renderer.get_background_as_channel_texture();
328 custom_shader.set_background_texture(self.cell_renderer.device(), bg_texture);
329
330 custom_shader
332 .update_use_background_as_channel0(self.cell_renderer.device(), use_background);
333
334 self.dirty = true;
335 }
336 }
337
338 pub fn update_background_as_channel0_with_mode(
343 &mut self,
344 use_background: bool,
345 background_mode: par_term_config::BackgroundMode,
346 color: [u8; 3],
347 ) {
348 if let Some(ref mut custom_shader) = self.custom_shader_renderer {
349 let bg_texture = match background_mode {
350 par_term_config::BackgroundMode::Default => {
351 log::info!("update_background_as_channel0_with_mode: Default mode, no texture");
352 None
353 }
354 par_term_config::BackgroundMode::Color => {
355 log::info!(
356 "update_background_as_channel0_with_mode: Color mode, creating solid color texture RGB({},{},{})",
357 color[0],
358 color[1],
359 color[2]
360 );
361 Some(self.cell_renderer.get_solid_color_as_channel_texture(color))
362 }
363 par_term_config::BackgroundMode::Image => {
364 let tex = self.cell_renderer.get_background_as_channel_texture();
365 log::info!(
366 "update_background_as_channel0_with_mode: Image mode, texture={}",
367 if tex.is_some() { "Some" } else { "None" }
368 );
369 tex
370 }
371 };
372
373 let has_texture = bg_texture.is_some();
374 custom_shader.set_background_texture(self.cell_renderer.device(), bg_texture);
375 custom_shader
376 .update_use_background_as_channel0(self.cell_renderer.device(), use_background);
377
378 log::info!(
379 "update_background_as_channel0_with_mode: use_background={}, has_texture={}",
380 use_background,
381 has_texture
382 );
383
384 self.dirty = true;
385 }
386 }
387}