]> git.lizzy.rs Git - minetest.git/blob - src/client/render/pipeline.cpp
13898f8a41f078bbe7beb97db2404218fe7e6a36
[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)
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 }
58
59 void TextureBuffer::setTexture(u8 index, v2f scale_factor, const std::string &name, video::ECOLOR_FORMAT format)
60 {
61         assert(index != NO_DEPTH_TEXTURE);
62
63         if (m_definitions.size() <= index)
64                 m_definitions.resize(index + 1);
65         
66         auto &definition = m_definitions[index];
67         definition.valid = true;
68         definition.dirty = true;
69         definition.fixed_size = false;
70         definition.scale_factor = scale_factor;
71         definition.name = name;
72         definition.format = format;
73 }
74
75 void TextureBuffer::reset(PipelineContext &context)
76 {
77         if (!m_driver)
78                 m_driver = context.device->getVideoDriver();
79
80         // remove extra textures
81         if (m_textures.size() > m_definitions.size()) {
82                 for (unsigned i = m_definitions.size(); i < m_textures.size(); i++)
83                         if (m_textures[i])
84                                 m_driver->removeTexture(m_textures[i]);
85
86                 m_textures.set_used(m_definitions.size());
87         }
88
89         // add placeholders for new definitions
90         while (m_textures.size() < m_definitions.size())
91                 m_textures.push_back(nullptr);
92
93         // change textures to match definitions
94         for (u32 i = 0; i < m_definitions.size(); i++) {
95                 video::ITexture **ptr = &m_textures[i];
96
97                 ensureTexture(ptr, m_definitions[i], context);
98                 m_definitions[i].dirty = false;
99         }
100         
101         RenderSource::reset(context);
102 }
103
104 bool TextureBuffer::ensureTexture(video::ITexture **texture, const TextureDefinition& definition, PipelineContext &context)
105 {
106         bool modify;
107         core::dimension2du size;
108         if (definition.valid) {
109                 if (definition.fixed_size)
110                         size = definition.size;
111                 else
112                         size = core::dimension2du(
113                                         (u32)(context.target_size.X * definition.scale_factor.X),
114                                         (u32)(context.target_size.Y * definition.scale_factor.Y));
115                 
116                 modify = definition.dirty || (*texture == nullptr) || (*texture)->getSize() != size;
117         }
118         else {
119                 modify = (*texture != nullptr);
120         }
121
122         if (!modify)
123                 return false;
124
125         if (*texture)
126                 m_driver->removeTexture(*texture);
127
128         if (definition.valid)
129                 *texture = m_driver->addRenderTargetTexture(size, definition.name.c_str(), definition.format);
130         else
131                 *texture = nullptr;
132
133         return true;
134 }
135
136 TextureBufferOutput::TextureBufferOutput(TextureBuffer *_buffer, u8 _texture_index)
137         : buffer(_buffer), texture_map({_texture_index})
138 {}
139
140 TextureBufferOutput::TextureBufferOutput(TextureBuffer *_buffer, const std::vector<u8> &_texture_map)
141         : buffer(_buffer), texture_map(_texture_map)
142 {}
143
144 TextureBufferOutput::TextureBufferOutput(TextureBuffer *_buffer, const std::vector<u8> &_texture_map, u8 _depth_stencil)
145         : buffer(_buffer), texture_map(_texture_map), depth_stencil(_depth_stencil)
146 {}
147
148 TextureBufferOutput::~TextureBufferOutput()
149 {
150         if (render_target && driver)
151                 driver->removeRenderTarget(render_target);
152 }
153
154 void TextureBufferOutput::activate(PipelineContext &context)
155 {
156         if (!driver)
157                 driver = context.device->getVideoDriver();
158
159         if (!render_target)
160                 render_target = driver->addRenderTarget();
161
162         core::array<video::ITexture *> textures;
163         core::dimension2du size(0, 0);
164         for (size_t i = 0; i < texture_map.size(); i++) {
165                 video::ITexture *texture = buffer->getTexture(texture_map[i]);
166                 textures.push_back(texture);
167                 if (texture && size.Width == 0)
168                         size = texture->getSize();
169         }
170
171         // Use legacy call when there's single texture without depth texture
172         // This binds default depth buffer to the FBO
173         if (textures.size() == 1 && depth_stencil == NO_DEPTH_TEXTURE) {
174                 driver->setRenderTarget(textures[0], m_clear, m_clear, context.clear_color);
175                 return;
176         }
177
178         video::ITexture *depth_texture = nullptr;
179         if (depth_stencil != NO_DEPTH_TEXTURE)
180                 depth_texture = buffer->getTexture(depth_stencil);
181
182         render_target->setTexture(textures, depth_texture);
183
184         driver->setRenderTargetEx(render_target, m_clear ? video::ECBF_ALL : video::ECBF_NONE, context.clear_color);
185         driver->OnResize(size);
186
187         RenderTarget::activate(context);
188 }
189
190 u8 DynamicSource::getTextureCount()
191 {
192         assert(isConfigured());
193         return upstream->getTextureCount();
194 }
195
196 video::ITexture *DynamicSource::getTexture(u8 index)
197 {
198         assert(isConfigured());
199         return upstream->getTexture(index);
200 }
201
202 void ScreenTarget::activate(PipelineContext &context)
203 {
204         auto driver = context.device->getVideoDriver();
205         driver->setRenderTarget(nullptr, m_clear, m_clear, context.clear_color);
206         driver->OnResize(size);
207         RenderTarget::activate(context);
208 }
209
210 void DynamicTarget::activate(PipelineContext &context)
211 {
212         if (!isConfigured())
213                 throw std::logic_error("Dynamic render target is not configured before activation.");
214         upstream->activate(context);
215 }
216
217 void ScreenTarget::reset(PipelineContext &context)
218 {
219         RenderTarget::reset(context);
220         size = context.device->getVideoDriver()->getScreenSize();
221 }
222
223 SetRenderTargetStep::SetRenderTargetStep(RenderStep *_step, RenderTarget *_target)
224         : step(_step), target(_target)
225 {
226 }
227
228 void SetRenderTargetStep::run(PipelineContext &context)
229 {
230         step->setRenderTarget(target);
231 }
232
233 RenderSource *RenderPipeline::getInput()
234 {
235         return &m_input;
236 }
237
238 RenderTarget *RenderPipeline::getOutput()
239 {
240         return &m_output;
241 }
242
243 void RenderPipeline::run(PipelineContext &context)
244 {
245         v2u32 original_size = context.target_size;
246         context.target_size = v2u32(original_size.X * scale.X, original_size.Y * scale.Y);
247
248         for (auto &object : m_objects)
249                 object->reset(context);
250
251         for (auto &step: m_pipeline)
252                 step->run(context);
253         
254         context.target_size = original_size;
255 }
256
257 void RenderPipeline::setRenderSource(RenderSource *source)
258 {
259         m_input.setRenderSource(source);
260 }
261
262 void RenderPipeline::setRenderTarget(RenderTarget *target)
263 {
264         m_output.setRenderTarget(target);
265 }