chore: initial commit
Signed-off-by: Alan Brault <alan.brault@visus.io>
This commit is contained in:
1
lib/vulkan/.gitignore
vendored
Normal file
1
lib/vulkan/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
56
lib/vulkan/build.gradle.kts
Normal file
56
lib/vulkan/build.gradle.kts
Normal file
@@ -0,0 +1,56 @@
|
||||
plugins {
|
||||
alias(libs.plugins.android.library)
|
||||
alias(libs.plugins.kotlin.android)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "io.visus.solanim.vulkan"
|
||||
compileSdk {
|
||||
version = release(36)
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 35
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
consumerProguardFiles("consumer-rules.pro")
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments += listOf("-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON")
|
||||
cppFlags("-std=c++17")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
path("src/main/cpp/CMakeLists.txt")
|
||||
version = "3.22.1"
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "11"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(libs.androidx.core.ktx)
|
||||
implementation(libs.androidx.appcompat)
|
||||
implementation(libs.material)
|
||||
testImplementation(libs.junit)
|
||||
androidTestImplementation(libs.androidx.junit)
|
||||
androidTestImplementation(libs.androidx.espresso.core)
|
||||
}
|
||||
0
lib/vulkan/consumer-rules.pro
Normal file
0
lib/vulkan/consumer-rules.pro
Normal file
21
lib/vulkan/proguard-rules.pro
vendored
Normal file
21
lib/vulkan/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
@@ -0,0 +1,24 @@
|
||||
package io.visus.solanim.vulkan
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ExampleInstrumentedTest {
|
||||
@Test
|
||||
fun useAppContext() {
|
||||
// Context of the app under test.
|
||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
assertEquals("io.visus.solanim.vulkan.test", appContext.packageName)
|
||||
}
|
||||
}
|
||||
4
lib/vulkan/src/main/AndroidManifest.xml
Normal file
4
lib/vulkan/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
</manifest>
|
||||
41
lib/vulkan/src/main/cpp/CMakeLists.txt
Normal file
41
lib/vulkan/src/main/cpp/CMakeLists.txt
Normal file
@@ -0,0 +1,41 @@
|
||||
# For more information about using CMake with Android Studio, read the
|
||||
# documentation: https://d.android.com/studio/projects/add-native-code.html.
|
||||
# For more examples on how to use CMake, see https://github.com/android/ndk-samples.
|
||||
|
||||
# Sets the minimum CMake version required for this project.
|
||||
cmake_minimum_required(VERSION 3.22.1)
|
||||
|
||||
# Declares the project name. The project name can be accessed via ${ PROJECT_NAME},
|
||||
# Since this is the top level CMakeLists.txt, the project name is also accessible
|
||||
# with ${CMAKE_PROJECT_NAME} (both CMake variables are in-sync within the top level
|
||||
# build script scope).
|
||||
project("vulkan")
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "Android")
|
||||
# ... other Android specific settings ...
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,max-page-size=16384")
|
||||
endif()
|
||||
|
||||
# Creates and names a library, sets it as either STATIC
|
||||
# or SHARED, and provides the relative paths to its source code.
|
||||
# You can define multiple libraries, and CMake builds them for you.
|
||||
# Gradle automatically packages shared libraries with your APK.
|
||||
#
|
||||
# In this top level CMakeLists.txt, ${CMAKE_PROJECT_NAME} is used to define
|
||||
# the target library name; in the sub-module's CMakeLists.txt, ${PROJECT_NAME}
|
||||
# is preferred for the same purpose.
|
||||
#
|
||||
# In order to load a library into your app from Java/Kotlin, you must call
|
||||
# System.loadLibrary() and pass the name of the library defined here;
|
||||
# for GameActivity/NativeActivity derived applications, the same library name must be
|
||||
# used in the AndroidManifest.xml file.
|
||||
add_library(${CMAKE_PROJECT_NAME} SHARED
|
||||
# List C/C++ source files with relative paths to this CMakeLists.txt.
|
||||
vulkan.cpp)
|
||||
|
||||
# Specifies libraries CMake should link to your target library. You
|
||||
# can link libraries from various origins, such as libraries defined in this
|
||||
# build script, prebuilt third-party libraries, or Android system libraries.
|
||||
target_link_libraries(${CMAKE_PROJECT_NAME}
|
||||
android
|
||||
log)
|
||||
262
lib/vulkan/src/main/cpp/core/VulkanRenderer.h
Normal file
262
lib/vulkan/src/main/cpp/core/VulkanRenderer.h
Normal file
@@ -0,0 +1,262 @@
|
||||
#ifndef VULKANRENDERER_H
|
||||
#define VULKANRENDERER_H
|
||||
|
||||
#include <android/asset_manager.h>
|
||||
#include <android/log.h>
|
||||
#include <android/native_window.h>
|
||||
#include <android/native_window_jni.h>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vulkan/vulkan_android.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <optional>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
struct QueueFamilyIndices {
|
||||
std::optional<uint32_t> graphicsFamily;
|
||||
std::optional<uint32_t> presentFamily;
|
||||
|
||||
bool isComplete() {
|
||||
return graphicsFamily.has_value() && presentFamily.has_value();
|
||||
}
|
||||
};
|
||||
|
||||
struct SwapChainSupportDetails {
|
||||
VkSurfaceCapabilitiesKHR capabilities;
|
||||
std::vector<VkSurfaceFormatKHR> formats;
|
||||
std::vector<VkPresentModeKHR> presentModes;
|
||||
};
|
||||
|
||||
struct ANativeWindowDestroyer {
|
||||
void operator()(ANativeWindow* window) const {
|
||||
if (window) {
|
||||
ANativeWindow_release(window);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class VulkanRenderer {
|
||||
#define LOG_TAG "VulkanRenderer"
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define LOGI(...) ((void)0)
|
||||
#define LOGE(...) ((void)0)
|
||||
#else
|
||||
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
|
||||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#define VK_CHECK(callback) { \
|
||||
do { \
|
||||
VkResult result = callback; \
|
||||
if (result != VK_SUCCESS) { \
|
||||
LOGE("Vulkan error: %d, at %s:%d", result, __FILE__, __LINE__); \
|
||||
abort(); \
|
||||
} \
|
||||
} while (0); \
|
||||
} \
|
||||
|
||||
public:
|
||||
void initialize();
|
||||
|
||||
bool isInitialized = false;
|
||||
|
||||
private:
|
||||
std::unique_ptr<ANativeWindow, ANativeWindowDestroyer> window_;
|
||||
|
||||
const std::vector<const char *> deviceExtensions_ = {VK_KHR_SWAPCHAIN_EXTENSION_NAME};
|
||||
|
||||
VkInstance instance_;
|
||||
VkSurfaceKHR surface_;
|
||||
VkPhysicalDevice physicalDevice_ = VK_NULL_HANDLE;
|
||||
VkDevice device_;
|
||||
|
||||
VkQueue graphicsQueue_;
|
||||
VkQueue presentQueue_;
|
||||
|
||||
void createInstance();
|
||||
void createLogicalDeviceAndQueue();
|
||||
void createSurface();
|
||||
bool checkDeviceExtensionSupport(VkPhysicalDevice device);
|
||||
QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) const;
|
||||
bool isDeviceSuitable(VkPhysicalDevice device);
|
||||
void pickPhysicalDevice();
|
||||
static std::vector<const char*> getRequiredExtensions();
|
||||
};
|
||||
|
||||
std::vector<const char*> VulkanRenderer::getRequiredExtensions() {
|
||||
std::vector<const char *> extensions;
|
||||
|
||||
extensions.push_back("VK_KHR_surface");
|
||||
extensions.push_back("VK_KHR_android_surface");
|
||||
|
||||
return extensions;
|
||||
}
|
||||
|
||||
void VulkanRenderer::initialize() {
|
||||
createInstance();
|
||||
createSurface();
|
||||
pickPhysicalDevice();
|
||||
createLogicalDeviceAndQueue();
|
||||
|
||||
isInitialized = true;
|
||||
LOGI("VulkanRenderer initialized successfully.");
|
||||
}
|
||||
|
||||
void VulkanRenderer::createInstance() {
|
||||
std::vector<const char*> requiredExtensions = getRequiredExtensions();
|
||||
|
||||
VkApplicationInfo appInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
|
||||
.pApplicationName = "Vulkan Renderer",
|
||||
.applicationVersion = VK_MAKE_VERSION(1, 0, 0),
|
||||
.pEngineName = "No Engine",
|
||||
.engineVersion = VK_MAKE_VERSION(1, 0, 0),
|
||||
.apiVersion = VK_API_VERSION_1_1
|
||||
};
|
||||
|
||||
VkInstanceCreateInfo createInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.pApplicationInfo = &appInfo,
|
||||
.enabledLayerCount = 0,
|
||||
.enabledExtensionCount = (uint32_t)requiredExtensions.size(),
|
||||
.ppEnabledExtensionNames = requiredExtensions.data(),
|
||||
};
|
||||
|
||||
VK_CHECK(vkCreateInstance(&createInfo, nullptr, &instance_))
|
||||
}
|
||||
|
||||
void VulkanRenderer::createLogicalDeviceAndQueue() {
|
||||
QueueFamilyIndices indices = findQueueFamilies(physicalDevice_);
|
||||
|
||||
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
|
||||
std::set<uint32_t> uniqueQueueFamilies = {
|
||||
indices.graphicsFamily.value(),
|
||||
indices.presentFamily.value()
|
||||
};
|
||||
|
||||
float queuePriority = 1.0f;
|
||||
|
||||
for (uint32_t queueFamily : uniqueQueueFamilies) {
|
||||
VkDeviceQueueCreateInfo queueCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
|
||||
.queueFamilyIndex = queueFamily,
|
||||
.queueCount = 1,
|
||||
.pQueuePriorities = &queuePriority,
|
||||
};
|
||||
queueCreateInfos.push_back(queueCreateInfo);
|
||||
}
|
||||
|
||||
VkPhysicalDeviceFeatures deviceFeatures{};
|
||||
|
||||
VkDeviceCreateInfo createInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
|
||||
.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size()),
|
||||
.pQueueCreateInfos = queueCreateInfos.data(),
|
||||
.enabledLayerCount = 0,
|
||||
.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions_.size()),
|
||||
.ppEnabledExtensionNames = deviceExtensions_.data(),
|
||||
.pEnabledFeatures = &deviceFeatures,
|
||||
};
|
||||
|
||||
VK_CHECK(vkCreateDevice(physicalDevice_, &createInfo, nullptr, &device_))
|
||||
|
||||
vkGetDeviceQueue(device_, indices.graphicsFamily.value(), 0, &graphicsQueue_);
|
||||
vkGetDeviceQueue(device_, indices.presentFamily.value(), 0, &presentQueue_);
|
||||
}
|
||||
|
||||
void VulkanRenderer::createSurface() {
|
||||
assert(window_ != nullptr);
|
||||
|
||||
const VkAndroidSurfaceCreateInfoKHR createInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.window = window_.get(),
|
||||
};
|
||||
|
||||
VK_CHECK(vkCreateAndroidSurfaceKHR(instance_, &createInfo, nullptr, &surface_))
|
||||
}
|
||||
|
||||
bool VulkanRenderer::checkDeviceExtensionSupport(VkPhysicalDevice device) {
|
||||
uint32_t extensionCount;
|
||||
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount,
|
||||
nullptr);
|
||||
|
||||
std::vector<VkExtensionProperties> availableExtensions(extensionCount);
|
||||
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount,
|
||||
availableExtensions.data());
|
||||
|
||||
std::set<std::string> requiredExtensions(deviceExtensions_.begin(), deviceExtensions_.end());
|
||||
|
||||
for (const VkExtensionProperties &extension : availableExtensions) {
|
||||
requiredExtensions.erase(extension.extensionName);
|
||||
}
|
||||
|
||||
return requiredExtensions.empty();
|
||||
}
|
||||
|
||||
QueueFamilyIndices VulkanRenderer::findQueueFamilies(VkPhysicalDevice device) const {
|
||||
QueueFamilyIndices indices;
|
||||
|
||||
uint32_t queueFamilyCount = 0;
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
|
||||
|
||||
std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount,
|
||||
queueFamilies.data());
|
||||
|
||||
for (uint32_t i = 0; i < queueFamilies.size(); i++) {
|
||||
if (queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
|
||||
indices.graphicsFamily = i;
|
||||
}
|
||||
|
||||
VkBool32 presentSupport = false;
|
||||
vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface_, &presentSupport);
|
||||
if (presentSupport) {
|
||||
indices.presentFamily = i;
|
||||
}
|
||||
|
||||
if (indices.isComplete()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return indices;
|
||||
}
|
||||
|
||||
bool VulkanRenderer::isDeviceSuitable(VkPhysicalDevice device) {
|
||||
QueueFamilyIndices indices = findQueueFamilies(device);
|
||||
|
||||
bool extensionsSupported = checkDeviceExtensionSupport(device);
|
||||
bool swapChainAdequate = false;
|
||||
|
||||
return indices.isComplete() && extensionsSupported && swapChainAdequate;
|
||||
}
|
||||
|
||||
|
||||
void VulkanRenderer::pickPhysicalDevice() {
|
||||
uint32_t deviceCount = 0;
|
||||
vkEnumeratePhysicalDevices(instance_, &deviceCount, nullptr);
|
||||
|
||||
assert(deviceCount > 0);
|
||||
|
||||
std::vector<VkPhysicalDevice> devices(deviceCount);
|
||||
vkEnumeratePhysicalDevices(instance_, &deviceCount, devices.data());
|
||||
|
||||
auto it = std::find_if(devices.begin(), devices.end(),
|
||||
[this](VkPhysicalDevice device) { return isDeviceSuitable(device); });
|
||||
|
||||
if (it != devices.end()) {
|
||||
physicalDevice_ = *it;
|
||||
} else {
|
||||
LOGE("Failed to find a suitable GPU!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif //VULKANRENDERER_H
|
||||
10
lib/vulkan/src/main/cpp/vulkan.cpp
Normal file
10
lib/vulkan/src/main/cpp/vulkan.cpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#include <jni.h>
|
||||
#include <string>
|
||||
|
||||
extern "C" JNIEXPORT jstring JNICALL
|
||||
Java_io_visus_solanim_vulkan_NativeLib_stringFromJNI(
|
||||
JNIEnv* env,
|
||||
jobject /* this */) {
|
||||
std::string hello = "Hello from C++";
|
||||
return env->NewStringUTF(hello.c_str());
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package io.visus.solanim.vulkan
|
||||
|
||||
class NativeLib {
|
||||
|
||||
/**
|
||||
* A native method that is implemented by the 'vulkan' native library,
|
||||
* which is packaged with this application.
|
||||
*/
|
||||
external fun stringFromJNI(): String
|
||||
|
||||
companion object {
|
||||
// Used to load the 'vulkan' library on application startup.
|
||||
init {
|
||||
System.loadLibrary("vulkan")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package io.visus.solanim.vulkan
|
||||
|
||||
import org.junit.Test
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
class ExampleUnitTest {
|
||||
@Test
|
||||
fun addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user