Merge pull request #36603 from m4gr3d/implement_vk_surface_view

Provide a Vulkan surface view base implementation
This commit is contained in:
Rémi Verschelde 2020-03-05 18:44:18 +01:00 committed by GitHub
commit 60ea8aea98
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 580 additions and 3 deletions

View file

@ -19,7 +19,8 @@ android_files = [
'java_godot_wrapper.cpp',
'java_godot_io_wrapper.cpp',
'jni_utils.cpp',
'android_keys_utils.cpp'
'android_keys_utils.cpp',
'vulkan/vk_renderer_jni.cpp'
]
env_android = env.Clone()

View file

@ -15,6 +15,7 @@ buildscript {
}
dependencies {
classpath libraries.androidGradlePlugin
classpath libraries.kotlinGradlePlugin
//CHUNK_BUILDSCRIPT_DEPENDENCIES_BEGIN
//CHUNK_BUILDSCRIPT_DEPENDENCIES_END
}
@ -34,6 +35,7 @@ allprojects {
dependencies {
implementation libraries.supportCoreUtils
implementation libraries.kotlinStdLib
if (rootProject.findProject(":lib")) {
implementation project(":lib")

View file

@ -4,13 +4,16 @@ ext.versions = [
minSdk : 18,
targetSdk : 29,
buildTools : '29.0.1',
supportCoreUtils : '28.0.0'
supportCoreUtils : '28.0.0',
kotlinVersion : '1.3.61'
]
ext.libraries = [
androidGradlePlugin: "com.android.tools.build:gradle:$versions.androidGradlePlugin",
supportCoreUtils : "com.android.support:support-core-utils:$versions.supportCoreUtils"
supportCoreUtils : "com.android.support:support-core-utils:$versions.supportCoreUtils",
kotlinGradlePlugin : "org.jetbrains.kotlin:kotlin-gradle-plugin:$versions.kotlinVersion",
kotlinStdLib : "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$versions.kotlinVersion"
]
ext.getExportPackageName = { ->

View file

@ -9,6 +9,7 @@ buildscript {
}
dependencies {
classpath libraries.androidGradlePlugin
classpath libraries.kotlinGradlePlugin
}
}

View file

@ -2,6 +2,7 @@ apply plugin: 'com.android.library'
dependencies {
implementation libraries.supportCoreUtils
implementation libraries.kotlinStdLib
}
def pathToRootDir = "../../../../"

View file

@ -0,0 +1,99 @@
/*************************************************************************/
/* VkRenderer.kt */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
@file:JvmName("VkRenderer")
package org.godotengine.godot.vulkan
import android.view.Surface
/**
* Responsible to setting up and driving the Vulkan rendering logic.
*
* <h3>Threading</h3>
* The renderer will be called on a separate thread, so that rendering
* performance is decoupled from the UI thread. Clients typically need to
* communicate with the renderer from the UI thread, because that's where
* input events are received. Clients can communicate using any of the
* standard Java techniques for cross-thread communication, or they can
* use the [VkSurfaceView.queueOnVkThread] convenience method.
*
* @see [VkSurfaceView.startRenderer]
*/
internal class VkRenderer {
/**
* Called when the surface is created and signals the beginning of rendering.
*/
fun onVkSurfaceCreated(surface: Surface) {
nativeOnVkSurfaceCreated(surface)
}
/**
* Called after the surface is created and whenever its size changes.
*/
fun onVkSurfaceChanged(surface: Surface, width: Int, height: Int) {
nativeOnVkSurfaceChanged(surface, width, height)
}
/**
* Called to draw the current frame.
*/
fun onVkDrawFrame() {
nativeOnVkDrawFrame()
}
/**
* Called when the rendering thread is resumed.
*/
fun onVkResume() {
nativeOnVkResume()
}
/**
* Called when the rendering thread is paused.
*/
fun onVkPause() {
nativeOnVkPause()
}
/**
* Called when the rendering thread is destroyed and used as signal to tear down the Vulkan logic.
*/
fun onVkDestroy() {
nativeOnVkDestroy()
}
private external fun nativeOnVkSurfaceCreated(surface: Surface)
private external fun nativeOnVkSurfaceChanged(surface: Surface, width: Int, height: Int)
private external fun nativeOnVkResume()
private external fun nativeOnVkDrawFrame()
private external fun nativeOnVkPause()
private external fun nativeOnVkDestroy()
}

View file

@ -0,0 +1,136 @@
/*************************************************************************/
/* VkSurfaceView.kt */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
@file:JvmName("VkSurfaceView")
package org.godotengine.godot.vulkan
import android.content.Context
import android.view.SurfaceHolder
import android.view.SurfaceView
/**
* An implementation of SurfaceView that uses the dedicated surface for
* displaying Vulkan rendering.
* <p>
* A [VkSurfaceView] provides the following features:
* <p>
* <ul>
* <li>Manages a surface, which is a special piece of memory that can be
* composited into the Android view system.
* <li>Accepts a user-provided [VkRenderer] object that does the actual rendering.
* <li>Renders on a dedicated [VkThread] thread to decouple rendering performance from the
* UI thread.
* </ul>
*/
internal class VkSurfaceView(context: Context) : SurfaceView(context), SurfaceHolder.Callback {
companion object {
fun checkState(expression: Boolean, errorMessage: Any) {
check(expression) { errorMessage.toString() }
}
}
/**
* Thread used to drive the vulkan logic.
*/
private val vkThread: VkThread by lazy {
VkThread(this, renderer)
}
/**
* Performs the actual rendering.
*/
private lateinit var renderer: VkRenderer
init {
isClickable = true
holder.addCallback(this)
}
/**
* Set the [VkRenderer] associated with the view, and starts the thread that will drive the vulkan
* rendering.
*
* This method should be called once and only once in the life-cycle of [VkSurfaceView].
*/
fun startRenderer(renderer: VkRenderer) {
checkState(!this::renderer.isInitialized, "startRenderer must only be invoked once")
this.renderer = renderer
vkThread.start()
}
/**
* Queues a runnable to be run on the Vulkan rendering thread.
*
* Must not be called before a [VkRenderer] has been set.
*/
fun queueOnVkThread(runnable: Runnable) {
vkThread.queueEvent(runnable)
}
/**
* Resumes the rendering thread.
*
* Must not be called before a [VkRenderer] has been set.
*/
fun onResume() {
vkThread.onResume()
}
/**
* Pauses the rendering thread.
*
* Must not be called before a [VkRenderer] has been set.
*/
fun onPause() {
vkThread.onPause()
}
/**
* Tear down the rendering thread.
*
* Must not be called before a [VkRenderer] has been set.
*/
fun onDestroy() {
vkThread.blockingExit()
}
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
vkThread.onSurfaceChanged(width, height)
}
override fun surfaceDestroyed(holder: SurfaceHolder) {
vkThread.onSurfaceDestroyed()
}
override fun surfaceCreated(holder: SurfaceHolder) {
vkThread.onSurfaceCreated()
}
}

View file

@ -0,0 +1,230 @@
/*************************************************************************/
/* VkThread.kt */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
@file:JvmName("VkThread")
package org.godotengine.godot.vulkan
import android.util.Log
import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock
/**
* Thread implementation for the [VkSurfaceView] onto which the vulkan logic is ran.
*
* The implementation is modeled after [android.opengl.GLSurfaceView]'s GLThread.
*/
internal class VkThread(private val vkSurfaceView: VkSurfaceView, private val vkRenderer: VkRenderer) : Thread(TAG) {
companion object {
private val TAG = VkThread::class.java.simpleName
}
/**
* Used to run events scheduled on the thread.
*/
private val eventQueue = ArrayList<Runnable>()
/**
* Used to synchronize interaction with other threads (e.g: main thread).
*/
private val lock = ReentrantLock()
private val lockCondition = lock.newCondition()
private var shouldExit = false
private var exited = false
private var rendererInitialized = false
private var rendererResumed = false
private var resumed = false
private var hasSurface = false
private var width = 0
private var height = 0
/**
* Determine when drawing can occur on the thread. This usually occurs after the
* [android.view.Surface] is available, the app is in a resumed state.
*/
private val readyToDraw
get() = hasSurface && resumed
private fun threadExiting() {
lock.withLock {
exited = true
lockCondition.signalAll()
}
}
/**
* Queue an event on the [VkThread].
*/
fun queueEvent(event: Runnable) {
lock.withLock {
eventQueue.add(event)
lockCondition.signalAll()
}
}
/**
* Request the thread to exit and block until it's done.
*/
fun blockingExit() {
lock.withLock {
shouldExit = true
lockCondition.signalAll()
while (!exited) {
try {
Log.i(TAG, "Waiting on exit for $name")
lockCondition.await()
} catch (ex: InterruptedException) {
currentThread().interrupt()
}
}
}
}
/**
* Invoked when the app resumes.
*/
fun onResume() {
lock.withLock {
resumed = true
lockCondition.signalAll()
}
}
/**
* Invoked when the app pauses.
*/
fun onPause() {
lock.withLock {
resumed = false
lockCondition.signalAll()
}
}
/**
* Invoked when the [android.view.Surface] has been created.
*/
fun onSurfaceCreated() {
// This is a no op because surface creation will always be followed by surfaceChanged()
// which provide all the needed information.
}
/**
* Invoked following structural updates to [android.view.Surface].
*/
fun onSurfaceChanged(width: Int, height: Int) {
lock.withLock {
hasSurface = true
this.width = width
this.height = height
lockCondition.signalAll()
}
}
/**
* Invoked when the [android.view.Surface] is no longer available.
*/
fun onSurfaceDestroyed() {
lock.withLock {
hasSurface = false
lockCondition.signalAll()
}
}
/**
* Thread loop modeled after [android.opengl.GLSurfaceView]'s GLThread.
*/
override fun run() {
try {
while (true) {
var event: Runnable? = null
lock.withLock {
while (true) {
// Code path for exiting the thread loop.
if (shouldExit) {
vkRenderer.onVkDestroy()
return
}
// Check for events and execute them outside of the loop if found to avoid
// blocking the thread lifecycle by holding onto the lock.
if (eventQueue.isNotEmpty()) {
event = eventQueue.removeAt(0)
break;
}
if (readyToDraw) {
if (!rendererResumed) {
rendererResumed = true
vkRenderer.onVkResume()
if (!rendererInitialized) {
rendererInitialized = true
vkRenderer.onVkSurfaceCreated(vkSurfaceView.holder.surface)
}
vkRenderer.onVkSurfaceChanged(vkSurfaceView.holder.surface, width, height)
}
// Break out of the loop so drawing can occur without holding onto the lock.
break;
} else if (rendererResumed) {
// If we aren't ready to draw but are resumed, that means we either lost a surface
// or the app was paused.
rendererResumed = false
vkRenderer.onVkPause()
}
// We only reach this state if we are not ready to draw and have no queued events, so
// we wait.
// On state change, the thread will be awoken using the [lock] and [lockCondition], and
// we will resume execution.
lockCondition.await()
}
}
// Run queued event.
if (event != null) {
event?.run()
continue
}
// Draw only when there no more queued events.
vkRenderer.onVkDrawFrame()
}
} catch (ex: InterruptedException) {
Log.i(TAG, ex.message)
} catch (ex: IllegalStateException) {
Log.i(TAG, ex.message)
} finally {
threadExiting()
}
}
}

View file

@ -0,0 +1,58 @@
/*************************************************************************/
/* vk_renderer_jni.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "vk_renderer_jni.h"
extern "C" {
JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkSurfaceCreated(JNIEnv *env, jobject obj, jobject j_surface) {
// TODO: complete
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkSurfaceChanged(JNIEnv *env, jobject object, jobject j_surface, jint width, jint height) {
// TODO: complete
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkResume(JNIEnv *env, jobject obj) {
// TODO: complete
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkDrawFrame(JNIEnv *env, jobject obj) {
// TODO: complete
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkPause(JNIEnv *env, jobject obj) {
// TODO: complete
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkDestroy(JNIEnv *env, jobject obj) {
// TODO: complete
}
}

View file

@ -0,0 +1,46 @@
/*************************************************************************/
/* vk_renderer_jni.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef VK_RENDERER_JNI_H
#define VK_RENDERER_JNI_H
#include <android/log.h>
#include <jni.h>
extern "C" {
JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkSurfaceCreated(JNIEnv *env, jobject obj, jobject j_surface);
JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkSurfaceChanged(JNIEnv *env, jobject object, jobject j_surface, jint width, jint height);
JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkResume(JNIEnv *env, jobject obj);
JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkDrawFrame(JNIEnv *env, jobject obj);
JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkPause(JNIEnv *env, jobject obj);
JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkDestroy(JNIEnv *env, jobject obj);
}
#endif // VK_RENDERER_JNI_H