VisuTwin Canvas
C++ 3D Engine — Metal Backend
Loading...
Searching...
No Matches
renderPassBloom.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2025-2026 Arnis Lektauers
3//
4//
5#include "renderPassBloom.h"
6
7#include <algorithm>
8#include <cmath>
9#include <string>
10
11#include "core/math/color.h"
14#include "renderPassUpsample.h"
15
16namespace visutwin::canvas
17{
18 RenderPassBloom::RenderPassBloom(const std::shared_ptr<GraphicsDevice>& device, Texture* sourceTexture,
19 const PixelFormat format)
20 : RenderPass(device), _sourceTexture(sourceTexture), _textureFormat(format)
21 {
22 _bloomRenderTarget = createRenderTarget(0, _bloomColorTexture);
23 _bloomTexture = _bloomColorTexture.get();
24 }
25
26 void RenderPassBloom::destroyRenderTargets(const int startIndex)
27 {
28 for (int i = std::max(startIndex, 0); i < static_cast<int>(_renderTargets.size()); ++i) {
29 _renderTargets[i].reset();
30 }
31 if (startIndex <= 0) {
32 _renderTargets.clear();
33 _ownedTextures.clear();
34 } else if (startIndex < static_cast<int>(_renderTargets.size())) {
35 _renderTargets.erase(_renderTargets.begin() + startIndex, _renderTargets.end());
36 if (startIndex < static_cast<int>(_ownedTextures.size())) {
37 _ownedTextures.erase(_ownedTextures.begin() + startIndex, _ownedTextures.end());
38 }
39 }
40 }
41
42 void RenderPassBloom::destroyRenderPasses()
43 {
45 }
46
47 std::shared_ptr<RenderTarget> RenderPassBloom::createRenderTarget(const int index,
48 std::shared_ptr<Texture>& outTexture) const
49 {
50 TextureOptions textureOptions;
51 textureOptions.name = "BloomTexture" + std::to_string(index);
52 textureOptions.width = 1;
53 textureOptions.height = 1;
54 textureOptions.format = _textureFormat;
55 textureOptions.mipmaps = false;
56 textureOptions.minFilter = FilterMode::FILTER_LINEAR;
57 textureOptions.magFilter = FilterMode::FILTER_LINEAR;
58 outTexture = std::make_shared<Texture>(device().get(), textureOptions);
59 outTexture->setAddressU(AddressMode::ADDRESS_CLAMP_TO_EDGE);
60 outTexture->setAddressV(AddressMode::ADDRESS_CLAMP_TO_EDGE);
61
62 RenderTargetOptions options;
63 options.graphicsDevice = device().get();
64 options.colorBuffer = outTexture.get();
65 options.depth = false;
66 options.stencil = false;
67 options.name = textureOptions.name;
68 return device()->createRenderTarget(options);
69 }
70
71 void RenderPassBloom::createRenderTargets(const int count)
72 {
73 _renderTargets.clear();
74 _ownedTextures.clear();
75 for (int i = 0; i < count; ++i) {
76 if (i == 0) {
77 _renderTargets.push_back(_bloomRenderTarget);
78 _ownedTextures.push_back(_bloomColorTexture);
79 } else {
80 std::shared_ptr<Texture> tex;
81 _renderTargets.push_back(createRenderTarget(i, tex));
82 _ownedTextures.push_back(std::move(tex));
83 }
84 }
85 }
86
87 int RenderPassBloom::calcMipLevels(const uint32_t width, const uint32_t height, const int minSize) const
88 {
89 const int minimum = std::max(static_cast<int>(std::min(width, height)), 1);
90 const float levels = std::log2(static_cast<float>(minimum)) - std::log2(static_cast<float>(std::max(minSize, 1)));
91 return std::max(static_cast<int>(std::floor(levels)), 1);
92 }
93
94 void RenderPassBloom::createRenderPasses(const int numPasses)
95 {
96 Texture* passSourceTexture = _sourceTexture;
97 for (int i = 0; i < numPasses; ++i) {
98 auto pass = std::make_shared<RenderPassDownsample>(device(), passSourceTexture);
99 auto options = std::make_shared<RenderPassOptions>();
100 options->resizeSource = std::shared_ptr<Texture>(passSourceTexture, [](Texture*) {});
101 options->scaleX = 0.5f;
102 options->scaleY = 0.5f;
103 pass->init(_renderTargets[i], options);
104 const Color clearBlack(0.0f, 0.0f, 0.0f, 1.0f);
105 pass->setClearColor(&clearBlack);
106 addBeforePass(pass);
107 passSourceTexture = _renderTargets[i]->colorBuffer();
108 }
109
110 passSourceTexture = _renderTargets[numPasses - 1]->colorBuffer();
111 for (int i = numPasses - 2; i >= 0; --i) {
112 auto pass = std::make_shared<RenderPassUpsample>(device(), passSourceTexture);
113 pass->init(_renderTargets[i]);
114 addBeforePass(pass);
115 passSourceTexture = _renderTargets[i]->colorBuffer();
116 }
117 }
118
120 {
121 if (!_renderTargets.empty() && _renderTargets[0]) {
122 _renderTargets[0]->resize(1, 1);
123 }
124
125 destroyRenderPasses();
126 destroyRenderTargets(1);
127 }
128
130 {
132 auto mutableThis = const_cast<RenderPassBloom*>(this);
133 if (!mutableThis->_sourceTexture) {
134 return;
135 }
136
137 const int maxNumPasses = calcMipLevels(mutableThis->_sourceTexture->width(), mutableThis->_sourceTexture->height(), 1);
138 const int numPasses = std::clamp(maxNumPasses, 1, mutableThis->_blurLevel);
139 if (static_cast<int>(mutableThis->_renderTargets.size()) != numPasses) {
140 mutableThis->destroyRenderPasses();
141 mutableThis->destroyRenderTargets(1);
142 mutableThis->createRenderTargets(numPasses);
143 mutableThis->createRenderPasses(numPasses);
144 }
145 }
146}
RenderPassBloom(const std::shared_ptr< GraphicsDevice > &device, Texture *sourceTexture, PixelFormat format)
virtual void frameUpdate() const
std::shared_ptr< GraphicsDevice > device() const
Definition renderPass.h:124
RenderPass(const std::shared_ptr< GraphicsDevice > &device)
Definition renderPass.h:66
void addBeforePass(const std::shared_ptr< RenderPass > &renderPass)
GPU texture resource supporting 2D, cubemap, volume, and array formats with mipmap management.
Definition texture.h:57