From: djsollen Date: Thu, 21 Apr 2016 14:59:44 +0000 (-0700) Subject: VulkanViewer on Android X-Git-Tag: accepted/tizen/5.0/unified/20181102.025319~129^2~877 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=12d62a7d51a1826e221ffd34224d01c9bec49bab;p=platform%2Fupstream%2FlibSkiaSharp.git VulkanViewer on Android GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1903253003 Review URL: https://codereview.chromium.org/1903253003 --- diff --git a/DEPS b/DEPS index 1710af6..dcec234 100644 --- a/DEPS +++ b/DEPS @@ -53,6 +53,7 @@ deps = { "third_party/externals/shaderc2/third_party/googletest" : "https://github.com/google/googletest.git@d225acc90bc3a8c420a9bcd1f033033c1ccd7fe0", "third_party/externals/shaderc2/third_party/glslang" : "https://github.com/google/glslang.git@e1cd410d9c03a24c00c570c91a99cad88bb475d1", "third_party/externals/shaderc2/third_party/spirv-tools" : "https://github.com/KhronosGroup/SPIRV-Tools.git@009c4358b5a1c93203166b3ed60a548f63522e81", + "third_party/externals/shaderc2/third_party/android-cmake" : "https://github.com/taka-no-me/android-cmake.git@556cc14296c226f753a3778d99d8b60778b7df4f", } deps_os = { diff --git a/gyp/most.gyp b/gyp/most.gyp index d4babe1..9e8c3aa 100644 --- a/gyp/most.gyp +++ b/gyp/most.gyp @@ -59,6 +59,11 @@ 'android_system.gyp:VisualBench_APK', ], }], + [ 'skia_vulkan == 1', { + 'dependencies': [ + 'android_system.gyp:VulkanViewer_APK', + ], + }], ], }], ['skia_os == "ios"', { @@ -79,7 +84,7 @@ 'skiaserve.gyp:skiaserve', ], }], - [ 'skia_vulkan == 0 or skia_os != "win"', { + [ 'skia_vulkan == 0 or skia_os != "win" or skia_os != "android"', { 'dependencies!': [ 'vulkanviewer.gyp:vulkanviewer', ], diff --git a/gyp/shaderc.gyp b/gyp/shaderc.gyp index 7732e22..3202685 100644 --- a/gyp/shaderc.gyp +++ b/gyp/shaderc.gyp @@ -28,13 +28,14 @@ 'shaderc_project_type' : 'ninja', }], ], - 'shaderc_out_path': '../out/<(CONFIGURATION_NAME)/shaderc_out_<(skia_arch_type)', + 'shaderc_out_path': '<(PRODUCT_DIR)/shaderc_out_<(skia_arch_type)', }, # Export out of nested variables. 'shaderc_build_configuration': '<(shaderc_build_configuration)', 'shaderc_project_type': '<(shaderc_project_type)', 'shaderc_out_path': '<(shaderc_out_path)', 'shaderc_lib_name': '<(shaderc_lib_name)', + 'android_toolchain%': '', # On Windows the library winds up inside a 'Debug' or 'Release' dir, not so # with ninja project build. @@ -62,12 +63,12 @@ 'outputs': [ '<(shaderc_lib_full_path)', ], - 'action': ['python', '../tools/build_shaderc.py', '-s', '../third_party/externals/shaderc2', '-o', '<(shaderc_out_path)', '-a', '<(skia_arch_type)', '-t', '<(shaderc_build_configuration)', '-p', '<(shaderc_project_type)'], + 'action': ['python', '../tools/build_shaderc.py', '-s', '../third_party/externals/shaderc2', '-o', '<(shaderc_out_path)', '-a', '<(skia_arch_type)', '-t', '<(shaderc_build_configuration)', '-p', '<(shaderc_project_type)', '-c', '<(android_toolchain)'], }, ], 'copies': [ { - 'destination': '../out/<(CONFIGURATION_NAME)', + 'destination': '<(PRODUCT_DIR)', 'files': ['<(shaderc_lib_full_path)'], }, ], diff --git a/gyp/vulkanviewer.gyp b/gyp/vulkanviewer.gyp index fb67e55..91c175b 100644 --- a/gyp/vulkanviewer.gyp +++ b/gyp/vulkanviewer.gyp @@ -42,6 +42,27 @@ 'tools.gyp:timer', 'tools.gyp:url_data_manager', ], + 'conditions' : [ + [ 'skia_os == "android"', { + 'dependencies': [ + 'android_deps.gyp:Android_EntryPoint', + 'android_deps.gyp:native_app_glue', + ], + 'link_settings': { + 'libraries': [ + '-landroid', + ], + }, + }], + ['skia_os != "android"', { + 'sources/': [ ['exclude', '_android.(h|cpp)$'], + ], + }], + ['skia_os != "win"', { + 'sources/': [ ['exclude', '_win.(h|cpp)$'], + ], + }], + ], }, ], } diff --git a/platform_tools/android/apps/settings.gradle b/platform_tools/android/apps/settings.gradle index f8872ca..9d103b4 100644 --- a/platform_tools/android/apps/settings.gradle +++ b/platform_tools/android/apps/settings.gradle @@ -2,3 +2,4 @@ include ':sample_app' include ':visualbench' include ':visualbenchsdl' include ':canvasproof' +include ':vulkanviewer' diff --git a/platform_tools/android/apps/vulkanviewer/build.gradle b/platform_tools/android/apps/vulkanviewer/build.gradle new file mode 100644 index 0000000..acb6740 --- /dev/null +++ b/platform_tools/android/apps/vulkanviewer/build.gradle @@ -0,0 +1,37 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +apply plugin: 'com.android.application' +android { + compileSdkVersion 19 + buildToolsVersion "22.0.1" + defaultConfig { + applicationId "org.skia.vulkanviewer" + minSdkVersion 9 + targetSdkVersion 19 + versionCode 1 + versionName "1.0" + signingConfig signingConfigs.debug + } + sourceSets.main.jni.srcDirs = [] //disable automatic ndk-build call + sourceSets.main.jniLibs.srcDir "src/main/libs" + productFlavors { arm {}; arm64 {}; x86 {}; x86_64 {}; mips {}; mips64 {}; } + applicationVariants.all{ variant -> + def buildNativeLib = task("${variant.name}_NativeLib", type:Exec) { + workingDir '../../../..' // top-level skia directory + commandLine constructBuildCommand(variant, "CopyVulkanViewerDeps").split() + environment PATH: getPathWithDepotTools() + environment ANDROID_SDK_ROOT: getSDKPath() + } + buildNativeLib.onlyIf { !project.hasProperty("suppressNativeBuild") } + TaskCollection assembleTask + assembleTask = project.tasks.matching { + it.name.contains("assemble") && + it.name.toLowerCase().endsWith(variant.name.toLowerCase()) + } + assembleTask.getAt(0).dependsOn buildNativeLib + } +} diff --git a/platform_tools/android/apps/vulkanviewer/src/main/AndroidManifest.xml b/platform_tools/android/apps/vulkanviewer/src/main/AndroidManifest.xml new file mode 100644 index 0000000..3220c31 --- /dev/null +++ b/platform_tools/android/apps/vulkanviewer/src/main/AndroidManifest.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + diff --git a/platform_tools/android/apps/vulkanviewer/src/main/java/com/skia/vulkanviewer/VulkanViewerActivity.java b/platform_tools/android/apps/vulkanviewer/src/main/java/com/skia/vulkanviewer/VulkanViewerActivity.java new file mode 100644 index 0000000..dcb0085 --- /dev/null +++ b/platform_tools/android/apps/vulkanviewer/src/main/java/com/skia/vulkanviewer/VulkanViewerActivity.java @@ -0,0 +1,28 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +package com.skia.vulkanviewer; + +import android.app.ActionBar; +import android.os.Bundle; +import android.provider.Settings; +import android.view.View; +import android.view.WindowManager; + +public class VulkanViewerActivity extends android.app.NativeActivity { + static { + System.loadLibrary("skia_android"); + } + + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + ActionBar ab = this.getActionBar(); + ab.hide(); + } +} diff --git a/platform_tools/android/bin/android_setup.sh b/platform_tools/android/bin/android_setup.sh index fd05a98..0084fc6 100755 --- a/platform_tools/android/bin/android_setup.sh +++ b/platform_tools/android/bin/android_setup.sh @@ -38,6 +38,8 @@ while (( "$#" )); do LOGCAT=1 elif [[ "$1" == "--verbose" ]]; then VERBOSE="true" + elif [[ "$1" == "--vulkan" ]]; then + SKIA_VULKAN="true" else APP_ARGS=("${APP_ARGS[@]}" "${1}") fi @@ -48,6 +50,10 @@ if [ "$USE_CLANG" == "true" ]; then export GYP_DEFINES="skia_clang_build=1 $GYP_DEFINES" fi +if [ "$SKIA_VULKAN" == "true" ]; then + export GYP_DEFINES="skia_vulkan=1 $GYP_DEFINES" +fi + function verbose { if [[ -n $VERBOSE ]]; then echo $@ @@ -167,7 +173,7 @@ setup_device() { source $SCRIPT_DIR/utils/setup_toolchain.sh fi - DEFINES="${DEFINES} android_toolchain=${TOOLCHAIN_TYPE}" + DEFINES="${DEFINES} android_toolchain=${ANDROID_TOOLCHAIN}" DEFINES="${DEFINES} android_buildtype=${BUILDTYPE}" exportVar GYP_DEFINES "$DEFINES $GYP_DEFINES" diff --git a/platform_tools/android/bin/utils/setup_toolchain.sh b/platform_tools/android/bin/utils/setup_toolchain.sh index 50098ba..6851931 100755 --- a/platform_tools/android/bin/utils/setup_toolchain.sh +++ b/platform_tools/android/bin/utils/setup_toolchain.sh @@ -42,6 +42,10 @@ function default_toolchain() { else API=14 # Android 4.0 fi + + if [ "$SKIA_VULKAN" == "true" ]; then + API=24 # Android N Preview + fi TOOLCHAIN=$ANDROID_ARCH-$NDK-$API HOST=`uname | tr '[A-Z]' '[a-z]'` diff --git a/platform_tools/android/gyp/skia_android.gypi b/platform_tools/android/gyp/skia_android.gypi index 8833afe..7767183 100644 --- a/platform_tools/android/gyp/skia_android.gypi +++ b/platform_tools/android/gyp/skia_android.gypi @@ -45,7 +45,10 @@ }], ], }, - 'includes' : [ 'canvasproof.gypi', ], + 'includes' : [ + 'canvasproof.gypi', + 'vulkanviewer.gypi', + ], 'targets': [ { 'target_name': 'CopySampleAppDeps', diff --git a/platform_tools/android/gyp/vulkanviewer.gypi b/platform_tools/android/gyp/vulkanviewer.gypi new file mode 100644 index 0000000..adcfd22 --- /dev/null +++ b/platform_tools/android/gyp/vulkanviewer.gypi @@ -0,0 +1,65 @@ +# Copyright 2015 Google Inc. +# +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +{ + 'targets': [ + { + 'target_name': 'CopyVulkanViewerDeps', + 'type': 'none', + 'dependencies': [ + 'skia_lib.gyp:skia_lib', + 'vulkanviewer.gyp:vulkanviewer', + ], + 'copies': [ + { + 'destination': '<(android_base)/apps/vulkanviewer/src/main/libs/<(android_arch)', + 'conditions': [ + [ 'skia_shared_lib', { + 'files': [ + '<(SHARED_LIB_DIR)/libskia_android.so', + '<(SHARED_LIB_DIR)/libvulkanviewer.so', + ]}, { + 'files': [ + '<(SHARED_LIB_DIR)/libvulkanviewer.so', + ]} + ], + ], + }, + ], + }, + { + 'target_name': 'VulkanViewer_APK', + 'type': 'none', + 'dependencies': [ 'CopyVulkanViewerDeps', ], + 'actions': [ + { + 'action_name': 'SkiaVulkanViewer_apk', + 'inputs': [ + '<(android_base)/apps/vulkanviewer/src/main/AndroidManifest.xml', + '<(android_base)/apps/vulkanviewer/src/main/java/com/skia/vulkanviewer/VulkanViewerActivity.java', + '<(android_base)/apps/vulkanviewer/src/main/libs/<(android_arch)/libvulkanviewer.so', + + ], + 'conditions': [ + [ 'skia_shared_lib', { + 'inputs': [ + '<(android_base)/apps/vulkanviewer/src/main/libs/<(android_arch)/libskia_android.so', + ], + }], + ], + 'outputs': [ + '../apps/vulkanviewer/build/outputs/apk/', + ], + 'action': [ + '<(android_base)/apps/gradlew', + ':vulkanviewer:assemble<(android_variant)Debug', + '-p<(android_base)/apps/vulkanviewer', + '-PsuppressNativeBuild', + '--daemon', + ], + }, + ], + }, + ], +} diff --git a/platform_tools/android/third_party/native_app_glue/android_native_app_glue.c b/platform_tools/android/third_party/native_app_glue/android_native_app_glue.c index 55a52bc..595bc76 100644 --- a/platform_tools/android/third_party/native_app_glue/android_native_app_glue.c +++ b/platform_tools/android/third_party/native_app_glue/android_native_app_glue.c @@ -419,6 +419,13 @@ static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) android_app_set_input((struct android_app*)activity->instance, NULL); } +static void onContentRectChanged(ANativeActivity* activity, const ARect* rect) { + struct android_app* android_app = (struct android_app*)activity->instance; + LOGV("OnContentRectChanged: %p\n", activity); + android_app->contentRect = *rect; + android_app_write_cmd(android_app, APP_CMD_CONTENT_RECT_CHANGED); +} + void ANativeActivity_onCreate(ANativeActivity* activity, void* savedState, size_t savedStateSize) { LOGV("Creating: %p\n", activity); @@ -435,6 +442,7 @@ void ANativeActivity_onCreate(ANativeActivity* activity, activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed; activity->callbacks->onInputQueueCreated = onInputQueueCreated; activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed; + activity->callbacks->onContentRectChanged = onContentRectChanged; activity->instance = android_app_create(activity, savedState, savedStateSize); } diff --git a/tools/build_shaderc.py b/tools/build_shaderc.py index 93b42bd..0a3fc3a 100644 --- a/tools/build_shaderc.py +++ b/tools/build_shaderc.py @@ -29,6 +29,8 @@ def main(): 'Directory for cmake build') parser.add_argument('-p', '--project_type', required=True, help= 'Project type to use. Must be "ninja", "MSVS2013", or "MSVS2015"') + parser.add_argument('-c', '--android_toolchain', required=False, help= + 'Location of standalone android toolchain to use for crosscompiling') args = parser.parse_args() args.src_dir = os.path.abspath(args.src_dir) @@ -44,12 +46,9 @@ def main(): else: args.exit('Invalid build type: ' + args.build_type); - if args.arch_type == 'x86': - vs_arch = '' - elif args.arch_type == 'x86_64': + vs_arch = '' + if args.arch_type == 'x86_64': vs_arch = ' Win64' - else: - sys.exit('Invalid arch type: ' + args.arch_type); if args.project_type == 'ninja': generator = 'Ninja' @@ -70,9 +69,17 @@ def main(): try: build_type_arg='-DCMAKE_BUILD_TYPE=' + args.build_type - subprocess.check_call(['cmake', '-G', generator, - '-DSPIRV_SKIP_EXECUTABLES=ON', '-DSHADERC_ENABLE_SHARED_CRT=ON', - args.src_dir, build_type_arg], cwd=args.output_dir) + cmake_cmd = ['cmake', '-G', generator, + '-DSPIRV_SKIP_EXECUTABLES=ON', + '-DSHADERC_ENABLE_SHARED_CRT=ON'] + if args.android_toolchain and args.android_toolchain.strip() : + cmake_cmd.append('-DCMAKE_TOOLCHAIN_FILE=' + args.src_dir +\ + '/third_party/android-cmake/android.toolchain.cmake') + cmake_cmd.append('-DANDROID_TOOLCHAIN_NAME=standalone-clang') + cmake_cmd.append('-DANDROID_STANDALONE_TOOLCHAIN=' +\ + os.path.abspath(args.android_toolchain)) + cmake_cmd.extend([build_type_arg, args.src_dir]) + subprocess.check_call(cmake_cmd, cwd=args.output_dir) except subprocess.CalledProcessError as error: sys.exit('Error (ret code: {code}) calling "{cmd}" in {dir}'.format( code = error.returncode, cmd = error.cmd, dir = args.src_dir)) @@ -86,4 +93,3 @@ def main(): if __name__ == '__main__': main() - diff --git a/tools/vulkan/Window.h b/tools/vulkan/Window.h index c8971ee..387f052 100644 --- a/tools/vulkan/Window.h +++ b/tools/vulkan/Window.h @@ -9,6 +9,7 @@ #define Window_DEFINED #include "SkTypes.h" +#include "SkRect.h" class SkCanvas; class VulkanTestContext; @@ -23,6 +24,10 @@ public: virtual void show() = 0; virtual void inval() = 0; + virtual bool scaleContentToFit() const { return false; } + virtual bool supportsContentRect() const { return false; } + virtual SkRect getContentRect() { return SkRect::MakeEmpty(); } + struct AttachmentInfo { int fSampleCount; int fStencilBits; diff --git a/tools/vulkan/android/VulkanTestContext_android.cpp b/tools/vulkan/android/VulkanTestContext_android.cpp new file mode 100644 index 0000000..522b839 --- /dev/null +++ b/tools/vulkan/android/VulkanTestContext_android.cpp @@ -0,0 +1,37 @@ + +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "VulkanTestContext_android.h" + +#include "vk/GrVkInterface.h" +#include "../../src/gpu/vk/GrVkUtil.h" + +VkSurfaceKHR VulkanTestContext::createVkSurface(void* platformData) { + // need better error handling here + SkASSERT(platformData); + ContextPlatformData_android* androidPlatformData = + reinterpret_cast(platformData); + VkSurfaceKHR surface; + + VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo; + memset(&surfaceCreateInfo, 0, sizeof(VkAndroidSurfaceCreateInfoKHR)); + surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR; + surfaceCreateInfo.pNext = nullptr; + surfaceCreateInfo.flags = 0; + surfaceCreateInfo.window = androidPlatformData->fNativeWindow; + + VkResult res = GR_VK_CALL(fBackendContext->fInterface, + CreateAndroidSurfaceKHR(fBackendContext->fInstance, + &surfaceCreateInfo, + nullptr, &surface)); + return (VK_SUCCESS == res) ? surface : VK_NULL_HANDLE; +} + +bool VulkanTestContext::canPresent(uint32_t queueFamilyIndex) { + return true; +} diff --git a/tools/vulkan/android/VulkanTestContext_android.h b/tools/vulkan/android/VulkanTestContext_android.h new file mode 100644 index 0000000..66ed35c --- /dev/null +++ b/tools/vulkan/android/VulkanTestContext_android.h @@ -0,0 +1,23 @@ + +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef VULKANTESTCONTEXT_ANDROID_DEFINED +#define VULKANTESTCONTEXT_ANDROID_DEFINED + +#ifdef SK_VULKAN + +#include "../VulkanTestContext.h" + +struct ANativeWindow; + +struct ContextPlatformData_android { + ANativeWindow* fNativeWindow; +}; + +#endif // SK_VULKAN + +#endif diff --git a/tools/vulkan/android/Window_android.cpp b/tools/vulkan/android/Window_android.cpp new file mode 100644 index 0000000..fb87a3f --- /dev/null +++ b/tools/vulkan/android/Window_android.cpp @@ -0,0 +1,179 @@ +/* +* Copyright 2016 Google Inc. +* +* Use of this source code is governed by a BSD-style license that can be +* found in the LICENSE file. +*/ + +#include "Window_android.h" + +#include "VulkanTestContext_android.h" + +Window* Window::CreateNativeWindow(void* platformData) { + Window_android* window = new Window_android(); + if (!window->init((android_app*)platformData)) { + delete window; + return nullptr; + } + return window; +} + +static void handle_cmd(struct android_app* app, int32_t cmd); +static int32_t handle_input(struct android_app* app, AInputEvent* event); + +bool Window_android::init(android_app* app) { + SkASSERT(app); + mApp = app; + mApp->userData = this; + mApp->onAppCmd = handle_cmd; + mApp->onInputEvent = handle_input; + return true; +} + +void Window_android::setTitle(const char* title) { + //todo + SkDebugf("Title: %s", title); +} + +bool Window_android::attach(BackEndType attachType, int msaaSampleCount, AttachmentInfo*) { + if (kVulkan_BackendType != attachType) { + return false; + } + + mSampleCount = msaaSampleCount; + + // We delay the creation of fTestContext until Android informs us that + // the native window is ready to use. + return true; +} + +void Window_android::initDisplay(ANativeWindow* window) { + SkASSERT(window); + ContextPlatformData_android platformData; + platformData.fNativeWindow = window; + fTestContext = VulkanTestContext::Create((void*)&platformData, mSampleCount); +} + +static void android_app_write_cmd(struct android_app* android_app, int8_t cmd) { + if (write(android_app->msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd)) { + SkDebugf("Failure writing android_app cmd: %s\n", strerror(errno)); + } +} + +void Window_android::inval() { + android_app_write_cmd(mApp, APP_CMD_INVAL_WINDOW); +} + +void Window_android::paintIfNeeded() { + if (mApp->window || !mContentRect.isEmpty()) { + this->onPaint(); + } +} + +/** + * Process the next main command. + */ +static void handle_cmd(struct android_app* app, int32_t cmd) { + Window_android* window = (Window_android*)app->userData; + switch (cmd) { + case APP_CMD_INIT_WINDOW: + // The window is being shown, get it ready. + SkASSERT(app->window); + window->initDisplay(app->window); + window->paintIfNeeded(); + break; + case APP_CMD_WINDOW_RESIZED: { + int width = ANativeWindow_getWidth(app->window); + int height = ANativeWindow_getHeight(app->window); + window->onResize(width, height); + break; + } + case APP_CMD_CONTENT_RECT_CHANGED: + window->setContentRect(app->contentRect.left, app->contentRect.top, + app->contentRect.right, app->contentRect.bottom); + window->paintIfNeeded(); + break; + case APP_CMD_TERM_WINDOW: + // The window is being hidden or closed, clean it up. + window->detach(); + break; + case APP_CMD_INVAL_WINDOW: + window->paintIfNeeded(); + break; + } +} + +static Window::Key get_key(int32_t keycode) { + static const struct { + int32_t fAndroidKey; + Window::Key fWindowKey; + } gPair[] = { + { AKEYCODE_BACK, Window::kBack_Key }, + { AKEYCODE_VOLUME_UP, Window::kLeft_Key }, + { AKEYCODE_VOLUME_DOWN, Window::kRight_Key } + }; + for (size_t i = 0; i < SK_ARRAY_COUNT(gPair); i++) { + if (gPair[i].fAndroidKey == keycode) { + return gPair[i].fWindowKey; + } + } + return Window::kNONE_Key; +} + +static Window::InputState get_action(int32_t action) { + static const struct { + int32_t fAndroidAction; + Window::InputState fInputState; + } gPair[] = { + { AKEY_STATE_DOWN, Window::kDown_InputState }, + { AKEY_STATE_UP, Window::kUp_InputState }, + }; + for (size_t i = 0; i < SK_ARRAY_COUNT(gPair); i++) { + if (gPair[i].fAndroidAction == action) { + return gPair[i].fInputState; + } + } + return Window::kMove_InputState; +} + +static int32_t get_key_modifiers(AInputEvent* event) { + static const struct { + int32_t fAndroidState; + int32_t fWindowModifier; + } gPair[] = { + { AMETA_SHIFT_ON, Window::kShift_ModifierKey }, + { AMETA_CTRL_ON, Window::kControl_ModifierKey }, + }; + + int32_t metaState = AKeyEvent_getMetaState(event); + int32_t modifiers = 0; + + if (AKeyEvent_getRepeatCount(event) == 0) { + modifiers |= Window::kFirstPress_ModifierKey; + } + + for (size_t i = 0; i < SK_ARRAY_COUNT(gPair); i++) { + if (gPair[i].fAndroidState == metaState) { + modifiers |= gPair[i].fWindowModifier; + } + } + return modifiers; +} + +/** + * Process the next input event. + */ +static int32_t handle_input(struct android_app* app, AInputEvent* event) { + Window_android* window = (Window_android*)app->userData; + switch(AInputEvent_getType(event)) { + case AINPUT_EVENT_TYPE_MOTION: + break; + case AINPUT_EVENT_TYPE_KEY: + Window::Key key = get_key(AKeyEvent_getKeyCode(event)); + Window::InputState state = get_action(AKeyEvent_getAction(event)); + int32_t mod = get_key_modifiers(event); + window->onKey(key, state, mod); + return true; // eat all key events + } + return 0; +} diff --git a/tools/vulkan/android/Window_android.h b/tools/vulkan/android/Window_android.h new file mode 100644 index 0000000..a4805bd --- /dev/null +++ b/tools/vulkan/android/Window_android.h @@ -0,0 +1,48 @@ +/* +* Copyright 2016 Google Inc. +* +* Use of this source code is governed by a BSD-style license that can be +* found in the LICENSE file. +*/ + +#ifndef Window_android_DEFINED +#define Window_android_DEFINED + +#include "../Window.h" +#include + +enum { + /** + * Leave plenty of space between this item and the ones defined in the glue layer + */ + APP_CMD_INVAL_WINDOW = 64, +}; + +class Window_android : public Window { +public: + Window_android() : Window() {} + ~Window_android() override {} + + bool init(android_app* app_state); + void initDisplay(ANativeWindow* window); + + void setTitle(const char*) override; + void show() override {} + + bool attach(BackEndType attachType, int msaaSampleCount, AttachmentInfo*) override; + void inval() override; + + void paintIfNeeded(); + + bool scaleContentToFit() const override { return true; } + bool supportsContentRect() const override { return true; } + SkRect getContentRect() override { return mContentRect; } + void setContentRect(int l, int t, int r, int b) { mContentRect.set(l,t,r,b); } + +private: + android_app* mApp = nullptr; + SkRect mContentRect; + int mSampleCount = 0; +}; + +#endif diff --git a/tools/vulkan/android/main_android.cpp b/tools/vulkan/android/main_android.cpp new file mode 100644 index 0000000..783cd1f --- /dev/null +++ b/tools/vulkan/android/main_android.cpp @@ -0,0 +1,67 @@ +/* +* Copyright 2016 Google Inc. +* +* Use of this source code is governed by a BSD-style license that can be +* found in the LICENSE file. +*/ + +#include +#include + +#include + +#include "../Application.h" +#include "Timer.h" + +static double now_ms() { return SkTime::GetNSecs() * 1e-6; } + +/** + * This is the main entry point of a native application that is using + * android_native_app_glue. It runs in its own thread, with its own + * event loop for receiving input events and doing other things. + */ +void android_main(struct android_app* state) { + // Make sure glue isn't stripped. + app_dummy(); + + static const char* gCmdLine[] = { + "vulkanviewer", + "--skps", + "/data/local/tmp/skp", + }; + + std::unique_ptr vkApp(Application::Create(SK_ARRAY_COUNT(gCmdLine), + const_cast(gCmdLine), + state)); + + double currentTime = 0.0; + double previousTime = 0.0; + + // loop waiting for stuff to do. + while (1) { + // Read all pending events. + int ident; + int events; + struct android_poll_source* source; + + // block forever waiting for events. + while ((ident=ALooper_pollAll(-1, NULL, &events, + (void**)&source)) >= 0) { + + // Process this event. + if (source != NULL) { + source->process(state, source); + } + + // Check if we are exiting. + if (state->destroyRequested != 0) { + return; + } + + previousTime = currentTime; + currentTime = now_ms(); + vkApp->onIdle(currentTime - previousTime); + } + } +} +//END_INCLUDE(all) diff --git a/tools/vulkan/viewer/GMSlide.h b/tools/vulkan/viewer/GMSlide.h index c9ed084..6b03527 100644 --- a/tools/vulkan/viewer/GMSlide.h +++ b/tools/vulkan/viewer/GMSlide.h @@ -16,6 +16,8 @@ public: GMSlide(skiagm::GM* gm); ~GMSlide() override; + SkISize getDimensions() const override { return fGM->getISize(); } + void draw(SkCanvas* canvas) override; bool animate(const SkAnimTimer&) override; diff --git a/tools/vulkan/viewer/SKPSlide.h b/tools/vulkan/viewer/SKPSlide.h index 73fd5a1..42845fa 100644 --- a/tools/vulkan/viewer/SKPSlide.h +++ b/tools/vulkan/viewer/SKPSlide.h @@ -16,6 +16,8 @@ public: SKPSlide(const SkString& name, const SkString& path); ~SKPSlide() override; + SkISize getDimensions() const override { return fCullRect.size(); } + void draw(SkCanvas* canvas) override; void load() override; void unload() override; diff --git a/tools/vulkan/viewer/Slide.h b/tools/vulkan/viewer/Slide.h index f75e3fd..cdc225b 100644 --- a/tools/vulkan/viewer/Slide.h +++ b/tools/vulkan/viewer/Slide.h @@ -9,6 +9,7 @@ #define Slide_DEFINED #include "SkRefCnt.h" +#include "SkSize.h" #include "SkString.h" class SkCanvas; @@ -18,6 +19,8 @@ class Slide : public SkRefCnt { public: virtual ~Slide() {} + virtual SkISize getDimensions() const = 0; + virtual void draw(SkCanvas* canvas) = 0; virtual bool animate(const SkAnimTimer&) { return false; } virtual void load() {} diff --git a/tools/vulkan/viewer/VulkanViewer.cpp b/tools/vulkan/viewer/VulkanViewer.cpp index a88a337..faa3014 100644 --- a/tools/vulkan/viewer/VulkanViewer.cpp +++ b/tools/vulkan/viewer/VulkanViewer.cpp @@ -81,10 +81,12 @@ VulkanViewer::VulkanViewer(int argc, char** argv, void* platformData) // set up slides this->initSlides(); + fAnimTimer.run(); + // set up first frame fCurrentSlide = 0; setupCurrentSlide(-1); - fLocalMatrix.reset(); + updateMatrix(); fWindow->show(); } @@ -254,10 +256,24 @@ bool VulkanViewer::onChar(SkUnichar c, uint32_t modifiers) { void VulkanViewer::onPaint(SkCanvas* canvas) { - canvas->clear(SK_ColorWHITE); - int count = canvas->save(); - canvas->setMatrix(fLocalMatrix); + + if (fWindow->supportsContentRect()) { + SkRect contentRect = fWindow->getContentRect(); + canvas->clipRect(contentRect); + canvas->translate(contentRect.fLeft, contentRect.fTop); + } + + canvas->clear(SK_ColorWHITE); + if (fWindow->supportsContentRect() && fWindow->scaleContentToFit()) { + const SkRect contentRect = fWindow->getContentRect(); + const SkISize slideSize = fSlides[fCurrentSlide]->getDimensions(); + const SkRect slideBounds = SkRect::MakeIWH(slideSize.width(), slideSize.height()); + SkMatrix matrix; + matrix.setRectToRect(slideBounds, contentRect, SkMatrix::kCenter_ScaleToFit); + canvas->concat(matrix); + } + canvas->concat(fLocalMatrix); fSlides[fCurrentSlide]->draw(canvas); canvas->restoreToCount(count); @@ -282,6 +298,12 @@ void VulkanViewer::drawStats(SkCanvas* canvas) { SkPaint paint; canvas->save(); + if (fWindow->supportsContentRect()) { + SkRect contentRect = fWindow->getContentRect(); + canvas->clipRect(contentRect); + canvas->translate(contentRect.fLeft, contentRect.fTop); + } + canvas->clipRect(rect); paint.setColor(SK_ColorBLACK); canvas->drawRect(rect, paint);