diff --git a/source/gloperate/CMakeLists.txt b/source/gloperate/CMakeLists.txt index dd60faec..2aa9cb64 100644 --- a/source/gloperate/CMakeLists.txt +++ b/source/gloperate/CMakeLists.txt @@ -76,6 +76,7 @@ set(headers ${include_path}/pipeline/Stage.inl ${include_path}/pipeline/Pipeline.h ${include_path}/pipeline/AbstractSlot.h + ${include_path}/pipeline/AbstractSlot.inl ${include_path}/pipeline/Slot.h ${include_path}/pipeline/Slot.inl ${include_path}/pipeline/Input.h diff --git a/source/gloperate/include/gloperate/pipeline/AbstractSlot.h b/source/gloperate/include/gloperate/pipeline/AbstractSlot.h index 2dc03e4d..8dee5181 100644 --- a/source/gloperate/include/gloperate/pipeline/AbstractSlot.h +++ b/source/gloperate/include/gloperate/pipeline/AbstractSlot.h @@ -268,6 +268,19 @@ class GLOPERATE_API AbstractSlot : public cppexpose::AbstractProperty */ virtual void onValueInvalidated() = 0; + /** + * @brief + * Check if the Slot is a slot of any type given by the template argument list + * + * @tparam Types + * The variadic template parameter list of all types to check + * + * @return + * 'true' if the slot is of any type given, else 'false' + */ + template + bool isOfAnyType() const; + protected: /** @@ -293,3 +306,6 @@ class GLOPERATE_API AbstractSlot : public cppexpose::AbstractProperty } // namespace cppexpose + + +#include diff --git a/source/gloperate/include/gloperate/pipeline/AbstractSlot.inl b/source/gloperate/include/gloperate/pipeline/AbstractSlot.inl new file mode 100644 index 00000000..ec93c6c9 --- /dev/null +++ b/source/gloperate/include/gloperate/pipeline/AbstractSlot.inl @@ -0,0 +1,68 @@ + +#pragma once + + +#include + + +namespace gloperate +{ + + +template +class Slot; + + +} // namespace gloperate + + +namespace +{ + + +template +struct isOfAnyTypeHelper; + +template +struct isOfAnyTypeHelper +{ + static bool value(const gloperate::AbstractSlot * slot) + { + return isOfAnyTypeHelper::value(slot) || isOfAnyTypeHelper::value(slot); + } +}; + +template +struct isOfAnyTypeHelper +{ + static bool value(const gloperate::AbstractSlot * slot) + { + return dynamic_cast *>(slot) != nullptr; + } +}; + +template <> +struct isOfAnyTypeHelper<> +{ + static bool value(const gloperate::AbstractSlot * /*slot*/) + { + return false; + } +}; + + +} // namespace + + +namespace gloperate +{ + + +template +bool AbstractSlot::isOfAnyType() const +{ + return isOfAnyTypeHelper::value(this); +} + + +} // namespace gloperate diff --git a/source/gloperate/include/gloperate/stages/base/ClearStage.h b/source/gloperate/include/gloperate/stages/base/ClearStage.h index d31c5b5b..284add1d 100644 --- a/source/gloperate/include/gloperate/stages/base/ClearStage.h +++ b/source/gloperate/include/gloperate/stages/base/ClearStage.h @@ -2,6 +2,8 @@ #pragma once +#include + #include #include @@ -22,6 +24,10 @@ namespace gloperate { +class AbstractClearInput; +class ClearValueAdder; + + /** * @brief * Stage that clears the screen with a background color @@ -31,6 +37,9 @@ namespace gloperate */ class GLOPERATE_API ClearStage : public Stage { + friend class ClearValueAdder; + + public: CPPEXPOSE_DECLARE_COMPONENT( ClearStage, gloperate::Stage @@ -42,13 +51,19 @@ class GLOPERATE_API ClearStage : public Stage , "v1.0.0" ) + /** + * @brief + * The list of supported types for framebuffer clearing + */ + using SupportedClearValueTypes = cppassist::TypeList, Color>; + public: // Interfaces RenderInterface renderInterface; ///< Renderinterface to manage render targets inputs and outputs // Inputs - Input clear; ///< Flag if buffers should get cleared + Input clear; ///< Flag if buffers should get cleared public: @@ -76,12 +91,16 @@ class GLOPERATE_API ClearStage : public Stage virtual void onContextInit(AbstractGLContext * content) override; virtual void onContextDeinit(AbstractGLContext * content) override; + /** + * @brief + * Reprocess inputs and build up input helper structure for easy clear value and render target association + */ + void reprocessInputs(); + protected: - std::vector *> m_colorValueInputs; ///< Color clear values - std::vector *> m_depthValueInputs; ///< Depth clear values - std::vector *> m_stencilValueInputs; ///< Stencil clear values - std::vector> *> m_depthStencilValueInputs; ///< Depth-stencil clear values + bool m_reprocessInputs; ///< Recreate input helper structure upon next process + std::vector> m_clearInputs; ///< Clear values of differing types }; diff --git a/source/gloperate/source/stages/base/ClearStage.cpp b/source/gloperate/source/stages/base/ClearStage.cpp index b9863198..0ac44856 100644 --- a/source/gloperate/source/stages/base/ClearStage.cpp +++ b/source/gloperate/source/stages/base/ClearStage.cpp @@ -1,6 +1,8 @@ #include +#include + #include #include @@ -13,6 +15,71 @@ #include +namespace +{ + + +/** +* @brief +* Clear buffer with generic clear value +* +* @param[in] fbo +* Framebuffer object +* @param[in] type +* Attachment type +* @param[in] type +* Buffer to clear +* @param[in] value +* Clear value +*/ +template +void clearBuffer(globjects::Framebuffer * fbo, gl::GLenum type, gl::GLint drawBuffer, T value) +{ + fbo->clearBuffer(type, drawBuffer, value); +} + +/** +* @brief +* Clear buffer with color value +* +* @param[in] fbo +* Framebuffer object +* @param[in] type +* Attachment type +* @param[in] type +* Buffer to clear +* @param[in] value +* Clear value +*/ +template <> +void clearBuffer(globjects::Framebuffer * fbo, gl::GLenum type, gl::GLint drawBuffer, gloperate::Color value) +{ + fbo->clearBuffer(type, drawBuffer, value.toVec4()); +} + +/** +* @brief +* Clear buffer with depth and stencil value +* +* @param[in] fbo +* Framebuffer object +* @param[in] type +* Attachment type +* @param[in] type +* Buffer to clear +* @param[in] value +* Clear value +*/ +template <> +void clearBuffer(globjects::Framebuffer * fbo, gl::GLenum type, gl::GLint drawBuffer, std::pair value) +{ + fbo->clearBuffer(type, value.first, value.second, drawBuffer); +} + + +} // namespace + + namespace gloperate { @@ -20,36 +87,109 @@ namespace gloperate CPPEXPOSE_COMPONENT(ClearStage, gloperate::Stage) -ClearStage::ClearStage(Environment * environment, const std::string & name) -: Stage(environment, "ClearStage", name) -, renderInterface(this) -, clear("clear", this, true) +/** +* @brief +* Base class for clear values +*/ +class AbstractClearInput { - inputAdded.connect( [this] (AbstractSlot * connectedInput) { - auto colorValueInput = dynamic_cast *>(connectedInput); - auto depthValueInput = dynamic_cast *>(connectedInput); - auto stencilValueInput = dynamic_cast *>(connectedInput); - auto depthStencilValueInput = dynamic_cast> *>(connectedInput); +public: + AbstractClearInput() + { + } - if (colorValueInput) - { - m_colorValueInputs.push_back(colorValueInput); - } + virtual ~AbstractClearInput() + { + } - if (depthValueInput) - { - m_depthValueInputs.push_back(depthValueInput); - } + virtual void clear(globjects::Framebuffer * fbo, gl::GLint drawBuffer) const = 0; + virtual bool isComplete() const = 0; + virtual const Input * renderTargetInput() const = 0; +}; + + +/** +* @brief +* Representation of a clear value +* +* A clear value corresponds to a render target input and its +* respective clear value input. +*/ +template +class ClearValueInput : public AbstractClearInput +{ +public: + ClearValueInput(const Input * clearValueInput, const Input * renderTargetInput) + : m_renderTargetInput(renderTargetInput) + , m_clearValueInput(clearValueInput) + { + } - if (stencilValueInput) - { - m_stencilValueInputs.push_back(stencilValueInput); - } + virtual void clear(globjects::Framebuffer * fbo, gl::GLint drawBuffer) const override + { + assert(isComplete()); - if (depthStencilValueInput) + clearBuffer(fbo, (**m_renderTargetInput)->attachmentGLType(), (**m_renderTargetInput)->clearBufferDrawBuffer(drawBuffer), m_clearValueInput->value()); + } + + virtual bool isComplete() const override + { + return m_clearValueInput != nullptr && m_renderTargetInput != nullptr; + } + + virtual const Input * renderTargetInput() const override + { + return m_renderTargetInput; + } + +protected: + const Input * m_renderTargetInput; ///< Input that contains the render target + const Input * m_clearValueInput; ///< Input that contains the clear value +}; + + +/** +* @brief +* Helper class to add a clear value of a specific type +*/ +class ClearValueAdder +{ +public: + ClearValueAdder(ClearStage * stage, AbstractSlot * clearValueInput, Input * renderTargetInput) + : m_stage(stage) + , m_clearValueInput(clearValueInput) + , m_renderTargetInput(renderTargetInput) + { + } + + template< typename T> + void operator()() + { + auto clearValueInputT = dynamic_cast *>(m_clearValueInput); + + if (clearValueInputT) { - m_depthStencilValueInputs.push_back(depthStencilValueInput); + m_stage->m_clearInputs.emplace_back(new ClearValueInput(clearValueInputT, m_renderTargetInput)); } + } + +protected: + ClearStage * m_stage; + const AbstractSlot * m_clearValueInput; + const Input * m_renderTargetInput; +}; + + +ClearStage::ClearStage(Environment * environment, const std::string & name) +: Stage(environment, "ClearStage", name) +, renderInterface(this) +, clear("clear", this, true) +, m_reprocessInputs(false) +{ + // Reconfigure clear stage whenever a new input has been added + inputAdded.connect([this] (AbstractSlot *) + { + m_reprocessInputs = true; }); } @@ -59,22 +199,41 @@ ClearStage::~ClearStage() void ClearStage::onContextInit(AbstractGLContext *) { + // Initialize render targets renderInterface.onContextInit(); } void ClearStage::onContextDeinit(AbstractGLContext *) { + // De-initialize render targets renderInterface.onContextDeinit(); } void ClearStage::onProcess() { + // Reconfigure clear stage if scheduled + if (m_reprocessInputs) + { + reprocessInputs(); + + m_reprocessInputs = false; + } + + // Check if clearing is enabled if (*clear) { - if (renderInterface.viewport->z >= 0.0 || renderInterface.viewport->w >= 0.0) { + // Initialize state + bool scissorEnabled = false; + + // Determine if scissor is enabled + if (renderInterface.viewport->z >= 0.0 || renderInterface.viewport->w >= 0.0) + { // Setup OpenGL state gl::glScissor(renderInterface.viewport->x, renderInterface.viewport->y, renderInterface.viewport->z, renderInterface.viewport->w); gl::glEnable(gl::GL_SCISSOR_TEST); + + // Scissor is enabled + scissorEnabled = true; } else { @@ -82,116 +241,89 @@ void ClearStage::onProcess() gl::glDisable(gl::GL_SCISSOR_TEST); } - size_t colorAttachmentIndex = 0; - size_t depthAttachmentIndex = 0; - size_t stencilAttachmentIndex = 0; - size_t depthStencilAttachmentIndex = 0; - std::set clearedDepthStencilTargets; + // Clear all render targets + size_t colorAttachmentIndex = 0; - renderInterface.pairwiseRenderTargetsDo([this, & colorAttachmentIndex](Input * input, Output * output) { - if (!output->isRequired() || !**input) - { - return; - } - - if (m_colorValueInputs.size() <= colorAttachmentIndex) + for (const auto & clearValueInput : m_clearInputs) + { + // Abort if pair of render target and clear value is invalid + if (!clearValueInput->isComplete()) { - return; + break; // All further clear value inputs won't be complete, either } - auto fbo = renderInterface.obtainFBO(colorAttachmentIndex, **input); + // Get render target and obtain FBO for it + AbstractRenderTarget * renderTarget = **clearValueInput->renderTargetInput(); + auto fbo = renderInterface.obtainFBO(colorAttachmentIndex, renderTarget); - const auto attachmentBuffer = (**input)->clearBufferAttachment(); - const auto attachmentDrawBuffer = (**input)->clearBufferDrawBuffer(colorAttachmentIndex); - const auto clearColor = **m_colorValueInputs.at(colorAttachmentIndex); - - auto clearColorF = clearColor.toVec4(); - fbo->clearBuffer(attachmentBuffer, attachmentDrawBuffer, clearColorF); - - ++colorAttachmentIndex; - }); + // Clear render target + clearValueInput->clear(fbo, colorAttachmentIndex); - renderInterface.pairwiseRenderTargetsDo([this, & depthAttachmentIndex, & depthStencilAttachmentIndex, & clearedDepthStencilTargets](Input * input, Output * output) { - if (!output->isRequired() || !**input) + // Count color attachments + if (renderTarget->underlyingAttachmentType() == AttachmentType::Color) { - return; + ++colorAttachmentIndex; } + } - if ((**input)->underlyingAttachmentType() == AttachmentType::Depth) - { - if (m_depthValueInputs.size() <= depthAttachmentIndex) - { - return; - } - - auto fbo = renderInterface.obtainFBO(depthAttachmentIndex, **input); + // Reset OpenGL state + if (scissorEnabled) + { + gl::glDisable(gl::GL_SCISSOR_TEST); + } + } - fbo->clearBuffer(gl::GL_DEPTH, (**input)->clearBufferDrawBuffer(depthAttachmentIndex), **m_depthValueInputs.at(depthAttachmentIndex)); + // Update outputs + renderInterface.updateRenderTargetOutputs(); +} - ++depthAttachmentIndex; - } - else if ((**input)->underlyingAttachmentType() == AttachmentType::DepthStencil) - { - if (m_depthStencilValueInputs.size() <= depthStencilAttachmentIndex) - { - return; - } +void ClearStage::reprocessInputs() +{ + // Reset configuration + m_clearInputs.clear(); - auto fbo = renderInterface.obtainFBO(depthStencilAttachmentIndex, **input); + // Call function on each slot, abort if condition is true + const auto skipUntil = [] (std::vector::iterator & it, std::vector::const_iterator end, std::function callback) + { + do + { + ++it; + } + while (it != end && !callback(*it)); + }; - fbo->clearBuffer(gl::GL_DEPTH_STENCIL, (**m_depthStencilValueInputs.at(depthStencilAttachmentIndex)).first, (**m_depthStencilValueInputs.at(depthStencilAttachmentIndex)).second, (**input)->clearBufferDrawBuffer(depthStencilAttachmentIndex)); + // Iterate over all inputs to match render targets with corresponing clear values + auto clearValueIt = m_inputs.begin(); + auto renderTargetIt = m_inputs.begin(); - ++depthStencilAttachmentIndex; - clearedDepthStencilTargets.insert(**input); - } + while (clearValueIt != m_inputs.end() && renderTargetIt != m_inputs.end()) + { + // Find next input that defines a clear value + skipUntil(clearValueIt, m_inputs.end(), [] (AbstractSlot * input) + { + return input->isOfAnyType>(); }); - renderInterface.pairwiseRenderTargetsDo([this, & stencilAttachmentIndex, & depthStencilAttachmentIndex, & clearedDepthStencilTargets](Input * input, Output * output) { - if (!output->isRequired() || !**input) - { - return; - } - - if ((**input)->underlyingAttachmentType() == AttachmentType::Stencil) - { - if (m_stencilValueInputs.size() <= stencilAttachmentIndex) - { - return; - } - - auto fbo = renderInterface.obtainFBO(stencilAttachmentIndex, **input); - - fbo->clearBuffer(gl::GL_STENCIL, (**input)->clearBufferDrawBuffer(stencilAttachmentIndex), **m_stencilValueInputs.at(stencilAttachmentIndex)); - - ++stencilAttachmentIndex; - } - else if ((**input)->underlyingAttachmentType() == AttachmentType::DepthStencil) - { - if (std::find(clearedDepthStencilTargets.begin(), clearedDepthStencilTargets.end(), **input) != clearedDepthStencilTargets.end()) - { - return; - } - - if (m_depthStencilValueInputs.size() <= depthStencilAttachmentIndex) - { - return; - } - - auto fbo = renderInterface.obtainFBO(depthStencilAttachmentIndex, **input); - - fbo->clearBuffer(gl::GL_DEPTH_STENCIL, (**m_depthStencilValueInputs.at(depthStencilAttachmentIndex)).first, (**m_depthStencilValueInputs.at(depthStencilAttachmentIndex)).second, (**input)->clearBufferDrawBuffer(depthStencilAttachmentIndex)); + // Find next input that defines a render target + skipUntil(renderTargetIt, m_inputs.end(), [] (AbstractSlot * input) + { + return input->isOfAnyType(); + }); - ++depthStencilAttachmentIndex; - } + // Abort if no further match has been found + if (clearValueIt == m_inputs.end() || renderTargetIt == m_inputs.end()) + { + break; + } - }); + // Get render target and clear value inputs + auto renderTargetInput = reinterpret_cast *>(*renderTargetIt); + auto clearValueInput = *clearValueIt; - // Reset OpenGL state - gl::glDisable(gl::GL_SCISSOR_TEST); + // Add clear value + SupportedClearValueTypes::apply(ClearValueAdder(this, clearValueInput, renderTargetInput)); } - - // Update outputs - renderInterface.updateRenderTargetOutputs(); } + } // namespace gloperate