END_TEST;
}
+
+int UtcDaliRendererAddDrawCommands(void)
+{
+ TestApplication application;
+
+ tet_infoline("Test adding draw commands to the renderer");
+
+ TestGlAbstraction &glAbstraction = application.GetGlAbstraction();
+ glAbstraction.EnableEnableDisableCallTrace(true);
+
+ Geometry geometry = CreateQuadGeometry();
+ Shader shader = Shader::New("vertexSrc", "fragmentSrc");
+ Renderer renderer = Renderer::New(geometry, shader);
+
+ renderer.SetProperty( Renderer::Property::BLEND_MODE, Dali::BlendMode::ON );
+ Actor actor = Actor::New();
+ actor.AddRenderer(renderer);
+ actor.SetProperty(Actor::Property::SIZE, Vector2(400.0f, 400.0f));
+ actor.SetProperty(Actor::Property::COLOR, Vector4(1.0f, 0.0f, 1.0f, 1.0f));
+ application.GetScene().Add(actor);
+
+ // Expect delivering a single draw call
+ auto &drawTrace = glAbstraction.GetDrawTrace();
+ drawTrace.Reset();
+ drawTrace.Enable(true);
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_EQUALS( drawTrace.CountMethod("DrawElements"), 1, TEST_LOCATION );
+
+ auto drawCommand1 = DevelRenderer::DrawCommand{};
+ drawCommand1.drawType = DevelRenderer::DrawType::INDEXED;
+ drawCommand1.firstIndex = 0;
+ drawCommand1.elementCount = 2;
+ drawCommand1.queue = DevelRenderer::RENDER_QUEUE_OPAQUE;
+
+ auto drawCommand2 = DevelRenderer::DrawCommand{};
+ drawCommand2.drawType = DevelRenderer::DrawType::INDEXED;
+ drawCommand2.firstIndex = 2;
+ drawCommand2.elementCount = 2;
+ drawCommand2.queue = DevelRenderer::RENDER_QUEUE_TRANSPARENT;
+
+ auto drawCommand3 = DevelRenderer::DrawCommand{};
+ drawCommand3.drawType = DevelRenderer::DrawType::ARRAY;
+ drawCommand3.firstIndex = 2;
+ drawCommand3.elementCount = 2;
+ drawCommand3.queue = DevelRenderer::RENDER_QUEUE_OPAQUE;
+
+ DevelRenderer::AddDrawCommand(renderer, drawCommand1);
+ DevelRenderer::AddDrawCommand(renderer, drawCommand2);
+ DevelRenderer::AddDrawCommand(renderer, drawCommand3);
+
+ drawTrace.Reset();
+ drawTrace.Enable(true);
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_EQUALS(drawTrace.CountMethod("DrawElements"), 3, TEST_LOCATION);
+
+ END_TEST;
+}
\ No newline at end of file
${devel_api_src_dir}/object/handle-devel.cpp
${devel_api_src_dir}/object/csharp-type-registry.cpp
${devel_api_src_dir}/rendering/frame-buffer-devel.cpp
+ ${devel_api_src_dir}/rendering/renderer-devel.cpp
${devel_api_src_dir}/scripting/scripting.cpp
${devel_api_src_dir}/signals/signal-delegate.cpp
${devel_api_src_dir}/threading/conditional-wait.cpp
--- /dev/null
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include <dali/internal/event/rendering/renderer-impl.h>
+
+namespace Dali
+{
+
+namespace DevelRenderer
+{
+
+void AddDrawCommand( Dali::Renderer renderer, const DrawCommand& drawCommand )
+{
+ auto& impl = GetImplementation( renderer );
+ impl.AddDrawCommand( drawCommand );
+}
+
+} // DevelRenderer
+} // Dali
\ No newline at end of file
namespace DevelRenderer
{
+/**
+ * The index of render queue used by the DrawCommand
+ */
+using RenderQueueIndex = uint32_t;
+
+constexpr RenderQueueIndex RENDER_QUEUE_OPAQUE = 0; ///< Queue for opaque elements
+constexpr RenderQueueIndex RENDER_QUEUE_TRANSPARENT = 1; ///<Queue for transparent elements
+constexpr RenderQueueIndex RENDER_QUEUE_MAX = 2;
+
+/**
+ * Enum describing way of rendering the primitives (indexed draw, array draw)
+ */
+enum class DrawType
+{
+ INDEXED,
+ ARRAY,
+};
+
+/**
+ * Draw command can be attached to the Dali::Renderer and override the default
+ * rendering functionality. Renderer may have several DrawCommands attached to it
+ * and the will be executed sequentially in the order of the 'queue' index.
+ */
+struct DrawCommand
+{
+ DrawType drawType; ///< Type of drawing (indexed, array)
+ uint32_t firstIndex ; ///< First index into the geometry array
+ uint32_t elementCount; ///< Number of elements to draw
+ RenderQueueIndex queue; ///< Queue index
+};
+
+/**
+ * @brief Adds a draw command to the renderer
+ * Once the draw command is added, the default Renderer's behaviour is overriden.
+ * @param[in] renderer a valid Renderer object
+ * @param[in] drawCommand Valid DrawCommand to add to the Renderer
+ */
+DALI_CORE_API void AddDrawCommand( Dali::Renderer renderer, const DrawCommand& drawCommand );
+
namespace Property
{
namespace Rendering
{
+
+
/**
* @brief Enumeration for the rendering behavior
*/
return valueSet;
}
+void Renderer::AddDrawCommand( const Dali::DevelRenderer::DrawCommand& command )
+{
+ if(!mDrawCommands.capacity())
+ {
+ mDrawCommands.reserve(8);
+ }
+
+ mDrawCommands.emplace_back( command );
+
+ Dali::Internal::SceneGraph::SetDrawCommandsMessage( GetEventThreadServices(),
+ GetRendererSceneObject(),
+ mDrawCommands.data(),
+ uint32_t(mDrawCommands.size())
+
+ );
+}
+
+
} // namespace Internal
} // namespace Dali
*/
virtual const PropertyInputImpl* GetSceneObjectInputProperty( Property::Index index ) const;
+ /**
+ * @brief Adds a draw command to the Renderer.
+ * DrawCommands override Renderer's default behaviour.
+ *
+ * @param[in] command Valid reference to a DrawCommand objects
+ */
+ void AddDrawCommand( const Dali::DevelRenderer::DrawCommand& command );
+
private: // implementation
/**
DepthTestMode::Type mDepthTestMode:3; ///< Local copy of the depth test mode
DevelRenderer::Rendering::Type mRenderingBehavior:2; ///< The rendering behavior
bool mPremultipledAlphaEnabled:1; ///< Flag indicating whether the Pre-multiplied Alpha Blending is required
+
+ std::vector<Dali::DevelRenderer::DrawCommand> mDrawCommands; ///< list of draw commands
};
} // namespace Internal
for( uint32_t index = 0u; index < count; ++index )
{
const RenderItem& item = renderList.GetItem( index );
+
DALI_PRINT_RENDER_ITEM( item );
// Set up clipping based on both the Renderer and Actor APIs.
SetupDepthBuffer( item, context, autoDepthTestMode, firstDepthBufferUse );
}
- // Render the item.
- item.mRenderer->Render( context, bufferIndex, *item.mNode, item.mModelMatrix, item.mModelViewMatrix,
- viewMatrix, projectionMatrix, item.mSize, !item.mIsOpaque, boundTextures, instruction ); // Added instruction for reflection effect
+ // Depending on whether the renderer has draw commands attached or not the rendering process will
+ // iterate through all the render queues. If there are no draw commands attached, only one
+ // iteration must be done and the default behaviour of the renderer will be executed.
+ // The queues allow to iterate over the same renderer multiple times changing the state of the renderer.
+ // It is similar to the multi-pass rendering.
+ auto const MAX_QUEUE = item.mRenderer->GetDrawCommands().empty() ? 1 : DevelRenderer::RENDER_QUEUE_MAX;
+ for( auto queue = 0u; queue < MAX_QUEUE; ++queue )
+ {
+ // Render the item.
+ item.mRenderer->Render(context, bufferIndex, *item.mNode, item.mModelMatrix, item.mModelViewMatrix,
+ viewMatrix, projectionMatrix, item.mSize, !item.mIsOpaque, boundTextures, instruction, queue);
+ }
}
}
}
mGeometry = geometry;
mUpdateAttributesLocation = true;
}
+void Renderer::SetDrawCommands( Dali::DevelRenderer::DrawCommand* pDrawCommands, uint32_t size )
+{
+ mDrawCommands.clear();
+ mDrawCommands.insert( mDrawCommands.end(), pDrawCommands, pDrawCommands+size );
+}
void Renderer::SetBlending( Context& context, bool blend )
{
const Vector3& size,
bool blend,
Vector<GLuint>& boundTextures,
- const Dali::Internal::SceneGraph::RenderInstruction& instruction )
+ const Dali::Internal::SceneGraph::RenderInstruction& instruction,
+ uint32_t queueIndex )
{
+ // Before doing anything test if the call happens in the right queue
+ if( mDrawCommands.empty() && queueIndex > 0 )
+ {
+ return;
+ }
+
+ // Prepare commands
+ std::vector<DevelRenderer::DrawCommand*> commands;
+ for( auto& cmd : mDrawCommands )
+ {
+ if(cmd.queue == queueIndex)
+ {
+ commands.emplace_back( &cmd );
+ }
+ }
+
+ // Have commands but nothing to be drawn - abort
+ if(!mDrawCommands.empty() && commands.empty())
+ {
+ return;
+ }
+
// Get the program to use:
Program* program = mRenderDataProvider->GetShader().GetProgram();
if( !program )
context.CullFace( mFaceCullingMode );
}
- //Set blending mode
- SetBlending( context, blend );
-
// Take the program into use so we can send uniforms to it
program->Use();
mUpdateAttributesLocation = false;
}
- mGeometry->Draw( context,
- bufferIndex,
- mAttributesLocation,
- mIndexedDrawFirstElement,
- mIndexedDrawElementsCount );
+ if(mDrawCommands.empty())
+ {
+ SetBlending( context, blend );
+ mGeometry->Draw( context,
+ bufferIndex,
+ mAttributesLocation,
+ mIndexedDrawFirstElement,
+ mIndexedDrawElementsCount );
+ }
+ else
+ {
+ for(auto& cmd : commands )
+ {
+ if(cmd->queue == queueIndex )
+ {
+ //Set blending mode
+ SetBlending(context, cmd->queue == DevelRenderer::RENDER_QUEUE_OPAQUE ? false : blend);
+ mGeometry->Draw(context, bufferIndex, mAttributesLocation,
+ cmd->firstIndex, cmd->elementCount);
+ }
+ }
+ }
mUpdated = false;
}
}
*/
void SetGeometry( Render::Geometry* geometry );
+ void SetDrawCommands( Dali::DevelRenderer::DrawCommand* pDrawCommands, uint32_t size );
+
+ /**
+ * @brief Returns a reference to an array of draw commands
+ * @return Valid array of draw commands (may be empty)
+ */
+ const std::vector<Dali::DevelRenderer::DrawCommand>& GetDrawCommands() const
+ {
+ return mDrawCommands;
+ }
+
/**
* Second-phase construction.
* This is called when the renderer is inside render thread
const Vector3& size,
bool blend,
Vector<GLuint>& boundTextures,
- const Dali::Internal::SceneGraph::RenderInstruction& instruction //for reflection effect
- );
+ const Dali::Internal::SceneGraph::RenderInstruction& instruction,
+ uint32_t queueIndex );
/**
* Write the renderer's sort attributes to the passed in reference
bool mUpdated:1;
+ std::vector<Dali::DevelRenderer::DrawCommand> mDrawCommands; // Devel stuff
};
} // namespace SceneGraph
RESEND_STENCIL_OPERATION_ON_Z_PASS = 1 << 17,
RESEND_WRITE_TO_COLOR_BUFFER = 1 << 18,
RESEND_SHADER = 1 << 19,
+ RESEND_DRAW_COMMANDS = 1 << 20
};
} // Anonymous namespace
new (slot) DerivedType( mRenderer, &Render::Renderer::SetGeometry, mGeometry );
}
+ if( mResendFlag & RESEND_DRAW_COMMANDS )
+ {
+ typedef MessageValue2< Render::Renderer, Dali::DevelRenderer::DrawCommand*, uint32_t > DerivedType;
+ uint32_t* slot = mSceneController->GetRenderQueue().ReserveMessageSlot( updateBufferIndex, sizeof( DerivedType ) );
+ new (slot) DerivedType( mRenderer, &Render::Renderer::SetDrawCommands, mDrawCommands.data(), mDrawCommands.size() );
+ }
+
if( mResendFlag & RESEND_FACE_CULLING_MODE )
{
typedef MessageValue1< Render::Renderer, FaceCullingMode::Type > DerivedType;
}
}
+void Renderer::SetDrawCommands( Dali::DevelRenderer::DrawCommand* pDrawCommands, uint32_t size )
+{
+ mDrawCommands.clear();
+ mDrawCommands.insert( mDrawCommands.end(), pDrawCommands, pDrawCommands+size );
+ mResendFlag |= RESEND_DRAW_COMMANDS;
+}
+
} // namespace SceneGraph
} // namespace Internal
} // namespace Dali
*/
virtual const CollectedUniformMap& GetUniformMap( BufferIndex bufferIndex ) const;
+ void SetDrawCommands( Dali::DevelRenderer::DrawCommand* pDrawCommands, uint32_t size );
+
private:
/**
bool mUniformMapChanged[2]; ///< Records if the uniform map has been altered this frame
bool mPremultipledAlphaEnabled:1; ///< Flag indicating whether the Pre-multiplied Alpha Blending is required
+ std::vector<Dali::DevelRenderer::DrawCommand> mDrawCommands;
+
public:
AnimatableProperty< float > mOpacity; ///< The opacity value
new (slot) LocalType( &renderer, &Renderer::SetRenderingBehavior, renderingBehavior );
}
+inline void SetDrawCommandsMessage( EventThreadServices& eventThreadServices, const Renderer& renderer, Dali::DevelRenderer::DrawCommand* pDrawCommands, uint32_t size )
+{
+ typedef MessageValue2< Renderer, Dali::DevelRenderer::DrawCommand*, uint32_t > LocalType;
+
+ // Reserve some memory inside the message queue
+ uint32_t* slot = eventThreadServices.ReserveMessageSlot( sizeof( LocalType ) );
+
+ new (slot) LocalType( &renderer, &Renderer::SetDrawCommands, pDrawCommands, size );
+}
+
+
} // namespace SceneGraph
} // namespace Internal
} // namespace Dali