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