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
59 changes: 49 additions & 10 deletions Engine/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,20 @@ if(IOS)
set(TEMPEST_BUILD_SHARED OFF)
endif()

if(ANDROID)
set(TEMPEST_BUILD_SHARED OFF)
# Android always uses Vulkan
set(TEMPEST_BUILD_VULKAN ON CACHE INTERNAL "")
set(TEMPEST_BUILD_METAL OFF CACHE INTERNAL "")
set(TEMPEST_BUILD_DIRECTX12 OFF CACHE INTERNAL "")

# Add android_native_app_glue
set(ANDROID_NATIVE_APP_GLUE_DIR "${ANDROID_NDK}/sources/android/native_app_glue")
add_library(native_app_glue STATIC "${ANDROID_NATIVE_APP_GLUE_DIR}/android_native_app_glue.c")
target_include_directories(native_app_glue PUBLIC "${ANDROID_NATIVE_APP_GLUE_DIR}")
target_include_directories(${PROJECT_NAME} PUBLIC "${ANDROID_NATIVE_APP_GLUE_DIR}")
endif()

### Compilers
if(MSVC)
add_definitions(-D_USE_MATH_DEFINES)
Expand Down Expand Up @@ -112,6 +126,20 @@ if(TEMPEST_BUILD_AUDIO)
set(ALSOFT_DLOPEN OFF CACHE INTERNAL "")
set(ALSOFT_NO_CONFIG_UTIL OFF CACHE INTERNAL "")
endif()
if(ANDROID)
set(ALSOFT_BACKEND_OPENSL ON CACHE INTERNAL "")
set(ALSOFT_REQUIRE_OPENSL ON CACHE INTERNAL "")
set(ALSOFT_BACKEND_WAVE OFF CACHE INTERNAL "")
set(ALSOFT_BACKEND_ALSA OFF CACHE INTERNAL "")
set(ALSOFT_BACKEND_OSS OFF CACHE INTERNAL "")
set(ALSOFT_BACKEND_PULSEAUDIO OFF CACHE INTERNAL "")
set(ALSOFT_BACKEND_PIPEWIRE OFF CACHE INTERNAL "")
set(ALSOFT_REQUIRE_PIPEWIRE OFF CACHE INTERNAL "")
set(ALSOFT_BACKEND_JACK OFF CACHE INTERNAL "")
set(ALSOFT_BACKEND_SNDIO OFF CACHE INTERNAL "")
set(ALSOFT_RTKIT OFF CACHE INTERNAL "")
set(ALSOFT_DLOPEN OFF CACHE INTERNAL "")
endif()
set(AL_LIBTYPE_STATIC ON CACHE INTERNAL "")
set(ALSOFT_EXAMPLES OFF CACHE INTERNAL "")
set(ALSOFT_UTILS OFF CACHE INTERNAL "")
Expand All @@ -124,6 +152,9 @@ if(TEMPEST_BUILD_AUDIO)
if(IOS)
target_compile_options(OpenAL PRIVATE -Wno-nullability-completeness)
endif()
if(ANDROID)
target_compile_options(OpenAL PRIVATE -Wno-nullability-completeness)
endif()
if(NOT MSVC)
target_compile_options(OpenAL PRIVATE -Wno-attributes -Wno-conversion -Wno-switch -Wno-unused-but-set-variable)
endif()
Expand All @@ -141,17 +172,22 @@ if(TEMPEST_BUILD_VULKAN)
add_definitions(-DVULKAN_HPP_NO_EXCEPTIONS)
add_definitions(-DVULKAN_HPP_NO_SMART_HANDLE)

if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
target_link_directories(${PROJECT_NAME} PRIVATE "$ENV{VULKAN_SDK}/lib")
else()
target_link_directories(${PROJECT_NAME} PRIVATE "$ENV{VULKAN_SDK}/Lib32")
endif()

target_include_directories(${PROJECT_NAME} PRIVATE "$ENV{VULKAN_SDK}/include")
if(WIN32)
target_link_libraries(${PROJECT_NAME} PRIVATE vulkan-1)
else()
if(ANDROID)
# Android NDK provides Vulkan headers and library
target_link_libraries(${PROJECT_NAME} PRIVATE vulkan)
else()
if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
target_link_directories(${PROJECT_NAME} PRIVATE "$ENV{VULKAN_SDK}/lib")
else()
target_link_directories(${PROJECT_NAME} PRIVATE "$ENV{VULKAN_SDK}/Lib32")
endif()

target_include_directories(${PROJECT_NAME} PRIVATE "$ENV{VULKAN_SDK}/include")
if(WIN32)
target_link_libraries(${PROJECT_NAME} PRIVATE vulkan-1)
else()
target_link_libraries(${PROJECT_NAME} PRIVATE vulkan)
endif()
endif()
endif()

