]> git.lizzy.rs Git - minetest.git/blob - src/client/render/pipeline.cpp
Clear exposure compensation state textures on creation (#13151)
[minetest.git] / src / client / render / pipeline.cpp
1 /*
2 Minetest
3 Copyright (C) 2022 x2048, Dmitry Kostenko <codeforsmile@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "pipeline.h"
21 #include "client/client.h"
22 #include "client/hud.h"
23
24 #include <vector>
25 #include <memory>
26
27
28 TextureBuffer::~TextureBuffer()
29 {
30         for (u32 index = 0; index < m_textures.size(); index++)
31                 m_driver->removeTexture(m_textures[index]);
32         m_textures.clear();
33 }
34
35 video::ITexture *TextureBuffer::getTexture(u8 index)
36 {
37         if (index >= m_textures.size())
38                 return nullptr;
39         return m_textures[index];
40 }
41
42
43 void TextureBuffer::setTexture(u8 index, core::dimension2du size, const std::string &name, video::ECOLOR_FORMAT format, bool clear)
44 {
45         assert(index != NO_DEPTH_TEXTURE);
46
47         if (m_definitions.size() <= index)
48                 m_definitions.resize(index + 1);
49
50         auto &definition = m_definitions[index];
51         definition.valid = true;
52         definition.dirty = true;
53         definition.fixed_size = true;
54         definition.size = size;
55         definition.name = name;
56         definition.format = format;
57         definition.clear = clear;
58 }
59
60 void TextureBuffer::setTexture(u8 index, v2f scale_factor, const std::string &name, video::ECOLOR_FORMAT format, bool clear)
61 {
62         assert(index != NO_DEPTH_TEXTURE);
63
64         if (m_definitions.size() <= index)
65                 m_definitions.resize(index + 1);
66         
67         auto &definition = m_definitions[index];
68         definition.valid = true;
69         definition.dirty = true;
70         definition.fixed_size = false;
71         definition.scale_factor = scale_factor;
72         definition.name = name;
73         definition.format = format;
74         definition.clear = clear;
75 }
76
77 void TextureBuffer::reset(PipelineContext &context)
78 {
79         if (!m_driver)
80                 m_driver = context.device->getVideoDriver();
81
82         // remove extra textures
83         if (m_textures.size() > m_definitions.size()) {
84                 for (unsigned i = m_definitions.size(); i < m_textures.size(); i++)
85                         if (m_textures[i])
86                                 m_driver->removeTexture(m_textures[i]);
87
88                 m_textures.set_used(m_definitions.size());
89         }
90
91         // add placeholders for new definitions
92         while (m_textures.size() < m_definitions.size())
93                 m_textures.push_back(nullptr);
94
95         // change textures to match definitions
96         for (u32 i = 0; i < m_definitions.size(); i++) {
97                 video::ITexture **ptr = &m_textures[i];
98
99                 ensureTexture(ptr, m_definitions[i], context);
100                 m_definitions[i].dirty = false;
101         }
102         
103         RenderSource::reset(context);
104 }
105
106 void TextureBuffer::swapTextures(u8 texture_a, u8 texture_b)
107 {
108         assert(m_definitions[texture_a].valid && m_definitions[texture_b].valid);
109
110         video::ITexture *temp = m_textures[texture_a];
111         m_textures[texture_a] = m_textures[texture_b];
112         m_textures[texture_b] = temp;
113 }
114
115
116 bool TextureBuffer::ensureTexture(video::ITexture **texture, const TextureDefinition& definition, PipelineContext &context)
117 {
118         bool modify;
119         core::dimension2du size;
120         if (definition.valid) {
121                 if (definition.fixed_size)
122                         size = definition.size;
123                 else
124                         size = core::dimension2du(
125                                         (u32)(context.target_size.X * definition.scale_factor.X),
126                                         (u32)(context.target_size.Y * definition.scale_factor.Y));
127                 
128                 modify = definition.dirty || (*texture == nullptr) || (*texture)->getSize() != size;
129         }
130         else {
131                 modify = (*texture != nullptr);
132         }
133
134         if (!modify)
135                 return false;
136
137         if (*texture)
138                 m_driver->removeTexture(*texture);
139
140         if (definition.valid) {
141                 if (definition.clear) {
142                         video::IImage *image = m_driver->createImage(definition.format, size);
143                         image->fill(0u);
144                         *texture = m_driver->addTexture(definition.name.c_str(), image);
145                         image->drop();
146                 }
147                 else {
148                         *texture = m_driver->addRenderTargetTexture(size, definition.name.c_str(), definition.format);
149                 }
150         }
151         else {
152                 *texture = nullptr;
153         }
154
155         return true;
156 }
157
158 TextureBufferOutput::TextureBufferOutput(TextureBuffer *_buffer, u8 _texture_index)
159         : buffer(_buffer), texture_map({_texture_index})
160 {}
161
162 TextureBufferOutput::TextureBufferOutput(TextureBuffer *_buffer, const std::vector<u8> &_texture_map)
163         : buffer(_buffer), texture_map(_texture_map)
164 {}
165
166 TextureBufferOutput::TextureBufferOutput(TextureBuffer *_buffer, const std::vector<u8> &_texture_map, u8 _depth_stencil)
167         : buffer(_buffer), texture_map(_texture_map), depth_stencil(_depth_stencil)
168 {}
169
170 TextureBufferOutput::~TextureBufferOutput()
171 {
172         if (render_target && driver)
173                 driver->removeRenderTarget(render_target);
174 }
175
176 void TextureBufferOutput::activate(PipelineContext &context)
177 {
178         if (!driver)
179                 driver = context.device->getVideoDriver();
180
181         if (!render_target)
182                 render_target = driver->addRenderTarget();
183
184         core::array<video::ITexture *> textures;
185         core::dimension2du size(0, 0);
186         for (size_t i = 0; i < texture_map.size(); i++) {
187                 video::ITexture *texture = buffer->getTexture(texture_map[i]);
188                 textures.push_back(texture);
189                 if (texture && size.Width == 0)
190                         size = texture->getSize();
191         }
192
193         // Use legacy call when there's single texture without depth texture
194         // This binds default depth buffer to the FBO
195         if (textures.size() == 1 && depth_stencil == NO_DEPTH_TEXTURE) {
196                 driver->setRenderTarget(textures[0], m_clear, m_clear, context.clear_color);
197                 return;
198         }
199
200         video::ITexture *depth_texture = nullptr;
201         if (depth_stencil != NO_DEPTH_TEXTURE)
202                 depth_texture = buffer->getTexture(depth_stencil);
203
204         render_target->setTexture(textures, depth_texture);
205
206         driver->setRenderTargetEx(render_target, m_clear ? video::ECBF_ALL : video::ECBF_NONE, context.clear_color);
207         driver->OnResize(size);
208
209         RenderTarget::activate(context);
210 }
211
212 u8 DynamicSource::getTextureCount()
213 {
214         assert(isConfigured());
215         return upstream->getTextureCount();
216 }
217
218 video::ITexture *DynamicSource::getTexture(u8 index)
219 {
220         assert(isConfigured());
221         return upstream->getTexture(index);
222 }
223
224 void ScreenTarget::activate(PipelineContext &context)
225 {
226         auto driver = context.device->getVideoDriver();
227         driver->setRenderTarget(nullptr, m_clear, m_clear, context.clear_color);
228         driver->OnResize(size);
229         RenderTarget::activate(context);
230 }
231
232 void DynamicTarget::activate(PipelineContext &context)
233 {
234         if (!isConfigured())
235                 throw std::logic_error("Dynamic render target is not configured before activation.");
236         upstream->activate(context);
237 }
238
239 void ScreenTarget::reset(PipelineContext &context)
240 {
241         RenderTarget::reset(context);
242         size = context.device->getVideoDriver()->getScreenSize();
243 }
244
245 SetRenderTargetStep::SetRenderTargetStep(RenderStep *_step, RenderTarget *_target)
246         : step(_step), target(_target)
247 {
248 }
249
250 void SetRenderTargetStep::run(PipelineContext &context)
251 {
252         step->setRenderTarget(target);
253 }
254
255 SwapTexturesStep::SwapTexturesStep(TextureBuffer *_buffer, u8 _texture_a, u8 _texture_b)
256                 : buffer(_buffer), texture_a(_texture_a), texture_b(_texture_b)
257 {
258 }
259
260 void SwapTexturesStep::run(PipelineContext &context)
261 {
262         buffer->swapTextures(texture_a, texture_b);
263 }
264
265 RenderSource *RenderPipeline::getInput()
266 {
267         return &m_input;
268 }
269
270 RenderTarget *RenderPipeline::getOutput()
271 {
272         return &m_output;
273 }
274
275 void RenderPipeline::run(PipelineContext &context)
276 {
277         v2u32 original_size = context.target_size;
278         context.target_size = v2u32(original_size.X * scale.X, original_size.Y * scale.Y);
279
280         for (auto &object : m_objects)
281                 object->reset(context);
282
283         for (auto &step: m_pipeline)
284                 step->run(context);
285         
286         context.target_size = original_size;
287 }
288
289 void RenderPipeline::setRenderSource(RenderSource *source)
290 {
291         m_input.setRenderSource(source);
292 }
293
294 void RenderPipeline::setRenderTarget(RenderTarget *target)
295 {
296         m_output.setRenderTarget(target);
297 }