|
|
@@ -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); |
|
|
} |