Skip to content

Native android application that showcases camera preview mapping on a spinning 3D cube.

License

Notifications You must be signed in to change notification settings

ktzevani/native-camera-vulkan

Repository files navigation

native-camera-vulkan

Donate

Native android application that showcases camera preview mapping on a spinning 3D cube.

Native android application developed with the use of Vulkan API that performs real-time camera preview mapping on a spinning 3D cube. The aim of the project is to provide with an elaborate example that showcases various useful, according to the author, basic-to-intermediate developing techniques for the creation of Vulkan-powered native android applications that make use of multiple hardware facilities.

Application Preview

Table of contents

Project Description

To TOC

This side-project started as an effort to refresh and update my C++ and 3D graphics programming skills. The initial purpose was the study of the latest C++ standards (C++17 and "C++20") as well as the study of the Vulkan library. After an introductory review, it became apparent that Vulkan is mainly used by C rather than C++ developers although the Vulkan C++ library (https://github.com/KhronosGroup/Vulkan-Hpp) is constantly gaining popularity. Also the latest developments on mobile graphics and camera hardware along the fact that Vulkan runs on Android devices, drove me down the path of exploring the possibilities of building system applications for Android (outside the JVM context) in order to take advantage of the Vulkan graphics and compute capabilities as well as other third-party system libraries (e.g. OpenCV, dlib, TensorFlow) for my domains of interest. These domains being Computer Vision, Computer Graphics and Augmented Reality.

By further studying and working on this idea, I also discovered that there aren't much resources out there on how to integrate the camera into a 3D native Android application using "immediate memory mapping" (external buffers). So I've decided to build a single-threaded (well if you don't count the JVM context threads, and the ones related to the hardware devices) Vulkan sample that showcases the following:

  • Usage of C++17, Vulkan Hpp and the latest NDK (see Pre-requisites) for building a native Android 3D application.

  • Management of Android user permissions outside the JVM context (i.e. without the use of JNI calls).

  • Design of a clean native program loop that manages user input, sensors (e.g. accelerometer, camera) and screen output.

  • Design of a basic Vulkan context for 3D rendering.

  • Camera-Vulkan APIs interoperability: Communication of camera data, via the usage of Camera2 API and utilization of external hardware buffers, to the Vulkan context.

In the references section, I list all sources that guided me in realizing this project.

Pre-requisites

The style and conventions of the project's codebase targets intermediate-level C++ programmers with some knowledge of the C++17 standard. The project is made in Android Studio with the aid of CMake. Thus, knowledge of this IDE and building tools (CMake, Gradle, AVD & SDK Managers) is required. The development OS was Windows 10. Finally, native-camera-vulkan, makes use of some external to NDK binary and header libraries, so the developer must configure the development environment in such a way (acquiring needed libraries, configure locations etc.), as to meet the following.

Development Tools
Android Studiov4.1.2
NDKv22.0.7026061
SDK Platformv30.3
SDK Build-Toolsv30.0.3
External (to AS) Development Tools
CMakev3.19.2
External (to NDK) Libraries
Vulkanv1.2.162
Vulkan HPPv1.2.162
Validation Layersv1.2.162
GLMv0.9.9.8
STBv2.26 (stb_image)

For more information see Vulkan C++ API and NDK and Build Instructions.

native-camera-vulkan is built to support devices running Android Oreo (v8.0) or newer (minimum SDK Version is 26), but... Some strong assumptions have been made here and there throughout the project. The application expects from the device, to support certain Vulkan extensions. Not all devices could run this application, but the ones that support the following (in addition to supporting running Vulkan, that is 😃):

Instance Extensions
VK_KHR_surface
VK_KHR_android_surface
VK_KHR_external_memory_capabilities
VK_KHR_external_semaphore_capabilities
VK_KHR_get_physical_device_properties2
VK_EXT_debug_utils
Logical Device Extensions
VK_KHR_swapchain
VK_KHR_maintenance1
VK_KHR_bind_memory2
VK_KHR_get_memory_requirements2
VK_KHR_sampler_ycbcr_conversion
VK_EXT_queue_family_foreign
VK_KHR_external_memory
VK_KHR_external_semaphore
VK_KHR_external_semaphore_fd
VK_KHR_dedicated_allocation
VK_ANDROID_external_memory_android_hardware_buffer

Also it is expected that the device has a front and a back camera. It is fixed for the back facing camera to be selected for usage (this can be changed through the code here). The application was tested successfully on a Nokia 6.1 and a Samsung Galaxy A50. For the development environment a Nvidia GTX 770 was used.

Vulkan C++ API and NDK

The Android NDK used in this project (see Pre-requisites), ships with an outdated version of the Vulkan headers (v1.2.121). These headers do not correspond to a Vulkan HPP library version (at least not an interesting one). So it is decided that for the purposes of the development, a newer version of the libraries as well as of the validation layers will be used (v1.2.162). In order to do so, besides including the newer headers in the project, the corresponding binaries of the validation layers had to also be introduced as dependencies in Gradle, see Build-Instructions for more details.

"Java-less" Native Application

The Android SDK provides the ability, to the developer, to build a native Android application without writing any Java code at all. One way of doing this, is by leveraging the fact that a Java NativeActivity can be defined implicitly via the AndroidManifest.xml in combination to making use of the android_native_app_glue.h helper library on the C++ side. In this way, the execution process creates a Java context (JavaVM) from which spawns a separate thread to be used as a starting point for native event loop handling, native procedures etc. (using the helper library) and all this are abstracted by the developer.

It is a common practice in native Android applications to use Java code for handling basic stuff, such as app lifecycle management, app permissions management, event management etc. and utilize Java Native Interface (i.e. JNI) to invoke native procedures. In this project the native code manages the application events by itself and also uses JNI in a reverse manner, from C++ to Java, in order to invoke Android Java facilities not available otherwise in the native side (e.g. app permissions management).

Camera Preview and Hardware Buffers

The communication of camera images to the graphics engine is made via the usage of AHardwareBuffer (see Native Hardware Buffer) objects. Camera device is set to work at a Preview mode in order for frames to captured as fast as possible (this doesn't favour quality, for more information see here). A simple synchronous image reader is implemented and governed by the application engine. This means that frames are requested at a rate that corresponds to the rendering frequency (i.e. as fast as possible). Typically, a camera device would be much slower than the graphics engine. For this, the image reader also plays another role. It decouples the camera device from the graphics engine so that the graphics engine can request frames as fast as it can without the burden of handling unanswered requests, while the camera device can serve frames at it's own pace. Off course, in the event of absence of a new image, graphics engine renders the old one(s).

Input and Accelerometer

Touchscreen (i.e. input) and accelerometer devices have also been integrated into the application. Any change of touch input (e.g. touch screen coordinates) or of the orientation of the physical device is reflected in the application as background color change.

Custom Logging Facility

Special care has been given to logging formatting. A custom logging library that wraps android logging facilities is implemented. The custom library produces among others, tabular output like the one presented below:

INFO: Version is: 0.2.0-1127 (build.1883)
DEBUG: Generic engine has been initialized.
INFO: Vulkan library loaded with success.
DEBUG: ┌---------------------------------------------------------------------┐
DEBUG: |                     GENERAL VULKAN INFORMATION                      |
DEBUG: |---------------------------------------------------------------------|
DEBUG: |                        Available Extensions                         |
DEBUG: |                        ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾                         |
DEBUG: |    VK_EXT_debug_report                                   v0.0.9     |
DEBUG: |    VK_EXT_swapchain_colorspace                           v0.0.4     |
DEBUG: |    VK_KHR_android_surface                                v0.0.6     |
DEBUG: |    VK_KHR_external_fence_capabilities                    v0.0.1     |
DEBUG: |    VK_KHR_external_memory_capabilities                   v0.0.1     |
DEBUG: |    VK_KHR_external_semaphore_capabilities                v0.0.1     |
DEBUG: |    VK_KHR_get_physical_device_properties2                v0.0.2     |
DEBUG: |    VK_KHR_get_surface_capabilities2                      v0.0.1     |
DEBUG: |    VK_KHR_surface                                        v0.0.25    |
DEBUG: |---------------------------------------------------------------------|
DEBUG: |                          Available Layers                           |
DEBUG: |                          ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾                           |
DEBUG: |    VK_LAYER_KHRONOS_validation                                      |
DEBUG: |      VK_EXT_debug_report                                 v0.0.9     |
DEBUG: |      VK_EXT_debug_utils                                  v0.0.2     |
DEBUG: └---------------------------------------------------------------------┘
INFO: Found a Vulkan device. Context created.
...

With the combination of an appropriate filter in logcat panel of Android Studio, one can greatly improve online logging inspection of the running application.

Build Instructions

To TOC

For a successful build, v3.19.2 of CMake must be installed in the development system and registered in the development environment via PATH variable. The project is configured to look for an externally managed (in respect to the Android Studio) cmake binary.

Also LIBRARIES_ROOT environment variable must point to a location where the dependencies listed in Pre-requisites exist in a pre-specified directory structure as detailed in the table below:

Local DirectoryMapped Source
%LIBRARIES_ROOT%\vulkan\vulkanRepository\include\vulkan
%LIBRARIES_ROOT%\vulkan_hpp\vulkan_hppRepository\vulkan
%LIBRARIES_ROOT%\stb\stbRepository\stb1
%LIBRARIES_ROOT%\glm\glmRepository\glm\glm
%LIBRARIES_ROOT%\vulkan_validation_layers\bin\android-1.2.162(extracted layer binaries .zip)
1 In the case of STB library you can isolate source files in new directory (i.e. include) and map that one to the specified local path, in order to avoid cluttering the IDE's autocomplete with non-header files.

Build Flavours

The provided cmake configuration is parameterized. The developer can control the build output by configuring two compilation aspects.

First, one can configure BUILD_FLAVOR variable in order to determine whether to build a simplified version of the application (Simple Vulkan Context) or the full (Complex Vulkan Context) version.

Second, by tweaking on and off certain compilation flags one can select whether to include or not in the final binary, some of the following:

  • Debug Build Indication
  • Validation Layer
  • Logging Facility
  • Profiling Facility

In total, 8 different build outputs can be produced by the various compilation configurations that can be specified. The following table provides an overview:

CMAKE Variable
BUILD_FLAVORSIMPLE_VULKAN: Basic Vulkan context, no graphics pipeline
COMPLEX_VULKAN: Complete Vulkan context
Compilation Flags
NDEBUGSet for release builds
NCV_VULKAN_VALIDATION_ENABLEDEnable validation layer
NCV_LOGGING_ENABLEDEnable custom logging facility
NCV_PROFILING_ENABLEDEnable profiling facility

References

To TOC

  1. A Tour of C++, 2nd Edition - Bjarne Stroustrup: Quick overview of C++17 and a general not-in-depth C++ refresher for system programmers.
  2. Vulkan Specification: The best Vulkan resource.
  3. NDK Guides: The best native Android development resource.
  4. Camera2 API Reference.
  5. Vulkan Samples: Official Vulkan samples repository.
  6. Vulkan C++ Examples and Demos: Awesome repository with a great number of helpful samples.
  7. Vulkan Tutorial: Great resource for Vulkan beginners.

Contributing

To TOC

Contributions are always welcome! I'll try to review new pull requests frequently, make sure to include a clear and detailed summary about the proposed changes. Also feel free to contact me for any questions, in any of the advertised ways in my profile.

Donations

To TOC

If you found this work helpful and want to support the author, consider making a donation.

License

To TOC

This project is licensed under the general terms of the Apache-2.0 License. See LICENSE for more information related to the usage and redistribution of this project.

Trademarks

About

Native android application that showcases camera preview mapping on a spinning 3D cube.

Resources

License

Stars

Watchers

Forks

Packages

No packages published