/* Minetest Copyright (C) 2022 x2048, Dmitry Kostenko 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 #include TextureBuffer::~TextureBuffer() { 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_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); 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); 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::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 for (u32 i = 0; i < m_definitions.size(); i++) { video::ITexture **ptr = &m_textures[i]; ensureTexture(ptr, m_definitions[i], context); m_definitions[i].dirty = false; } RenderSource::reset(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_map({_texture_index}) {} TextureBufferOutput::TextureBufferOutput(TextureBuffer *_buffer, const std::vector &_texture_map) : buffer(_buffer), texture_map(_texture_map) {} TextureBufferOutput::TextureBufferOutput(TextureBuffer *_buffer, const std::vector &_texture_map, u8 _depth_stencil) : buffer(_buffer), texture_map(_texture_map), depth_stencil(_depth_stencil) {} TextureBufferOutput::~TextureBufferOutput() { if (render_target && driver) driver->removeRenderTarget(render_target); } void TextureBufferOutput::activate(PipelineContext &context) { if (!driver) driver = context.device->getVideoDriver(); if (!render_target) render_target = driver->addRenderTarget(); core::array textures; core::dimension2du size(0, 0); for (size_t i = 0; i < texture_map.size(); i++) { video::ITexture *texture = buffer->getTexture(texture_map[i]); textures.push_back(texture); if (texture && size.Width == 0) size = texture->getSize(); } video::ITexture *depth_texture = nullptr; if (depth_stencil != NO_DEPTH_TEXTURE) depth_texture = buffer->getTexture(depth_stencil); render_target->setTexture(textures, depth_texture); driver->setRenderTargetEx(render_target, m_clear ? video::ECBF_ALL : video::ECBF_NONE, context.clear_color); driver->OnResize(size); 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); }