From 0f706df555d61f34d3ba272e6e5cea42f4a75292 Mon Sep 17 00:00:00 2001 From: "Jim McCann (on silkmoth)" Date: Thu, 16 Nov 2023 10:59:12 -0500 Subject: [PATCH] looks like we needed an alpha channel in the swapchain to get framebuffer complete --- Maekfile.js | 15 ++++++++-- XR.cpp | 85 +++++++++++++++++++++++++++++++++++++++++++++-------- main.cpp | 63 ++++++++++++++++++++++++++++++++++----- 3 files changed, 141 insertions(+), 22 deletions(-) diff --git a/Maekfile.js b/Maekfile.js index b699e06..37f7122 100644 --- a/Maekfile.js +++ b/Maekfile.js @@ -156,6 +156,7 @@ maek.TARGETS = [game_exe, show_meshes_exe, show_scene_exe, ...copies]; //android sdk location: const ANDROID_SDK = `../android-sdk`; +const OVR_OPENXR_SDK = `../ovr-openxr-sdk`; //versions of various android sdk packages: const NDK = `${ANDROID_SDK}/ndk/26.1.10909125`; @@ -179,8 +180,8 @@ const android_options = { '-Wall', '-Werror', '-target', 'aarch64-linux-android29', `-I`, `../nest-libs/linux/glm/include`, - `-I`, `../ovr-openxr-sdk/OpenXR/Include`, - `-I`, `../ovr-openxr-sdk/3rdParty/khronos/openxr/OpenXR-SDK/include`, + `-I`, `${OVR_OPENXR_SDK}/OpenXR/Include`, + `-I`, `${OVR_OPENXR_SDK}/3rdParty/khronos/openxr/OpenXR-SDK/include`, `-I`, `${NDK}/sources/android/native_app_glue/`, ], CPPFlags: [], //extra flags for c++ compiler @@ -188,6 +189,10 @@ const android_options = { `${NDK}/toolchains/llvm/prebuilt/linux-x86_64/bin/clang++`, '-target', 'aarch64-linux-android29', '-shared', + '-lEGL', + '-lGLESv3', + '-landroid', + `-L${OVR_OPENXR_SDK}/OpenXR/Libs/Android/arm64-v8a/Release/`, `-lopenxr_loader`, '-Wl,-Bsymbolic', //look for global symbols inside library first '-Wl,-soname,libgame.so', //specify name of output library (suggested by https://android.googlesource.com/platform/ndk/+/master/docs/BuildSystemMaintainers.md#Additional-Required-Arguments ) ], @@ -196,6 +201,12 @@ const android_options = { const android_game_objs = game_sources.map((x) => maek.CPP(x, undefined, android_options)); + +//very much a hack: +android_options.CPP[0] = `${NDK}/toolchains/llvm/prebuilt/linux-x86_64/bin/clang`; +android_game_objs.push( maek.CPP(`${NDK}/sources/android/native_app_glue/android_native_app_glue.c`, `objs/android/android_native_app_glue.o`, android_options) ); +android_options.CPP[0] = `${NDK}/toolchains/llvm/prebuilt/linux-x86_64/bin/clang++`; + const android_common_objs = common_sources.map((x) => maek.CPP(x, undefined, android_options)); const android_game_so = maek.LINK([...android_game_objs, ...android_common_objs], 'objs/android/apk/lib/arm64-v8a/libgame.so', android_options); diff --git a/XR.cpp b/XR.cpp index eac55f0..97c38b3 100644 --- a/XR.cpp +++ b/XR.cpp @@ -30,6 +30,7 @@ #include +#include #include #include #include @@ -47,6 +48,22 @@ XR::XR( ) { std::cout << "--- initializing OpenXR ---" << std::endl; +#ifdef __ANDROID__ + { //call xrInitializeLoaderKHR (based on XrApp.cpp from OVR OpenXR SDK): + PFN_xrInitializeLoaderKHR xrInitializeLoaderKHR; + xrGetInstanceProcAddr(XR_NULL_HANDLE, "xrInitializeLoaderKHR", reinterpret_cast< PFN_xrVoidFunction* >(&xrInitializeLoaderKHR)); + if (xrInitializeLoaderKHR != NULL) { + XrLoaderInitInfoAndroidKHR loader_init_info = {XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR}; + loader_init_info.applicationVM = platform.application_vm; + loader_init_info.applicationContext = platform.application_activity; //TODO: is this right? OVR sample does it, but docs say this should be a "android.content.Context" (which seems different from the Activity below) + if (XrResult res = xrInitializeLoaderKHR(reinterpret_cast< XrLoaderInitInfoBaseHeaderKHR *>(&loader_init_info)); + res != XR_SUCCESS) { + std::cerr << "WARNING: xrInitializeLoaderKHR failed: " << to_string(res) << std::endl; + } + } + } +#endif //__ANDROID__ + XrInstanceCreateInfo create_info{XR_TYPE_INSTANCE_CREATE_INFO}; //with reference to openxr_program.cpp from openxr-sdk-source + the openxr specification @@ -64,8 +81,15 @@ XR::XR( create_info.applicationInfo.apiVersion = XR_CURRENT_API_VERSION; - //TODO: ON ANROID --> set create_info->next to point to an XrInstanceCreateInfoAndroidKHR structure - // filled in as per openxr-sdk-source's platformplugin_android.cpp +#ifdef __ANDROID__ + //extra android-specific creation info: + XrInstanceCreateInfoAndroidKHR create_info_android{XR_TYPE_INSTANCE_CREATE_INFO_ANDROID_KHR}; + + create_info_android.applicationVM = platform.application_vm; + create_info_android.applicationActivity = platform.application_activity; + + create_info.next = &create_info_android; +#endif //__ANDROID__ std::vector< const char * > extensions{ #ifdef __ANDROID__ @@ -168,6 +192,7 @@ XR::XR( GLint minor = 0; glGetIntegerv(GL_MAJOR_VERSION, &major); glGetIntegerv(GL_MINOR_VERSION, &minor); + std::cout << "Current OpenGL version is " << major << "." << minor << std::endl; if (XR_MAKE_VERSION(major, minor, 0) < graphics_requirements.minApiVersionSupported) { std::cerr << "ERROR: reported OpenGL version (" << major << "." << minor << ") is less than the minimum that OpenXR reports as supporting. (Continuing, but don't expect things to work.)" << std::endl; } @@ -287,20 +312,28 @@ XR::XR( } { //swapchain creation - std::array< int64_t, 64 > formats; - uint32_t format_capacity = uint32_t(formats.size()); + uint32_t format_capacity = 0; + //query needed format capacity: + if (XrResult res = xrEnumerateSwapchainFormats(session, 0, &format_capacity, nullptr); + res != XR_SUCCESS) { + throw std::runtime_error("Failed to count swapchain formats: " + to_string(res)); + } + //allocate enough space and get them: + std::vector< int64_t > formats(format_capacity, 0); uint32_t format_count = 0; if (XrResult res = xrEnumerateSwapchainFormats(session, format_capacity, &format_count, formats.data()); res != XR_SUCCESS) { - throw std::runtime_error("Failed to get enumerate swapchain formats: " + to_string(res)); + throw std::runtime_error("Failed to enumerate swapchain formats: " + to_string(res)); } - bool have_SRGB8 = false; + GLenum wanted_format = GL_SRGB8_ALPHA8; + std::string wanted_format_name = "GL_SRGB8_ALPHA8"; //for error/info messages + bool have_wanted_format = false; std::cout << "Got " << format_count << " swapchain formats." << std::endl; for (uint32_t f = 0; f < format_count; ++f) { int64_t format = formats[f]; - if (format == GL_SRGB8) have_SRGB8 = true; + if (format == wanted_format) have_wanted_format = true; std::cout << " [" << f << "]: "; #define DO(fmt) if (format == fmt) { std::cout << #fmt; } else @@ -315,17 +348,20 @@ XR::XR( DO(GL_DEPTH_COMPONENT32F) { std::cout << " as-of-yet untranslated enum 0x" << std::hex << format << std::dec; } std::cout << std::endl; + #undef DO } - if (!have_SRGB8) { - throw std::runtime_error("GL_SRGB8 was not among the preferred formats; this code expects it to be."); + if (!have_wanted_format) { + throw std::runtime_error(wanted_format_name + " was not among the preferred formats; this code expects it to be."); + } else { + std::cerr << "Chose " << wanted_format_name << " for swapchain format." << std::endl; } XrSwapchainCreateInfo create_info{XR_TYPE_SWAPCHAIN_CREATE_INFO}; create_info.createFlags = 0; create_info.usageFlags = XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT | XR_SWAPCHAIN_USAGE_SAMPLED_BIT; //NOTE: USAGE_SAMPLED_BIT was used by the ovr sdk sample; not sure if this is actually needed - create_info.format = GL_SRGB8; + create_info.format = wanted_format; create_info.sampleCount = 1; create_info.width = size.x; create_info.height = size.y; @@ -362,6 +398,7 @@ XR::XR( view.framebuffers.resize(images.size()); for (uint32_t i = 0; i < view.framebuffers.size(); ++i) { view.framebuffers[i].color_tex = images[i].image; + //set texture sampling state: (ovr sdk sample does this; not sure if it is needed) glBindTexture(GL_TEXTURE_2D, view.framebuffers[i].color_tex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); @@ -370,22 +407,46 @@ XR::XR( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glBindTexture(GL_TEXTURE_2D, 0); + GL_ERRORS(); + //allocate depth renderbuffer: glGenRenderbuffers(1, &view.framebuffers[i].depth_rb); glBindRenderbuffer(GL_RENDERBUFFER, view.framebuffers[i].depth_rb); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, size.x, size.y); glBindRenderbuffer(GL_RENDERBUFFER, 0); + GL_ERRORS(); + //allocate framebuffer: glGenFramebuffers(1, &view.framebuffers[i].fb); glBindFramebuffer(GL_FRAMEBUFFER, view.framebuffers[i].fb); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, view.framebuffers[i].depth_rb); + //glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, view.framebuffers[i].depth_rb); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, view.framebuffers[i].color_tex, 0); + GL_ERRORS(); + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { - throw std::runtime_error("Failed to create a complete framebuffer " + std::to_string(status)); + #define DO(name) \ + if (status == name) { throw std::runtime_error("Failed to create a complete framebuffer:" + std::string(#name)); } else + + DO(GL_FRAMEBUFFER_UNDEFINED); + DO(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT); + DO(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT); + DO(GL_FRAMEBUFFER_UNSUPPORTED); + DO(GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE); + #ifndef __ANDROID__ + DO(GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER); + DO(GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER); + DO(GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS); + #endif + { + std::ostringstream str; + str << "0x" << std::hex << status; + throw std::runtime_error("Failed to create a complete framebuffer: unknown status " + str.str()); + } + #undef DO } glBindFramebuffer(GL_FRAMEBUFFER, 0); diff --git a/main.cpp b/main.cpp index 0badfbc..9478f64 100644 --- a/main.cpp +++ b/main.cpp @@ -15,8 +15,8 @@ #ifdef __ANDROID__ #include -//#include -//#include +#include +#include #else //Includes for desktop platforms: @@ -36,8 +36,6 @@ #ifdef __ANDROID__ -/* - //This delightful redirect hack based on: // https://codelab.wordpress.com/2014/11/03/how-to-use-standard-output-streams-for-logging-in-android-apps/ @@ -75,18 +73,17 @@ int start_logger() { pthread_detach(thr); return 0; } -*/ //modeled on OpenXR's "hello_xr" example's main.cpp: // https://github.com/KhronosGroup/OpenXR-SDK-Source/blob/main/src/tests/hello_xr/main.cpp void android_main(struct android_app* app) { -/* + if (start_logger() != 0) { __android_log_write(ANDROID_LOG_FATAL, tag, "Failed to start log thread!"); return; } -*/ + try { JNIEnv* Env; app->activity->vm->AttachCurrentThread(&Env, nullptr); @@ -126,6 +123,8 @@ void android_main(struct android_app* app) { EGL_SAMPLES, 0, EGL_CONFORMANT, EGL_OPENGL_ES3_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT, + //EGL_BIND_TO_TEXTURE_RGB, EGL_TRUE, + EGL_BIND_TO_TEXTURE_RGBA, EGL_TRUE, EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT, EGL_NONE }; @@ -146,7 +145,54 @@ void android_main(struct android_app* app) { config = configs[0]; } - //TODO: perhaps dump config info for later debugging help? + { //dump info about config for debugging purposes + EGLint value = 0; + std::string info = "Chosen config attributes:\n"; + #define DO(name) \ + info += " " #name ": "; \ + if (EGLBoolean res = eglGetConfigAttrib(display, config, name, &value); \ + res == EGL_TRUE) { \ + info += std::to_string(value) + "\n"; \ + } else { \ + info += "(failed to get value!)\n"; \ + } + + DO(EGL_RED_SIZE) + DO(EGL_GREEN_SIZE) + DO(EGL_BLUE_SIZE) + DO(EGL_ALPHA_SIZE) + DO(EGL_DEPTH_SIZE) + DO(EGL_STENCIL_SIZE) + DO(EGL_ALPHA_MASK_SIZE) + DO(EGL_SAMPLE_BUFFERS) + DO(EGL_SAMPLES) + DO(EGL_BIND_TO_TEXTURE_RGB) + DO(EGL_BIND_TO_TEXTURE_RGBA) + DO(EGL_BUFFER_SIZE) + DO(EGL_COLOR_BUFFER_TYPE) + DO(EGL_CONFIG_CAVEAT) + DO(EGL_CONFIG_ID) + DO(EGL_CONFORMANT) + DO(EGL_LEVEL) + DO(EGL_LUMINANCE_SIZE) + DO(EGL_MAX_PBUFFER_WIDTH) + DO(EGL_MAX_PBUFFER_HEIGHT) + DO(EGL_MAX_PBUFFER_PIXELS) + DO(EGL_MAX_SWAP_INTERVAL) + DO(EGL_MIN_SWAP_INTERVAL) + DO(EGL_NATIVE_RENDERABLE) + DO(EGL_NATIVE_VISUAL_ID) + DO(EGL_NATIVE_VISUAL_TYPE) + DO(EGL_RENDERABLE_TYPE) + DO(EGL_SURFACE_TYPE) + DO(EGL_TRANSPARENT_TYPE) + DO(EGL_TRANSPARENT_RED_VALUE) + DO(EGL_TRANSPARENT_GREEN_VALUE) + DO(EGL_TRANSPARENT_BLUE_VALUE) + + #undef DO + std::cout << info; std::cout.flush(); + } //create a surface (required when making a context current): EGLSurface surface = EGL_NO_SURFACE; @@ -168,6 +214,7 @@ void android_main(struct android_app* app) { { const EGLint attrib_list[] = { EGL_CONTEXT_MAJOR_VERSION, 3, + EGL_CONTEXT_MINOR_VERSION, 2, EGL_NONE };