diff options
author | x2048 <codeforsmile@gmail.com> | 2022-09-06 08:25:18 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-09-06 08:25:18 +0200 |
commit | ff6dcfea82974df6db5a557e31aaddb6bdb7a71f (patch) | |
tree | 18bafaedcfdff36a0c0719653b99370b7434b344 /src/client/render/pipeline.cpp | |
parent | 464043b8abdbd936640757604ecb21662592043b (diff) | |
download | minetest-ff6dcfea82974df6db5a557e31aaddb6bdb7a71f.tar.xz |
Implement rendering pipeline and post-processing (#12465)
Co-authored-by: Lars Mueller <appgurulars@gmx.de>
Co-authored-by: sfan5 <sfan5@live.de>
Co-authored-by: lhofhansl <lhofhansl@yahoo.com>
Diffstat (limited to 'src/client/render/pipeline.cpp')
-rw-r--r-- | src/client/render/pipeline.cpp | 277 |
1 files changed, 277 insertions, 0 deletions
diff --git a/src/client/render/pipeline.cpp b/src/client/render/pipeline.cpp new file mode 100644 index 000000000..baf215d8e --- /dev/null +++ b/src/client/render/pipeline.cpp @@ -0,0 +1,277 @@ +/* +Minetest +Copyright (C) 2022 x2048, Dmitry Kostenko <codeforsmile@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "pipeline.h" +#include "client/client.h" +#include "client/hud.h" + +#include <vector> +#include <memory> + + +TextureBuffer::~TextureBuffer() +{ + if (m_render_target) + m_driver->removeRenderTarget(m_render_target); + m_render_target = nullptr; + for (u32 index = 0; index < m_textures.size(); index++) + m_driver->removeTexture(m_textures[index]); + m_textures.clear(); +} + +video::ITexture *TextureBuffer::getTexture(u8 index) +{ + if (index == m_depth_texture_index) + return m_depth_texture; + if (index >= m_textures.size()) + return nullptr; + return m_textures[index]; +} + + +void TextureBuffer::setTexture(u8 index, core::dimension2du size, const std::string &name, video::ECOLOR_FORMAT format) +{ + assert(index != NO_DEPTH_TEXTURE); + + if (m_definitions.size() <= index) + m_definitions.resize(index + 1); + + if (m_depth_texture_index == index) + m_depth_texture_index = NO_DEPTH_TEXTURE; + + auto &definition = m_definitions[index]; + definition.valid = true; + definition.dirty = true; + definition.fixed_size = true; + definition.size = size; + definition.name = name; + definition.format = format; +} + +void TextureBuffer::setTexture(u8 index, v2f scale_factor, const std::string &name, video::ECOLOR_FORMAT format) +{ + assert(index != NO_DEPTH_TEXTURE); + + if (m_definitions.size() <= index) + m_definitions.resize(index + 1); + + if (m_depth_texture_index == index) + m_depth_texture_index = NO_DEPTH_TEXTURE; + + auto &definition = m_definitions[index]; + definition.valid = true; + definition.dirty = true; + definition.fixed_size = false; + definition.scale_factor = scale_factor; + definition.name = name; + definition.format = format; +} + +void TextureBuffer::setDepthTexture(u8 index, core::dimension2du size, const std::string &name, video::ECOLOR_FORMAT format) +{ + assert(index != NO_DEPTH_TEXTURE); + setTexture(index, size, name, format); + m_depth_texture_index = index; +} + +void TextureBuffer::setDepthTexture(u8 index, v2f scale_factor, const std::string &name, video::ECOLOR_FORMAT format) +{ + assert(index != NO_DEPTH_TEXTURE); + setTexture(index, scale_factor, name, format); + m_depth_texture_index = index; +} + +void TextureBuffer::reset(PipelineContext &context) +{ + if (!m_driver) + m_driver = context.device->getVideoDriver(); + + // remove extra textures + if (m_textures.size() > m_definitions.size()) { + for (unsigned i = m_definitions.size(); i < m_textures.size(); i++) + if (m_textures[i]) + m_driver->removeTexture(m_textures[i]); + + m_textures.set_used(m_definitions.size()); + } + + // add placeholders for new definitions + while (m_textures.size() < m_definitions.size()) + m_textures.push_back(nullptr); + + // change textures to match definitions + bool modified = false; + for (u32 i = 0; i < m_definitions.size(); i++) { + video::ITexture **ptr = &m_textures[i]; + if (i == m_depth_texture_index) { + if (*ptr) { + m_driver->removeTexture(*ptr); + *ptr = nullptr; + } + ptr = &m_depth_texture; + } + + if (ensureTexture(ptr, m_definitions[i], context)) + modified = true; + m_definitions[i].dirty = false; + } + + // make sude depth texture is removed and reset + if (m_depth_texture_index == NO_DEPTH_TEXTURE && m_depth_texture) { + m_driver->removeTexture(m_depth_texture); + m_depth_texture = nullptr; + } + + if (!m_render_target) + m_render_target = m_driver->addRenderTarget(); + + if (modified) + m_render_target->setTexture(m_textures, m_depth_texture); + + RenderTarget::reset(context); +} + +void TextureBuffer::activate(PipelineContext &context) +{ + m_driver->setRenderTargetEx(m_render_target, m_clear ? video::ECBF_DEPTH | video::ECBF_COLOR : 0, context.clear_color); + RenderTarget::activate(context); +} + +bool TextureBuffer::ensureTexture(video::ITexture **texture, const TextureDefinition& definition, PipelineContext &context) +{ + bool modify; + core::dimension2du size; + if (definition.valid) { + if (definition.fixed_size) + size = definition.size; + else + size = core::dimension2du( + (u32)(context.target_size.X * definition.scale_factor.X), + (u32)(context.target_size.Y * definition.scale_factor.Y)); + + modify = definition.dirty || (*texture == nullptr) || (*texture)->getSize() != size; + } + else { + modify = (*texture != nullptr); + } + + if (!modify) + return false; + + if (*texture) + m_driver->removeTexture(*texture); + + if (definition.valid) + *texture = m_driver->addRenderTargetTexture(size, definition.name.c_str(), definition.format); + else + *texture = nullptr; + + return true; +} + +TextureBufferOutput::TextureBufferOutput(TextureBuffer *_buffer, u8 _texture_index) + : buffer(_buffer), texture_index(_texture_index) +{} + +void TextureBufferOutput::activate(PipelineContext &context) +{ + auto texture = buffer->getTexture(texture_index); + auto driver = context.device->getVideoDriver(); + driver->setRenderTarget(texture, m_clear, m_clear, context.clear_color); + driver->OnResize(texture->getSize()); + + RenderTarget::activate(context); +} + +u8 DynamicSource::getTextureCount() +{ + assert(isConfigured()); + return upstream->getTextureCount(); +} + +video::ITexture *DynamicSource::getTexture(u8 index) +{ + assert(isConfigured()); + return upstream->getTexture(index); +} + +void ScreenTarget::activate(PipelineContext &context) +{ + auto driver = context.device->getVideoDriver(); + driver->setRenderTarget(nullptr, m_clear, m_clear, context.clear_color); + driver->OnResize(size); + RenderTarget::activate(context); +} + +void DynamicTarget::activate(PipelineContext &context) +{ + if (!isConfigured()) + throw std::logic_error("Dynamic render target is not configured before activation."); + upstream->activate(context); +} + +void ScreenTarget::reset(PipelineContext &context) +{ + RenderTarget::reset(context); + size = context.device->getVideoDriver()->getScreenSize(); +} + +SetRenderTargetStep::SetRenderTargetStep(RenderStep *_step, RenderTarget *_target) + : step(_step), target(_target) +{ +} + +void SetRenderTargetStep::run(PipelineContext &context) +{ + step->setRenderTarget(target); +} + +RenderSource *RenderPipeline::getInput() +{ + return &m_input; +} + +RenderTarget *RenderPipeline::getOutput() +{ + return &m_output; +} + +void RenderPipeline::run(PipelineContext &context) +{ + v2u32 original_size = context.target_size; + context.target_size = v2u32(original_size.X * scale.X, original_size.Y * scale.Y); + + for (auto &object : m_objects) + object->reset(context); + + for (auto &step: m_pipeline) + step->run(context); + + context.target_size = original_size; +} + +void RenderPipeline::setRenderSource(RenderSource *source) +{ + m_input.setRenderSource(source); +} + +void RenderPipeline::setRenderTarget(RenderTarget *target) +{ + m_output.setRenderTarget(target); +} |