Expand Down Expand Up @@ -325,6 +361,9 @@ elseif(IOS)
target_link_libraries(${PROJECT_NAME} PRIVATE "-framework UiKit" "-framework Foundation" "-framework QuartzCore" "-framework Metal")
elseif(APPLE)
target_link_libraries(${PROJECT_NAME} PRIVATE "-framework AppKit" "-framework QuartzCore" "-framework Metal")
elseif(ANDROID)
#NOTE: use Android NDK toolchain for configure
target_link_libraries(${PROJECT_NAME} PRIVATE android log native_app_glue)
elseif(UNIX)
target_link_libraries(${PROJECT_NAME} PRIVATE X11 Xcursor)
endif()
Expand Down
104 changes: 98 additions & 6 deletions Engine/gapi/vulkan/vswapchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@
# define VK_USE_PLATFORM_WIN32_KHR
# include <windows.h>
# include <vulkan/vulkan_win32.h>
#elif defined(__ANDROID__)
# define VK_USE_PLATFORM_ANDROID_KHR
# include <android/native_window.h>
# include <vulkan/vulkan_android.h>
extern "C" ANativeWindow* tempest_android_get_native_window();
#elif defined(__UNIX__)
# define VK_USE_PLATFORM_XLIB_KHR
# include <X11/Xlib.h>
Expand Down Expand Up @@ -168,6 +173,9 @@ VSwapchain::~VSwapchain() {
bool VSwapchain::checkPresentSupport(VkPhysicalDevice device, uint32_t queueFamilyIndex) {
#if defined(__WINDOWS__)
const bool presentSupport = vkGetPhysicalDeviceWin32PresentationSupportKHR(device, queueFamilyIndex)!=VK_FALSE;
#elif defined(__ANDROID__)
// Android always supports presentation if Vulkan is available
const bool presentSupport = true;
#elif defined(__UNIX__)
bool presentSupport = false;
if(auto dpy = reinterpret_cast<Display*>(X11Api::display())){
Expand All @@ -177,6 +185,7 @@ bool VSwapchain::checkPresentSupport(VkPhysicalDevice device, uint32_t queueFami
}
#else
#warning "wsi for vulkan not implemented on this platform"
const bool presentSupport = false;
#endif
return presentSupport;
}
Expand Down Expand Up @@ -218,8 +227,14 @@ void VSwapchain::cleanupSurface() noexcept {
}

void VSwapchain::reset() {
Tempest::Log::i("VSwapchain::reset() called - possible screen rotation detected");
Rect rect = SystemApi::windowClientRect(hwnd);
Tempest::Log::i("Window client rect: ", rect.w, "x", rect.h);

cleanupSwapchain();
createSwapchain(device);

Tempest::Log::i("VSwapchain::reset() completed - swapchain recreated");
}

void VSwapchain::cleanup() noexcept {
Expand All @@ -238,6 +253,15 @@ VkSurfaceKHR VSwapchain::createSurface(VkInstance instance, void* hwnd) {
createInfo.hwnd = HWND(hwnd);
if(vkCreateWin32SurfaceKHR(instance,&createInfo,nullptr,&ret)!=VK_SUCCESS)
throw std::system_error(Tempest::GraphicsErrc::NoDevice);
#elif defined(__ANDROID__)
ANativeWindow* window = tempest_android_get_native_window();
if(window==nullptr)
throw std::system_error(Tempest::GraphicsErrc::NoDevice);
VkAndroidSurfaceCreateInfoKHR createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
createInfo.window = window;
if(vkCreateAndroidSurfaceKHR(instance, &createInfo, nullptr, &ret)!=VK_SUCCESS)
throw std::system_error(Tempest::GraphicsErrc::NoDevice);
#elif defined(__UNIX__)
VkXlibSurfaceCreateInfoKHR createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
Expand Down Expand Up @@ -309,7 +333,51 @@ VkResult VSwapchain::createSwapchain(VDevice& device, const SwapChainSupport& sw
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
}

createInfo.preTransform = swapChainSupport.capabilities.currentTransform;
// Determine the correct transform based on window dimensions for Android
VkSurfaceTransformFlagBitsKHR selectedTransform = swapChainSupport.capabilities.currentTransform;

#if defined(__ANDROID__)
// On Android, manually determine transform from window dimensions if driver doesn't report correctly
bool isLandscape = (extent.width > extent.height);

// Check if currentTransform makes sense for current orientation
bool needManualTransform = false;
if(isLandscape) {
// For landscape, we expect either IDENTITY or ROTATE_270
if(swapChainSupport.capabilities.currentTransform != VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR &&
swapChainSupport.capabilities.currentTransform != VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR) {
needManualTransform = true;
}
} else {
// For portrait, we expect ROTATE_90 or ROTATE_180
if(swapChainSupport.capabilities.currentTransform != VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR &&
swapChainSupport.capabilities.currentTransform != VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR) {
needManualTransform = true;
}
}

if(needManualTransform && (swapChainSupport.capabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)) {
selectedTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
Tempest::Log::i("Android rotation fix: Using manual IDENTITY transform for ", isLandscape ? "landscape" : "portrait");
}
#endif

createInfo.preTransform = selectedTransform;

// Log the final transform being used
const char* transformName = "UNKNOWN";
switch(selectedTransform) {
case VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR: transformName = "IDENTITY (0°)"; break;
case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR: transformName = "ROTATE_90 (90°)"; break;
case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR: transformName = "ROTATE_180 (180°)"; break;
case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR: transformName = "ROTATE_270 (270°)"; break;
case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR: transformName = "HORIZONTAL_MIRROR"; break;
case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR: transformName = "HORIZONTAL_MIRROR_ROTATE_90"; break;
case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR: transformName = "HORIZONTAL_MIRROR_ROTATE_180"; break;
case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR: transformName = "HORIZONTAL_MIRROR_ROTATE_270"; break;
case VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR: transformName = "INHERIT"; break;
}
Tempest::Log::i("Vulkan surface preTransform: ", transformName, " (", uint32_t(selectedTransform), ")");
createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
createInfo.presentMode = presentMode;
createInfo.clipped = VK_FALSE;
Expand All @@ -320,6 +388,8 @@ VkResult VSwapchain::createSwapchain(VDevice& device, const SwapChainSupport& sw
swapChainImageFormat = surfaceFormat.format;
swapChainExtent = extent;

Tempest::Log::i("VSwapchain: swapChainExtent set to ", extent.width, "x", extent.height);

createImageViews(device);

sync.resize(views.size());
Expand Down Expand Up @@ -428,11 +498,22 @@ uint32_t VSwapchain::findImageCount(const SwapChainSupport& support) const {
void VSwapchain::acquireNextImage() {
VkResult code = implAcquireNextImage();

if(code==VK_ERROR_OUT_OF_DATE_KHR || code==VK_SUBOPTIMAL_KHR)
if(code==VK_ERROR_OUT_OF_DATE_KHR) {
Tempest::Log::i("VSwapchain::acquireNextImage: OUT_OF_DATE - rotation likely occurred");
throw SwapchainSuboptimal();

}
#if defined(__ANDROID__)
// On Android, ignore SUBOPTIMAL to avoid constant swapchain recreation
if(code!=VK_SUCCESS && code!=VK_SUBOPTIMAL_KHR)
vkAssert(code);
#else
if(code==VK_SUBOPTIMAL_KHR) {
Tempest::Log::i("VSwapchain::acquireNextImage: SUBOPTIMAL - rotation change detected");
throw SwapchainSuboptimal();
}
if(code!=VK_SUCCESS)
vkAssert(code);
#endif
}

uint32_t VSwapchain::currentBackBufferIndex() {
Expand Down Expand Up @@ -520,8 +601,21 @@ void VSwapchain::present() {

auto tx = Application::tickCount();
VkResult code = device.presentQueue->present(presentInfo);
if(code==VK_ERROR_OUT_OF_DATE_KHR || code==VK_SUBOPTIMAL_KHR)
if(code==VK_ERROR_OUT_OF_DATE_KHR) {
Tempest::Log::i("VSwapchain::present: OUT_OF_DATE - rotation detected during present");
throw SwapchainSuboptimal();
}
#if defined(__ANDROID__)
// On Android, ignore SUBOPTIMAL to avoid constant swapchain recreation
if(code!=VK_SUCCESS && code!=VK_SUBOPTIMAL_KHR)
Detail::vkAssert(code);
#else
if(code==VK_SUBOPTIMAL_KHR) {
Tempest::Log::i("VSwapchain::present: SUBOPTIMAL - rotation change during present");
throw SwapchainSuboptimal();
}
Detail::vkAssert(code);
#endif
tx = Application::tickCount()-tx;
if(tx > 2) {
// std::chrono::system_clock::time_point p = std::chrono::system_clock::now();
Expand All @@ -530,8 +624,6 @@ void VSwapchain::present() {
// strftime(str, sizeof(str), "%H:%M.%S", localtime(&t));
// Log::i(str," : vkQueuePresentKHR[",imgIndex,"] = ", tx);
}
//Log::i("vkQueuePresentKHR[",imgIndex,"] = ", tx);
Detail::vkAssert(code);

acquireNextImage();
}
Expand Down
7 changes: 5 additions & 2 deletions Engine/gapi/vulkanapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,14 @@
using namespace Tempest;
using namespace Tempest::Detail;

#define VK_KHR_WIN32_SURFACE_EXTENSION_NAME "VK_KHR_win32_surface"
#define VK_KHR_XLIB_SURFACE_EXTENSION_NAME "VK_KHR_xlib_surface"
#define VK_KHR_WIN32_SURFACE_EXTENSION_NAME "VK_KHR_win32_surface"
#define VK_KHR_XLIB_SURFACE_EXTENSION_NAME "VK_KHR_xlib_surface"
#define VK_KHR_ANDROID_SURFACE_EXTENSION_NAME "VK_KHR_android_surface"

#if defined(__WINDOWS__)
#define SURFACE_EXTENSION_NAME VK_KHR_WIN32_SURFACE_EXTENSION_NAME
#elif defined(__ANDROID__)
#define SURFACE_EXTENSION_NAME VK_KHR_ANDROID_SURFACE_EXTENSION_NAME
#elif defined(__UNIX__)
#define SURFACE_EXTENSION_NAME VK_KHR_XLIB_SURFACE_EXTENSION_NAME
#endif
Expand Down
Loading