Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.10)

project(CORE_ENGINE)
project(RENDERING_ENGINE)

if (NOT ANDROID_NDK_TOOLCHAIN_INCLUDED)
message(FATAL_ERROR "-- Toolchain file not included, see https://developer.android.com/ndk/guides/cmake")
Expand All @@ -11,7 +11,7 @@ add_library(
SHARED
app/src/main/native/cpp/main.cpp
app/src/main/native/cpp/base_renderer.cpp
app/src/main/native/cpp/core_engine.cpp
app/src/main/native/cpp/rendering_engine.cpp
app/src/main/native/cpp/opengl_renderer.cpp
app/src/main/native/cpp/vulkan_renderer.cpp
app/src/main/native/cpp/vulkan_wrapper.cpp
Expand Down
16 changes: 16 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,27 @@ android {
versionName "1.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

// libs/shaderc/c++_static/ only ships arm64-v8a binaries.
ndk {
abiFilters 'arm64-v8a'
}
}

ndkVersion = '25.1.8937393'
signingConfigs {
// Personal-use convenience: sign release builds with the local debug
// keystore so `adb install` works out of the box. Not for distribution.
release {
storeFile file("${System.getProperty('user.home')}/.android/debug.keystore")
storePassword 'android'
keyAlias 'androiddebugkey'
keyPassword 'android'
}
}
buildTypes {
release {
signingConfig signingConfigs.release
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
ndk {
Expand Down
260 changes: 78 additions & 182 deletions app/src/main/java/com/dz/camerafast/CameraActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,34 @@ package com.dz.camerafast

import android.Manifest
import android.annotation.SuppressLint
import android.os.Build
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.result.contract.ActivityResultContracts
import androidx.camera.core.CameraSelector
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.content.PermissionChecker
import com.dz.camerafast.camera.Camera2
import com.dz.camerafast.camera.CameraData
import com.dz.camerafast.camera.CameraX

class CameraActivity : ComponentActivity() {

private val previewEngineList = listOf(
CoreEngine(RenderingMode.VULKAN),
CoreEngine(RenderingMode.OPEN_GL_ES)
)
private var cameraModeState = mutableStateOf(CameraMode.NONE)
private val vulkanRenderingEngine = RenderingEngine(RenderingMode.VULKAN)
private val openGlRenderingEngine = RenderingEngine(RenderingMode.OPEN_GL_ES)
private var initialCameraMode = CameraMode.NONE

private val requestPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted ->
if (isGranted) {
cameraModeState.value = CameraMode.CAMERA_X
initialCameraMode = CameraMode.CAMERA_X
} else {
finish()
}
Expand All @@ -55,174 +39,85 @@ class CameraActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val cameraMode by remember {
cameraModeState
var vulkanCameraData by remember {
mutableStateOf(CameraData(initialCameraMode, CameraSelector.LENS_FACING_FRONT))
}

var lensFacing by remember {
mutableIntStateOf(CameraSelector.LENS_FACING_FRONT)
var openGlCameraData by remember {
mutableStateOf(CameraData(initialCameraMode, CameraSelector.LENS_FACING_FRONT))
}

var displayMode by remember {
mutableStateOf(DisplayMode.BOTH)
}

var vulkanWeightValue by remember {
mutableFloatStateOf(1.0f)
}

val vulkanWeight by animateFloatAsState(
targetValue = vulkanWeightValue,
// TODO increase duration and try make dynamic view resize less laggy
animationSpec = tween(durationMillis = 0),
label = "Vulkan",
finishedListener = { endValue ->
displayMode = if (endValue == 1.0f) {
DisplayMode.BOTH
} else {
DisplayMode.OPEN_GL_ES
}
}
)

var openGlWeightValue by remember {
mutableFloatStateOf(1.0f)
}

val openGlWeight by animateFloatAsState(
targetValue = openGlWeightValue,
// TODO increase duration and try make dynamic view resize less laggy
animationSpec = tween(durationMillis = 0),
label = "OpenGL",
finishedListener = { endValue ->
displayMode = if (endValue == 1.0f) {
DisplayMode.BOTH
} else {
DisplayMode.VULKAN
}
}
// When an engine moves onto the source the other engine is already on, adopt
// the other engine's lens so both states agree before the camera opens.
fun moveTo(self: CameraData, other: CameraData, target: CameraMode) = CameraData(
cameraMode = target,
lensOrientation = if (other.cameraMode == target) other.lensOrientation else self.lensOrientation
)

if (cameraMode == CameraMode.CAMERA_X) {
CameraX(
coreEngines = previewEngineList,
lensFacing = lensFacing
)
}

if (cameraMode == CameraMode.CAMERA_2) {
// Vulkan is not supported yet
displayMode = DisplayMode.OPEN_GL_ES
Camera2(
coreEngines = previewEngineList,
lensFacing = lensFacing
)
}

Column {
Text(
text = cameraMode.name,
color = Color.White,
textAlign = TextAlign.Center,
fontSize = 30.sp,
modifier = Modifier
.background(Color.DarkGray)
.fillMaxWidth()
.padding(10.dp)
)
if (displayMode != DisplayMode.VULKAN) {
Box(
contentAlignment = Alignment.TopStart,
if (vulkanCameraData.cameraMode != CameraMode.NONE) {
Column {
CameraBox(
modifier = Modifier
.weight(openGlWeight)
.fillMaxWidth()
) {
CameraPreviewView(
coreEngine = previewEngineList.find { it.renderingMode == RenderingMode.OPEN_GL_ES }!!,
modifier = Modifier
.matchParentSize()
.clickable(enabled = displayMode == DisplayMode.BOTH) {
vulkanWeightValue = 0.1f
}
)
Text(
text = "OpenGL",
fontSize = 20.sp,
color = Color.Black,
modifier = Modifier.padding(8.dp)
)
}
}
if (displayMode != DisplayMode.OPEN_GL_ES) {
Box(
contentAlignment = Alignment.TopStart,
modifier = Modifier
.weight(vulkanWeight)
.fillMaxWidth()
) {
CameraPreviewView(
coreEngine = previewEngineList.find { it.renderingMode == RenderingMode.VULKAN }!!,
modifier = Modifier
.matchParentSize()
.clickable(enabled = displayMode == DisplayMode.BOTH) {
openGlWeightValue = 0.1f
}
)
Text(
text = "Vulkan",
fontSize = 20.sp,
color = Color.White,
modifier = Modifier.padding(8.dp)
)
}
}
if (cameraMode != CameraMode.NONE) {
Row {
Button(
onClick = {
lensFacing = if (lensFacing == CameraSelector.LENS_FACING_FRONT) {
CameraSelector.LENS_FACING_BACK
} else {
CameraSelector.LENS_FACING_FRONT
}
},
modifier = Modifier
.padding(8.dp)
.weight(1.0f)
) {
Text(text = "Switch camera")
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
Button(
onClick = {
if (cameraMode == CameraMode.CAMERA_X) {
cameraModeState.value = CameraMode.CAMERA_2
} else {
cameraModeState.value = CameraMode.CAMERA_X
}
},
modifier = Modifier
.padding(8.dp)
.weight(1.0f)
) {
Text(text = "To ${if (cameraMode == CameraMode.CAMERA_X) "Camera2" else "CameraX"}")
.weight(1f)
.fillMaxWidth(),
cameraData = vulkanCameraData,
renderingEngine = vulkanRenderingEngine,
onCameraChanged = { cameraMode ->
vulkanCameraData = moveTo(vulkanCameraData, openGlCameraData, cameraMode)
},
onLensOrientationChanged = { lensOrientation ->
vulkanCameraData = vulkanCameraData.copy(lensOrientation = lensOrientation)
if (openGlCameraData.cameraMode == vulkanCameraData.cameraMode) {
openGlCameraData = openGlCameraData.copy(lensOrientation = lensOrientation)
}
}
if (displayMode != DisplayMode.BOTH && cameraMode != CameraMode.CAMERA_2) {
Button(
onClick = {
displayMode = DisplayMode.BOTH
vulkanWeightValue = 1.0f
openGlWeightValue = 1.0f
},
modifier = Modifier
.padding(8.dp)
.weight(1.0f)
) {
Text(text = "Back to both")
)
CameraBox(
modifier = Modifier
.weight(1f)
.fillMaxWidth(),
cameraData = openGlCameraData,
renderingEngine = openGlRenderingEngine,
onCameraChanged = { cameraMode ->
openGlCameraData = moveTo(openGlCameraData, vulkanCameraData, cameraMode)
},
onLensOrientationChanged = { lensOrientation ->
openGlCameraData = openGlCameraData.copy(lensOrientation = lensOrientation)
if (openGlCameraData.cameraMode == vulkanCameraData.cameraMode) {
vulkanCameraData = vulkanCameraData.copy(lensOrientation = lensOrientation)
}
}
}
)
}

val cameraXEngines = buildList {
if (vulkanCameraData.cameraMode == CameraMode.CAMERA_X) add(vulkanRenderingEngine)
if (openGlCameraData.cameraMode == CameraMode.CAMERA_X) add(openGlRenderingEngine)
}
if (cameraXEngines.isNotEmpty()) {
CameraX(
renderingEngines = cameraXEngines,
lensFacing = if (vulkanCameraData.cameraMode == CameraMode.CAMERA_X) {
vulkanCameraData.lensOrientation
} else {
openGlCameraData.lensOrientation
}
)
}

val camera2Engines = buildList {
if (vulkanCameraData.cameraMode == CameraMode.CAMERA_2) add(vulkanRenderingEngine)
if (openGlCameraData.cameraMode == CameraMode.CAMERA_2) add(openGlRenderingEngine)
}
if (camera2Engines.isNotEmpty()) {
Camera2(
renderingEngines = camera2Engines,
lensFacing = if (vulkanCameraData.cameraMode == CameraMode.CAMERA_2) {
vulkanCameraData.lensOrientation
} else {
openGlCameraData.lensOrientation
}
)
}
}
}
Expand All @@ -237,13 +132,14 @@ class CameraActivity : ComponentActivity() {
) {
requestPermissionLauncher.launch(Manifest.permission.CAMERA)
} else {
cameraModeState.value = CameraMode.CAMERA_X
initialCameraMode = CameraMode.CAMERA_X
}
}

override fun onDestroy() {
super.onDestroy()
previewEngineList.forEach { it.destroy() }
openGlRenderingEngine.destroy()
vulkanRenderingEngine.destroy()
}

internal companion object {
Expand Down
Loading