android 表面,扩展,实例都创建成功
This commit is contained in:
+5
-1
@@ -10,13 +10,15 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Android")
|
||||
message(STATUS "Building for Android")
|
||||
|
||||
include_directories(
|
||||
|
||||
vulkan
|
||||
)
|
||||
|
||||
add_library(sample SHARED
|
||||
app/src/main/cpp/main.cpp
|
||||
app/src/main/cpp/AndroidOut.cpp
|
||||
vulkan/AppBase.h
|
||||
vulkan/AppBase.cpp
|
||||
vulkan/Application.h
|
||||
vulkan/Application.cpp
|
||||
)
|
||||
|
||||
@@ -53,7 +55,9 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Windows" OR WIN32)
|
||||
# 将源代码添加到此项目的可执行文件。
|
||||
add_executable (sample
|
||||
"vulkan/main.cpp"
|
||||
"vulkan/AppBase.h"
|
||||
"vulkan/AppBase.cpp"
|
||||
"vulkan/Application.h"
|
||||
"vulkan/Application.cpp"
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -2,6 +2,9 @@ plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
}
|
||||
|
||||
ext.vvl_version='1.4.321.0'
|
||||
apply from: "./download_vvl.gradle"
|
||||
|
||||
android {
|
||||
namespace 'com.hmwl.sample'
|
||||
compileSdk 35
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
/*
|
||||
* Copyright (c) 2022-2024, The Android Open Source Project.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 the "License";
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Download validation layer binary release zip file from Khronos github repo
|
||||
* https://github.com/KhronosGroup/Vulkan-ValidationLayers/releases
|
||||
*
|
||||
* To use this script, add the following to your module's build.gradle:
|
||||
* ext.vvl_version='your-new-version'
|
||||
* apply from: "${PATH-TO-THIS}/download_vvl.gradle"
|
||||
* To update to a new version:
|
||||
* - change the ext.vvl_version to a new version string.
|
||||
* - delete directory pointed by ${VVL_JNILIB_DIR}.
|
||||
* - sync gradle script in IDE and rebuild project.
|
||||
*
|
||||
* Note: binary release can also be manually downloaded and put it into
|
||||
* the default jniLibs directory at app/src/main/jniLibs.
|
||||
*/
|
||||
|
||||
// get the validation layer version.
|
||||
def VVL_VER = "1.4.321.0"
|
||||
if (ext.has("vvl_version")) {
|
||||
VVL_VER = ext.vvl_version
|
||||
}
|
||||
|
||||
// declare local variables shared between downloading and unzipping.
|
||||
def VVL_SITE ="https://github.com/KhronosGroup/Vulkan-ValidationLayers"
|
||||
def VVL_LIB_ROOT= rootDir.absolutePath.toString() + "/layerLib"
|
||||
def VVL_JNILIB_DIR="${VVL_LIB_ROOT}/jniLibs"
|
||||
def VVL_SO_NAME = "libVkLayer_khronos_validation.so"
|
||||
|
||||
// download the release zip file to ${VVL_LIB_ROOT}/
|
||||
task download {
|
||||
def VVL_ZIP_NAME = "releases/download/vulkan-sdk-${VVL_VER}/android-binaries-${VVL_VER}.zip"
|
||||
mkdir "${VVL_LIB_ROOT}"
|
||||
def f = new File("${VVL_LIB_ROOT}/android-binaries-${VVL_VER}.zip")
|
||||
new URL("${VVL_SITE}/${VVL_ZIP_NAME}")
|
||||
.withInputStream { i -> f.withOutputStream { it << i } }
|
||||
}
|
||||
|
||||
// unzip the downloaded VVL zip archive to the ${VVL_JNILIB_DIR} for APK packaging.
|
||||
task unzip(dependsOn: download, type: Copy) {
|
||||
from zipTree(file("${VVL_LIB_ROOT}/android-binaries-${VVL_VER}.zip"))
|
||||
into file("${VVL_JNILIB_DIR}")
|
||||
}
|
||||
android.sourceSets.main.jniLibs {
|
||||
srcDirs += ["${VVL_JNILIB_DIR}/android-binaries-${VVL_VER}"]
|
||||
}
|
||||
|
||||
// add vvl download as an application dependency.
|
||||
dependencies {
|
||||
def ARM64_VVL_FILE = "${VVL_JNILIB_DIR}/arm64-v8a/${VVL_SO_NAME}"
|
||||
if(!file("${ARM64_VVL_FILE}").exists()) {
|
||||
implementation files("${ARM64_VVL_FILE}") {
|
||||
builtBy 'unzip'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
project.afterEvaluate{
|
||||
project.getTasks().getByName("mergeDebugJniLibFolders").dependsOn(unzip)
|
||||
project.getTasks().getByName("mergeReleaseJniLibFolders").dependsOn(unzip)
|
||||
}
|
||||
+20
-10
@@ -3,14 +3,21 @@
|
||||
#include <game-activity/native_app_glue/android_native_app_glue.h>
|
||||
#include <game-activity/GameActivity.h>
|
||||
#include "AndroidOut.h"
|
||||
#include "Application.h"
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
|
||||
using namespace std;
|
||||
extern "C" {
|
||||
|
||||
android_app* g_android_app = nullptr;
|
||||
Application* g_Application = nullptr;
|
||||
|
||||
void handle_cmd(android_app *pApp, int32_t cmd) {
|
||||
switch (cmd) {
|
||||
case APP_CMD_INIT_WINDOW:
|
||||
aout << "APP_CMD_INIT_WINDOW" << std::endl;
|
||||
g_Application->initVulkan();
|
||||
break;
|
||||
case APP_CMD_TERM_WINDOW:
|
||||
aout << "APP_CMD_TERM_WINDOW" << std::endl;
|
||||
@@ -20,26 +27,24 @@ void handle_cmd(android_app *pApp, int32_t cmd) {
|
||||
}
|
||||
}
|
||||
|
||||
android_app* g_android_app = nullptr;
|
||||
|
||||
void android_main(struct android_app *pApp) {
|
||||
aout << "Welcome to android_main" << std::endl;
|
||||
|
||||
g_android_app = pApp;
|
||||
Application application;
|
||||
g_Application = &application;
|
||||
pApp->onAppCmd = handle_cmd;
|
||||
|
||||
android_app_set_motion_event_filter(pApp, nullptr);
|
||||
|
||||
// This sets up a typical game/event loop. It will run until the app is destroyed.
|
||||
do {
|
||||
auto ms = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count();
|
||||
// Process all pending events before running game logic.
|
||||
bool done = false;
|
||||
while (!done) {
|
||||
// 0 is non-blocking.
|
||||
int timeout = 0;
|
||||
int events;
|
||||
android_poll_source *pSource;
|
||||
int result = ALooper_pollOnce(timeout, nullptr, &events,
|
||||
reinterpret_cast<void**>(&pSource));
|
||||
int result = ALooper_pollOnce(timeout, nullptr, &events, reinterpret_cast<void**>(&pSource));
|
||||
switch (result) {
|
||||
case ALOOPER_POLL_TIMEOUT:
|
||||
[[clang::fallthrough]];
|
||||
@@ -58,8 +63,13 @@ void android_main(struct android_app *pApp) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(application.isInited())
|
||||
{
|
||||
application.drawFrame();
|
||||
}
|
||||
auto end_ms = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count();
|
||||
this_thread::sleep_for(chrono::milliseconds((end_ms-ms)));
|
||||
} while (!pApp->destroyRequested);
|
||||
application.cleanup();
|
||||
}
|
||||
}
|
||||
+27
-1
@@ -1,4 +1,11 @@
|
||||
#include "AppBase.h"
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
void VK_CHECK(VkResult ret)
|
||||
{
|
||||
assert(ret == VK_SUCCESS);
|
||||
}
|
||||
|
||||
std::vector<char> AppBase::readFile(const std::string& enginePath)
|
||||
{
|
||||
@@ -55,6 +62,18 @@ bool AppBase::checkValidationLayerSupport() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void checkInstanceExtensions() {
|
||||
uint32_t extensionCount = 0;
|
||||
vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr);
|
||||
|
||||
std::vector<VkExtensionProperties> extensions(extensionCount);
|
||||
vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data());
|
||||
|
||||
logOut << "Android Available instance extensions:" << std::endl;
|
||||
for (const auto& extension : extensions) {
|
||||
logOut << "ExtensionName:" << extension.extensionName << " Version:" << extension.specVersion << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void AppBase::createInstance()
|
||||
{
|
||||
@@ -84,7 +103,14 @@ void AppBase::createInstance()
|
||||
for(int i = 0; i < glfwExtensionCount; i++) {
|
||||
extensions.push_back(glfwExtensions[i]);
|
||||
}
|
||||
#else
|
||||
checkInstanceExtensions();
|
||||
// Android: 手动添加必要的扩展
|
||||
extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
|
||||
extensions.push_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME);
|
||||
|
||||
// 可选:添加这些扩展以获得更好的兼容性
|
||||
extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
|
||||
#endif
|
||||
|
||||
if (enableValidationLayers) {
|
||||
@@ -160,7 +186,7 @@ QueueFamilyIndices AppBase::findQueueFamilies(VkPhysicalDevice device, VkSurface
|
||||
indices.graphicsFamily = i;
|
||||
}
|
||||
VkBool32 presentSupport = false;
|
||||
vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);
|
||||
VK_CHECK(vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport));
|
||||
if (presentSupport) {
|
||||
indices.presentFamily = i;
|
||||
}
|
||||
|
||||
+17
-10
@@ -1,23 +1,28 @@
|
||||
#ifndef VULKAN_UTILS_H
|
||||
#define VULKAN_UTILS_H
|
||||
|
||||
#define VK_CHECK(x) \
|
||||
do \
|
||||
{ \
|
||||
VkResult err = x; \
|
||||
if (err) \
|
||||
{ \
|
||||
assert(false); \
|
||||
} \
|
||||
} while (0)
|
||||
//#define VK_CHECK(x) \
|
||||
// do \
|
||||
// { \
|
||||
// VkResult ret = x; \
|
||||
// if (ret != VK_SUCCESS) \
|
||||
// { \
|
||||
// assert(false); \
|
||||
// } \
|
||||
// } while (0)
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
#define logOut std::cout
|
||||
#define GLFW_INCLUDE_VULKAN
|
||||
#include <GLFW/glfw3.h>
|
||||
#else
|
||||
#include "../app/src/main/cpp/AndroidOut.h"
|
||||
#define logOut aout
|
||||
#define VK_USE_PLATFORM_ANDROID_KHR
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vulkan/vulkan_android.h>
|
||||
#include <android/native_window.h>
|
||||
#endif
|
||||
|
||||
|
||||
@@ -29,7 +34,9 @@
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <assert.h>
|
||||
|
||||
void VK_CHECK(VkResult ret);
|
||||
|
||||
|
||||
struct QueueFamilyIndices {
|
||||
std::optional<uint32_t> graphicsFamily;
|
||||
|
||||
+10
-10
@@ -44,7 +44,7 @@ void Application::createSurface() {
|
||||
}
|
||||
#else
|
||||
|
||||
VkSurfaceKHR surface{};
|
||||
//VkSurfaceKHR surface{};
|
||||
|
||||
VkAndroidSurfaceCreateInfoKHR info{VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR};
|
||||
|
||||
@@ -73,6 +73,7 @@ void Application::initVulkan()
|
||||
createCommandPool(); // 创建命令池
|
||||
createCommandBuffer(); // 创建命令缓冲区
|
||||
createSyncObjects(); // 创建同步对象
|
||||
inited = true;
|
||||
}
|
||||
|
||||
void Application::createImageViews() {
|
||||
@@ -259,6 +260,7 @@ void Application::createSwapChain() {
|
||||
|
||||
swapChainImageFormat = surfaceFormat.format;
|
||||
swapChainExtent = extent;
|
||||
MAX_FRAMES_IN_FLIGHT = imageCount;
|
||||
}
|
||||
|
||||
void Application::createPipelineLayout() {
|
||||
@@ -447,12 +449,11 @@ void Application::createCommandBuffer()
|
||||
|
||||
void Application::createSyncObjects()
|
||||
{
|
||||
int swapChainImageCount = swapChainImages.size();
|
||||
|
||||
imageAvailableSemaphores.resize(swapChainImageCount);
|
||||
renderFinishedSemaphores.resize(swapChainImageCount);
|
||||
imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
|
||||
renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
|
||||
inFlightFences.resize(MAX_FRAMES_IN_FLIGHT);
|
||||
imagesInFlight.resize(swapChainImageCount, VK_NULL_HANDLE);
|
||||
imagesInFlight.resize(MAX_FRAMES_IN_FLIGHT, VK_NULL_HANDLE);
|
||||
|
||||
VkSemaphoreCreateInfo semaphoreInfo{};
|
||||
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
||||
@@ -462,7 +463,7 @@ void Application::createSyncObjects()
|
||||
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
|
||||
|
||||
// 为每个交换链图像创建信号量
|
||||
for (size_t i = 0; i < swapChainImageCount; i++) {
|
||||
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
|
||||
if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphores[i]) != VK_SUCCESS ||
|
||||
vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphores[i]) != VK_SUCCESS) {
|
||||
throw std::runtime_error("failed to create synchronization objects!");
|
||||
@@ -516,15 +517,14 @@ void Application::recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t im
|
||||
}
|
||||
}
|
||||
|
||||
void Application::drawFrame() {
|
||||
void Application::drawFrame()
|
||||
{
|
||||
// 1. 等待前一帧完成
|
||||
vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX);
|
||||
|
||||
// 2. 获取交换链图像 - 使用当前帧的信号量
|
||||
uint32_t imageIndex;
|
||||
VkResult result = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX,
|
||||
imageAvailableSemaphores[currentFrame],
|
||||
VK_NULL_HANDLE, &imageIndex);
|
||||
VkResult result = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX,imageAvailableSemaphores[currentFrame],VK_NULL_HANDLE, &imageIndex);
|
||||
|
||||
if (result != VK_SUCCESS) {
|
||||
throw std::runtime_error("failed to acquire swap chain image!");
|
||||
|
||||
@@ -11,7 +11,6 @@ class Application : public AppBase
|
||||
public:
|
||||
void run();
|
||||
|
||||
protected:
|
||||
void initVulkan();
|
||||
void initWindow();
|
||||
void createSurface();
|
||||
@@ -31,6 +30,7 @@ protected:
|
||||
void recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex);
|
||||
void createSyncObjects();
|
||||
void drawFrame();
|
||||
bool isInited() { return inited; }
|
||||
private:
|
||||
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; // 物理设备
|
||||
VkDevice device; // 逻辑设备
|
||||
@@ -55,4 +55,5 @@ private:
|
||||
std::vector<VkFence> imagesInFlight; // 跟踪每个图像的使用状态
|
||||
int currentFrame = 0;
|
||||
int MAX_FRAMES_IN_FLIGHT = 2;
|
||||
bool inited = false;
|
||||
};
|
||||
Reference in New Issue
Block a user