GCC Code Coverage Report


./
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
0 of 381, 0 excluded
0.0%
Functions:
0 of 40, 0 excluded
0.0%
Branches:
0 of 468, 0 excluded
0.0%

libs/render/src/eu/render/postproc.cc
Line Branch Exec Source
1 #include "eu/render/postproc.h"
2
3 #include "eu/assert/assert.h"
4
5 #include "eu/log/log.h"
6
7 #include "eu/render/opengl_utils.h"
8 #include "eu/render/postproc.internal.h"
9 #include "eu/render/renderer.h"
10 #include "eu/render/renderer.pimpl.h"
11 #include "eu/render/shader_resource.h"
12 #include "eu/render/shader.h"
13 #include "eu/render/state.h"
14 #include "eu/render/camera.h"
15 #include "eu/render/shadow.h"
16
17 #if FF_HAS(EU_DEBUG_RUNNER)
18 #include "eu/imgui/ui.h"
19 #include "dear_imgui/imgui.h"
20 #endif
21
22
23 namespace eu::render
24 {
25
26
27 ///////////////////////////////////////////////////////////////////////////////////////////////////
28 // Effect
29
30 ✗ bool Effect::enabled() const
31 {
32 ✗ return is_enabled;
33 }
34
35 ✗ void Effect::set_enabled(bool n)
36 {
37 ✗ if (is_enabled == n)
38 {
39 ✗ return;
40 }
41
42 ✗ is_enabled = n;
43 ✗ if (owner != nullptr)
44 {
45 ✗ owner->dirty = true;
46 }
47 }
48
49
50 ///////////////////////////////////////////////////////////////////////////////////////////////////
51 // RenderWorld
52
53 // todo(Gustav): should this be a user config option? evaluate higher/lower bits, probably 16 or 32 since it needs to be floating point for hdr
54 constexpr ColorBitsPerPixel render_world_color_bits_per_pixel = ColorBitsPerPixel::use_16;
55
56 ✗ static std::optional<BloomRender> build_bloom(const Size& size, ExtractShader* sh, PingPongBlurShader* ping_sh)
57 {
58 ✗ if (sh == nullptr) { return std::nullopt; }
59
60 ✗ auto bloom_buffer = build_simple_framebuffer(USE_DEBUG_LABEL_MANY("bloom extraction buffer") size);
61
62 ✗ auto ping_one = build_simple_framebuffer(USE_DEBUG_LABEL_MANY("ping-pong bloom buffer one") size);
63 ✗ auto ping_two = build_simple_framebuffer(USE_DEBUG_LABEL_MANY("ping-pong bloom buffer two") size);
64
65 ✗ return BloomRender
66 {
67 .extract_shader = sh,
68 .bloom_buffer = bloom_buffer,
69 .ping_pong_shader = ping_sh,
70 .ping_pong_buffer = {ping_one, ping_two}
71 ✗ };
72 ✗ }
73
74 ✗ RenderWorld::RenderWorld(const Size& size, RealizeShader* re_sh, ExtractShader* ex_sh, PingPongBlurShader* ping_sh, int msaa_samples, bool* h, float* e)
75 ✗ : window_size(size)
76 ✗ , use_hdr(h)
77 ✗ , exposure(e)
78 ✗ , msaa_buffer(build_msaa_framebuffer(USE_DEBUG_LABEL_MANY("msaa buffer")
79 size, msaa_samples, render_world_color_bits_per_pixel))
80 ✗ , realized_buffer(build_hdr_floating_framebuffer(USE_DEBUG_LABEL_MANY("realized msaa buffer") size, render_world_color_bits_per_pixel))
81 ✗ , realize_shader(re_sh)
82 ✗ , bloom_render(build_bloom(size, ex_sh, ping_sh))
83 ✗ , last_bloom_blur_index(0)
84 {
85 ✗ ASSERT(use_hdr);
86 ✗ ASSERT(exposure);
87 ✗ }
88
89 ✗ void RenderWorld::update(const PostProcArg& arg)
90 {
91 // render shadow buffer
92 ✗ const auto shadow_size = arg.renderer->settings.shadow_map_resolution;
93 {
94 ✗ bool update_shadow_buffer = shadow_buffer == nullptr;
95 ✗ if (shadow_buffer && shadow_buffer->size != shadow_size)
96 {
97 ✗ update_shadow_buffer = true;
98 }
99
100 ✗ if (update_shadow_buffer)
101 {
102 ✗ shadow_buffer = build_shadow_framebuffer(USE_DEBUG_LABEL_MANY("shadow buffer") shadow_size);
103 }
104 }
105
106 ✗ std::optional<CompiledCamera> opt_compiled_shadow_camera;
107
108 ✗ if (arg.world->lights.directional_lights.empty() == false)
109 {
110 ✗ SCOPED_DEBUG_GROUP("render shadow buffer"sv);
111
112 ✗ const auto compiled_shadow_camera = compile_the_shadow_camera(
113 ✗ *arg.camera, window_size, arg.world->lights.directional_lights[0], arg.renderer->settings, *arg.world
114 );
115
116 ✗ auto bound = BoundFbo{shadow_buffer};
117 ✗ set_gl_viewport({
118 ✗ .width = shadow_buffer->size.width,
119 ✗ .height = shadow_buffer->size.height
120 });
121 ✗ arg.renderer->render_shadows(shadow_size, *arg.world, compiled_shadow_camera);
122 ✗ }
123
124 // render into msaa buffer
125 {
126 ✗ SCOPED_DEBUG_GROUP("rendering into msaa buffer"sv);
127
128 ✗ const ShadowContext shadow_context = opt_compiled_shadow_camera ? ShadowContext{
129 ✗ .directional_shadow_map = shadow_buffer.get(),
130 ✗ .directional_shadow_clip_from_world = opt_compiled_shadow_camera->clip_from_view * opt_compiled_shadow_camera->view_from_world
131 } : ShadowContext{
132 .directional_shadow_map = nullptr,
133 .directional_shadow_clip_from_world = m4_identity
134 ✗ };
135
136 ✗ auto bound = BoundFbo{msaa_buffer};
137 ✗ set_gl_viewport({
138 ✗ .width = msaa_buffer->size.width,
139 ✗ .height = msaa_buffer->size.height
140 });
141 ✗ arg.renderer->render_world(window_size, *arg.world, compile(*arg.camera, window_size), shadow_context);
142 ✗ }
143
144 // copy msaa buffer to realized
145 {
146 ✗ SCOPED_DEBUG_GROUP("resolving msaa buffer"sv);
147 ✗ resolve_multisampled_buffer(*msaa_buffer, realized_buffer.get());
148 ✗ }
149
150 // extract overexposed
151 ✗ if (bloom_render.has_value())
152 {
153 // extract overexposed pixels
154 {
155 ✗ SCOPED_DEBUG_GROUP("extracting to overexposed buffer"sv);
156 ✗ StateChanger{arg.renderer->pimpl->states}
157 ✗ .cull_face(false)
158 ✗ .stencil_test(false)
159 ✗ .depth_test(false)
160 ✗ .depth_mask(false)
161 ✗ .blending(false);
162
163 ✗ glClearColor(0, 0, 0, 1.0f);
164 ✗ glClear(GL_COLOR_BUFFER_BIT);
165
166 // todo(Gustav): why bind this late... shouldn't this be first???
167 ✗ auto bound = BoundFbo{bloom_render->bloom_buffer};
168
169 {
170 ✗ const auto& container = bloom_render->extract_shader;
171 ✗ const auto& shader = container->shader;
172 ✗ const auto& program = shader->program;
173 ✗ program->use();
174 ✗ program->set_float(container->cutoff_uniform, arg.renderer->settings.bloom_cutoff);
175 ✗ program->set_float(container->softness_uniform, arg.renderer->settings.bloom_softness);
176 ✗ bind_texture_2d(arg.renderer->pimpl->states, shader->tex_input_uniform, *realized_buffer);
177
178 ✗ render_geom(*arg.renderer->pimpl->full_screen_geom);
179 }
180 ✗ }
181
182 // blur the buffers
183 {
184 ✗ SCOPED_DEBUG_GROUP("blur extracted pixels for bloom"sv);
185 ✗ bool is_horizontal = true;
186 ✗ for (int blur_iteration = 0; blur_iteration < arg.renderer->settings.bloom_blur_steps; blur_iteration += 1)
187 {
188 ✗ const auto is_first_iteration = blur_iteration == 0;
189
190 ✗ const std::size_t source_index = is_horizontal ? 1 : 0;
191 ✗ const std::size_t target_index = is_horizontal ? 0 : 1;
192 ✗ last_bloom_blur_index = target_index;
193
194 ✗ SCOPED_DEBUG_GROUP(
195 fmt::format("blur pass #{} ({}, {})",
196 blur_iteration,
197 (is_horizontal ? "hori" : "vert"),
198 (is_first_iteration ? "first" : "after"))
199 );
200 ✗ auto bound = BoundFbo{bloom_render->ping_pong_buffer[target_index]};
201 ✗ StateChanger{arg.renderer->pimpl->states}
202 ✗ .cull_face(false)
203 ✗ .stencil_test(false)
204 ✗ .depth_test(false)
205 ✗ .depth_mask(false)
206 ✗ .blending(false);
207
208 ✗ glClearColor(0, 0, 0, 1.0f);
209 ✗ glClear(GL_COLOR_BUFFER_BIT);
210
211 {
212 ✗ const auto& container = bloom_render->ping_pong_shader;
213 ✗ const auto& shader = container->shader;
214 ✗ const auto& program = shader->program;
215 ✗ program->use();
216 ✗ program->set_bool(container->is_horizontal_uniform, is_horizontal);
217
218 ✗ auto& src_texture = is_first_iteration ? bloom_render->bloom_buffer
219 ✗ : bloom_render->ping_pong_buffer[source_index];
220 ✗ bind_texture_2d(arg.renderer->pimpl->states, shader->tex_input_uniform, *src_texture);
221
222 ✗ render_geom(*arg.renderer->pimpl->full_screen_geom);
223 }
224
225 ✗ is_horizontal = ! is_horizontal;
226 ✗ }
227 ✗ }
228 }
229
230 // blur overexposed using ping-pong gaussian blur
231 ✗ }
232
233 ✗ void RenderWorld::render(const PostProcArg& arg)
234 {
235 // render realized (with shader)
236 ✗ SCOPED_DEBUG_GROUP("render realized msaa buffer"sv);
237 ✗ StateChanger{arg.renderer->pimpl->states}
238 ✗ .cull_face(false)
239 ✗ .stencil_test(false)
240 ✗ .depth_test(false)
241 ✗ .depth_mask(false)
242 ✗ .blending(false);
243
244 ✗ glClearColor(0, 0, 0, 1.0f);
245 ✗ glClear(GL_COLOR_BUFFER_BIT);
246
247 ✗ ASSERT(use_hdr);
248 ✗ ASSERT(exposure);
249
250 {
251 ✗ const auto& container = realize_shader;
252 ✗ const auto& program = container->program;
253 ✗ program->use();
254 ✗ program->set_float(container->gamma_uniform, arg.renderer->settings.gamma);
255 ✗ program->set_float(container->exposure_uniform, *use_hdr ? *exposure : -1.0f);
256 ✗ program->set_bool(container->use_blur_uniform, bloom_render.has_value());
257 ✗ bind_texture_2d(arg.renderer->pimpl->states, container->tex_input_uniform, *realized_buffer);
258 ✗ if (bloom_render.has_value())
259 {
260 ✗ bind_texture_2d(arg.renderer->pimpl->states, container->tex_blurred_bloom_uniform, *bloom_render->ping_pong_buffer[last_bloom_blur_index]);
261 }
262
263 ✗ render_geom(*arg.renderer->pimpl->full_screen_geom);
264 }
265 ✗ }
266
267
268
269 ✗ void RenderWorld::gui(imgui::ImguiShaderCache* cache)
270 {
271 // todo(Gustav): refactor
272 #if FF_HAS(EU_DEBUG_RUNNER)
273 ✗ if (bloom_render && bloom_render->bloom_buffer)
274 {
275 ✗ imgui::imgui_image("bloom buffer", *bloom_render->bloom_buffer, cache, imgui::ImageShader::tonemap_and_gamma);
276
277 ✗ imgui::imgui_image("ping-pong A", *bloom_render->ping_pong_buffer[0], cache, imgui::ImageShader::tonemap_and_gamma);
278 ✗ imgui::imgui_image("ping-pong B", *bloom_render->ping_pong_buffer[1], cache, imgui::ImageShader::tonemap_and_gamma);
279 }
280 #endif
281 ✗ }
282
283
284 ///////////////////////////////////////////////////////////////////////////////////////////////////
285 // RenderTextureWithShader
286
287 ✗ RenderTextureWithShader::RenderTextureWithShader(std::string n, std::shared_ptr<RenderSource> s, std::shared_ptr<FrameBuffer> f, ShaderPropertyProvider* e)
288 ✗ : name(std::move(n))
289 ✗ , source(std::move(s))
290 ✗ , fbo(std::move(f))
291 ✗ , effect(e)
292 {
293 ✗ ASSERT(effect);
294 ✗ }
295
296 ✗ void RenderTextureWithShader::render(const PostProcArg& arg)
297 {
298 ✗ ASSERT(effect);
299
300 ✗ SCOPED_DEBUG_GROUP(fmt::format("Rendering task {}", name));
301
302 ✗ StateChanger{arg.renderer->pimpl->states}
303 ✗ .cull_face(false)
304 ✗ .stencil_test(false)
305 ✗ .depth_test(false)
306 ✗ .depth_mask(false)
307 ✗ .blending(false);
308
309 ✗ glClearColor(0, 0, 0, 1.0f);
310 ✗ glClear(GL_COLOR_BUFFER_BIT);
311
312 ✗ effect->use_shader(arg, *fbo);
313 ✗ render_geom(*arg.renderer->pimpl->full_screen_geom);
314 ✗ }
315
316 ✗ void RenderTextureWithShader::update(const PostProcArg& arg)
317 {
318 ✗ SCOPED_DEBUG_GROUP(fmt::format("Updating task {}", name));
319 ✗ auto bound = BoundFbo{fbo};
320 ✗ set_gl_viewport(fbo->size);
321 ✗ source->render(arg);
322 ✗ }
323
324
325 ///////////////////////////////////////////////////////////////////////////////////////////////////
326 // EffectStack
327
328 ✗ void EffectStack::update(float dt) const
329 {
330 ✗ for (const auto& e: effects)
331 {
332 ✗ if (e->is_enabled == false)
333 {
334 ✗ continue;
335 }
336
337 ✗ e->update(dt);
338 }
339 ✗ }
340
341 ✗ void EffectStack::render(const PostProcArg& arg)
342 {
343 // todo(Gustav): owners are only used when we have added an effect and the effect can change dirty flag
344 // make this a requirement when creating the effect instead and remove this loop?
345 ✗ for (const auto& e: effects)
346 {
347 ✗ if (e->owner != this)
348 {
349 ✗ e->owner = this;
350 ✗ dirty = true;
351 }
352 }
353
354 ✗ if (render_world == nullptr)
355 {
356 ✗ dirty = true;
357 }
358
359 ✗ if (window_size.has_value() == false || *window_size != arg.window_size)
360 {
361 ✗ window_size = arg.window_size;
362 ✗ dirty = true;
363 }
364
365 {
366 ✗ const auto latest_msaa = arg.renderer->settings.msaa;
367 ✗ if (current_msaa_setting != latest_msaa)
368 {
369 ✗ current_msaa_setting = latest_msaa;
370 ✗ dirty = true;
371 }
372 }
373
374 ✗ const auto should_use_bloom = arg.renderer->settings.use_bloom;
375 {
376 ✗ const auto is_currently_using_bloom = render_world && render_world->bloom_render.has_value();
377 ✗ if (is_currently_using_bloom != should_use_bloom)
378 {
379 ✗ dirty = true;
380 }
381 }
382
383 // if dirty, update the compiled state
384 ✗ if (dirty)
385 {
386 ✗ dirty = false;
387 ✗ LOG_INFO("Building effects stack");
388
389 ✗ compiled.targets.clear();
390
391 auto created_world = std::make_shared<RenderWorld>(
392 ✗ arg.window_size,
393 ✗ &arg.renderer->pimpl->shaders_resources.pp_realize,
394 ✗ should_use_bloom ? &arg.renderer->pimpl->shaders_resources.pp_extract : nullptr,
395 ✗ should_use_bloom ? &arg.renderer->pimpl->shaders_resources.pp_ping : nullptr,
396 ✗ current_msaa_setting,
397 ✗ &use_hdr,
398 ✗ &exposure
399 ✗ );
400 ✗ compiled.last_source = created_world;
401 ✗ render_world = created_world;
402
403 ✗ for (auto& e: effects)
404 {
405 ✗ if (e->is_enabled)
406 {
407 ✗ e->build({
408 ✗ .builder = &compiled,
409 .window_size = arg.window_size
410 });
411 }
412 }
413 ✗ }
414
415 // the stack is now compiled
416 // before rendering, update all targets/fbos and present
417 {
418 ✗ SCOPED_DEBUG_GROUP("updating postproc render_world"sv);
419 ✗ render_world->update(arg);
420 ✗ }
421
422 {
423 ✗ SCOPED_DEBUG_GROUP("rendering postproc actions"sv);
424 ✗ for (const auto& action: compiled.targets)
425 {
426 ✗ action->update(arg);
427 }
428 ✗ }
429
430 // render the final image to the screen
431 {
432 ✗ SCOPED_DEBUG_GROUP("rendering postproc to screen"sv);
433 ✗ set_gl_viewport(arg.final_rect);
434 ✗ compiled.last_source->render(arg);
435 ✗ }
436 ✗ }
437
438 ✗ void EffectStack::gui(imgui::ImguiShaderCache* cache)
439 {
440 #if FF_HAS(EU_DEBUG_RUNNER)
441 ✗ ImGui::Checkbox("HDR", &use_hdr);
442 ✗ ImGui::SliderFloat("Exposure", &exposure, 0.01f, 20.0f);
443
444 ✗ if (render_world)
445 {
446 ✗ render_world->gui(cache);
447 }
448
449 ✗ int index = 0;
450 ✗ for (const auto& e: effects)
451 {
452 ✗ ImGui::PushID(index);
453 ✗ e->gui();
454 ✗ ImGui::PopID();
455 ✗ index += 1;
456 }
457 #endif
458 ✗ }
459
460
461 ///////////////////////////////////////////////////////////////////////////////////////////////////
462 // FactorEffect
463
464 ✗ FactorEffect::FactorEffect()
465 {
466 ✗ set_enabled(false);
467 ✗ }
468
469 ✗ float FactorEffect::get_factor() const
470 {
471 ✗ return factor;
472 }
473
474 ✗ void FactorEffect::set_factor(float f)
475 {
476 ✗ factor = f;
477 ✗ set_enabled(factor > kk::almost_zero);
478 ✗ }
479
480
481
482 ///////////////////////////////////////////////////////////////////////////////////////////////////
483 // FloatDragShaderProp
484
485 ✗ FloatDragShaderProp::FloatDragShaderProp(const LoadedPostProcShader& shader, const std::string& n, float v, float s)
486 ✗ : uniform(shader.program->get_uniform(n))
487 ✗ , name(n)
488 ✗ , value(v)
489 ✗ , speed(s)
490 {
491 ✗ }
492
493 ✗ void FloatDragShaderProp::use(const PostProcArg&, ShaderProgram& shader)
494 {
495 ✗ shader.set_float(uniform, value);
496 ✗ }
497
498 ✗ void FloatDragShaderProp::gui()
499 {
500 #if FF_HAS(EU_DEBUG_RUNNER)
501 ✗ ImGui::DragFloat(name.c_str(), &value, speed);
502 #endif
503 ✗ }
504
505
506
507 ///////////////////////////////////////////////////////////////////////////////////////////////////
508 // FloatSliderShaderProp
509
510 ✗ FloatSliderShaderProp::FloatSliderShaderProp(
511 const LoadedPostProcShader& shader, const std::string& n, float v, float mi, float ma
512 ✗ )
513 ✗ : uniform(shader.program->get_uniform(n))
514 ✗ , name(n)
515 ✗ , value(v)
516 ✗ , min(mi)
517 ✗ , max(ma)
518 {
519 ✗ }
520
521 ✗ void FloatSliderShaderProp::use(const PostProcArg&, ShaderProgram& shader)
522 {
523 ✗ shader.set_float(uniform, value);
524 ✗ }
525
526 ✗ void FloatSliderShaderProp::gui()
527 {
528 #if FF_HAS(EU_DEBUG_RUNNER)
529 ✗ ImGui::SliderFloat(name.c_str(), &value, min, max);
530 #endif
531 ✗ }
532
533
534 ///////////////////////////////////////////////////////////////////////////////////////////////////
535 // SimpleEffect
536
537 ✗ SimpleEffect::SimpleEffect(std::string n, std::shared_ptr<LoadedPostProcShader> s)
538 ✗ : name(std::move(n))
539 ✗ , shader(std::move(s))
540 {
541 ✗ ASSERT(shader->factor_uni.has_value());
542 ✗ }
543
544 ✗ void SimpleEffect::add_float_drag_prop(const std::string& prop_name, float value, float speed)
545 {
546 ✗ properties.emplace_back(std::make_shared<FloatDragShaderProp>(*shader, prop_name, value, speed));
547 ✗ }
548
549 ✗ void SimpleEffect::add_float_slider_prop(const std::string& prop_name, float value, float min, float max)
550 {
551 ✗ properties.emplace_back(std::make_shared<FloatSliderShaderProp>(*shader, prop_name, value, min, max));
552 ✗ }
553
554 ✗ void SimpleEffect::gui()
555 {
556 #if FF_HAS(EU_DEBUG_RUNNER)
557 ✗ if (properties.empty())
558 {
559 ✗ return;
560 }
561
562 ✗ int index = 0;
563
564 ✗ if (ImGui::TreeNode(name.c_str()) == false)
565 {
566 ✗ return;
567 }
568
569 ✗ for (auto& p: properties)
570 {
571 ✗ ImGui::PushID(index);
572 ✗ p->gui();
573 ✗ ImGui::PopID();
574 ✗ index += 1;
575 }
576
577 ✗ ImGui::TreePop();
578 #endif
579 }
580
581 ✗ void SimpleEffect::update(float dt)
582 {
583 ✗ time += dt;
584 ✗ }
585
586 ✗ static v2 v2_from_size(const Size& s)
587 {
588 ✗ return { float_from_int(s.width), float_from_int(s.height) };
589 }
590
591 ✗ void SimpleEffect::use_shader(const PostProcArg& a, const FrameBuffer& t)
592 {
593 ✗ shader->program->use();
594
595 ✗ const auto& shader_factor = shader->factor_uni;
596 ✗ const auto& shader_resolution = shader->resolution_uni;
597 ✗ const auto& shader_time = shader->time_uni;
598
599 ✗ if (shader_factor)
600 {
601 ✗ shader->program->set_float(*shader_factor, get_factor());
602 }
603 ✗ if (shader_resolution)
604 {
605 ✗ shader->program->set_vec2(*shader_resolution, v2_from_size(a.window_size));
606 }
607 ✗ if (shader_time)
608 {
609 ✗ shader->program->set_float(*shader_time, time);
610 }
611 ✗ for (auto& p: properties)
612 {
613 ✗ p->use(a, *shader->program);
614 }
615 ✗ bind_texture_2d(a.renderer->pimpl->states, shader->tex_input_uniform, t);
616 ✗ }
617
618 ✗ void SimpleEffect::build(const BuildArg& arg)
619 {
620 ✗ time = 0.0f;
621
622 // todo(Gustav): reuse buffers created from an earlier postproc build
623 // todo(Gustav): reuse buffers from earlier in the postproc stack, that aren't in use
624 ✗ auto fbo = build_simple_framebuffer(USE_DEBUG_LABEL_MANY(fmt::format("fbo for {}", name)) arg.window_size);
625
626 ✗ auto src = arg.builder->last_source;
627 ✗ auto target = std::make_shared<RenderTextureWithShader>(name, src, fbo, this);
628
629 ✗ arg.builder->targets.emplace_back(target);
630 ✗ arg.builder->last_source = target;
631 ✗ }
632
633
634
635 ///////////////////////////////////////////////////////////////////////////////////////////////////
636 // VertProvider
637
638 ✗ BlurVerticalProvider::BlurVerticalProvider(BlurEffect* b)
639 ✗ : blur(b)
640 {
641 ✗ }
642
643
644 ✗ void BlurVerticalProvider::use_shader(const PostProcArg& a, const FrameBuffer& t)
645 {
646 ✗ blur->use_vert_shader(a, t);
647 ✗ }
648
649
650
651 ///////////////////////////////////////////////////////////////////////////////////////////////////
652 // HoriProvider
653
654 ✗ BurHorizontalProvider::BurHorizontalProvider(BlurEffect* b)
655 ✗ : blur(b)
656 {
657 ✗ }
658
659 ✗ void BurHorizontalProvider::use_shader(const PostProcArg& a, const FrameBuffer& t)
660 {
661 ✗ blur->use_hori_shader(a, t);
662 ✗ }
663
664
665
666
667 ///////////////////////////////////////////////////////////////////////////////////////////////////
668 // BlurEffect
669
670
671 ✗ BlurEffect::BlurEffect(std::string n, std::shared_ptr<LoadedPostProcShader> v, std::shared_ptr<LoadedPostProcShader> h)
672 ✗ : name(std::move(n))
673 ✗ , vert_p(this)
674 ✗ , hori_p(this)
675 ✗ , vert(std::move(v))
676 ✗ , hori(std::move(h))
677 ✗ , blur_size_v(vert->program->get_uniform("u_blur_size"))
678 ✗ , blur_size_h(hori->program->get_uniform("u_blur_size"))
679 #if FF_HAS(BLUR_USE_GAUSS)
680 , std_dev_v(vert->program->get_uniform("u_std_dev"))
681 , std_dev_h(hori->program->get_uniform("u_std_dev"))
682 #endif
683 {
684 ✗ ASSERT(vert->factor_uni.has_value());
685 ✗ ASSERT(hori->factor_uni.has_value());
686 ✗ }
687
688 ✗ void BlurEffect::gui()
689 {
690 #if FF_HAS(EU_DEBUG_RUNNER)
691 ✗ if (ImGui::TreeNode(name.c_str()) == false)
692 {
693 ✗ return;
694 }
695
696 ✗ ImGui::SliderFloat("Blur size", &blur_size, 0.0f, 0.2f);
697 #if FF_HAS(BLUR_USE_GAUSS)
698 ImGui::SliderFloat("Standard deviation", &std_dev, 0.0f, 0.1f);
699 #endif
700
701 ✗ ImGui::TreePop();
702 #endif
703 }
704
705 ✗ void BlurEffect::update(float)
706 {
707 // no update needed
708 ✗ }
709
710 ✗ void BlurEffect::use_vert_shader(const PostProcArg& a, const FrameBuffer& t) const
711 {
712 ✗ const auto& factor_uniform = vert->factor_uni;
713
714 ✗ vert->program->use();
715 ✗ ASSERT(factor_uniform);
716 ✗ if (factor_uniform)
717 {
718 ✗ vert->program->set_float(*factor_uniform, get_factor());
719 }
720 ✗ vert->program->set_float(blur_size_v, blur_size);
721 #if FF_HAS(BLUR_USE_GAUSS)
722 vert->program->set_float(std_dev_v, std_dev);
723 #endif
724 ✗ bind_texture_2d(a.renderer->pimpl->states, vert->tex_input_uniform, t);
725 ✗ }
726
727 ✗ void BlurEffect::use_hori_shader(const PostProcArg& a, const FrameBuffer& t)
728 {
729 ✗ const auto& factor_uniform = hori->factor_uni;
730 ✗ const auto& resolution_uniform = hori->resolution_uni;
731
732 ✗ hori->program->use();
733 ✗ ASSERT(factor_uniform);
734 ✗ if (factor_uniform)
735 {
736 ✗ hori->program->set_float(*factor_uniform, get_factor());
737 }
738 ✗ ASSERT(resolution_uniform);
739 ✗ if (resolution_uniform)
740 {
741 ✗ hori->program->set_vec2(*resolution_uniform, v2_from_size(a.window_size));
742 }
743 ✗ hori->program->set_float(blur_size_h, blur_size);
744 #if FF_HAS(BLUR_USE_GAUSS)
745 hori->program->set_float(std_dev_h, std_dev);
746 #endif
747 ✗ bind_texture_2d(a.renderer->pimpl->states, hori->tex_input_uniform, t);
748 ✗ }
749
750 ✗ void BlurEffect::build(const BuildArg& arg)
751 {
752 ✗ auto src = arg.builder->last_source;
753
754 // todo(Gustav): modify resolution to get better blur and at a lower cost!
755
756 // step 1: vertical
757 ✗ auto fbo_v = build_simple_framebuffer(USE_DEBUG_LABEL_MANY("blur vertical") arg.window_size);
758 ✗ auto target_v = std::make_shared<RenderTextureWithShader>("blur vertical", src, fbo_v, &vert_p);
759 ✗ arg.builder->targets.emplace_back(target_v);
760
761 // step 2: horizontal
762 ✗ auto fbo_h = build_simple_framebuffer(USE_DEBUG_LABEL_MANY("blur horizontal") arg.window_size);
763 ✗ auto target_h = std::make_shared<RenderTextureWithShader>("blur horizontal", target_v, fbo_h, &hori_p);
764 ✗ arg.builder->targets.emplace_back(target_h);
765
766 // done
767 ✗ arg.builder->last_source = target_h;
768 ✗ }
769
770 } // namespace eu::render
771