From 0255ba0039cf628d8dbc82d8898000da98833532 Mon Sep 17 00:00:00 2001 From: Adam Bialogonski Date: Mon, 20 Jun 2022 07:39:39 +0100 Subject: [PATCH 1/1] DrawableViewNativeRenderer for Direct Rendering The DirectViewNativeRenderer has been introduced in order to provide: 1) parallel rendering compatible with the current GlView behaviour 2) provide a switch between 'injected' direct rendering and parallel. Change-Id: I9e2acd19247c5232c995e520111100644d027aee --- automated-tests/src/dali-toolkit/CMakeLists.txt | 1 + .../test-graphics-controller.cpp | 9 + .../dali-toolkit/toolkit-direct-rendering-egl.cpp | 140 ++++ .../utc-Dali-GlViewDirectRendering.cpp | 129 +++- build/tizen/CMakeLists.txt | 2 +- .../controls/gl-view/drawable-view-impl.cpp | 57 +- .../internal/controls/gl-view/drawable-view-impl.h | 27 +- .../gl-view/drawable-view-native-renderer.cpp | 823 +++++++++++++++++++++ .../gl-view/drawable-view-native-renderer.h | 114 +++ dali-toolkit/internal/file.list | 1 + .../public-api/controls/gl-view/gl-view.cpp | 3 +- dali-toolkit/public-api/controls/gl-view/gl-view.h | 15 + packaging/dali-toolkit.spec | 4 + 13 files changed, 1288 insertions(+), 37 deletions(-) create mode 100644 automated-tests/src/dali-toolkit/toolkit-direct-rendering-egl.cpp create mode 100644 dali-toolkit/internal/controls/gl-view/drawable-view-native-renderer.cpp create mode 100644 dali-toolkit/internal/controls/gl-view/drawable-view-native-renderer.h diff --git a/automated-tests/src/dali-toolkit/CMakeLists.txt b/automated-tests/src/dali-toolkit/CMakeLists.txt index 1dbbf90..7076e8a 100755 --- a/automated-tests/src/dali-toolkit/CMakeLists.txt +++ b/automated-tests/src/dali-toolkit/CMakeLists.txt @@ -7,6 +7,7 @@ SET(CAPI_LIB "dali-toolkit") # List of test case sources (Only these get parsed for test cases) SET(TC_SOURCES + toolkit-direct-rendering-egl.cpp utc-Dali-Alignment.cpp utc-Dali-AnimatedImageVisual.cpp utc-Dali-AnimatedVectorImageVisual.cpp diff --git a/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-graphics-controller.cpp b/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-graphics-controller.cpp index 6a8eab4..fc176e5 100644 --- a/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-graphics-controller.cpp +++ b/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-graphics-controller.cpp @@ -32,6 +32,8 @@ #include #include +#include + namespace Dali { std::ostream& operator<<(std::ostream& o, const Graphics::BufferCreateInfo& bufferCreateInfo) @@ -696,6 +698,13 @@ void TestGraphicsController::ProcessCommandBuffer(TestGraphicsCommandBuffer& com case CommandType::DRAW_NATIVE: { auto info = &cmd.data.draw.drawNative.drawNativeInfo; + + if(info->glesNativeInfo.eglSharedContextStoragePointer) + { + auto* anyContext = reinterpret_cast(info->glesNativeInfo.eglSharedContextStoragePointer); + *anyContext = reinterpret_cast(0x12345678u); + } + CallbackBase::ExecuteReturn(*info->callback, info->userData); break; } diff --git a/automated-tests/src/dali-toolkit/toolkit-direct-rendering-egl.cpp b/automated-tests/src/dali-toolkit/toolkit-direct-rendering-egl.cpp new file mode 100644 index 0000000..35ee43e --- /dev/null +++ b/automated-tests/src/dali-toolkit/toolkit-direct-rendering-egl.cpp @@ -0,0 +1,140 @@ +/* +* Copyright (c) 2022 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. +* +*/ + +#include +#include +extern "C" +{ + +// Flag to be set when we want shader compilation fail +bool gDirectRenderingFailCreateShader = false; + +// Flag to be set when we want program linking fail +bool gDirectRenderingFailCreateProgram = false; + +/** + * To test the multithreaded variant we need override EGL api + * + * The Direct Rendering uses GL directly and it's needed to override certain funtions in order + * to force code execution. + */ +EGLContext eglCreateContext (EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint *attrib_list) +{ + return EGLContext(0x12345678); +} + +EGLBoolean eglGetConfigs (EGLDisplay dpy, EGLConfig *configs, EGLint config_size, EGLint *num_config) +{ + static EGLConfig config; + if(num_config) + { + *num_config = 1; + } + if(configs) + { + configs[0] = config; + } + + return EGL_TRUE; +} + +GLuint glCreateProgram (void) +{ + static uint32_t programId = 1; + return programId++; +} + +GLuint glCreateShader(GLenum type) +{ + static uint32_t shaderId = 1; + return shaderId++; +} + +void glCompileShader(GLuint shader) +{ +} + +void glLinkProgram (GLuint program) +{ +} + +void glGenTextures(GLsizei n, GLuint *textures) +{ + static GLuint texId = 1u; + for(auto i = 0; i < n; ++i) + { + textures[i] = texId++; + } +} + +void glGetShaderiv(GLuint shader, GLenum pname, GLint *params) +{ + if(pname == GL_COMPILE_STATUS) + { + params[0] = gDirectRenderingFailCreateShader ? GL_FALSE : GL_TRUE; + } + else if(pname == GL_INFO_LOG_LENGTH) + { + params[0] = 4; + } +} + +void glGetProgramiv(GLuint shader, GLenum pname, GLint *params) +{ + if(pname == GL_LINK_STATUS) + { + params[0] = gDirectRenderingFailCreateProgram ? GL_FALSE : GL_TRUE; + } + else if(pname == GL_INFO_LOG_LENGTH) + { + params[0] = 4; + } +} + +void glGetShaderInfoLog (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog) +{ + infoLog[0] = '0'; + infoLog[1] = '\n'; +} + +void glGetProgramInfoLog (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog) +{ + infoLog[0] = '0'; + infoLog[1] = '\n'; +} + +void glDeleteSync (GLsync sync) +{ +} + +GLenum glClientWaitSync (GLsync sync, GLbitfield flags, GLuint64 timeout) +{ + return GL_CONDITION_SATISFIED; +} + +GLsync glFenceSync (GLenum condition, GLbitfield flags) +{ + static uint32_t syncId = 0; + return reinterpret_cast(++syncId); +} + +GLenum glCheckFramebufferStatus (GLenum target) +{ + return GL_FRAMEBUFFER_COMPLETE; +} + +} \ No newline at end of file diff --git a/automated-tests/src/dali-toolkit/utc-Dali-GlViewDirectRendering.cpp b/automated-tests/src/dali-toolkit/utc-Dali-GlViewDirectRendering.cpp index 82e3be9..75eb570 100644 --- a/automated-tests/src/dali-toolkit/utc-Dali-GlViewDirectRendering.cpp +++ b/automated-tests/src/dali-toolkit/utc-Dali-GlViewDirectRendering.cpp @@ -23,10 +23,13 @@ #include #include #include +#include + using namespace Dali; using namespace Dali::Toolkit; + // Positive test case for a method int UtcDaliGlViewDirectRenderingNew(void) { @@ -202,6 +205,24 @@ void glTerminate(void) { } + +// Internal callback function +void glInitMT(Dali::RenderCallbackInput& input) +{ +} + +int gDRFramesRendered = 0; + +int glRenderFrameMT(Dali::RenderCallbackInput& input) +{ + gDRFramesRendered++; + return 1; +} + +void glTerminateMT(Dali::RenderCallbackInput& input) +{ +} + void resizeCB(Vector2 size) { } @@ -385,4 +406,110 @@ int UtcDaliGlViewDirectRenderingTerminateCallback(void) DALI_TEST_CHECK(true); END_TEST; -} \ No newline at end of file +} + +// Positive test case for a method +int UtcDaliGlViewDirectRenderingThreadedNew(void) +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliGlViewDirectRenderingThreadedNew"); + GlView view = GlView::New(GlView::BackendMode::DIRECT_RENDERING_THREADED, GlView::ColorFormat::RGBA8888); + DALI_TEST_CHECK(view); + + auto mode1 = view.GetBackendMode(); + + DALI_TEST_EQUALS(mode1, GlView::BackendMode::DIRECT_RENDERING_THREADED, TEST_LOCATION); + + END_TEST; +} + +int UtcDaliGlViewDirectRenderingThreadedOnScene(void) +{ + ToolkitTestApplication application; + + GlView view = Toolkit::GlView::New(GlView::BackendMode::DIRECT_RENDERING_THREADED, GlView::ColorFormat::RGB888); + + //Onscene + application.GetScene().Add(view); + view.SetRenderingMode(GlView::RenderingMode::CONTINUOUS); + view.SetGraphicsConfig(true, true, 0, GlView::GraphicsApiVersion::GLES_VERSION_3_0); + view.RegisterGlCallbacks(Dali::MakeCallback(DirectRenderingCode::glInitMT), Dali::MakeCallback(DirectRenderingCode::glRenderFrameMT), Dali::MakeCallback(DirectRenderingCode::glTerminateMT)); + view.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + view.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT); + + // Set size on the actor (half the window size to show that glClear() and scissor test work together) + view.SetProperty(Actor::Property::SIZE, Size(100, 100)); + view.SetProperty(Actor::Property::POSITION, Vector2(0,0)); + + while( DirectRenderingCode::gDRFramesRendered < 1 ) + { + application.SendNotification(); + application.Render(); + } + DALI_TEST_CHECK(true); + END_TEST; +} + +extern "C" bool gDirectRenderingFailCreateShader; +extern "C" bool gDirectRenderingFailCreateProgram; + + +int UtcDaliGlViewDirectRenderingThreadedOnScene1(void) +{ + ToolkitTestApplication application; + + GlView view = Toolkit::GlView::New(GlView::BackendMode::DIRECT_RENDERING_THREADED, GlView::ColorFormat::RGB888); + + // This test will fail instantiating shaders + gDirectRenderingFailCreateShader = true; + + //Onscene + application.GetScene().Add(view); + view.SetRenderingMode(GlView::RenderingMode::CONTINUOUS); + view.SetGraphicsConfig(true, true, 0, GlView::GraphicsApiVersion::GLES_VERSION_3_0); + view.RegisterGlCallbacks(Dali::MakeCallback(DirectRenderingCode::glInitMT), Dali::MakeCallback(DirectRenderingCode::glRenderFrameMT), Dali::MakeCallback(DirectRenderingCode::glTerminateMT)); + view.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + view.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT); + + // Set size on the actor (half the window size to show that glClear() and scissor test work together) + view.SetProperty(Actor::Property::SIZE, Size(100, 100)); + view.SetProperty(Actor::Property::POSITION, Vector2(0,0)); + + while( DirectRenderingCode::gDRFramesRendered < 1 ) + { + application.SendNotification(); + application.Render(); + } + DALI_TEST_CHECK(true); + END_TEST; +} + +int UtcDaliGlViewDirectRenderingThreadedOnScene2(void) +{ + ToolkitTestApplication application; + + GlView view = Toolkit::GlView::New(GlView::BackendMode::DIRECT_RENDERING_THREADED, GlView::ColorFormat::RGB888); + + // This test will fail instantiating shaders + gDirectRenderingFailCreateProgram = true; + + //Onscene + application.GetScene().Add(view); + view.SetRenderingMode(GlView::RenderingMode::CONTINUOUS); + view.SetGraphicsConfig(true, true, 0, GlView::GraphicsApiVersion::GLES_VERSION_3_0); + view.RegisterGlCallbacks(Dali::MakeCallback(DirectRenderingCode::glInitMT), Dali::MakeCallback(DirectRenderingCode::glRenderFrameMT), Dali::MakeCallback(DirectRenderingCode::glTerminateMT)); + view.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + view.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT); + + // Set size on the actor (half the window size to show that glClear() and scissor test work together) + view.SetProperty(Actor::Property::SIZE, Size(100, 100)); + view.SetProperty(Actor::Property::POSITION, Vector2(0,0)); + + while( DirectRenderingCode::gDRFramesRendered < 1 ) + { + application.SendNotification(); + application.Render(); + } + DALI_TEST_CHECK(true); + END_TEST; +} diff --git a/build/tizen/CMakeLists.txt b/build/tizen/CMakeLists.txt index 2258fab..31cfe9f 100644 --- a/build/tizen/CMakeLists.txt +++ b/build/tizen/CMakeLists.txt @@ -376,7 +376,7 @@ TARGET_LINK_LIBRARIES( ${name} ) IF( ANDROID ) - TARGET_LINK_LIBRARIES( ${name} log ) + TARGET_LINK_LIBRARIES( ${name} log GLESv3 EGL) ENDIF() SET_TARGET_PROPERTIES( ${name} diff --git a/dali-toolkit/internal/controls/gl-view/drawable-view-impl.cpp b/dali-toolkit/internal/controls/gl-view/drawable-view-impl.cpp index 9b94546..4cbda06 100644 --- a/dali-toolkit/internal/controls/gl-view/drawable-view-impl.cpp +++ b/dali-toolkit/internal/controls/gl-view/drawable-view-impl.cpp @@ -24,34 +24,40 @@ #include #include #include +#include namespace Dali::Toolkit::Internal { -Dali::Toolkit::GlView DrawableView::New() +Dali::Toolkit::GlView DrawableView::New(GlView::BackendMode backendMode) { - auto* impl = new DrawableView(); + auto* impl = new DrawableView(backendMode); Dali::Toolkit::GlView handle = Dali::Toolkit::GlView(*impl); impl->Initialize(); return handle; } -DrawableView::DrawableView() -: Dali::Toolkit::Internal::GlViewImpl( GlView::BackendMode::DIRECT_RENDERING ), +DrawableView::DrawableView(GlView::BackendMode backendMode) +: Dali::Toolkit::Internal::GlViewImpl( backendMode), mRenderingMode(Toolkit::GlView::RenderingMode::CONTINUOUS), mDepth(false), mStencil(false), mMSAA(0) { mRenderCallback = RenderCallback::New( this, &DrawableView::OnRenderCallback); + + // Create NativeRenderer + Dali::Internal::NativeRendererCreateInfo createInfo; + createInfo.maxOffscreenBuffers = 2u; + createInfo.threadEnabled = (backendMode == GlView::BackendMode::DIRECT_RENDERING_THREADED); + createInfo.presentationMode = Dali::Internal::NativeRendererCreateInfo::PresentationMode::FIFO; + mNativeRenderer = std::make_unique(createInfo); } DrawableView::~DrawableView() = default; void DrawableView::RegisterGlCallbacks(CallbackBase* initCallback, CallbackBase* renderFrameCallback, CallbackBase* terminateCallback) { - mOnInitCallback.reset( initCallback ); - mOnRenderCallback.reset(renderFrameCallback ); - mOnTerminateCallback. reset( terminateCallback ); + mNativeRenderer->RegisterGlCallbacks( initCallback, renderFrameCallback, terminateCallback ); } void DrawableView::SetResizeCallback(CallbackBase* resizeCallback) @@ -61,8 +67,8 @@ void DrawableView::SetResizeCallback(CallbackBase* resizeCallback) bool DrawableView::SetGraphicsConfig(bool depth, bool stencil, int msaa, Dali::Toolkit::GlView::GraphicsApiVersion version) { - DALI_LOG_ERROR( "DrawableView::SetGraphicsConfig() is currently not implemented"); - + // Currently, the settings are not relevant for the DirectRendering feature as all the + // setup is inherited from DALi graphics backend. return true; } @@ -110,8 +116,9 @@ void DrawableView::OnSizeSet(const Vector3& targetSize) mSurfaceSize = targetSize; // If the callbacks are set then schedule execution of resize callback - if(mRenderCallback && mOnResizeCallback) + if(mRenderCallback && mNativeRenderer) { + mNativeRenderer->Resize( uint32_t(targetSize.width), uint32_t(targetSize.height)); mSurfaceResized = true; } } @@ -144,6 +151,8 @@ void DrawableView::OnSceneConnection(int depth) void DrawableView::OnSceneDisconnection() { Control::OnSceneDisconnection(); + + mNativeRenderer->Terminate(); } void DrawableView::AddRenderer() @@ -155,17 +164,24 @@ void DrawableView::AddRenderer() bool DrawableView::OnRenderCallback( const RenderCallbackInput& renderCallbackInput ) { + if(mNativeRenderer) + { + mNativeRenderer->PushRenderCallbackInputData( renderCallbackInput ); + } + // Init state if( mCurrentViewState == ViewState::INIT ) { - if(mOnInitCallback) - { - CallbackBase::Execute(*mOnInitCallback); - } + mNativeRenderer->InvokeGlInitCallback(renderCallbackInput); mCurrentViewState = ViewState::RENDER; } - int renderFrameResult = 0; + if(mSurfaceResized) + { + mNativeRenderer->Resize( uint32_t(mSurfaceSize.width), uint32_t(mSurfaceSize.height) ); + mSurfaceResized = false; + } + if( mCurrentViewState == ViewState::RENDER ) { // The mSurfaceResized is set by another thread so atomic check must be provided @@ -178,20 +194,13 @@ bool DrawableView::OnRenderCallback( const RenderCallbackInput& renderCallbackIn CallbackBase::Execute(*mOnResizeCallback, static_cast(mSurfaceSize.x), static_cast(mSurfaceSize.y)); } - if(mOnRenderCallback) - { - renderFrameResult = CallbackBase::ExecuteReturn(*mOnRenderCallback); - if(renderFrameResult) - { - // TODO: may be utilized for RenderOnce feature - } - } + mNativeRenderer->InvokeGlRenderCallback(renderCallbackInput); } // The terminate callback isn't easy to implement for DR. The NativeImage backend // calls it when the GlView is being destroyed. For DrawableView it means that // the RenderCallback won't be executed (as it is a part of graphics pipeline). - // We don't have currenty no way to know whether the View will be destroyed and + // We don't have currently have any way to know whether the View will be destroyed and // to execute last native draw command in the pipeline. // // else if( mCurrentViewState == ViewState::TERMINATE ) diff --git a/dali-toolkit/internal/controls/gl-view/drawable-view-impl.h b/dali-toolkit/internal/controls/gl-view/drawable-view-impl.h index c61832a..55f1db3 100644 --- a/dali-toolkit/internal/controls/gl-view/drawable-view-impl.h +++ b/dali-toolkit/internal/controls/gl-view/drawable-view-impl.h @@ -2,7 +2,7 @@ #define DALI_TOOLKIT_INTERNAL_DRAWABLE_VIEW_H /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * Copyright (c) 2022 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. @@ -29,6 +29,7 @@ // INTERNAL INCLUDES #include +#include #include #include @@ -44,15 +45,23 @@ protected: virtual ~DrawableView(); public: + /** - * @copydoc Dali::Toolkit::GlView::New() + * @brief Creates GlView interface object using DrawableView implementation + * + * @param[in] backendMode Backend mode to be used. Only DIRECT_RENDERING and DIRECT_RENDERING_THREADED + * are accepted. + * @return Valid GlView object or nullptr on error */ - static Dali::Toolkit::GlView New(); + static Dali::Toolkit::GlView New(GlView::BackendMode backendMode); /** - * Construct a new GlView. + * @brief Constructor creates GlView interface object using DrawableView implementation + * + * @param[in] backendMode Backend mode to be used. Only DIRECT_RENDERING and DIRECT_RENDERING_THREADED + * are accepted. */ - DrawableView(); + explicit DrawableView(GlView::BackendMode backendMode); /** * @copydoc Dali::Toolkit::GlView::RegisterGlCallbacks() @@ -150,15 +159,13 @@ private: ViewState mCurrentViewState{ViewState::INIT}; ///< state within RenderCallback - // These callbacks are stored for GLView API compatibility - std::unique_ptr mOnInitCallback; - std::unique_ptr mOnRenderCallback; - std::unique_ptr mOnTerminateCallback; - std::unique_ptr mOnResizeCallback; + std::unique_ptr mOnResizeCallback; ///< Resize callback called when surface size changes std::atomic_bool mSurfaceResized{false}; ///< Flag to invoke surface resize callback Size mSurfaceSize{}; ///< Surface size + + std::unique_ptr mNativeRenderer; ///< Pointer to the native renderer }; } // namespace Internal diff --git a/dali-toolkit/internal/controls/gl-view/drawable-view-native-renderer.cpp b/dali-toolkit/internal/controls/gl-view/drawable-view-native-renderer.cpp new file mode 100644 index 0000000..78f45a7 --- /dev/null +++ b/dali-toolkit/internal/controls/gl-view/drawable-view-native-renderer.cpp @@ -0,0 +1,823 @@ +/* + * Copyright (c) 2021 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. + * + */ + +#include "drawable-view-native-renderer.h" +#include +#include +#include +#include +#include +#include + +// GLES3+ is required for this to work! +#include +#include +#include + +#include +#include + +#define GL(x) \ + { \ + glGetError(); \ + { \ + x; \ + }; \ + auto err = glGetError(); \ + if(err) \ + { \ + printf("%p:%d: ERROR: 0x%X\n", this, __LINE__, int(err)); \ + } \ + } + +namespace +{ +/** + * Vertices of quad to display when using offscreen rendering + */ +constexpr float QUAD_VERTS[] = { + // positions // colors // texture coords + 1.0f, 1.0f, + 1.0f, -1.0f, + -1.0f, -1.0f, + -1.0f, 1.0f, +}; + +/** + * Indices of quad for offscreen rendering + */ +constexpr unsigned short QUAD_INDICES[] = { + 0, 1, 2, + 3, 0, 2 +}; + +/** + * UV coords of quad for offscreen rendering + */ +constexpr float QUAD_UV[] = { + // positions // colors // texture coords + 1.0f, 1.0f, // top right + 1.0f, 0.0f, // bottom right + 0.0f, 0.0f, // bottom left + 0.0f, 1.0f // top left +}; +} + +namespace Dali::Internal +{ +struct DrawableViewNativeRenderer::Impl +{ + /** + * This structure associates framebuffer with texture and fence object + */ + struct FrameBufferTexture + { + uint32_t textureId{0u}; + uint32_t framebufferId{0u}; + GLsync fence{nullptr}; + }; + + // Queues management + bool DequeueTextureDrawBuffer( uint32_t& outIndex ) + { + std::scoped_lock lock(mTextureQueueMutex); + if(mTextureDrawQueue.empty()) + { + // TODO: probably add textures if necessary + return false; + } + + auto retval = mTextureDrawQueue.front(); + mTextureDrawQueue.pop_front(); + outIndex = retval; + return true; + } + + /** + * Enqueues framebuffer for the Read queue to be used by + * the CONSUMER. + */ + void EnqueueTextureReadBuffer(uint32_t fbId) + { + // push ready texture to front of 'read' queue + std::scoped_lock lock(mTextureQueueMutex); + + auto& fb = mFramebufferTexture[fbId]; + + // Check state of fence whether the texture can be passed to the CONSUMER + if(fb.fence) + { + auto checkFenceState = glClientWaitSync(fb.fence, GL_SYNC_FLUSH_COMMANDS_BIT, 0); + if(checkFenceState == GL_ALREADY_SIGNALED || checkFenceState == GL_CONDITION_SATISFIED) + { + // Ready so push directly to Read queue + mTextureReadQueue.push_back(fbId); + } + else + { + // Still busy so push to Stage queue + mTextureStageQueue.push_back(fbId); + } + } + } + + void EnqueueTextureDrawBuffer(uint32_t fbId) + { + // push ready texture to front of 'read' queue + std::scoped_lock lock(mTextureQueueMutex); + mTextureDrawQueue.push_back(fbId); + } + + int32_t DequeueTextureReadBuffer(FrameBufferTexture& framebufferTexture) + { + // executed by DALi RenderThread! + std::deque backTextures; + std::scoped_lock lock(mTextureQueueMutex); + + if(mTextureReadQueue.empty()) + { + EnqueueStagedTexture(); + } + else + { + while(!mTextureStageQueue.empty()) + { + // we have something to render, so discard + auto stagedId = mTextureStageQueue.back(); + EnqueueTextureDrawBuffer(stagedId); + mTextureStageQueue.pop_back(); + } + } + + if(mTextureReadQueue.empty()) + { + return -1; + } + + auto retval = mTextureReadQueue.back(); + mTextureReadQueue.pop_back(); + + // drain all back images and return them to the 'draw' queue + // and remove old images + while(!mTextureReadQueue.empty()) + { + auto texId = mTextureReadQueue.back(); + if(framebufferTexture.fence) + { + glDeleteSync(framebufferTexture.fence); + framebufferTexture.fence = nullptr; + } + mTextureDrawQueue.push_front(texId); + mTextureReadQueue.pop_back(); + } + + return int32_t(retval); + } + + /** + * Enqueues previously staged texture + */ + uint32_t EnqueueStagedTexture() + { + // test stage queue + std::deque stagedQueue; + bool found = false; + uint32_t retval = 0; + while(!mTextureStageQueue.empty()) + { + auto stagedId = mTextureStageQueue.front(); + auto& fb = mFramebufferTexture[stagedId]; + if(!found) + { + auto syncResult = glClientWaitSync(fb.fence, GL_SYNC_FLUSH_COMMANDS_BIT, 0); + if(syncResult == GL_CONDITION_SATISFIED || syncResult == GL_ALREADY_SIGNALED) + { + // push texture into the queue + mTextureReadQueue.push_back(stagedId); + retval = stagedId; + found = true; + } + else + { + stagedQueue.push_back(stagedId); + } + } + else + { + stagedQueue.push_back(stagedId); + } + mTextureStageQueue.pop_front(); + } + mTextureStageQueue = std::move(stagedQueue); + return retval; + } + + uint32_t CreateFramebuffer(uint32_t index, uint32_t width, uint32_t height) + { + auto& fb = mFramebufferTexture[index]; + if(!fb.framebufferId) + { + GLuint offscreenFramebuffer, renderBuffer; + GL(glGenFramebuffers(1, &offscreenFramebuffer)) + GL(glBindFramebuffer(GL_FRAMEBUFFER, offscreenFramebuffer)); + GL(glGenRenderbuffers(1, &renderBuffer)); + GL(glBindRenderbuffer(GL_RENDERBUFFER, renderBuffer)); + GL(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb.textureId, 0)); + GL(glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height)) + GL(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderBuffer)); + fb.framebufferId = offscreenFramebuffer; + + [[maybe_unused]] auto result = glCheckFramebufferStatus(GL_FRAMEBUFFER); + DALI_ASSERT_ALWAYS( result == GL_FRAMEBUFFER_COMPLETE && "Framebuffer incomplete!"); + return offscreenFramebuffer; + } + return 0u; + } + + + /** + * Constructor + */ + explicit Impl(const NativeRendererCreateInfo& renderCreateInfo) + : mCreateInfo(renderCreateInfo) + { + } + + /** + * Destructor + */ + ~Impl() + { + Terminate(); + if(mThread) + { + mThread->join(); + } + } + + /** + * Initializes renderer thread for offscreen rendering + */ + void InitializeThread() + { + mThread = std::make_unique(&Impl::StartThread, this); + } + + void PushRenderCallbackInputData( const Dali::RenderCallbackInput& renderCallbackInput ) + { + + std::scoped_lock lock(mRenderCallbackInputDataMutex); + mRenderCallbackInputData = renderCallbackInput; + } + + void PopRenderCallbackInputData( Dali::RenderCallbackInput& renderCallbackInput ) + { + std::scoped_lock lock(mRenderCallbackInputDataMutex); + renderCallbackInput = mRenderCallbackInputData; + } + + void Terminate() + { + mRunning = false; + } + + /** + * Function initializes thread for parallel rendering. + * + * The internal loop runs until the private EGL context has been + * initialized. + */ + void StartThread() + { + mRunning = true; + + // We need to acquire shared context, while this is not done + // it's necessary to wait for context to be bound. + while(mRunning && !mEglContextBound) + { + // Wait for context to be given + if(!mEglContext) + { + continue; + } + if(!eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, mEglContext)) + { + [[maybe_unused]] auto err = eglGetError(); + printf("%d\n", int(err)); + } + mEglContextBound = true; + } + + InitializeOffscreenFramebuffers(); + + PrepareBlitShader(); + + ThreadRunRender(); + } + + /** + * Reinitializes offscreen framebuffers and textures in case + * the resize has been requested. + */ + void ReinitializeFramebuffers() + { + int index = 0; + for(auto& fb : mFramebufferTexture) + { + if(fb.fence) + { + GL(glDeleteSync(fb.fence)); + } + if(fb.framebufferId) + { + GL(glDeleteFramebuffers(1, &fb.framebufferId)) + fb.framebufferId = 0u; + } + if(fb.textureId) + { + GL(glDeleteTextures(1, &fb.textureId)) + fb.textureId = 0u; + } + fb.textureId = CreateOffscreenTexture( mWidth, mHeight ); + fb.framebufferId = CreateFramebuffer( index, mWidth, mHeight ); + index++; + } + } + + void ThreadRunRender() + { + while(mRunning) + { + // If there is a resize request waiting, then recreate all framebuffers + if(mResizeRequest) + { + ReinitializeFramebuffers(); + mResizeRequest = false; + } + + Dali::RenderCallbackInput input; + + PopRenderCallbackInputData( input ); + + uint32_t index{0u}; + auto result = DequeueTextureDrawBuffer(index); + if(!result) + { + continue; + } + + auto& fb = mFramebufferTexture[index]; + GL(glBindFramebuffer(GL_FRAMEBUFFER, fb.framebufferId)) + GL(glClear(0)); + + + // Invoke callback + if(mOnRenderCallback) + { + CallbackBase::ExecuteReturn(*mOnRenderCallback); + } + + // If the framebuffer is guarded with fence object then + // delete it as at this point it is no longer valid. + if(fb.fence) + { + // Make sure GPU finished + glDeleteSync(fb.fence); + fb.fence = nullptr; + } + + // Inject sync object into the GL commands stream + fb.fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + EnqueueTextureReadBuffer(index); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } + } + + void Resize(uint32_t width, uint32_t height ) + { + mWidth = width; + mHeight = height; + mResizeRequest = true; + } + + uint32_t mWidth {0u}; + uint32_t mHeight {0u}; + std::atomic_bool mResizeRequest { false }; + + /** + * Clones current EGL context, this function must be called from the render callback + * and be executed on the DALi RenderThread + */ + void CloneEglContext() + { + // extract shared context (void*) + auto context = eglGetCurrentContext(); + + // Obtain configs + EGLint configId{0u}; + EGLint size{0u}; + eglGetConfigs(mEglDisplay, nullptr, 0, &size); + std::vector configs; + configs.resize(size); + eglGetConfigs(mEglDisplay, configs.data(), EGLint(configs.size()), &size); + + // Find out which config is used by current context + eglQueryContext(mEglDisplay, context, EGL_CONFIG_ID, &configId); + + // Setup EGL version + auto version = int(30); // TODO: get context version and select the same one + std::vector attribs; + attribs.push_back(EGL_CONTEXT_MAJOR_VERSION_KHR); + attribs.push_back(version / 10); + attribs.push_back(EGL_CONTEXT_MINOR_VERSION_KHR); + attribs.push_back(version % 10); + attribs.push_back(EGL_NONE); + + // Create cloned context with shared context + mEglContext = eglCreateContext(mEglDisplay, configs[configId], mEglSharedContext, attribs.data()); + } + + // Pre-, Post- functions are being called from the callbacks + void GlViewPreInit( const Dali::RenderCallbackInput& input ) + { + // This runs on DALi RenderThread!!! + + // Bind the shared context in case of threaded rendering + if(mThread && !mEglContextBound) + { + // Store the shared context just once + if(!mEglSharedContext ) + { + // Store the shared context returned by the drawable callback + mEglSharedContext = std::any_cast(input.eglContext); + } + // Setup the EGL context + mEglDisplay = eglGetCurrentDisplay(); + + // switch to shared context in order to create shared GL resources + auto currentContext = eglGetCurrentContext(); + + // Retrieve current surfaces (read and draw) + mDrawSurface = eglGetCurrentSurface(EGL_DRAW); + mReadSurface = eglGetCurrentSurface(EGL_READ); + + eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, mEglSharedContext); + + [[maybe_unused]] auto eglError = eglGetError(); + + // Now clone it to create compatible context for our threaded rendering + CloneEglContext(); + + // Bring back current context + eglMakeCurrent(mEglDisplay, mDrawSurface, mReadSurface, currentContext); + } + } + + GLuint CreateProgram(const char* vertexSource, const char* fragmentSource) + { + GLuint vertexShader = LoadShader(GL_VERTEX_SHADER, vertexSource); + if(!vertexShader) + { + return 0; + } + GLuint fragmentShader = LoadShader(GL_FRAGMENT_SHADER, fragmentSource); + if(!fragmentShader) + { + return 0; + } + GLuint program = glCreateProgram(); + if(program) + { + GL(glAttachShader(program, vertexShader)); + GL(glAttachShader(program, fragmentShader)); + GL(glLinkProgram(program)); + GLint linkStatus = GL_FALSE; + GL(glGetProgramiv(program, GL_LINK_STATUS, &linkStatus)); + if(linkStatus != GL_TRUE) + { + GLint bufLength = 0; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength); + if(bufLength) + { + char* buf = (char*)malloc(bufLength); + if(buf) + { + glGetProgramInfoLog(program, bufLength, NULL, buf); + free(buf); + } + } + glDeleteProgram(program); + program = 0; + } + } + return program; + } + + GLuint LoadShader(GLenum shaderType, const char* shaderSource) + { + GLuint shader = glCreateShader(shaderType); + if(shader != 0) + { + GL(glShaderSource(shader, 1, &shaderSource, NULL)); + GL(glCompileShader(shader)); + GLint compiled = 0; + glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); + if(compiled != GL_TRUE) + { + GLint infoLen = 0; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); + + if(infoLen > 0) + { + char* logBuffer = (char*)malloc(infoLen); + + if(logBuffer != NULL) + { + glGetShaderInfoLog(shader, infoLen, NULL, logBuffer); + + DALI_ASSERT_ALWAYS( true && logBuffer); + + free(logBuffer); + logBuffer = NULL; + } + + glDeleteShader(shader); + shader = 0; + } + } + } + return shader; + } + + void PrepareBlitShader() + { + static const char glVertexShader[] = + "attribute vec2 vertexPosition;\n" + "attribute vec2 texCoords;\n" + "varying vec2 vTexCoords ;\n" + "void main()\n" + "{\n" + " gl_Position = vec4(vertexPosition, 0.0, 1.0);\n" + " vTexCoords = texCoords;\n" + "}\n"; + + static const char glFragmentShader[] = + "precision mediump float;\n" + "varying vec2 vTexCoords;\n" + "uniform sampler2D tex;\n" + "void main()\n" + "{\n" + " gl_FragColor = texture2D(tex, vTexCoords);\n" + "}\n"; + + mBlitProgram = CreateProgram(glVertexShader, glFragmentShader); + mBlitVertexLocation = glGetAttribLocation(mBlitProgram, "vertexPosition"); + mBlitTexCoord = glGetAttribLocation(mBlitProgram, "texCoords"); + } + + GLuint mBlitProgram{0u}; + GLuint mBlitVertexLocation{0u}; + GLuint mBlitTexCoord{0u}; + + /** + * Initializes FBO textures + */ + void InitializeOffscreenFramebuffers() + { + for(auto i = 0u; i < mCreateInfo.maxOffscreenBuffers; ++i) + { + mFramebufferTexture.emplace_back(); + mFramebufferTexture.back().textureId = CreateOffscreenTexture(mWidth, mHeight); + + // Populate Draw queue entries + mTextureDrawQueue.push_back(i); + + // Create framebuffers + CreateFramebuffer( i, mWidth, mHeight); + } + } + + /** + * Creates an offscreen texture for threaded renderer + */ + uint32_t CreateOffscreenTexture(uint32_t width, uint32_t height) + { + GLuint offscreenTexture{0u}; + GL(glGenTextures(1, &offscreenTexture)); + GL(glBindTexture(GL_TEXTURE_2D, offscreenTexture)); + GL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr)); + GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + return offscreenTexture; + } + + void GlViewInit(const Dali::RenderCallbackInput& input) + { + if(mOnInitCallback) + { + GlViewPreInit(input); + CallbackBase::Execute(*mOnInitCallback, input); + } + } + + int GlViewRender(const Dali::RenderCallbackInput& input) + { + // Non-threaded solution invokes callback directly + int retval = 0; + if(!mCreateInfo.threadEnabled) + { + if(mOnRenderCallback) + { + retval = CallbackBase::ExecuteReturn(*mOnRenderCallback, input); + } + } + else + { + BlitTexture(); + } + return retval; + } + + void GlViewTerminate(const Dali::RenderCallbackInput& input) + { + // Non-threaded solution invokes callback directly + if(!mCreateInfo.threadEnabled) + { + if(mOnTerminateCallback) + { + CallbackBase::Execute(*mOnTerminateCallback, input); + } + } + } + + void BlitTexture() + { + // If no threaded mode, return + if(!mCreateInfo.threadEnabled) + { + return; + } + + // Read input + auto x = 0; + auto y = 0; + auto w = mWidth; + auto h = mHeight; + + // Deqeueue texture, there should be always something waiting to be drawn, if not, ignore + FrameBufferTexture fb; + auto textureBufferIndex = DequeueTextureReadBuffer(fb); + + // Do nothing if frame not ready + if(textureBufferIndex < 0) + { + if(mLastTextureBufferIndex >= 0) + { + textureBufferIndex = mLastTextureBufferIndex; + } + else + { + return; + } + } + else + { + // return last texture to the pull + if(mLastTextureBufferIndex >= 0) + { + // return it to the queue + EnqueueTextureDrawBuffer( mLastTextureBufferIndex ); + } + } + + GL(glViewport(x, y, w, h)); + if(!mBlitStateDone) + { + mBlitStateDone = true; + GL(glUseProgram(mBlitProgram)); + GL(glVertexAttribPointer(mBlitVertexLocation, 2, GL_FLOAT, GL_FALSE, 0, QUAD_VERTS)); + GL(glEnableVertexAttribArray(mBlitVertexLocation)); + GL(glVertexAttribPointer(mBlitTexCoord, 2, GL_FLOAT, GL_FALSE, 0, QUAD_UV)); + GL(glEnableVertexAttribArray(mBlitTexCoord)); + GL(glActiveTexture(GL_TEXTURE0)); + } + GL(glBindTexture(GL_TEXTURE_2D, mFramebufferTexture[textureBufferIndex].textureId)); + + GL(glDrawElements( GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, QUAD_INDICES)) + + mLastTextureBufferIndex = textureBufferIndex; + } + + // List of offscreen framebuffers + std::vector mFramebufferTexture{}; + + /** + * Rendering pipeline uses 3 queues: + * Draw - the producer queue (NativeRenderer thread writes to it) + * Read - the consumer queue (RenderThread reads from it) + * Stage - Already written but not ready to be read (not signaled) + * + * // Rendering offscreen + * 1. PRODUCER pops framebuffer from Draw queue + * 2. PRODUCER renders into the framebuffer + * 3. PRODUCER pushes framebuffer into Stage queue + * + * // Rendering onscreen + * 1. CONSUMER checks Stage queue for signaled (ready) framebuffers + * 2. If framebuffers are ready, pushes them into the Read queue + * 3. If MAILBOX mode, CONSUMER discards all 'outdated' framebuffers and displays the most recent + * 4. If FIFO mode, CONSUMER displays all the images in order of being produced. + */ + std::deque mTextureDrawQueue; + std::deque mTextureStageQueue; + std::deque mTextureReadQueue; + + // Mutex guarding the queues reads/writes + std::recursive_mutex mTextureQueueMutex; + std::unique_ptr mThread; ///< Thread for parallel mode + bool mRunning{false}; ///< Thread running flag + EGLContext mEglContext{nullptr}; ///< EGL context associated with the thread + EGLContext mEglSharedContext{nullptr}; + + EGLDisplay mEglDisplay{nullptr}; ///< Current EGL display + std::atomic_bool mEglContextBound{false}; ///< Flag indicating whether EGL context is bound + EGLSurface mDrawSurface{EGL_NO_SURFACE}; ///< Current EGL draw surface + EGLSurface mReadSurface{EGL_NO_SURFACE}; ///< Current EGL read surface + + // Callbacks associated with GlView interface + std::unique_ptr mOnInitCallback{nullptr}; + std::unique_ptr mOnRenderCallback{nullptr}; + std::unique_ptr mOnTerminateCallback{nullptr}; + + int32_t mLastTextureBufferIndex{-1}; + bool mBlitStateDone{false}; + + std::mutex mRenderCallbackInputDataMutex{}; + Dali::RenderCallbackInput mRenderCallbackInputData{}; + + NativeRendererCreateInfo mCreateInfo{}; +}; + +DrawableViewNativeRenderer::DrawableViewNativeRenderer(const NativeRendererCreateInfo& createInfo) +: mImpl(new Impl(createInfo)) +{ + if(createInfo.threadEnabled) + { + mImpl->InitializeThread(); + } +} + +DrawableViewNativeRenderer::~DrawableViewNativeRenderer() = default; + +void DrawableViewNativeRenderer::RegisterGlCallbacks(Dali::CallbackBase* onInitCallback, Dali::CallbackBase* onRenderCallback, Dali::CallbackBase* onTerminateCallback) +{ + mImpl->mOnInitCallback.reset(onInitCallback); + mImpl->mOnRenderCallback.reset(onRenderCallback); + mImpl->mOnTerminateCallback.reset(onTerminateCallback); +} + +void DrawableViewNativeRenderer::InvokeGlInitCallback(const RenderCallbackInput& renderCallbackInput) +{ + mImpl->GlViewInit(renderCallbackInput); +} + +void DrawableViewNativeRenderer::InvokeGlRenderCallback(const RenderCallbackInput& renderCallbackInput) +{ + mImpl->GlViewRender(renderCallbackInput); +} + +void DrawableViewNativeRenderer::InvokeGlTerminateCallback(const RenderCallbackInput& renderCallbackInput) +{ + mImpl->GlViewTerminate(renderCallbackInput); +} + +void DrawableViewNativeRenderer::Resize(uint32_t width, uint32_t height) +{ + mImpl->Resize(width, height); +} + +void DrawableViewNativeRenderer::PushRenderCallbackInputData( const Dali::RenderCallbackInput& renderCallbackInput ) +{ + mImpl->PushRenderCallbackInputData(renderCallbackInput); +} + +void DrawableViewNativeRenderer::Terminate() +{ + mImpl->Terminate(); +} + +} // namespace Dali::Internal \ No newline at end of file diff --git a/dali-toolkit/internal/controls/gl-view/drawable-view-native-renderer.h b/dali-toolkit/internal/controls/gl-view/drawable-view-native-renderer.h new file mode 100644 index 0000000..005b9c1 --- /dev/null +++ b/dali-toolkit/internal/controls/gl-view/drawable-view-native-renderer.h @@ -0,0 +1,114 @@ +#ifndef DALI_PROJECT_DRAWABLE_VIEW_NATIVE_RENDERER_H +#define DALI_PROJECT_DRAWABLE_VIEW_NATIVE_RENDERER_H + +/* +* Copyright (c) 2021 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. +* + */ + +#include +#include +#include + + +namespace Dali::Internal +{ +/** + * The structure containing the initialization data for the + * DrawableViewNativeRenderer instance. + */ +struct NativeRendererCreateInfo +{ + /** + * Presentation mode is used only for threaded renderer + */ + enum class PresentationMode + { + FIFO, // First-in first-out + MAILBOX // Only most recent out + }; + + bool threadEnabled { false }; + uint32_t maxOffscreenBuffers { 3u }; + PresentationMode presentationMode {PresentationMode::FIFO}; +}; + +/** + * The DrawableViewNativeRenderer is responsible for delegating rendering + * either to the own thread (in parallel mode) or calling the GlView render callbacks + * directly. + * + * The parallel mode creates the thread that invokes GLView callbacks directly. + * The actual render callback will only pass the input data and blit the result. + * + * Parallel mode renders always to the offscreen framebuffer. + */ +class DrawableViewNativeRenderer +{ +public: + explicit DrawableViewNativeRenderer( const NativeRendererCreateInfo& createInfo ); + ~DrawableViewNativeRenderer(); + + /** + * Registers GlView callbacks + */ + void RegisterGlCallbacks( Dali::CallbackBase* onInitCallback,Dali::CallbackBase* onRenderCallback, Dali::CallbackBase* onTerminateCallback ); + + /** + * Dispatches the GlView init callback + * @param renderCallbackInput + */ + void InvokeGlInitCallback( const RenderCallbackInput& renderCallbackInput ); + + /** + * Dispatches the GlView render callback + * @param renderCallbackInput + */ + void InvokeGlRenderCallback( const RenderCallbackInput& renderCallbackInput ); + + /** + * Dispatches the GlView terminate callback + * @param[in] renderCallbackInput + */ + void InvokeGlTerminateCallback( const RenderCallbackInput& renderCallbackInput ); + + /** + * @brief Resizes the render surface + * + * @param[in] width Width of surface + * @param[in] height Height of surface + */ + void Resize( uint32_t width, uint32_t height ); + + /** + * @brief Pushes render callback input data into the native renderer thread + * + * @param[in] renderCallbackInput Valid RenderCallbackInput object + */ + void PushRenderCallbackInputData( const Dali::RenderCallbackInput& renderCallbackInput ); + + /** + * @brief Terminates thread in parallel mode + */ + void Terminate(); + +private: + + struct Impl; + std::unique_ptr mImpl; +}; +} + +#endif // DALI_PROJECT_DRAWABLE_VIEW_NATIVE_RENDERER_H diff --git a/dali-toolkit/internal/file.list b/dali-toolkit/internal/file.list index 41d8c0d..d80bffe 100644 --- a/dali-toolkit/internal/file.list +++ b/dali-toolkit/internal/file.list @@ -119,6 +119,7 @@ SET( toolkit_src_files ${toolkit_src_dir}/controls/web-view/web-view-impl.cpp ${toolkit_src_dir}/controls/camera-view/camera-view-impl.cpp ${toolkit_src_dir}/controls/gl-view/drawable-view-impl.cpp + ${toolkit_src_dir}/controls/gl-view/drawable-view-native-renderer.cpp ${toolkit_src_dir}/controls/gl-view/gl-view-impl.cpp ${toolkit_src_dir}/controls/gl-view/gl-view-render-thread.cpp ${toolkit_src_dir}/accessibility-manager/accessibility-manager-impl.cpp diff --git a/dali-toolkit/public-api/controls/gl-view/gl-view.cpp b/dali-toolkit/public-api/controls/gl-view/gl-view.cpp index 91939c0..77e75e8 100644 --- a/dali-toolkit/public-api/controls/gl-view/gl-view.cpp +++ b/dali-toolkit/public-api/controls/gl-view/gl-view.cpp @@ -49,8 +49,9 @@ GlView GlView::New(BackendMode backendMode, ColorFormat colorFormat) switch(backendMode) { case BackendMode::DIRECT_RENDERING: + case BackendMode::DIRECT_RENDERING_THREADED: { - return Internal::DrawableView::New(); + return Internal::DrawableView::New(backendMode); } case BackendMode::EGL_IMAGE_OFFSCREEN_RENDERING: { diff --git a/dali-toolkit/public-api/controls/gl-view/gl-view.h b/dali-toolkit/public-api/controls/gl-view/gl-view.h index 099b00f..a3b18c6 100644 --- a/dali-toolkit/public-api/controls/gl-view/gl-view.h +++ b/dali-toolkit/public-api/controls/gl-view/gl-view.h @@ -54,20 +54,35 @@ public: * pipeline. When Renderer is about to be drawn, the callback * will be executed and the custom code "injected" into the pipeline. * This allows rendering directly to the surface rather than offscreen. + * + * * @SINCE_2_1.18 */ DIRECT_RENDERING = 0, /** + * DIRECT_RENDERING_THREADED mode executes GL code on separate thread + * and then blits the result within DALi graphics commands stream. + * The mode is logically compatible with the EGL_IMAGE_OFFSCREEN_RENDERING. + * + * @SINCE_2_1.30 + */ + DIRECT_RENDERING_THREADED, + + /** * EGL_IMAGE_OFFSCREEN_RENDERING mode executes GL code in own thread * and renders to the offscreen NativeImage (EGL) buffer. This backend * will render in parallel but has higher memory footprint and may suffer * performance issues due to using EGL image. + * + * @SINCE_2_1.18 */ EGL_IMAGE_OFFSCREEN_RENDERING, /** * The default mode is set to EGL_IMAGE_OFFSCREEN_RENDERING for backwards * compatibility. + * + * @SINCE_2_1.18 */ DEFAULT = EGL_IMAGE_OFFSCREEN_RENDERING }; diff --git a/packaging/dali-toolkit.spec b/packaging/dali-toolkit.spec index 1818b43..e9b88b9 100644 --- a/packaging/dali-toolkit.spec +++ b/packaging/dali-toolkit.spec @@ -14,6 +14,10 @@ BuildRequires: pkgconfig BuildRequires: pkgconfig(dlog) BuildRequires: pkgconfig(dali2-core) BuildRequires: pkgconfig(dali2-adaptor) +BuildRequires: pkgconfig(gles20) +BuildRequires: pkgconfig(glesv2) +BuildRequires: pkgconfig(egl) + BuildRequires: gettext BuildRequires: pkgconfig(libtzplatform-config) -- 2.7.4