X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;ds=sidebyside;f=dali%2Finternal%2Fgraphics%2Fgles-impl%2Fegl-graphics-controller.cpp;h=2206b12ca0e2e0785369eac7b9e9b519aa4ea316;hb=738af85a1b87813312227305117798edf85af875;hp=b17e6f804c48b6f16a53a3206ef1007280edd491;hpb=ba00d1444bc1ac81fd2793ecd36ee83e5a12fc7f;p=platform%2Fcore%2Fuifw%2Fdali-adaptor.git diff --git a/dali/internal/graphics/gles-impl/egl-graphics-controller.cpp b/dali/internal/graphics/gles-impl/egl-graphics-controller.cpp index b17e6f8..2206b12 100644 --- a/dali/internal/graphics/gles-impl/egl-graphics-controller.cpp +++ b/dali/internal/graphics/gles-impl/egl-graphics-controller.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * Copyright (c) 2024 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. @@ -17,21 +17,38 @@ // CLASS HEADER #include +// EXTERNAL INCLUDES +#include +#include + // INTERNAL INCLUDES #include #include #include #include +#include +#include +#include #include #include +#include #include #include #include #include #include +#include #include -#include -#include "gles-graphics-program.h" +#include + +#include + +#include + +// Uncomment the following define to turn on frame dumping +//#define ENABLE_COMMAND_BUFFER_FRAME_DUMP 1 +#include +DUMP_FRAME_INIT(); namespace Dali::Graphics { @@ -81,6 +98,24 @@ auto NewObject(const GfxCreateInfo& info, EglGraphicsController& controller, T&& } else // Use standard allocator { + // We are given all object for recycling + if(oldObject) + { + auto reusedObject = oldObject.release(); + // If succeeded, attach the object to the unique_ptr and return it back + if(static_cast(reusedObject)->TryRecycle(info, controller)) + { + return UPtr(reusedObject, GLESDeleter()); + } + else + { + // can't reuse so kill object by giving it back to original + // unique pointer. + oldObject.reset(reusedObject); + } + } + + // Create brand new object return UPtr(new GLESType(info, controller), GLESDeleter()); } } @@ -91,33 +126,61 @@ T0* CastObject(T1* apiObject) return static_cast(apiObject); } +// Maximum size of texture upload buffer. +const uint32_t TEXTURE_UPLOAD_MAX_BUFER_SIZE_MB = 1; + +DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_EGL, false); } // namespace -EglGraphicsController::~EglGraphicsController() = default; +EglGraphicsController::EglGraphicsController() +: mTextureDependencyChecker(*this), + mSyncPool(*this) +{ +} + +EglGraphicsController::~EglGraphicsController() +{ + while(!mPresentationCommandBuffers.empty()) + { + auto presentCommandBuffer = const_cast(mPresentationCommandBuffers.front()); + delete presentCommandBuffer; + mPresentationCommandBuffers.pop(); + } +} void EglGraphicsController::InitializeGLES(Integration::GlAbstraction& glAbstraction) { - DALI_LOG_RELEASE_INFO("Initializing New Graphics Controller #1\n"); - mGlAbstraction = &glAbstraction; - mContext = std::make_unique(*this); + DALI_LOG_RELEASE_INFO("Initializing Graphics Controller Phase 1\n"); + mGlAbstraction = &glAbstraction; + mContext = std::make_unique(*this, mGlAbstraction); + mCurrentContext = mContext.get(); } -void EglGraphicsController::Initialize(Integration::GlSyncAbstraction& glSyncAbstraction, +void EglGraphicsController::Initialize(Integration::GraphicsSyncAbstraction& syncImplementation, Integration::GlContextHelperAbstraction& glContextHelperAbstraction, Internal::Adaptor::GraphicsInterface& graphicsInterface) { - DALI_LOG_RELEASE_INFO("Initializing New Graphics Controller #2\n"); - mGlSyncAbstraction = &glSyncAbstraction; + DALI_LOG_RELEASE_INFO("Initializing Graphics Controller Phase 2\n"); + auto* syncImplPtr = static_cast(&syncImplementation); + + mEglSyncImplementation = syncImplPtr; mGlContextHelperAbstraction = &glContextHelperAbstraction; mGraphics = &graphicsInterface; } +void EglGraphicsController::FrameStart() +{ + mCapacity = 0; // Reset the command buffer capacity at the start of the frame. +} + void EglGraphicsController::SubmitCommandBuffers(const SubmitInfo& submitInfo) { for(auto& cmdbuf : submitInfo.cmdBuffer) { // Push command buffers - mCommandQueue.push(static_cast(cmdbuf)); + auto* commandBuffer = static_cast(cmdbuf); + mCapacity += commandBuffer->GetCapacity(); + mCommandQueue.push(commandBuffer); } // If flush bit set, flush all pending tasks @@ -127,13 +190,27 @@ void EglGraphicsController::SubmitCommandBuffers(const SubmitInfo& submitInfo) } } +void EglGraphicsController::WaitIdle() +{ + Flush(); +} + void EglGraphicsController::PresentRenderTarget(RenderTarget* renderTarget) { - // Use command buffer to execute presentation (we should pool it) - CommandBufferCreateInfo info; - info.SetLevel(CommandBufferLevel::PRIMARY); - info.fixedCapacity = 1; // only one command - auto presentCommandBuffer = new GLES::CommandBuffer(info, *this); + GLES::CommandBuffer* presentCommandBuffer{nullptr}; + if(mPresentationCommandBuffers.empty()) + { + CommandBufferCreateInfo info; + info.SetLevel(CommandBufferLevel::PRIMARY); + info.fixedCapacity = 1; // only one command + presentCommandBuffer = new GLES::CommandBuffer(info, *this); + } + else + { + presentCommandBuffer = const_cast(mPresentationCommandBuffers.front()); + presentCommandBuffer->Reset(); + mPresentationCommandBuffers.pop(); + } presentCommandBuffer->PresentRenderTarget(static_cast(renderTarget)); SubmitInfo submitInfo; submitInfo.cmdBuffer = {presentCommandBuffer}; @@ -143,6 +220,8 @@ void EglGraphicsController::PresentRenderTarget(RenderTarget* renderTarget) void EglGraphicsController::ResolvePresentRenderTarget(GLES::RenderTarget* renderTarget) { + mCurrentContext->InvalidateDepthStencilBuffers(); + auto* rt = static_cast(renderTarget); if(rt->GetCreateInfo().surface) { @@ -152,16 +231,16 @@ void EglGraphicsController::ResolvePresentRenderTarget(GLES::RenderTarget* rende } } -Integration::GlAbstraction& EglGraphicsController::GetGlAbstraction() +void EglGraphicsController::PostRender() { - DALI_ASSERT_DEBUG(mGlAbstraction && "Graphics controller not initialized"); - return *mGlAbstraction; + mTextureDependencyChecker.Reset(); + mSyncPool.AgeSyncObjects(); } -Integration::GlSyncAbstraction& EglGraphicsController::GetGlSyncAbstraction() +Integration::GlAbstraction& EglGraphicsController::GetGlAbstraction() { - DALI_ASSERT_DEBUG(mGlSyncAbstraction && "Graphics controller not initialized"); - return *mGlSyncAbstraction; + DALI_ASSERT_DEBUG(mGlAbstraction && "Graphics controller not initialized"); + return *mGlAbstraction; } Integration::GlContextHelperAbstraction& EglGraphicsController::GetGlContextHelperAbstraction() @@ -170,6 +249,12 @@ Integration::GlContextHelperAbstraction& EglGraphicsController::GetGlContextHelp return *mGlContextHelperAbstraction; } +Internal::Adaptor::EglSyncImplementation& EglGraphicsController::GetEglSyncImplementation() +{ + DALI_ASSERT_DEBUG(mEglSyncImplementation && "Sync implementation not initialized"); + return *mEglSyncImplementation; +} + Graphics::UniquePtr EglGraphicsController::CreateCommandBuffer( const CommandBufferCreateInfo& commandBufferCreateInfo, Graphics::UniquePtr&& oldCommandBuffer) @@ -215,7 +300,7 @@ Graphics::UniquePtr EglGraphicsController::CreatePipeline( Graphics::UniquePtr EglGraphicsController::CreateProgram( const ProgramCreateInfo& programCreateInfo, UniquePtr&& oldProgram) { - // Create program cache if needed + // Create pipeline cache if needed if(!mPipelineCache) { mPipelineCache = std::make_unique(*this); @@ -226,7 +311,12 @@ Graphics::UniquePtr EglGraphicsController::CreateProgram( Graphics::UniquePtr EglGraphicsController::CreateShader(const ShaderCreateInfo& shaderCreateInfo, Graphics::UniquePtr&& oldShader) { - return NewObject(shaderCreateInfo, *this, std::move(oldShader)); + // Create pipeline cache if needed + if(!mPipelineCache) + { + mPipelineCache = std::make_unique(*this); + } + return mPipelineCache->GetShader(shaderCreateInfo, std::move(oldShader)); } Graphics::UniquePtr EglGraphicsController::CreateSampler(const SamplerCreateInfo& samplerCreateInfo, Graphics::UniquePtr&& oldSampler) @@ -239,11 +329,94 @@ Graphics::UniquePtr EglGraphicsController::CreateRenderTarget(cons return NewObject(renderTargetCreateInfo, *this, std::move(oldRenderTarget)); } +Graphics::UniquePtr EglGraphicsController::CreateSyncObject(const SyncObjectCreateInfo& syncObjectCreateInfo, + UniquePtr&& oldSyncObject) +{ + if(GetGLESVersion() < GLES::GLESVersion::GLES_30) + { + return NewObject(syncObjectCreateInfo, *this, std::move(oldSyncObject)); + } + else + { + return NewObject(syncObjectCreateInfo, *this, std::move(oldSyncObject)); + } +} + +MemoryRequirements EglGraphicsController::GetBufferMemoryRequirements(Buffer& buffer) const +{ + MemoryRequirements requirements{}; + + auto gl = GetGL(); + if(gl) + { + GLint align; + gl->GetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &align); + requirements.alignment = align; + } + return requirements; +} + +TextureProperties EglGraphicsController::GetTextureProperties(const Texture& texture) +{ + const GLES::Texture* glesTexture = static_cast(&texture); + const auto& createInfo = glesTexture->GetCreateInfo(); + + TextureProperties properties{}; + properties.format = createInfo.format; + properties.compressed = glesTexture->IsCompressed(); + properties.extent2D = createInfo.size; + properties.nativeHandle = glesTexture->GetGLTexture(); + // TODO: Skip format1, emulated, packed, directWriteAccessEnabled of TextureProperties for now + + return properties; +} + const Graphics::Reflection& EglGraphicsController::GetProgramReflection(const Graphics::Program& program) { return static_cast(&program)->GetReflection(); } +void EglGraphicsController::CreateSurfaceContext(Dali::RenderSurfaceInterface* surface) +{ + std::unique_ptr context = std::make_unique(*this, mGlAbstraction); + mSurfaceContexts.push_back(std::move(std::make_pair(surface, std::move(context)))); +} + +void EglGraphicsController::DeleteSurfaceContext(Dali::RenderSurfaceInterface* surface) +{ + mSurfaceContexts.erase(std::remove_if( + mSurfaceContexts.begin(), mSurfaceContexts.end(), [surface](SurfaceContextPair& iter) { return surface == iter.first; }), + mSurfaceContexts.end()); +} + +void EglGraphicsController::ActivateResourceContext() +{ + mCurrentContext = mContext.get(); + mCurrentContext->GlContextCreated(); + if(!mSharedContext) + { + auto eglGraphics = dynamic_cast(mGraphics); + if(eglGraphics) + { + mSharedContext = eglGraphics->GetEglImplementation().GetContext(); + } + } +} + +void EglGraphicsController::ActivateSurfaceContext(Dali::RenderSurfaceInterface* surface) +{ + if(surface && mGraphics->IsResourceContextSupported()) + { + auto iter = std::find_if(mSurfaceContexts.begin(), mSurfaceContexts.end(), [surface](SurfaceContextPair& iter) { return (iter.first == surface); }); + + if(iter != mSurfaceContexts.end()) + { + mCurrentContext = iter->second.get(); + mCurrentContext->GlContextCreated(); + } + } +} + void EglGraphicsController::AddTexture(GLES::Texture& texture) { // Assuming we are on the correct context @@ -264,6 +437,8 @@ void EglGraphicsController::AddFramebuffer(GLES::Framebuffer& framebuffer) void EglGraphicsController::ProcessDiscardQueues() { + DALI_TRACE_SCOPE(gTraceFilter, "DALI_EGL_CONTROLLER_DISCARD_QUEUE"); + // Process textures ProcessDiscardQueue(mDiscardTextureQueue); @@ -273,10 +448,24 @@ void EglGraphicsController::ProcessDiscardQueues() // Process Framebuffers ProcessDiscardQueue(mDiscardFramebufferQueue); + // Process RenderPass + ProcessDiscardQueue(mDiscardRenderPassQueue); + + // Process RenderTarget + ProcessDiscardQueue(mDiscardRenderTargetQueue); + // Process pipelines - ProcessDiscardQueue(mDiscardPipelineQueue); + if(mPipelineCache && !mDiscardPipelineQueue.empty()) + { + mPipelineCache->MarkPipelineCacheFlushRequired(); + } + ProcessDiscardQueue(mDiscardPipelineQueue); // Process programs + if(mPipelineCache && !mDiscardProgramQueue.empty()) + { + mPipelineCache->MarkProgramCacheFlushRequired(); + } ProcessDiscardQueue(mDiscardProgramQueue); // Process shaders @@ -291,6 +480,7 @@ void EglGraphicsController::ProcessDiscardQueues() void EglGraphicsController::ProcessCreateQueues() { + DALI_TRACE_SCOPE(gTraceFilter, "DALI_EGL_CONTROLLER_CREATE_QUEUE"); // Process textures ProcessCreateQueue(mCreateTextureQueue); @@ -301,10 +491,18 @@ void EglGraphicsController::ProcessCreateQueues() ProcessCreateQueue(mCreateFramebufferQueue); } -void EglGraphicsController::ProcessCommandBuffer(GLES::CommandBuffer& commandBuffer) +void EglGraphicsController::ProcessCommandBuffer(const GLES::CommandBuffer& commandBuffer) { - for(auto& cmd : commandBuffer.GetCommands()) + auto count = 0u; + const auto commands = commandBuffer.GetCommands(count); + + DALI_TRACE_BEGIN_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_EGL_CONTROLLER_PROCESS", [&](std::ostringstream& oss) { + oss << "[commandCount:" << count << "]"; + }); + + for(auto i = 0u; i < count; ++i) { + auto& cmd = commands[i]; // process command switch(cmd.type) { @@ -315,24 +513,24 @@ void EglGraphicsController::ProcessCommandBuffer(GLES::CommandBuffer& commandBuf } case GLES::CommandType::BIND_TEXTURES: { - mContext->BindTextures(cmd.bindTextures.textureBindings); + mCurrentContext->BindTextures(cmd.bindTextures.textureBindings.Ptr(), cmd.bindTextures.textureBindingsCount); break; } case GLES::CommandType::BIND_VERTEX_BUFFERS: { - auto& bindings = cmd.bindVertexBuffers.vertexBufferBindings; - mContext->BindVertexBuffers(bindings); + auto bindings = cmd.bindVertexBuffers.vertexBufferBindings.Ptr(); + mCurrentContext->BindVertexBuffers(bindings, cmd.bindVertexBuffers.vertexBufferBindingsCount); break; } case GLES::CommandType::BIND_UNIFORM_BUFFER: { auto& bindings = cmd.bindUniformBuffers; - mContext->BindUniformBuffers(bindings.uniformBufferBindings, bindings.standaloneUniformsBufferBinding); + mCurrentContext->BindUniformBuffers(bindings.uniformBufferBindingsCount ? bindings.uniformBufferBindings.Ptr() : nullptr, bindings.uniformBufferBindingsCount, bindings.standaloneUniformsBufferBinding); break; } case GLES::CommandType::BIND_INDEX_BUFFER: { - mContext->BindIndexBuffer(cmd.bindIndexBuffer); + mCurrentContext->BindIndexBuffer(cmd.bindIndexBuffer); break; } case GLES::CommandType::BIND_SAMPLERS: @@ -342,22 +540,22 @@ void EglGraphicsController::ProcessCommandBuffer(GLES::CommandBuffer& commandBuf case GLES::CommandType::BIND_PIPELINE: { auto pipeline = static_cast(cmd.bindPipeline.pipeline); - mContext->BindPipeline(pipeline); + mCurrentContext->BindPipeline(pipeline); break; } case GLES::CommandType::DRAW: { - mContext->Flush(false, cmd.draw); + mCurrentContext->Flush(false, cmd.draw, mTextureDependencyChecker); break; } case GLES::CommandType::DRAW_INDEXED: { - mContext->Flush(false, cmd.draw); + mCurrentContext->Flush(false, cmd.draw, mTextureDependencyChecker); break; } case GLES::CommandType::DRAW_INDEXED_INDIRECT: { - mContext->Flush(false, cmd.draw); + mCurrentContext->Flush(false, cmd.draw, mTextureDependencyChecker); break; } case GLES::CommandType::SET_SCISSOR: // @todo Consider correcting for orientation here? @@ -367,14 +565,7 @@ void EglGraphicsController::ProcessCommandBuffer(GLES::CommandBuffer& commandBuf } case GLES::CommandType::SET_SCISSOR_TEST: { - if(cmd.scissorTest.enable) - { - mGlAbstraction->Enable(GL_SCISSOR_TEST); - } - else - { - mGlAbstraction->Disable(GL_SCISSOR_TEST); - } + mCurrentContext->SetScissorTestEnabled(cmd.scissorTest.enable); break; } case GLES::CommandType::SET_VIEWPORT: // @todo Consider correcting for orientation here? @@ -382,22 +573,110 @@ void EglGraphicsController::ProcessCommandBuffer(GLES::CommandBuffer& commandBuf mGlAbstraction->Viewport(cmd.viewport.region.x, cmd.viewport.region.y, cmd.viewport.region.width, cmd.viewport.region.height); break; } + + case GLES::CommandType::SET_COLOR_MASK: + { + mCurrentContext->ColorMask(cmd.colorMask.enabled); + break; + } + case GLES::CommandType::CLEAR_STENCIL_BUFFER: + { + mCurrentContext->ClearStencilBuffer(); + break; + } + case GLES::CommandType::CLEAR_DEPTH_BUFFER: + { + mCurrentContext->ClearDepthBuffer(); + break; + } + + case GLES::CommandType::SET_STENCIL_TEST_ENABLE: + { + mCurrentContext->SetStencilTestEnable(cmd.stencilTest.enabled); + break; + } + + case GLES::CommandType::SET_STENCIL_FUNC: + { + mCurrentContext->StencilFunc(cmd.stencilFunc.compareOp, + cmd.stencilFunc.reference, + cmd.stencilFunc.compareMask); + break; + } + + case GLES::CommandType::SET_STENCIL_WRITE_MASK: + { + mCurrentContext->StencilMask(cmd.stencilWriteMask.mask); + break; + } + + case GLES::CommandType::SET_STENCIL_OP: + { + mCurrentContext->StencilOp(cmd.stencilOp.failOp, + cmd.stencilOp.depthFailOp, + cmd.stencilOp.passOp); + break; + } + + case GLES::CommandType::SET_DEPTH_COMPARE_OP: + { + mCurrentContext->SetDepthCompareOp(cmd.depth.compareOp); + break; + } + case GLES::CommandType::SET_DEPTH_TEST_ENABLE: + { + mCurrentContext->SetDepthTestEnable(cmd.depth.testEnabled); + break; + } + case GLES::CommandType::SET_DEPTH_WRITE_ENABLE: + { + mCurrentContext->SetDepthWriteEnable(cmd.depth.writeEnabled); + break; + } + case GLES::CommandType::BEGIN_RENDERPASS: { - mContext->BeginRenderPass(cmd.beginRenderPass); + auto& renderTarget = *cmd.beginRenderPass.renderTarget; + const auto& targetInfo = renderTarget.GetCreateInfo(); + + if(targetInfo.surface) + { + // switch to surface context + mGraphics->ActivateSurfaceContext(static_cast(targetInfo.surface)); + } + else if(targetInfo.framebuffer) + { + // switch to resource context + mGraphics->ActivateResourceContext(); + } + + mCurrentContext->BeginRenderPass(cmd.beginRenderPass); + break; } case GLES::CommandType::END_RENDERPASS: { - mContext->EndRenderPass(); + mCurrentContext->EndRenderPass(mTextureDependencyChecker); + + // This sync object is to enable cpu to wait for rendering to complete, not gpu. + // It's only needed for reading the framebuffer texture in the client. + auto syncObject = const_cast(static_cast(cmd.endRenderPass.syncObject)); + if(syncObject) + { + syncObject->InitializeResource(); + } break; } case GLES::CommandType::PRESENT_RENDER_TARGET: { ResolvePresentRenderTarget(cmd.presentRenderTarget.targetToPresent); - // push this command buffer to the discard queue - mDiscardCommandBufferQueue.push(&commandBuffer); + // The command buffer will be pushed into the queue of presentation command buffers + // for further reuse. + if(commandBuffer.GetCreateInfo().fixedCapacity == 1) + { + mPresentationCommandBuffers.push(&commandBuffer); + } break; } case GLES::CommandType::EXECUTE_COMMAND_BUFFERS: @@ -406,32 +685,58 @@ void EglGraphicsController::ProcessCommandBuffer(GLES::CommandBuffer& commandBuf // todo: check validity of the secondaries // there are operations which are illigal to be done // within secondaries. - for(auto& buf : cmd.executeCommandBuffers.buffers) + auto buffers = cmd.executeCommandBuffers.buffers.Ptr(); + for(auto j = 0u; j < cmd.executeCommandBuffers.buffersCount; ++j) { - ProcessCommandBuffer(*static_cast(buf)); + auto& buf = buffers[j]; + ProcessCommandBuffer(*static_cast(buf)); } break; } + case GLES::CommandType::DRAW_NATIVE: + { + auto* info = &cmd.drawNative.drawNativeInfo; + + mCurrentContext->PrepareForNativeRendering(); + + if(info->glesNativeInfo.eglSharedContextStoragePointer) + { + auto* anyContext = reinterpret_cast(info->glesNativeInfo.eglSharedContextStoragePointer); + *anyContext = mSharedContext; + } + + CallbackBase::ExecuteReturn(*info->callback, info->userData); + + mCurrentContext->RestoreFromNativeRendering(); + break; + } } } + DALI_TRACE_END(gTraceFilter, "DALI_EGL_CONTROLLER_PROCESS"); } void EglGraphicsController::ProcessCommandQueues() { - // TODO: command queue per context, sync between queues should be - // done externally - currentFramebuffer = nullptr; + DUMP_FRAME_START(); while(!mCommandQueue.empty()) { auto cmdBuf = mCommandQueue.front(); mCommandQueue.pop(); + DUMP_FRAME_COMMAND_BUFFER(cmdBuf); ProcessCommandBuffer(*cmdBuf); } + + DUMP_FRAME_END(); } void EglGraphicsController::ProcessTextureUpdateQueue() { + if(mTextureUpdateRequests.empty()) + { + return; + } + DALI_TRACE_SCOPE(gTraceFilter, "DALI_EGL_CONTROLLER_TEXTURE_UPDATE"); while(!mTextureUpdateRequests.empty()) { TextureUpdateRequest& request = mTextureUpdateRequests.front(); @@ -439,31 +744,145 @@ void EglGraphicsController::ProcessTextureUpdateQueue() auto& info = request.first; auto& source = request.second; - if(source.sourceType == Graphics::TextureUpdateSourceInfo::Type::MEMORY) - { - // GPU memory must be already allocated (glTexImage2D()) - auto* texture = static_cast(info.dstTexture); - const auto& createInfo = texture->GetCreateInfo(); - - mGlAbstraction->PixelStorei(GL_UNPACK_ALIGNMENT, 1); - - mGlAbstraction->BindTexture(GL_TEXTURE_2D, texture->GetGLTexture()); - mGlAbstraction->TexSubImage2D(GL_TEXTURE_2D, - info.level, - info.dstOffset2D.x, - info.dstOffset2D.y, - info.srcExtent2D.width, - info.srcExtent2D.height, - GLES::GLTextureFormatType(createInfo.format).format, - GLES::GLTextureFormatType(createInfo.format).type, - source.memorySource.memory); - - // free staging memory - free(source.memorySource.memory); - } - else + switch(source.sourceType) { - // TODO: other sources + case Graphics::TextureUpdateSourceInfo::Type::MEMORY: + case Graphics::TextureUpdateSourceInfo::Type::PIXEL_DATA: + { + // GPU memory must be already allocated. + + // Check if it needs conversion + auto* texture = static_cast(info.dstTexture); + const auto& createInfo = texture->GetCreateInfo(); + auto srcFormat = GLES::GLTextureFormatType(info.srcFormat).format; + auto srcType = GLES::GLTextureFormatType(info.srcFormat).type; + auto destInternalFormat = GLES::GLTextureFormatType(createInfo.format).internalFormat; + auto destFormat = GLES::GLTextureFormatType(createInfo.format).format; + + // From render-texture.cpp + const bool isSubImage(info.dstOffset2D.x != 0 || info.dstOffset2D.y != 0 || + info.srcExtent2D.width != (createInfo.size.width / (1 << info.level)) || + info.srcExtent2D.height != (createInfo.size.height / (1 << info.level))); + + uint8_t* sourceBuffer = nullptr; + bool sourceBufferReleaseRequired = false; + if(source.sourceType == Graphics::TextureUpdateSourceInfo::Type::MEMORY) + { + sourceBuffer = reinterpret_cast(source.memorySource.memory); + sourceBufferReleaseRequired = true; + } + else + { + Dali::Integration::PixelDataBuffer pixelBufferData = Dali::Integration::GetPixelDataBuffer(source.pixelDataSource.pixelData); + + sourceBuffer = pixelBufferData.buffer + info.srcOffset; + sourceBufferReleaseRequired = Dali::Integration::IsPixelDataReleaseAfterUpload(source.pixelDataSource.pixelData) && info.srcOffset == 0u; + } + + auto sourceStride = info.srcStride; + std::vector tempBuffer; + + if(mGlAbstraction->TextureRequiresConverting(srcFormat, destFormat, isSubImage)) + { + // Convert RGB to RGBA if necessary. + if(texture->TryConvertPixelData(sourceBuffer, info.srcFormat, createInfo.format, info.srcSize, info.srcStride, info.srcExtent2D.width, info.srcExtent2D.height, tempBuffer)) + { + sourceBuffer = &tempBuffer[0]; + sourceStride = 0u; // Converted buffer compacted. make stride as 0. + srcFormat = destFormat; + srcType = GLES::GLTextureFormatType(createInfo.format).type; + } + } + + // Calculate the maximum mipmap level for the texture + texture->SetMaxMipMapLevel(std::max(texture->GetMaxMipMapLevel(), info.level)); + + GLenum bindTarget{GL_TEXTURE_2D}; + GLenum target{GL_TEXTURE_2D}; + + if(createInfo.textureType == Graphics::TextureType::TEXTURE_CUBEMAP) + { + bindTarget = GL_TEXTURE_CUBE_MAP; + target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + info.layer; + } + + mGlAbstraction->PixelStorei(GL_UNPACK_ALIGNMENT, 1); + mGlAbstraction->PixelStorei(GL_UNPACK_ROW_LENGTH, sourceStride); + + mCurrentContext->BindTexture(bindTarget, texture->GetTextureTypeId(), texture->GetGLTexture()); + + if(!isSubImage) + { + if(!texture->IsCompressed()) + { + mGlAbstraction->TexImage2D(target, + info.level, + destInternalFormat, + info.srcExtent2D.width, + info.srcExtent2D.height, + 0, + srcFormat, + srcType, + sourceBuffer); + } + else + { + mGlAbstraction->CompressedTexImage2D(target, + info.level, + destInternalFormat, + info.srcExtent2D.width, + info.srcExtent2D.height, + 0, + info.srcSize, + sourceBuffer); + } + } + else + { + if(!texture->IsCompressed()) + { + mGlAbstraction->TexSubImage2D(target, + info.level, + info.dstOffset2D.x, + info.dstOffset2D.y, + info.srcExtent2D.width, + info.srcExtent2D.height, + srcFormat, + srcType, + sourceBuffer); + } + else + { + mGlAbstraction->CompressedTexSubImage2D(target, + info.level, + info.dstOffset2D.x, + info.dstOffset2D.y, + info.srcExtent2D.width, + info.srcExtent2D.height, + srcFormat, + info.srcSize, + sourceBuffer); + } + } + + if(sourceBufferReleaseRequired && sourceBuffer != nullptr) + { + if(source.sourceType == Graphics::TextureUpdateSourceInfo::Type::MEMORY) + { + free(reinterpret_cast(sourceBuffer)); + } + else + { + Dali::Integration::ReleasePixelDataBuffer(source.pixelDataSource.pixelData); + } + } + break; + } + default: + { + // TODO: other sources + break; + } } mTextureUpdateRequests.pop(); @@ -489,15 +908,24 @@ void EglGraphicsController::UpdateTextures(const std::vector& // TODO: using PBO with GLES3, this is just naive // oldschool way - char* stagingBuffer = reinterpret_cast(malloc(info.srcSize)); - std::copy(&reinterpret_cast(source.memorySource.memory)[info.srcOffset], - reinterpret_cast(source.memorySource.memory) + info.srcSize, - stagingBuffer); + uint8_t* stagingBuffer = reinterpret_cast(malloc(info.srcSize)); + + uint8_t* srcMemory = &reinterpret_cast(source.memorySource.memory)[info.srcOffset]; + + std::copy(srcMemory, srcMemory + info.srcSize, stagingBuffer); + + mTextureUploadTotalCPUMemoryUsed += info.srcSize; // store staging buffer source.memorySource.memory = stagingBuffer; break; } + case Graphics::TextureUpdateSourceInfo::Type::PIXEL_DATA: + { + // Increase CPU memory usage since ownership of PixelData is now on mTextureUpdateRequests. + mTextureUploadTotalCPUMemoryUsed += info.srcSize; + break; + } case Graphics::TextureUpdateSourceInfo::Type::BUFFER: { // TODO, with PBO support @@ -510,16 +938,48 @@ void EglGraphicsController::UpdateTextures(const std::vector& } } } + + // If upload buffer exceeds maximum size, flush. + if(mTextureUploadTotalCPUMemoryUsed > TEXTURE_UPLOAD_MAX_BUFER_SIZE_MB * 1024 * 1024) + { + Flush(); + mTextureUploadTotalCPUMemoryUsed = 0; + } } -Graphics::UniquePtr EglGraphicsController::MapBufferRange(const MapBufferInfo& mapInfo) +void EglGraphicsController::ProcessTextureMipmapGenerationQueue() +{ + if(mTextureMipmapGenerationRequests.empty()) + { + return; + } + DALI_TRACE_SCOPE(gTraceFilter, "DALI_EGL_CONTROLLER_TEXTURE_MIPMAP"); + while(!mTextureMipmapGenerationRequests.empty()) + { + auto* texture = mTextureMipmapGenerationRequests.front(); + + mCurrentContext->BindTexture(texture->GetGlTarget(), texture->GetTextureTypeId(), texture->GetGLTexture()); + mCurrentContext->GenerateMipmap(texture->GetGlTarget()); + + mTextureMipmapGenerationRequests.pop(); + } +} + +void EglGraphicsController::GenerateTextureMipmaps(const Graphics::Texture& texture) { - mGraphics->ActivateResourceContext(); + mTextureMipmapGenerationRequests.push(static_cast(&texture)); +} +Graphics::UniquePtr EglGraphicsController::MapBufferRange(const MapBufferInfo& mapInfo) +{ // Mapping buffer requires the object to be created NOW // Workaround - flush now, otherwise there will be given a staging buffer // in case when the buffer is not there yet - ProcessCreateQueues(); + if(!mCreateBufferQueue.empty()) + { + mGraphics->ActivateResourceContext(); + ProcessCreateQueues(); + } if(GetGLESVersion() < GLES::GLESVersion::GLES_30) { @@ -541,4 +1001,57 @@ GLES::PipelineCache& EglGraphicsController::GetPipelineCache() const return *mPipelineCache; } +Graphics::Texture* EglGraphicsController::CreateTextureByResourceId(uint32_t resourceId, const Graphics::TextureCreateInfo& createInfo) +{ + Graphics::Texture* ret = nullptr; + Graphics::UniquePtr texture; + + auto iter = mExternalTextureResources.find(resourceId); + DALI_ASSERT_ALWAYS(iter == mExternalTextureResources.end()); + + texture = CreateTexture(createInfo, std::move(texture)); + + ret = texture.get(); + + mExternalTextureResources.insert(std::make_pair(resourceId, std::move(texture))); + + return ret; +} + +void EglGraphicsController::DiscardTextureFromResourceId(uint32_t resourceId) +{ + auto iter = mExternalTextureResources.find(resourceId); + if(iter != mExternalTextureResources.end()) + { + mExternalTextureResources.erase(iter); + } +} + +Graphics::Texture* EglGraphicsController::GetTextureFromResourceId(uint32_t resourceId) +{ + Graphics::Texture* ret = nullptr; + + auto iter = mExternalTextureResources.find(resourceId); + if(iter != mExternalTextureResources.end()) + { + ret = iter->second.get(); + } + + return ret; +} + +Graphics::UniquePtr EglGraphicsController::ReleaseTextureFromResourceId(uint32_t resourceId) +{ + Graphics::UniquePtr texture; + + auto iter = mExternalTextureResources.find(resourceId); + if(iter != mExternalTextureResources.end()) + { + texture = std::move(iter->second); + mExternalTextureResources.erase(iter); + } + + return texture; +} + } // namespace Dali::Graphics