From 58cdea07c4fd3f5b0fac0a67db4d01104e1a153d Mon Sep 17 00:00:00 2001 From: Cryolitia PukNgae Date: Wed, 25 Sep 2024 12:46:21 +0800 Subject: [PATCH] feat: Androind support [2 of series] --- README.md | 15 +- cmake/SkiaUtils.cmake | 1 + include/Utility/Log.hpp | 60 ++++- include/VGG/Container/AndroidContainer.hpp | 48 ++++ lib/CMakeLists.txt | 9 + src/Adapter/CMakeLists.txt | 7 + .../Container/AndroidGraphicsContext.cpp | 238 ++++++++++++++++++ .../Container/AndroidGraphicsContext.h | 48 ++++ src/CMakeLists.txt | 7 + src/Entry/Container/AndroidContainer.cpp | 85 +++++++ src/Entry/Container/CMakeLists.txt | 26 +- src/Entry/Container/Container.cpp | 4 + 12 files changed, 540 insertions(+), 8 deletions(-) create mode 100644 include/VGG/Container/AndroidContainer.hpp create mode 100644 src/Adapter/Container/AndroidGraphicsContext.cpp create mode 100644 src/Adapter/Container/AndroidGraphicsContext.h create mode 100644 src/Entry/Container/AndroidContainer.cpp diff --git a/README.md b/README.md index a21afae00..367d02a6a 100644 --- a/README.md +++ b/README.md @@ -115,14 +115,19 @@ You need to download Android NDK r27 from here: https://github.com/android/ndk/r Build & install `vgg_container` libraries for [vgg_android](https://github.com/verygoodgraphics/vgg_android). ```bash -mkdir build.android -cd build.android # For x86_64 -cmake .. -DCMAKE_TOOLCHAIN_FILE=/build/cmake/android.toolchain.cmake -DVGG_VAR_TARGET="Android-x86_64" -DANDROID_NDK= -DANDROID_PLATFORM=android-24 +mkdir build.android.x86_64 +cd build.android.x86_64 +cmake .. -DCMAKE_TOOLCHAIN_FILE=/build/cmake/android.toolchain.cmake -DVGG_VAR_TARGET="Android-x86_64" -DANDROID_NDK= -DANDROID_PLATFORM=android-24 -DANDROID_ABI=x86_64 +cmake --build . --parallel +cmake --install . --prefix /VggRuntime/external/x86_64 # For arm64 -cmake .. -DCMAKE_TOOLCHAIN_FILE=/build/cmake/android.toolchain.cmake -DVGG_VAR_TARGET="Android-arm64-v8a" -DANDROID_NDK= -DANDROID_PLATFORM=android-24 +cd .. +mkdir build.android.arm64 +cd build.android.arm64 +cmake .. -DCMAKE_TOOLCHAIN_FILE=/build/cmake/android.toolchain.cmake -DVGG_VAR_TARGET="Android-arm64-v8a" -DANDROID_NDK= -DANDROID_PLATFORM=android-24 -DANDROID_ABI=arm64-v8a cmake --build . --parallel -cmake --install . --prefix +cmake --install . --prefix /VggRuntime/external/arm64-v8a ``` #### Qt building example diff --git a/cmake/SkiaUtils.cmake b/cmake/SkiaUtils.cmake index 61b59939c..4e4d78e36 100644 --- a/cmake/SkiaUtils.cmake +++ b/cmake/SkiaUtils.cmake @@ -235,6 +235,7 @@ skia_use_expat=true skia_use_freetype=true skia_use_system_expat=false skia_use_system_icu=false +skia_use_egl=true skia_use_vulkan=${VULKAN_AVAILABLE}") cmake_minimum_required(VERSION 3.19) # for string(JSON ...) diff --git a/include/Utility/Log.hpp b/include/Utility/Log.hpp index c624f6748..f16ddabb1 100644 --- a/include/Utility/Log.hpp +++ b/include/Utility/Log.hpp @@ -43,6 +43,11 @@ #define MOD4 KMOD_CTRL #endif +#ifdef __ANDROID__ +#include +#include +#endif + /**************************************************************************************************/ /* Common */ /**************************************************************************************************/ @@ -159,7 +164,7 @@ inline std::string scalarfmt(double v, size_t maxFractionalDigitNums = 2) #define _BG_WHITE_ "47" /// Common formatters -#if !defined(EMSCRIPTEN) && !defined(VGG_TARGET_PLATFORM_iOS) +#if !defined(EMSCRIPTEN) && !defined(VGG_TARGET_PLATFORM_iOS) && !defined(VGG_TARGET_PLATFORM_Android) #define FAIL_COLOR(X) "\x1b[" _FMT_BRIGHT_ ";" _FG_RED_ "m" X "\x1b[" _FMT_RESET_ "m" #define WARN_COLOR(X) "\x1b[" _FMT_BRIGHT_ ";" _FG_YELLOW_ "m" X "\x1b[" _FMT_RESET_ "m" #define INFO_COLOR(X) "\x1b[" _FMT_BRIGHT_ ";" _FG_BLUE_ "m" X "\x1b[" _FMT_RESET_ "m" @@ -198,6 +203,36 @@ static inline FILE* _log_file_() } // namespace LOG /// Private logging macros + +#if defined(__ANDROID__) + +// Android don't use stdout, stderr, use android log instead + +#define _MSG_(type, log, msg, ...) \ + do \ + { \ + constexpr auto _fnTypeToAndroidLevel = [](const char* _str_type) constexpr \ + { \ + char _c = _str_type[0]; \ + switch (_c) \ + { \ + case 'F': \ + return ANDROID_LOG_ERROR; \ + case 'W': \ + return ANDROID_LOG_WARN; \ + case 'I': \ + return ANDROID_LOG_INFO; \ + case 'D': \ + return ANDROID_LOG_DEBUG; \ + default: \ + return ANDROID_LOG_VERBOSE; \ + } \ + }; \ + __android_log_print(_fnTypeToAndroidLevel(type), "VGG", "" msg, ##__VA_ARGS__); \ + } while (0) + +#else + #define _MSG_(type, log, msg, ...) \ do \ { \ @@ -205,12 +240,31 @@ static inline FILE* _log_file_() fflush((log)); \ } while (0) +#endif + /// Public logging macros #define FAIL(msg, ...) \ do \ { \ _MSG_("FAIL", LOG::_log_file_(), FAIL_COLOR(msg), ##__VA_ARGS__); \ } while (0) + +#if defined(__ANDROID__) +// make abort message on one line, a fatal log will implicitly call set_abort_message +// format: ("%s:%d " msg, __FILENAME__, __LINE__, ##__VA_ARGS__) +#define FAILED(msg, ...) \ + do \ + { \ + __android_log_print( \ + ANDROID_LOG_FATAL, \ + "VGG", \ + "%s:%d " msg, \ + __FILENAME__, \ + __LINE__, \ + ##__VA_ARGS__); \ + std::abort(); \ + } while (0) +#else #define FAILED(msg, ...) \ do \ { \ @@ -218,6 +272,8 @@ static inline FILE* _log_file_() FAIL(msg, ##__VA_ARGS__); \ std::abort(); \ } while (0) +#endif + #define WARN(msg, ...) \ do \ { \ @@ -259,7 +315,7 @@ static inline FILE* _log_file_() { \ if (!(x)) \ { \ - if (strcmp((msg), "")) \ + if (strcmp((msg), "") != 0) \ { \ FAILED(STR(x) " : " msg, ##__VA_ARGS__); \ } \ diff --git a/include/VGG/Container/AndroidContainer.hpp b/include/VGG/Container/AndroidContainer.hpp new file mode 100644 index 000000000..652cf3a90 --- /dev/null +++ b/include/VGG/Container/AndroidContainer.hpp @@ -0,0 +1,48 @@ +/* +* Copyright 2023-2024 VeryGoodGraphics LTD + * + * Licensed under the VGG License, Version 1.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.verygoodgraphics.com/licenses/LICENSE-1.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. + */ + +#pragma once + +#include "Event.hpp" +#include "IContainer.hpp" +#include "ISdk.hpp" +#include "VggTypes.hpp" + +#include + +struct ANativeWindow; +typedef struct ANativeWindow ANativeWindow; + +namespace VGG +{ + +class AndroidContainerImpl; +class AndroidContainer : public IContainer +{ + std::unique_ptr m_impl; + +public: + + AndroidContainer(); + ~AndroidContainer(); + + void setView(ANativeWindow* window); + +private: + virtual IContainer* container() override; +}; + +} // namespace VGG diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 82ffb9a21..883ef6d9f 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -236,6 +236,7 @@ if(VGG_VAR_TARGET MATCHES "^iOS") ) elseif(VGG_VAR_TARGET MATCHES "^Android") target_link_directories(vgg_libnode PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/${LIB_NODE_FOLDER}/bin) + target_link_directories(vgg_libnode PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/${LIB_NODE_FOLDER}/bin/${ANDROID_ABI}) elseif(APPLE) target_link_directories(vgg_libnode PUBLIC ${NODE_FOLDER}/out/Release) if (VGG_VAR_TARGET_ARCH STREQUAL "ARM") @@ -322,3 +323,11 @@ if(VGG_CONTAINER_FOR_QT) ) endif() endif() + +if(VGG_VAR_TARGET MATCHES "^Android") + # install libnode.so + install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${LIB_NODE_FOLDER}/bin/${ANDROID_ABI}/libnode.so + COMPONENT container + DESTINATION lib + ) +endif () diff --git a/src/Adapter/CMakeLists.txt b/src/Adapter/CMakeLists.txt index 81f2b831b..28616c95f 100644 --- a/src/Adapter/CMakeLists.txt +++ b/src/Adapter/CMakeLists.txt @@ -20,6 +20,13 @@ else() target_include_directories(vgg_adapter PUBLIC ${CMAKE_SOURCE_DIR}/include/Application/Event ) + elseif(VGG_VAR_TARGET MATCHES "^Android") + target_sources(vgg_adapter PRIVATE + Container/AndroidGraphicsContext.cpp + ) + target_include_directories(vgg_adapter PUBLIC + ${CMAKE_SOURCE_DIR}/include/Application/Event + ) elseif(VGG_CONTAINER_FOR_QT) target_sources(vgg_adapter PRIVATE Container/QtGraphicsContext.cpp diff --git a/src/Adapter/Container/AndroidGraphicsContext.cpp b/src/Adapter/Container/AndroidGraphicsContext.cpp new file mode 100644 index 000000000..2e9d50848 --- /dev/null +++ b/src/Adapter/Container/AndroidGraphicsContext.cpp @@ -0,0 +1,238 @@ +/* + * Copyright 2023-2024 VeryGoodGraphics LTD + * + * Licensed under the VGG License, Version 1.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.verygoodgraphics.com/licenses/LICENSE-1.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 "AndroidGraphicsContext.h" + +// skia +#include "include/core/SkColorSpace.h" +#include "include/core/SkSurface.h" +#include "include/core/SkGraphics.h" +#include "include/gpu/GrDirectContext.h" +#include "include/android/SkSurfaceAndroid.h" +#include "include/gpu/gl/GrGLInterface.h" +#include "include/gpu/GrDirectContext.h" +#include "include/gpu/GrBackendSurface.h" +#include "include/gpu/ganesh/SkSurfaceGanesh.h" +#include "include/core/SkSurface.h" +#include "include/gpu/GrDirectContext.h" +#include "src/gpu/ganesh/GrDirectContextPriv.h" +#include "src/gpu/ganesh/GrGpu.h" + +// android +#include +#include + +// egl +#include +#include +#include +#include +#include + +#include "Utility/Log.hpp" + +#include +#include + +namespace VGG +{ +// impl ------------------------------------------------------------------------ +class AndroidGraphicsContextImpl +{ + + // RGBA_8888 + static constexpr uint32_t kBytePerPixel = 4; + + AndroidGraphicsContext* m_api = nullptr; + ANativeWindow* m_window = nullptr; + // int m_currentBufferIndex = -1; + sk_sp m_context; + EGLDisplay m_egl_display = nullptr; + EGLSurface m_egl_surface = nullptr; + EGLContext m_egl_context = nullptr; + +public: + AndroidGraphicsContextImpl(AndroidGraphicsContext* api, ANativeWindow* window) + : m_api(api) + { + ASSERT(window); + setView(window); + } + + int width() + { + return m_window ? ANativeWindow_getWidth(m_window) : 0; + } + + int height() + { + return m_window ? ANativeWindow_getHeight(m_window) : 0; + } + + bool swap() + { + bool rc = false; + if (m_window != nullptr && m_egl_display != nullptr && m_egl_surface != nullptr) + { + m_context->flush(); + rc = eglSwapBuffers(m_egl_display, m_egl_surface); + } + __android_log_print(ANDROID_LOG_DEBUG, "VGG", "swap %d", rc); + return true; + } + + SurfaceCreateProc surfaceCreateProc() + { + auto fn = [](GrRecordingContext* context, int w, int h, const VGG::layer::ContextConfig& cfg) + { + + GrGLint buffer; + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &buffer); + + GrGLFramebufferInfo bufInfo = {}; + bufInfo.fFormat = GL_RGBA8; + bufInfo.fFBOID = buffer; + + GrBackendRenderTarget desc(w, h, 0, 8, bufInfo); + + auto surface = ::SkSurfaces::WrapBackendRenderTarget( + context, + desc, + kBottomLeft_GrSurfaceOrigin, + kRGBA_8888_SkColorType, + nullptr, + nullptr, + nullptr); + + ASSERT(surface); + + return surface; + }; + return fn; + } + + ContextCreateProc contextCreateProc() + { + return [this]() + { + initContext(); + return m_context; + }; + } + + void onInitProperties(VGG::layer::ContextProperty& property) + { + // TODO: implement dpi scaling + property.dpiScaling = 1.f; + property.api = VGG::layer::EGraphicsAPIBackend::API_CUSTOM; + } + + void initContext() + { + if (m_context) + { + return; + } + + ASSERT(m_window); + + // get current display, context and surface + EGLDisplay display = eglGetCurrentDisplay(); + EGLContext context = eglGetCurrentContext(); + EGLSurface surface = eglGetCurrentSurface(EGL_DRAW); + ASSERT(display && context && surface); + m_egl_display = display; + m_egl_context = context; + m_egl_surface = surface; + + auto grGlInterface = GrGLMakeNativeInterface(); + ASSERT(grGlInterface); + m_context = GrDirectContext::MakeGL(grGlInterface); + ASSERT(m_context); + } + +private: + void setView(ANativeWindow* window) + { + m_window = window; + if (!m_window) + { + // clear the context + m_context.reset(); + m_egl_display = nullptr; + m_egl_surface = nullptr; + m_egl_context = nullptr; + } + } +}; + +// api ------------------------------------------------------------------------ +AndroidGraphicsContext::AndroidGraphicsContext(ANativeWindow* window) + : m_impl(new AndroidGraphicsContextImpl(this, window)) +{ +} + +int AndroidGraphicsContext::width() +{ + return m_impl->width(); +} + +int AndroidGraphicsContext::height() +{ + return m_impl->height(); +} + +bool AndroidGraphicsContext::swap() +{ + return m_impl->swap(); +} + +bool AndroidGraphicsContext::makeCurrent() +{ + return true; +} + +void AndroidGraphicsContext::shutdown() +{ + return; +} + +void* AndroidGraphicsContext::contextInfo() +{ + return nullptr; +} + +bool AndroidGraphicsContext::onInit() +{ + return true; +} + +SurfaceCreateProc AndroidGraphicsContext::surfaceCreateProc() +{ + return m_impl->surfaceCreateProc(); +} + +ContextCreateProc AndroidGraphicsContext::contextCreateProc() +{ + return m_impl->contextCreateProc(); +} + +void AndroidGraphicsContext::onInitProperties(VGG::layer::ContextProperty& property) +{ + m_impl->onInitProperties(property); +} + +} // namespace VGG diff --git a/src/Adapter/Container/AndroidGraphicsContext.h b/src/Adapter/Container/AndroidGraphicsContext.h new file mode 100644 index 000000000..c0c21ef27 --- /dev/null +++ b/src/Adapter/Container/AndroidGraphicsContext.h @@ -0,0 +1,48 @@ +/* + * Copyright 2023-2024 VeryGoodGraphics LTD + * + * Licensed under the VGG License, Version 1.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.verygoodgraphics.com/licenses/LICENSE-1.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. + */ + +#pragma once + +#include "Layer/Graphics/ContextSkBase.hpp" +#include "Layer/Graphics/GraphicsContext.hpp" + +#include "VGG/Container/AndroidContainer.hpp" + +namespace VGG +{ +class AndroidGraphicsContextImpl; + +class AndroidGraphicsContext : public VGG::layer::SkiaGraphicsContext +{ + AndroidGraphicsContextImpl* m_impl; + +public: + AndroidGraphicsContext(ANativeWindow* window); + + int width(); + int height(); + + virtual bool swap() override; + virtual bool makeCurrent() override; + virtual void shutdown() override; + virtual void* contextInfo() override; + virtual bool onInit() override; + virtual void onInitProperties(VGG::layer::ContextProperty& property) override; + virtual SurfaceCreateProc surfaceCreateProc() override; + virtual ContextCreateProc contextCreateProc() override; +}; + +} // namespace VGG diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6efbb09e5..9dc628b65 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -6,6 +6,13 @@ else() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror -Wno-unknown-warning-option -Wno-deprecated-declarations -Wno-nonnull -Wno-unused-private-field -Wno-error=unused-function -Wno-maybe-uninitialized -Wno-error=restrict") endif() +if (VGG_VAR_TARGET MATCHES "^Android") + # -fPIC + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -fvisibility=protected") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC -fvisibility=protected") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,max-page-size=16384 -Wl,--no-allow-shlib-undefined,--no-undefined -Wl,-z,defs,-z,now,-z,relro") +endif () + include_directories(${CMAKE_CURRENT_SOURCE_DIR}) include(GetGitRevisionDescription) diff --git a/src/Entry/Container/AndroidContainer.cpp b/src/Entry/Container/AndroidContainer.cpp new file mode 100644 index 000000000..74eee2827 --- /dev/null +++ b/src/Entry/Container/AndroidContainer.cpp @@ -0,0 +1,85 @@ +/* + * Copyright 2023-2024 VeryGoodGraphics LTD + * + * Licensed under the VGG License, Version 1.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.verygoodgraphics.com/licenses/LICENSE-1.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 "Container.hpp" + +#include "Adapter/Container/AndroidGraphicsContext.h" + +#include "VGG/Container/AndroidContainer.hpp" + +namespace VGG +{ + +// impl ---------------------------------------------------------------------- +class AndroidContainerImpl : public IContainer +{ + friend AndroidContainer; + + AndroidContainer* m_api; + std::unique_ptr m_impl; + +public: + AndroidContainerImpl(AndroidContainer* api) + : m_api(api) + { + m_impl.reset(new Container); + } + + IContainer* container() override + { + return m_impl.get(); + } + + void setView(ANativeWindow* window) + { + INFO("AndroidContainerImpl::setView, window: %p", window); + if (window) + { + auto androidContext = new AndroidGraphicsContext(window); + std::unique_ptr graphicsContext{ androidContext }; + m_impl->setGraphicsContext( + graphicsContext, + androidContext->width(), + androidContext->height()); + __android_log_print(ANDROID_LOG_DEBUG, "VGG", "setGraphicsContext"); + } + else + { + // TODO: remove graphics context, eg active window is paused, lose GL context + } + } +}; + +// api ---------------------------------------------------------------------- + +AndroidContainer::AndroidContainer() + : m_impl(new AndroidContainerImpl(this)) +{ +} + +AndroidContainer::~AndroidContainer() = default; + +void AndroidContainer::setView(ANativeWindow* window) +{ + m_impl->setView(window); +} + +IContainer* AndroidContainer::container() +{ + return m_impl.get(); +} + +} // namespace VGG diff --git a/src/Entry/Container/CMakeLists.txt b/src/Entry/Container/CMakeLists.txt index 9e7f606ee..8ff23d085 100644 --- a/src/Entry/Container/CMakeLists.txt +++ b/src/Entry/Container/CMakeLists.txt @@ -1,4 +1,4 @@ -if(VGG_CONTAINER_FOR_QT) +if(VGG_CONTAINER_FOR_QT OR VGG_VAR_TARGET MATCHES "^Android") add_library(vgg_container SHARED Container.cpp ) @@ -12,6 +12,10 @@ if(VGG_VAR_TARGET MATCHES "^iOS") target_sources(vgg_container PRIVATE MetalContainer.cpp ) +elseif(VGG_VAR_TARGET MATCHES "^Android") + target_sources(vgg_container PRIVATE + AndroidContainer.cpp + ) elseif(VGG_CONTAINER_FOR_QT) target_sources(vgg_container PRIVATE QtContainer.cpp @@ -46,6 +50,21 @@ else() ) endif() +if(VGG_VAR_TARGET MATCHES "^Android") + target_link_libraries(vgg_container PUBLIC + vgg_adapter + vgg_application + vgg_libnode + node + + android + GLESv2 + EGL + log + ) + target_link_options(vgg_container PRIVATE "-Bsymbolic") +endif () + install(TARGETS vgg_container COMPONENT container DESTINATION lib @@ -92,6 +111,11 @@ if(VGG_VAR_TARGET MATCHES "^iOS") install(DIRECTORY ${CMAKE_SOURCE_DIR}/lib/nodejs-mobile-ios/NodeMobile.xcframework COMPONENT container DESTINATION .) +elseif(VGG_VAR_TARGET MATCHES "^Android") + install(FILES + ${CMAKE_SOURCE_DIR}/include/VGG/Container/AndroidContainer.hpp + COMPONENT container + DESTINATION VGG) elseif(VGG_CONTAINER_FOR_QT) install(FILES ${CMAKE_SOURCE_DIR}/include/VGG/Container/QtContainer.hpp diff --git a/src/Entry/Container/Container.cpp b/src/Entry/Container/Container.cpp index 4f102a200..37eebf372 100644 --- a/src/Entry/Container/Container.cpp +++ b/src/Entry/Container/Container.cpp @@ -114,6 +114,10 @@ class ContainerImpl const char* designDocSchemaFilePath = nullptr, const char* layoutDocSchemaFilePath = nullptr) override { + INFO("ContainerImpl::load, filePath: %s, designDocSchemaFilePath: %s, layoutDocSchemaFilePath: %s", + filePath.c_str(), + designDocSchemaFilePath ? designDocSchemaFilePath : "", + layoutDocSchemaFilePath ? layoutDocSchemaFilePath : ""); return m_mainComposer->controller()->start( filePath, designDocSchemaFilePath,