From 1743626113aa48b005742c57d56e50297c6318c1 Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Sun, 29 May 2022 18:44:27 -0400 Subject: [PATCH] Sweeping Media Layer Changes (GL ES 2.0 Support) --- .gitignore | 2 + CMakeLists.txt | 9 +- dependencies/minecraft-pi/CMakeLists.txt | 7 +- docs/COMMAND_LINE.md | 5 +- launcher/src/bootstrap.c | 12 +- launcher/src/bootstrap.h | 2 +- launcher/src/client/launcher.cpp | 2 +- launcher/src/crash-report.c | 95 ++++--- launcher/src/server/launcher.c | 2 +- libreborn/include/libreborn/util.h | 4 +- libreborn/src/util/util.c | 16 ++ media-layer/CMakeLists.txt | 13 +- media-layer/core/CMakeLists.txt | 22 +- media-layer/core/src/audio/api.cpp | 2 + media-layer/core/src/audio/file.cpp | 15 +- media-layer/core/src/media.c | 23 +- media-layer/core/src/screenshot.c | 8 +- media-layer/extras/CMakeLists.txt | 7 +- media-layer/gles/CMakeLists.txt | 29 ++ .../gles/src/compatibility-layer/draw.c | 255 ++++++++++++++++++ .../gles/src/compatibility-layer/matrix.c | 132 +++++++++ .../gles/src/compatibility-layer/matrix.h | 9 + .../src/compatibility-layer/passthrough.c | 113 ++++++++ .../src/compatibility-layer/passthrough.h | 19 ++ .../gles/src/compatibility-layer/state.c | 178 ++++++++++++ .../gles/src/compatibility-layer/state.h | 50 ++++ .../src/GLESv1_CM.c => gles/src/stubs.c} | 48 ++++ media-layer/include/GLES/gl.h | 22 +- media-layer/include/media-layer/audio.h | 7 + media-layer/proxy/CMakeLists.txt | 20 +- media-layer/proxy/src/GLESv1_CM.c | 6 +- media-layer/stubs/CMakeLists.txt | 14 - mods/CMakeLists.txt | 18 +- mods/src/benchmark/benchmark.cpp | 45 +++- mods/src/chat/chat.cpp | 2 +- mods/src/init/init.c | 2 +- mods/src/init/init.h | 2 +- mods/src/misc/logging.cpp | 16 -- mods/src/misc/misc.cpp | 26 +- mods/src/misc/misc.h | 1 + mods/src/server/server.cpp | 71 +++-- mods/src/sign/sign.cpp | 4 +- mods/src/textures/textures.cpp | 11 +- .../tools/generate-appimage-builder-yaml.js | 25 +- 44 files changed, 1181 insertions(+), 190 deletions(-) create mode 100644 media-layer/gles/CMakeLists.txt create mode 100644 media-layer/gles/src/compatibility-layer/draw.c create mode 100644 media-layer/gles/src/compatibility-layer/matrix.c create mode 100644 media-layer/gles/src/compatibility-layer/matrix.h create mode 100644 media-layer/gles/src/compatibility-layer/passthrough.c create mode 100644 media-layer/gles/src/compatibility-layer/passthrough.h create mode 100644 media-layer/gles/src/compatibility-layer/state.c create mode 100644 media-layer/gles/src/compatibility-layer/state.h rename media-layer/{stubs/src/GLESv1_CM.c => gles/src/stubs.c} (73%) delete mode 100644 media-layer/stubs/CMakeLists.txt diff --git a/.gitignore b/.gitignore index d6bb353c..f8236c81 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,5 @@ appimage-builder-cache appimage-build AppDir *.zsync +core* +qemu_* diff --git a/CMakeLists.txt b/CMakeLists.txt index f72db20b..bcab3232 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,15 +2,15 @@ cmake_minimum_required(VERSION 3.13.0) # Specify Options option(MCPI_IS_MIXED_BUILD "Whether The Architecture-Independent And ARM Code Are Different Architecture" FALSE) -if(MCPI_IS_MIXED_BUILD) - option(MCPI_BUNDLE_ARMHF_SYSROOT "Whether To Include An ARMHF Sysroot" TRUE) -endif() +option(MCPI_BUNDLE_ARMHF_SYSROOT "Whether To Include An ARMHF Sysroot" ${MCPI_IS_MIXED_BUILD}) option(MCPI_SERVER_MODE "Server Mode" FALSE) option(MCPI_HEADLESS_MODE "Headless Mode" ${MCPI_SERVER_MODE}) if(NOT MCPI_HEADLESS_MODE) option(MCPI_USE_MEDIA_LAYER_PROXY "Whether To Enable The Media Layer Proxy" ${MCPI_IS_MIXED_BUILD}) + option(MCPI_USE_GLES1_COMPATIBILITY_LAYER "Whether To Enable The GLESv1_CM Compatibility Layer" TRUE) else() set(MCPI_USE_MEDIA_LAYER_PROXY FALSE) + set(MCPI_USE_GLES1_COMPATIBILITY_LAYER FALSE) endif() set(MCPI_BUILD_MODE "both" CACHE STRING "\"arm\" = Build Only Code That Must Be ARM; \"native\" = Build Architecture-Independent Code; \"both\" = Build All Code As ARM") set_property(CACHE MCPI_BUILD_MODE PROPERTY STRINGS "both" "arm" "native") @@ -118,6 +118,9 @@ endif() if(MCPI_BUNDLE_ARMHF_SYSROOT) add_definitions(-DMCPI_BUNDLE_ARMHF_SYSROOT) endif() +if(MCPI_USE_GLES1_COMPATIBILITY_LAYER) + add_definitions(-DMCPI_USE_GLES1_COMPATIBILITY_LAYER) +endif() # Version set_property( diff --git a/dependencies/minecraft-pi/CMakeLists.txt b/dependencies/minecraft-pi/CMakeLists.txt index 647410df..ee796be7 100644 --- a/dependencies/minecraft-pi/CMakeLists.txt +++ b/dependencies/minecraft-pi/CMakeLists.txt @@ -12,4 +12,9 @@ FetchContent_Declare( FetchContent_Populate(minecraft-pi) # Install -install(DIRECTORY "${minecraft-pi_SOURCE_DIR}/" DESTINATION "${MCPI_INSTALL_DIR}" USE_SOURCE_PERMISSIONS) +install( + DIRECTORY "${minecraft-pi_SOURCE_DIR}/" + DESTINATION "${MCPI_INSTALL_DIR}" + USE_SOURCE_PERMISSIONS + REGEX "api" EXCLUDE +) diff --git a/docs/COMMAND_LINE.md b/docs/COMMAND_LINE.md index 8e8498e8..cc39efda 100644 --- a/docs/COMMAND_LINE.md +++ b/docs/COMMAND_LINE.md @@ -2,8 +2,11 @@ ## Command Line Arguments +### ``--version`` (Or ``-v``) +If you run MCPI-Reborn with ``--version`` it will print its version to ``stdout``. + ### ``--print-available-feature-flags`` (Client Mode Only) -If you run MCPI-Reborn with ``--print-available-feature-flags``, it will print the available feature flags and then immediately exit. +If you run MCPI-Reborn with ``--print-available-feature-flags``, it will print the available feature flags to ``stdout`` and then immediately exit. The feature flags are printed in the following format: ``` diff --git a/launcher/src/bootstrap.c b/launcher/src/bootstrap.c index 020d7850..40a8dec5 100644 --- a/launcher/src/bootstrap.c +++ b/launcher/src/bootstrap.c @@ -149,7 +149,17 @@ static void exit_handler(__attribute__((unused)) int signal_id) { } // Pre-Bootstrap -void pre_bootstrap() { +void pre_bootstrap(int argc, char *argv[]) { + // Print Version + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "--version") == 0 || strcmp(argv[i], "-v") == 0) { + // Print + printf("Reborn v%s\n", VERSION); + fflush(stdout); + exit(EXIT_SUCCESS); + } + } + // GTK Dark Mode #ifndef MCPI_SERVER_MODE set_and_print_env("GTK_THEME", "Adwaita:dark"); diff --git a/launcher/src/bootstrap.h b/launcher/src/bootstrap.h index 14c60fc5..f530cc80 100644 --- a/launcher/src/bootstrap.h +++ b/launcher/src/bootstrap.h @@ -6,7 +6,7 @@ extern "C" { void set_and_print_env(const char *name, char *value); -void pre_bootstrap(); +void pre_bootstrap(int argc, char *argv[]); void bootstrap(int argc, char *argv[]); #ifdef __cplusplus diff --git a/launcher/src/client/launcher.cpp b/launcher/src/client/launcher.cpp index 7c545501..b24649cd 100644 --- a/launcher/src/client/launcher.cpp +++ b/launcher/src/client/launcher.cpp @@ -127,7 +127,7 @@ static void run_zenity_and_set_env(const char *env_name, std::vector #include #include +#include #include @@ -41,14 +42,16 @@ static void exit_handler(__attribute__((unused)) int signal) { } // Setup +#define PIPE_READ 0 +#define PIPE_WRITE 1 void setup_crash_report() { // Store Output -#ifndef MCPI_HEADLESS_MODE int output_pipe[2]; safe_pipe2(output_pipe, 0); int error_pipe[2]; safe_pipe2(error_pipe, 0); -#endif + int input_pipe[2]; + safe_pipe2(input_pipe, 0); // Fork pid_t ret = fork(); @@ -58,14 +61,15 @@ void setup_crash_report() { // Child Process // Pipe stdio -#ifndef MCPI_HEADLESS_MODE - dup2(output_pipe[1], STDOUT_FILENO); - close(output_pipe[0]); - close(output_pipe[1]); - dup2(error_pipe[1], STDERR_FILENO); - close(error_pipe[0]); - close(error_pipe[1]); -#endif + dup2(output_pipe[PIPE_WRITE], STDOUT_FILENO); + close(output_pipe[PIPE_READ]); + close(output_pipe[PIPE_WRITE]); + dup2(error_pipe[PIPE_WRITE], STDERR_FILENO); + close(error_pipe[PIPE_READ]); + close(error_pipe[PIPE_WRITE]); + dup2(input_pipe[PIPE_READ], STDIN_FILENO); + close(input_pipe[PIPE_READ]); + close(input_pipe[PIPE_WRITE]); // Create New Process Group setpgid(0, 0); @@ -87,13 +91,12 @@ void setup_crash_report() { act_sigterm.sa_handler = &exit_handler; sigaction(SIGTERM, &act_sigterm, NULL); - // Capture stdio -#ifndef MCPI_HEADLESS_MODE // Close Unneeded File Descriptors - close(output_pipe[1]); - close(error_pipe[1]); + close(output_pipe[PIPE_WRITE]); + close(error_pipe[PIPE_WRITE]); + close(input_pipe[PIPE_READ]); - // Create A Buffer + // Setup Logging #define BUFFER_SIZE 1024 char buf[BUFFER_SIZE]; @@ -105,10 +108,11 @@ void setup_crash_report() { } // Setup Polling - int number_fds = 2; + int number_fds = 3; struct pollfd poll_fds[number_fds]; - poll_fds[0].fd = output_pipe[0]; - poll_fds[1].fd = error_pipe[0]; + poll_fds[0].fd = output_pipe[PIPE_READ]; + poll_fds[1].fd = error_pipe[PIPE_READ]; + poll_fds[2].fd = STDIN_FILENO; for (int i = 0; i < number_fds; i++) { poll_fds[i].events = POLLIN; } @@ -129,31 +133,51 @@ void setup_crash_report() { for (int i = 0; i < number_fds; i++) { if (poll_fds[i].revents != 0) { if (poll_fds[i].revents & POLLIN) { - // Data Available - ssize_t bytes_read = read(poll_fds[i].fd, (void *) buf, BUFFER_SIZE - 1 /* Account For NULL-Terminator */); - if (bytes_read == -1) { - ERR("Unable To Read Log Data: %s", strerror(errno)); - } + if (poll_fds[i].fd == STDIN_FILENO) { + // Data Available From stdin + int bytes_available; + if (ioctl(fileno(stdin), FIONREAD, &bytes_available) == -1) { + bytes_available = 0; + } + // Read + ssize_t bytes_read = read(poll_fds[i].fd, (void *) buf, BUFFER_SIZE); + if (bytes_read == -1) { + ERR("Unable To Read Log Data: %s", strerror(errno)); + } + // Write To Child + if (write(input_pipe[PIPE_WRITE], (void *) buf, bytes_read) == -1) { + ERR("Unable To Write Input To Child: %s", strerror(errno)); + } + } else { + // Data Available From Child's stdout/stderr + ssize_t bytes_read = read(poll_fds[i].fd, (void *) buf, BUFFER_SIZE - 1 /* Account For NULL-Terminator */); + if (bytes_read == -1) { + ERR("Unable To Read Log Data: %s", strerror(errno)); + } - // Print To Terminal - buf[bytes_read] = '\0'; - fprintf(i == 0 ? stdout : stderr, "%s", buf); + // Print To Terminal + buf[bytes_read] = '\0'; + fprintf(i == 0 ? stdout : stderr, "%s", buf); - // Write To log - if (write(log_file_fd, (void *) buf, bytes_read) == -1) { - ERR("Unable To Write Log Data: %s", strerror(errno)); + // Write To log + if (write(log_file_fd, (void *) buf, bytes_read) == -1) { + ERR("Unable To Write Log Data: %s", strerror(errno)); + } } } else { // File Descriptor No Longer Accessible - if (close(poll_fds[i].fd) == -1) { + if (poll_fds[i].events != 0 && close(poll_fds[i].fd) == -1) { ERR("Unable To Close File Descriptor: %s", strerror(errno)); } + poll_fds[i].events = 0; number_open_fds--; } } } } -#endif + + // Close Input Pipe + close(input_pipe[PIPE_WRITE]); // Get Return Code int status; @@ -176,33 +200,30 @@ void setup_crash_report() { fprintf(stderr, "%s", exit_code_line); // Write Exit Code Log Line -#ifndef MCPI_HEADLESS_MODE if (write(log_file_fd, (void *) exit_code_line, strlen(exit_code_line)) == -1) { ERR("Unable To Write Exit Code To Log: %s", strerror(errno)); } -#endif // Free Exit Code Log Line free(exit_code_line); } - // Show Crash Log -#ifndef MCPI_HEADLESS_MODE // Close Log File FD if (close(log_file_fd) == -1) { ERR("Unable To Close Log File Descriptor: %s", strerror(errno)); } - // Show Report + // Show Crash Log +#ifndef MCPI_HEADLESS_MODE if (is_crash) { show_report(log_filename); } +#endif // Delete Log File if (unlink(log_filename) == -1) { ERR("Unable To Delete Log File: %s", strerror(errno)); } -#endif // Exit exit(WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE); diff --git a/launcher/src/server/launcher.c b/launcher/src/server/launcher.c index c739dbc2..2b0579e6 100644 --- a/launcher/src/server/launcher.c +++ b/launcher/src/server/launcher.c @@ -5,7 +5,7 @@ int main(int argc, char *argv[]) { // Pre-Bootstrap - pre_bootstrap(); + pre_bootstrap(argc, argv); // Set Home To Current Directory, So World Data Is Stored There char *launch_directory = getcwd(NULL, 0); diff --git a/libreborn/include/libreborn/util.h b/libreborn/include/libreborn/util.h index df091106..bf7dd472 100644 --- a/libreborn/include/libreborn/util.h +++ b/libreborn/include/libreborn/util.h @@ -25,7 +25,7 @@ dlerror(); \ real_##name = (name##_t) dlsym(RTLD_NEXT, #name); \ if (!real_##name) { \ - ERR("Error Resolving Symbol: "#name": %s", dlerror()); \ + ERR("Error Resolving Symbol: " #name ": %s", dlerror()); \ } \ } \ } \ @@ -49,6 +49,8 @@ extern "C" { // Safe Version Of pipe() void safe_pipe2(int pipefd[2], int flags); +// Check If Two Percentages Are Different Enough To Be Logged +int is_progress_difference_significant(int32_t new_val, int32_t old_val); #ifdef __cplusplus } diff --git a/libreborn/src/util/util.c b/libreborn/src/util/util.c index 38a9561d..98d9dcfb 100644 --- a/libreborn/src/util/util.c +++ b/libreborn/src/util/util.c @@ -6,3 +6,19 @@ void safe_pipe2(int pipefd[2], int flags) { ERR("Unable To Create Pipe: %s", strerror(errno)); } } + +// Check If Two Percentages Are Different Enough To Be Logged +#define SIGNIFICANT_PROGRESS 5 +int is_progress_difference_significant(int32_t new_val, int32_t old_val) { + if (new_val != old_val) { + if (new_val == -1 || old_val == -1) { + return 1; + } else if (new_val == 0 || new_val == 100) { + return 1; + } else { + return new_val - old_val >= SIGNIFICANT_PROGRESS; + } + } else { + return 0; + } +} diff --git a/media-layer/CMakeLists.txt b/media-layer/CMakeLists.txt index 9cb30ce5..26033151 100644 --- a/media-layer/CMakeLists.txt +++ b/media-layer/CMakeLists.txt @@ -1,14 +1,16 @@ project(media-layer) -# Check Options -if(MCPI_USE_MEDIA_LAYER_PROXY AND MCPI_BUILD_MODE STREQUAL "both") - message(FATAL_ERROR "Media Layer Proxy Is Redundant When Building ARM And Native Components In The Same Build") -endif() +# Target Notes: +# media-layer-core-real: Fully Built Media Layer Core +# media-layer-core: Alias Target That Points To The Library MCPI Should Link To # Add Headers add_library(media-layer-headers INTERFACE) target_include_directories(media-layer-headers INTERFACE include) +# Add GLESv1_CM Stubs Or Compatibility Layer +add_subdirectory(gles) + # Add Core add_subdirectory(core) @@ -17,8 +19,5 @@ if(MCPI_USE_MEDIA_LAYER_PROXY) add_subdirectory(proxy) endif() -# Add Stubs -add_subdirectory(stubs) - # Add Extras add_subdirectory(extras) diff --git a/media-layer/core/CMakeLists.txt b/media-layer/core/CMakeLists.txt index 41759bf7..5aa8c09b 100644 --- a/media-layer/core/CMakeLists.txt +++ b/media-layer/core/CMakeLists.txt @@ -8,26 +8,30 @@ endif() # Build if(MCPI_USE_MEDIA_LAYER_PROXY AND BUILD_NATIVE_COMPONENTS) - # Building Native Components - add_library(media-layer-core OBJECT ${CORE_SRC}) # Dependencies Are Setup Later -elseif(NOT MCPI_USE_MEDIA_LAYER_PROXY AND BUILD_ARM_COMPONENTS) - # Building ARM Components - add_library(media-layer-core SHARED ${CORE_SRC}) # Dependencies Are Setup Later + # Build Media Layer Core Natively And Use Proxy + add_library(media-layer-core-real OBJECT ${CORE_SRC}) # Dependencies Are Setup Later +endif() +if(NOT MCPI_USE_MEDIA_LAYER_PROXY AND BUILD_ARM_COMPONENTS) + # Directly Link Media Layer Core To MCPI + add_library(media-layer-core-real SHARED ${CORE_SRC}) # Dependencies Are Setup Later + set_target_properties(media-layer-core-real PROPERTIES OUTPUT_NAME "media-layer-core") # Install - install(TARGETS media-layer-core DESTINATION "${MCPI_LIB_DIR}") + install(TARGETS media-layer-core-real DESTINATION "${MCPI_LIB_DIR}") + # Create Alias Target For Linking + add_library(media-layer-core ALIAS media-layer-core-real) endif() # Configure Media Layer Core If Built -if(TARGET media-layer-core) +if(TARGET media-layer-core-real) # Link - target_link_libraries(media-layer-core media-layer-headers reborn-util pthread dl) + target_link_libraries(media-layer-core-real media-layer-headers reborn-util pthread dl) if(NOT MCPI_HEADLESS_MODE) # Find FreeImage find_library(FREEIMAGE_LIBRARY NAMES freeimage libfreeimage.so.3 REQUIRED) # OpenAL find_library(OPENAL_LIBRARY NAMES openal REQUIRED) # Link - target_link_libraries(media-layer-core "${FREEIMAGE_LIBRARY}" "${OPENAL_LIBRARY}" m GLESv1_CM glfw) + target_link_libraries(media-layer-core-real "${FREEIMAGE_LIBRARY}" "${OPENAL_LIBRARY}" m GLESv1_CM glfw) endif() endif() diff --git a/media-layer/core/src/audio/api.cpp b/media-layer/core/src/audio/api.cpp index 8460eebf..c8dc2d1b 100644 --- a/media-layer/core/src/audio/api.cpp +++ b/media-layer/core/src/audio/api.cpp @@ -15,6 +15,7 @@ static std::vector &get_sources() { return sources; } +// Error Checking #define AL_ERROR_CHECK() AL_ERROR_CHECK_MANUAL(alGetError()) #define AL_ERROR_CHECK_MANUAL(val) \ { \ @@ -73,6 +74,7 @@ void media_audio_update(float volume, float x, float y, float z, float yaw) { } } +// Play void media_audio_play(const char *source, const char *name, float x, float y, float z, float pitch, float volume, int is_ui) { // Check if (_media_audio_is_loaded()) { diff --git a/media-layer/core/src/audio/file.cpp b/media-layer/core/src/audio/file.cpp index 35a661cf..d91ac163 100644 --- a/media-layer/core/src/audio/file.cpp +++ b/media-layer/core/src/audio/file.cpp @@ -207,21 +207,18 @@ static ALuint load_sound(const char *source, const char *name) { } // Store Buffers -static std::unordered_map &get_buffers() { - static std::unordered_map buffers; - return buffers; -} +static std::unordered_map buffers; // Get Buffer For Sound ALuint _media_audio_get_buffer(const char *source, const char *name) { // Check if (_media_audio_is_loaded()) { - if (get_buffers().count(name) > 0) { + if (buffers.count(name) > 0) { // Return - return get_buffers()[name]; + return buffers[name]; } else { // Load And Return - get_buffers()[name] = load_sound(source, name); + buffers[name] = load_sound(source, name); return _media_audio_get_buffer(source, name); } } else { @@ -232,11 +229,11 @@ ALuint _media_audio_get_buffer(const char *source, const char *name) { // Delete Buffers void _media_audio_delete_buffers() { if (_media_audio_is_loaded()) { - for (auto it : get_buffers()) { + for (auto it : buffers) { if (it.second && alIsBuffer(it.second)) { alDeleteBuffers(1, &it.second); } } } - get_buffers().clear(); + buffers.clear(); } diff --git a/media-layer/core/src/media.c b/media-layer/core/src/media.c index 2d367249..15b1907b 100644 --- a/media-layer/core/src/media.c +++ b/media-layer/core/src/media.c @@ -1,7 +1,6 @@ #include #include -#include #ifndef MCPI_HEADLESS_MODE #define GLFW_INCLUDE_NONE @@ -225,7 +224,7 @@ static void glfw_scroll(__attribute__((unused)) GLFWwindow *window, __attribute_ #endif // Track Media Layer State -static int is_running = 0; +static volatile int is_running = 0; // Track If Raw Mouse Motion Is Enabled static int raw_mouse_motion_enabled = 1; @@ -253,6 +252,8 @@ void media_disable_vsync() { } // Init Media Layer +#define GL_VERSION 0x1f02 +typedef const unsigned char *(*glGetString_t)(unsigned int name); void SDL_WM_SetCaption(const char *title, __attribute__((unused)) const char *icon) { // Don't Enable GLFW In Headless Mode #ifndef MCPI_HEADLESS_MODE @@ -263,10 +264,15 @@ void SDL_WM_SetCaption(const char *title, __attribute__((unused)) const char *ic ERR("Unable To Initialize GLFW"); } - // Create OpenGL ES 1.1 Context + // Create OpenGL ES Context glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); +#ifdef MCPI_USE_GLES1_COMPATIBILITY_LAYER + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); +#else glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 1); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); +#endif // Use EGL glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); // Extra Settings @@ -289,6 +295,10 @@ void SDL_WM_SetCaption(const char *title, __attribute__((unused)) const char *ic // Make Window Context Current glfwMakeContextCurrent(glfw_window); + // Debug + glGetString_t glGetString = (glGetString_t) glfwGetProcAddress("glGetString"); + DEBUG("Using %s", (*glGetString)(GL_VERSION)); + // Init OpenAL _media_audio_init(); #else @@ -303,6 +313,9 @@ void SDL_WM_SetCaption(const char *title, __attribute__((unused)) const char *ic if (disable_vsync) { media_disable_vsync(); } + + // Always Cleanup Media Layer + atexit(media_cleanup); } void media_swap_buffers() { @@ -384,10 +397,6 @@ void media_cleanup() { is_running = 0; } } -// Always Cleanup Media Layer -__attribute__((destructor)) static void always_cleanup() { - media_cleanup(); -} // Store Cursor State static int cursor_grabbed = 0; diff --git a/media-layer/core/src/screenshot.c b/media-layer/core/src/screenshot.c index f883beee..32921429 100644 --- a/media-layer/core/src/screenshot.c +++ b/media-layer/core/src/screenshot.c @@ -70,7 +70,7 @@ void media_take_screenshot(char *home) { int height = viewport[3]; // Get Line Size - int line_size = width * 3; + int line_size = width * 4; { // Handle Alignment int alignment; @@ -85,14 +85,14 @@ void media_take_screenshot(char *home) { // Read Pixels unsigned char pixels[size]; - glReadPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels); + glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); // Handle Little Endian Systems #if __BYTE_ORDER == __LITTLE_ENDIAN // Swap Red And Blue for (int j = 0; j < width; j++) { for (int k = 0; k < height; k++) { - int pixel = (k * line_size) + (j * 3); + int pixel = (k * line_size) + (j * 4); // Swap int red = pixels[pixel]; int blue = pixels[pixel + 2]; @@ -103,7 +103,7 @@ void media_take_screenshot(char *home) { #endif // Save Image - FIBITMAP *image = FreeImage_ConvertFromRawBits(pixels, width, height, line_size, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK, 0); + FIBITMAP *image = FreeImage_ConvertFromRawBits(pixels, width, height, line_size, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK, 0); if (!FreeImage_Save(FIF_PNG, image, file, 0)) { INFO("Screenshot Failed: %s", file); } else { diff --git a/media-layer/extras/CMakeLists.txt b/media-layer/extras/CMakeLists.txt index 39681524..f5566d8e 100644 --- a/media-layer/extras/CMakeLists.txt +++ b/media-layer/extras/CMakeLists.txt @@ -2,5 +2,10 @@ project(media-layer-extras) if(BUILD_ARM_COMPONENTS) # Add Source To Media Core - target_sources(media-layer-core PRIVATE src/SDL.c) + if(TARGET media-layer-core-real) + set(TARGET media-layer-core-real) + elseif(TARGET media-layer-proxy-server) + set(TARGET media-layer-proxy-server) + endif() + target_sources("${TARGET}" PRIVATE src/SDL.c) endif() diff --git a/media-layer/gles/CMakeLists.txt b/media-layer/gles/CMakeLists.txt new file mode 100644 index 00000000..1426959b --- /dev/null +++ b/media-layer/gles/CMakeLists.txt @@ -0,0 +1,29 @@ +project(media-layer-stubs) + +# Stubs Only Needed For ARM +if(MCPI_USE_GLES1_COMPATIBILITY_LAYER AND BUILD_NATIVE_COMPONENTS AND NOT MCPI_HEADLESS_MODE) + # GLESv1_CM Compatibility Layer + set(GLES1_LINK_MODE "SHARED") + if(MCPI_USE_MEDIA_LAYER_PROXY) + # Link To Media Layer Proxy Client Statically + # (This is so it doesn't interfere with the Media Layer Proxy Server's libGLESv1_CM.so.1 symlink.) + set(GLES1_LINK_MODE "OBJECT") + endif() + add_library(GLESv1_CM "${GLES1_LINK_MODE}" src/compatibility-layer/state.c src/compatibility-layer/passthrough.c src/compatibility-layer/matrix.c src/compatibility-layer/draw.c) + target_link_libraries(GLESv1_CM glfw reborn-util dl m) + # Install + if(NOT MCPI_USE_MEDIA_LAYER_PROXY) + install(TARGETS GLESv1_CM DESTINATION "${MCPI_LIB_DIR}") + endif() +else() + # Add GLESv1_CM Stubs For Linking + add_library(GLESv1_CM SHARED src/stubs.c) + # Install Fake GLESv1_CM Stubs In Server Mode + if(MCPI_HEADLESS_MODE AND BUILD_ARM_COMPONENTS) + install(TARGETS GLESv1_CM DESTINATION "${MCPI_LIB_DIR}") + endif() +endif() + +# Common +target_link_libraries(GLESv1_CM media-layer-headers) +set_target_properties(GLESv1_CM PROPERTIES SOVERSION "1") diff --git a/media-layer/gles/src/compatibility-layer/draw.c b/media-layer/gles/src/compatibility-layer/draw.c new file mode 100644 index 00000000..b7c4253a --- /dev/null +++ b/media-layer/gles/src/compatibility-layer/draw.c @@ -0,0 +1,255 @@ +#include "state.h" +#include "passthrough.h" + +#include + +#include + +// Shaders +#define REAL_GL_FRAGMENT_SHADER 0x8b30 +#define REAL_GL_VERTEX_SHADER 0x8b31 +#define REAL_GL_INFO_LOG_LENGTH 0x8b84 +#define REAL_GL_COMPILE_STATUS 0x8b81 +GL_FUNC(glUseProgram, void, (GLuint program)); +GL_FUNC(glGetUniformLocation, GLint, (GLuint program, const GLchar *name)); +GL_FUNC(glUniformMatrix4fv, void, (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)); +GL_FUNC(glUniform1i, void, (GLint location, GLint v0)); +GL_FUNC(glUniform1f, void, (GLint location, GLfloat v0)); +GL_FUNC(glUniform4f, void, (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3)); +GL_FUNC(glGetAttribLocation, GLint, (GLuint program, const GLchar *name)); +GL_FUNC(glEnableVertexAttribArray, void, (GLuint index)); +GL_FUNC(glDisableVertexAttribArray, void, (GLuint index)); +GL_FUNC(glVertexAttribPointer, void, (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer)); +GL_FUNC(glVertexAttrib3f, void, (GLuint index, GLfloat v0, GLfloat v1, GLfloat v2)); +GL_FUNC(glVertexAttrib4f, void, (GLuint index, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3)); +GL_FUNC(glCreateShader, GLuint, (GLenum type)); +GL_FUNC(glShaderSource, void, (GLuint shader, GLsizei count, const GLchar *const *string, const GLint *length)); +GL_FUNC(glCompileShader, void, (GLuint shader)); +GL_FUNC(glCreateProgram, GLuint, ()); +GL_FUNC(glAttachShader, void, (GLuint program, GLuint shader)); +GL_FUNC(glLinkProgram, void, (GLuint program)); +GL_FUNC(glGetShaderiv, void, (GLuint shader, GLenum pname, GLint *params)); +GL_FUNC(glGetShaderInfoLog, void, (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog)); + +// Compile Shader +static void log_shader(GLuint shader, const char *name) { + // Log + GLint log_length = 0; + real_glGetShaderiv()(shader, REAL_GL_INFO_LOG_LENGTH, &log_length); + GLchar *log = malloc(log_length * sizeof (GLchar)); + ALLOC_CHECK(log); + real_glGetShaderInfoLog()(shader, log_length, &log_length, log); + if (log_length > 0) { + if (log_length > 1 && log[log_length - 1] == '\n') { + log[log_length - 1] = '\0'; + } + DEBUG("%s Shader Compile Log: %s", name, log); + } + free(log); + + // Check Status + GLint is_compiled = 0; + real_glGetShaderiv()(shader, REAL_GL_COMPILE_STATUS, &is_compiled); + if (!is_compiled) { + ERR("Failed To Compile %s Shader", name); + } +} +static GLuint compile_shader(const char *vertex_shader_text, const char *fragment_shader_text) { + // Vertex Shader + const GLuint vertex_shader = real_glCreateShader()(REAL_GL_VERTEX_SHADER); + real_glShaderSource()(vertex_shader, 1, &vertex_shader_text, NULL); + real_glCompileShader()(vertex_shader); + log_shader(vertex_shader, "Vertex"); + + // Fragment Shader + const GLuint fragment_shader = real_glCreateShader()(REAL_GL_FRAGMENT_SHADER); + real_glShaderSource()(fragment_shader, 1, &fragment_shader_text, NULL); + real_glCompileShader()(fragment_shader); + log_shader(fragment_shader, "Fragment"); + + // Link + GLuint program = real_glCreateProgram()(); + real_glAttachShader()(program, vertex_shader); + real_glAttachShader()(program, fragment_shader); + real_glLinkProgram()(program); + + // Return + return program; +} + +// Shader +static GLuint get_shader() { + static GLuint program = 0; + if (program == 0) { + static const char *vertex_shader_text = + "#version 100\n" + "precision mediump float;\n" + // Matrices + "uniform mat4 u_projection;\n" + "uniform mat4 u_model_view;\n" + "uniform mat4 u_texture;\n" + // Texture + "attribute vec3 a_vertex_coords;\n" + "attribute vec2 a_texture_coords;\n" + "varying vec4 v_texture_pos;\n" + // Color + "attribute vec4 a_color;\n" + "varying vec4 v_color;\n" + // Fog + "varying vec4 v_fog_eye_position;\n" + // Main + "void main() {\n" + " v_texture_pos = u_texture * vec4(a_texture_coords.xy, 0.0, 1.0);\n" + " gl_Position = u_projection * u_model_view * vec4(a_vertex_coords.xyz, 1.0);\n" + " v_color = a_color;\n" + " v_fog_eye_position = u_model_view * vec4(a_vertex_coords.xyz, 1.0);\n" + "}"; + static const char *fragment_shader_text = + "#version 100\n" + "precision mediump float;\n" + // Texture + "uniform bool u_has_texture;" + "uniform sampler2D u_texture_unit;\n" + // Color + "varying vec4 v_color;\n" + "varying vec4 v_texture_pos;\n" + // Alpha Test + "uniform bool u_alpha_test;\n" + // Fog + "uniform bool u_fog;\n" + "uniform vec4 u_fog_color;\n" + "uniform bool u_fog_is_linear;\n" + "uniform float u_fog_start;\n" + "uniform float u_fog_end;\n" + "varying vec4 v_fog_eye_position;\n" + // Main + "void main(void) {\n" + " gl_FragColor = v_color;\n" + " if (u_has_texture) {\n" + " gl_FragColor *= texture2D(u_texture_unit, v_texture_pos.xy);\n" + " }\n" + " if (u_alpha_test && gl_FragColor.a <= 0.1) {\n" + " discard;\n" + " }\n" + " if (u_fog) {\n" + " float fog_factor;\n" + " if (u_fog_is_linear) {\n" + " fog_factor = (u_fog_end - length(v_fog_eye_position)) / (u_fog_end - u_fog_start);\n" + " } else {\n" + " fog_factor = exp(-u_fog_start * length(v_fog_eye_position));\n" + " }\n" + " gl_FragColor = mix(gl_FragColor, u_fog_color, 1.0 - clamp(fog_factor, 0.0, 1.0));\n" + " }\n" + "}"; + program = compile_shader(vertex_shader_text, fragment_shader_text); + } + return program; +} + +// Shader Switching +static void use_shader(GLuint program) { + static GLuint current_program = 0; + if (current_program != program) { + real_glUseProgram()(program); + current_program = program; + } +} + +// Array Pointer Drawing +GL_FUNC(glDrawArrays, void, (GLenum mode, GLint first, GLsizei count)); +void glDrawArrays(GLenum mode, GLint first, GLsizei count) { + // Verify + if (gl_state.array_pointers.vertex.size != 3 || !gl_state.array_pointers.vertex.enabled || gl_state.array_pointers.vertex.type != GL_FLOAT) { + ERR("Unsupported Vertex Conifguration"); + } + + // Check Mode + int use_color_pointer = gl_state.array_pointers.color.enabled; + if (use_color_pointer && (gl_state.array_pointers.color.size != 4 || gl_state.array_pointers.color.type != GL_UNSIGNED_BYTE)) { + ERR("Unsupported Color Conifguration"); + } + int use_texture = gl_state.texture_2d && gl_state.array_pointers.tex_coord.enabled; + if (use_texture && (gl_state.array_pointers.tex_coord.size != 2 || gl_state.array_pointers.tex_coord.type != GL_FLOAT)) { + ERR("Unsupported Texture Conifguration"); + } + + // Load Shader + GLuint program = get_shader(); + use_shader(program); + + // Projection Matrix + GLint u_projection_handle = real_glGetUniformLocation()(program, "u_projection"); + matrix_t *p = &gl_state.matrix_stacks.projection.stack[gl_state.matrix_stacks.projection.i]; + real_glUniformMatrix4fv()(u_projection_handle, 1, 0, (GLfloat *) &p->data[0][0]); + + // Model View Matrix + GLint u_model_view_handle = real_glGetUniformLocation()(program, "u_model_view"); + p = &gl_state.matrix_stacks.model_view.stack[gl_state.matrix_stacks.model_view.i]; + real_glUniformMatrix4fv()(u_model_view_handle, 1, 0, (GLfloat *) &p->data[0][0]); + + // Has Texture + GLint u_has_texture_handle = real_glGetUniformLocation()(program, "u_has_texture"); \ + real_glUniform1i()(u_has_texture_handle, use_texture); \ + + // Texture Matrix + GLint u_texture_handle = real_glGetUniformLocation()(program, "u_texture"); + p = &gl_state.matrix_stacks.texture.stack[gl_state.matrix_stacks.texture.i]; + real_glUniformMatrix4fv()(u_texture_handle, 1, 0, (GLfloat *) &p->data[0][0]); + + // Texture Unit + GLint u_texture_unit_handle = real_glGetUniformLocation()(program, "u_texture_unit"); + real_glUniform1i()(u_texture_unit_handle, 0); + + // Alpha Test + GLint u_alpha_test_handle = real_glGetUniformLocation()(program, "u_alpha_test"); + real_glUniform1i()(u_alpha_test_handle, gl_state.alpha_test); + + // Color + GLint a_color_handle = real_glGetAttribLocation()(program, "a_color"); + if (use_color_pointer) { + real_glVertexAttribPointer()(a_color_handle, gl_state.array_pointers.color.size, gl_state.array_pointers.color.type, 1, gl_state.array_pointers.color.stride, gl_state.array_pointers.color.pointer); + real_glEnableVertexAttribArray()(a_color_handle); + } else { + real_glVertexAttrib4f()(a_color_handle, gl_state.color.red, gl_state.color.green, gl_state.color.blue, gl_state.color.alpha); + } + + // Fog + GLint u_fog_handle = real_glGetUniformLocation()(program, "u_fog"); + real_glUniform1i()(u_fog_handle, gl_state.fog.enabled); + if (gl_state.fog.enabled) { + GLint u_fog_color_handle = real_glGetUniformLocation()(program, "u_fog_color"); + real_glUniform4f()(u_fog_color_handle, gl_state.fog.color[0], gl_state.fog.color[1], gl_state.fog.color[2], gl_state.fog.color[3]); + GLint u_fog_is_linear_handle = real_glGetUniformLocation()(program, "u_fog_is_linear"); + real_glUniform1i()(u_fog_is_linear_handle, gl_state.fog.mode == GL_LINEAR); + GLint u_fog_start_handle = real_glGetUniformLocation()(program, "u_fog_start"); + real_glUniform1f()(u_fog_start_handle, gl_state.fog.start); + GLint u_fog_end_handle = real_glGetUniformLocation()(program, "u_fog_end"); + real_glUniform1f()(u_fog_end_handle, gl_state.fog.end); + } + + // Vertices + GLint a_vertex_coords_handle = real_glGetAttribLocation()(program, "a_vertex_coords"); + real_glVertexAttribPointer()(a_vertex_coords_handle, gl_state.array_pointers.vertex.size, gl_state.array_pointers.vertex.type, 0, gl_state.array_pointers.vertex.stride, gl_state.array_pointers.vertex.pointer); + real_glEnableVertexAttribArray()(a_vertex_coords_handle); + + // Texture Coordinates + GLint a_texture_coords_handle = real_glGetAttribLocation()(program, "a_texture_coords"); + if (use_texture) { + real_glVertexAttribPointer()(a_texture_coords_handle, gl_state.array_pointers.tex_coord.size, gl_state.array_pointers.tex_coord.type, 0, gl_state.array_pointers.tex_coord.stride, gl_state.array_pointers.tex_coord.pointer); + real_glEnableVertexAttribArray()(a_texture_coords_handle); + } else { + real_glVertexAttrib3f()(a_texture_coords_handle, 0, 0, 0); + } + + // Draw + real_glDrawArrays()(mode, first, count); + + // Cleanup + if (use_color_pointer) { + real_glDisableVertexAttribArray()(a_color_handle); + } + real_glDisableVertexAttribArray()(a_vertex_coords_handle); + if (use_texture) { + real_glDisableVertexAttribArray()(a_texture_coords_handle); + } +} diff --git a/media-layer/gles/src/compatibility-layer/matrix.c b/media-layer/gles/src/compatibility-layer/matrix.c new file mode 100644 index 00000000..22610d7c --- /dev/null +++ b/media-layer/gles/src/compatibility-layer/matrix.c @@ -0,0 +1,132 @@ +#include +#include + +#include + +#include "state.h" +#include "passthrough.h" + +// Matrix Common +static void matrix_copy(matrix_t *src, matrix_t *dst) { + memcpy((void *) dst->data, (void *) src->data, MATRIX_DATA_SIZE); +} + +// Identity Matrix +static matrix_t identity_matrix = { + .data = { + {1, 0, 0, 0}, + {0, 1, 0, 0}, + {0, 0, 1, 0}, + {0, 0, 0, 1} + } +}; +static void init_matrix_stack(matrix_stack_t *stack) { + matrix_copy(&identity_matrix, &stack->stack[0]); +} +__attribute__((constructor)) static void init_matrix_stacks() { + init_matrix_stack(&gl_state.matrix_stacks.model_view); + init_matrix_stack(&gl_state.matrix_stacks.projection); + init_matrix_stack(&gl_state.matrix_stacks.texture); +} + +// Matrix Mode +static matrix_stack_t *get_matrix_stack() { + switch (gl_state.matrix_stacks.mode) { + case GL_MODELVIEW: { + return &gl_state.matrix_stacks.model_view; + } + case GL_PROJECTION: { + return &gl_state.matrix_stacks.projection; + } + case GL_TEXTURE: { + return &gl_state.matrix_stacks.texture; + } + default: { + ERR("Unsupported Matrix Mode: %i", gl_state.matrix_stacks.mode); + } + } +} + +// Matrix Functions +void glMatrixMode(GLenum mode) { + gl_state.matrix_stacks.mode = mode; +} +void glPopMatrix() { + get_matrix_stack()->i--; +} +void glLoadIdentity() { + matrix_stack_t *stack = get_matrix_stack(); + matrix_copy(&identity_matrix, &stack->stack[stack->i]); +} +void glPushMatrix() { + matrix_stack_t *stack = get_matrix_stack(); + matrix_copy(&stack->stack[stack->i], &stack->stack[stack->i + 1]); + stack->i++; +} +void glMultMatrixf(const GLfloat *m) { + matrix_t new_matrix; + matrix_stack_t *stack = get_matrix_stack(); + matrix_t *current_matrix = &stack->stack[stack->i]; + for (int x = 0; x < MATRIX_SIZE; x++) { + for (int y = 0; y < MATRIX_SIZE; y++) { + GLfloat result = 0; + for (int i = 0; i < MATRIX_SIZE; i++) { + result += (current_matrix->data[i][y] * m[(x * MATRIX_SIZE) + i]); + } + new_matrix.data[x][y] = result; + } + } + matrix_copy(&new_matrix, current_matrix); +} +void glScalef(GLfloat x, GLfloat y, GLfloat z) { + GLfloat m[] = { + x, 0, 0, 0, + 0, y, 0, 0, + 0, 0, z, 0, + 0, 0, 0, 1 + }; + glMultMatrixf(m); +} +void glTranslatef(GLfloat x, GLfloat y, GLfloat z) { + GLfloat m[] = { + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + x, y, z, 1 + }; + glMultMatrixf(m); +} +void glOrthof(GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat near, GLfloat far) { + GLfloat m[] = { + (2.f / (right - left)), 0, 0, 0, + 0, (2.f / (top - bottom)), 0, 0, + 0, 0, (-2.f / (far - near)), 0, + -((right + left) / (right - left)), -((top + bottom) / (top - bottom)), -((far + near) / (far - near)), 1 + }; + glMultMatrixf(m); +} +#define DEG2RAD (M_PI / 180.f) +void glRotatef(GLfloat angle, GLfloat x, GLfloat y, GLfloat z) { + // Normalize + GLfloat length = sqrtf((x * x) + (y * y) + (z * z)); + x /= length; + y /= length; + z /= length; + + // Values + GLfloat angle_radians = angle * DEG2RAD; + GLfloat c = cosf(angle_radians); + GLfloat s = sinf(angle_radians); + GLfloat x2 = x * x; + GLfloat y2 = y * y; + GLfloat z2 = z * z; + + // Multiply + GLfloat m[] = { + x2 * (1.f - c) + c, (x * y) * (1.f - c) + (z * s), (x * z) * (1.f - c) - (y * s), 0, + (x * y) * (1.f - c) - (z * s), y2 * (1.f - c) + c, (y * z) * (1.f - c) + (x * s), 0, + (x * z) * (1.f - c) + (y * s), (y * z) * (1.f - c) - (x * s), z2 * (1.f - c) + c, 0, + 0, 0, 0, 1.f + }; + glMultMatrixf(m); +} diff --git a/media-layer/gles/src/compatibility-layer/matrix.h b/media-layer/gles/src/compatibility-layer/matrix.h new file mode 100644 index 00000000..796fb21d --- /dev/null +++ b/media-layer/gles/src/compatibility-layer/matrix.h @@ -0,0 +1,9 @@ +#include + +// Matrix Common +#define MATRIX_SIZE 4 +#define MATRIX_DATA_SIZE (sizeof (float) * MATRIX_SIZE * MATRIX_SIZE) +// OpenGL Matricies Are Column-Major +typedef struct { + GLfloat data[MATRIX_SIZE][MATRIX_SIZE]; +} matrix_t; diff --git a/media-layer/gles/src/compatibility-layer/passthrough.c b/media-layer/gles/src/compatibility-layer/passthrough.c new file mode 100644 index 00000000..31a9a340 --- /dev/null +++ b/media-layer/gles/src/compatibility-layer/passthrough.c @@ -0,0 +1,113 @@ +#include + +#include "passthrough.h" + +// Simple v1.1 -> v2.0 Passthrough Functions +GL_FUNC(glLineWidth, void, (GLfloat width)); +void glLineWidth(GLfloat width) { + real_glLineWidth()(width); +} +GL_FUNC(glBlendFunc, void, (GLenum sfactor, GLenum dfactor)); +void glBlendFunc(GLenum sfactor, GLenum dfactor) { + real_glBlendFunc()(sfactor, dfactor); +} +GL_FUNC(glClear, void, (GLbitfield mask)); +void glClear(GLbitfield mask) { + real_glClear()(mask); +} +GL_FUNC(glBufferData, void, (GLenum target, GLsizeiptr size, const void *data, GLenum usage)); +void glBufferData(GLenum target, GLsizeiptr size, const void *data, GLenum usage) { + real_glBufferData()(target, size, data, usage); +} +GL_FUNC(glScissor, void, (GLint x, GLint y, GLsizei width, GLsizei height)); +void glScissor(GLint x, GLint y, GLsizei width, GLsizei height) { + real_glScissor()(x, y, width, height); +} +GL_FUNC(glTexParameteri, void, (GLenum target, GLenum pname, GLint param)); +void glTexParameteri(GLenum target, GLenum pname, GLint param) { + real_glTexParameteri()(target, pname, param); +} +GL_FUNC(glTexImage2D, void, (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels)); +void glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels) { + real_glTexImage2D()(target, level, internalformat, width, height, border, format, type, pixels); +} +GL_FUNC(glPolygonOffset, void, (GLfloat factor, GLfloat units)); +void glPolygonOffset(GLfloat factor, GLfloat units) { + real_glPolygonOffset()(factor, units); +} +GL_FUNC(glDepthRangef, void, (GLclampf near, GLclampf far)); +void glDepthRangef(GLclampf near, GLclampf far) { + real_glDepthRangef()(near, far); +} +GL_FUNC(glDepthFunc, void, (GLenum func)); +void glDepthFunc(GLenum func) { + real_glDepthFunc()(func); +} +GL_FUNC(glBindBuffer, void, (GLenum target, GLuint buffer)); +void glBindBuffer(GLenum target, GLuint buffer) { + real_glBindBuffer()(target, buffer); +} +GL_FUNC(glClearColor, void, (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha)); +void glClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) { + real_glClearColor()(red, green, blue, alpha); +} +GL_FUNC(glDepthMask, void, (GLboolean flag)); +void glDepthMask(GLboolean flag) { + real_glDepthMask()(flag); +} +GL_FUNC(glHint, void, (GLenum target, GLenum mode)); +void glHint(GLenum target, GLenum mode) { + if (target != GL_PERSPECTIVE_CORRECTION_HINT) { + real_glHint()(target, mode); + } +} +GL_FUNC(glDeleteBuffers, void, (GLsizei n, const GLuint *buffers)); +void glDeleteBuffers(GLsizei n, const GLuint *buffers) { + real_glDeleteBuffers()(n, buffers); +} +GL_FUNC(glColorMask, void, (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha)); +void glColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) { + real_glColorMask()(red, green, blue, alpha); +} +GL_FUNC(glTexSubImage2D, void, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)); +void glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels) { + real_glTexSubImage2D()(target, level, xoffset, yoffset, width, height, format, type, pixels); +} +GL_FUNC(glGenTextures, void, (GLsizei n, GLuint *textures)); +void glGenTextures(GLsizei n, GLuint *textures) { + real_glGenTextures()(n, textures); +} +GL_FUNC(glDeleteTextures, void, (GLsizei n, const GLuint *textures)); +void glDeleteTextures(GLsizei n, const GLuint *textures) { + real_glDeleteTextures()(n, textures); +} +GL_FUNC(glBindTexture, void, (GLenum target, GLuint texture)); +void glBindTexture(GLenum target, GLuint texture) { + real_glBindTexture()(target, texture); +} +GL_FUNC(glCullFace, void, (GLenum mode)); +void glCullFace(GLenum mode) { + real_glCullFace()(mode); +} +GL_FUNC(glViewport, void, (GLint x, GLint y, GLsizei width, GLsizei height)); +void glViewport(GLint x, GLint y, GLsizei width, GLsizei height) { + real_glViewport()(x, y, width, height); +} +GL_FUNC(glIsEnabled, GLboolean, (GLenum cap)); +GLboolean glIsEnabled(GLenum cap) { + return real_glIsEnabled()(cap); +} +GL_FUNC(glGetIntegerv, void, (GLenum pname, GLint *data)); +void glGetIntegerv(GLenum pname, GLint *data) { + real_glGetIntegerv()(pname, data); +} +GL_FUNC(glReadPixels, void, (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *data)); +void glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *data) { + real_glReadPixels()(x, y, width, height, format, type, data); +} +void glShadeModel(__attribute__((unused)) GLenum mode) { + // Do Nothing +} +void glNormal3f(__attribute__((unused)) GLfloat nx, __attribute__((unused)) GLfloat ny, __attribute__((unused)) GLfloat nz) { + // Do Nothing +} diff --git a/media-layer/gles/src/compatibility-layer/passthrough.h b/media-layer/gles/src/compatibility-layer/passthrough.h new file mode 100644 index 00000000..78b732cb --- /dev/null +++ b/media-layer/gles/src/compatibility-layer/passthrough.h @@ -0,0 +1,19 @@ +#define GLFW_INCLUDE_NONE +#include + +#include + +// Load GL Function +#define GL_FUNC(name, return_type, args) \ + typedef return_type (*real_##name##_t)args; \ + \ + __attribute__((__unused__)) static real_##name##_t real_##name() { \ + static real_##name##_t func = NULL; \ + if (!func) { \ + func = (real_##name##_t) glfwGetProcAddress(#name); \ + if (!func) { \ + ERR("Error Resolving GL Symbol: " #name ": %s", dlerror()); \ + } \ + } \ + return func; \ + } diff --git a/media-layer/gles/src/compatibility-layer/state.c b/media-layer/gles/src/compatibility-layer/state.c new file mode 100644 index 00000000..26caad08 --- /dev/null +++ b/media-layer/gles/src/compatibility-layer/state.c @@ -0,0 +1,178 @@ +#include + +#include "state.h" +#include "passthrough.h" + +// GL State +gl_state_t gl_state = { + .color = { + .red = 1, + .green = 1, + .blue = 1, + .alpha = 1 + }, + .matrix_stacks = { + .mode = GL_MODELVIEW + }, + .alpha_test = 0, + .texture_2d = 0, + .fog = { + .enabled = 0, + .mode = GL_LINEAR, + .color = {0, 0, 0, 0}, + .start = 0, + .end = 1 + } +}; + +// Change Color +void glColor4f(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) { + gl_state.color.red = red; + gl_state.color.green = green; + gl_state.color.blue = blue; + gl_state.color.alpha = alpha; +} + +// Array Pointer Storage +#define ARRAY_POINTER_FUNC(func, name) \ + void func(GLint size, GLenum type, GLsizei stride, const void *pointer) { \ + gl_state.array_pointers.name.size = size; \ + gl_state.array_pointers.name.type = type; \ + gl_state.array_pointers.name.stride = stride; \ + gl_state.array_pointers.name.pointer = pointer; \ + } +ARRAY_POINTER_FUNC(glVertexPointer, vertex) +ARRAY_POINTER_FUNC(glColorPointer, color) +ARRAY_POINTER_FUNC(glTexCoordPointer, tex_coord) +static array_pointer_t *get_array_pointer(GLenum array) { + switch (array) { + case GL_VERTEX_ARRAY: { + return &gl_state.array_pointers.vertex; + } + case GL_COLOR_ARRAY: { + return &gl_state.array_pointers.color; + } + case GL_TEXTURE_COORD_ARRAY: { + return &gl_state.array_pointers.tex_coord; + } + default: { + ERR("Unsupported Array Pointer: %i", array); + } + } +} +void glEnableClientState(GLenum array) { + get_array_pointer(array)->enabled = 1; +} +void glDisableClientState(GLenum array) { + get_array_pointer(array)->enabled = 0; +} + +// Enable/Disable State +GL_FUNC(glEnable, void, (GLenum cap)); +void glEnable(GLenum cap) { + switch (cap) { + case GL_ALPHA_TEST: { + gl_state.alpha_test = 1; + break; + } + case GL_TEXTURE_2D: { + gl_state.texture_2d = 1; + break; + } + case GL_COLOR_MATERIAL: { + // Ignore + break; + } + case GL_FOG: { + gl_state.fog.enabled = 1; + break; + } + default: { + real_glEnable()(cap); + break; + } + } +} +GL_FUNC(glDisable, void, (GLenum cap)); +void glDisable(GLenum cap) { + switch (cap) { + case GL_ALPHA_TEST: { + gl_state.alpha_test = 0; + break; + } + case GL_TEXTURE_2D: { + gl_state.texture_2d = 0; + break; + } + case GL_COLOR_MATERIAL: { + // Ignore + break; + } + case GL_FOG: { + gl_state.fog.enabled = 0; + break; + } + default: { + real_glDisable()(cap); + break; + } + } +} +void glAlphaFunc(GLenum func, GLclampf ref) { + if (func != GL_GREATER && ref != 0.1f) { + ERR("Unsupported Alpha Function"); + } +} + +// Fog +#define UNSUPPORTED_FOG() ERR("Unsupported Fog Configuration") +void glFogfv(GLenum pname, const GLfloat *params) { + if (pname == GL_FOG_COLOR) { + memcpy((void *) gl_state.fog.color, params, sizeof (gl_state.fog.color)); + } else { + UNSUPPORTED_FOG(); + } +} +void glFogx(GLenum pname, GLfixed param) { + if (pname == GL_FOG_MODE && (param == GL_LINEAR || param == GL_EXP)) { + gl_state.fog.mode = param; + } else { + UNSUPPORTED_FOG(); + } +} +void glFogf(GLenum pname, GLfloat param) { + switch (pname) { + case GL_FOG_DENSITY: + case GL_FOG_START: { + gl_state.fog.start = param; + break; + } + case GL_FOG_END: { + gl_state.fog.end = param; + break; + } + default: { + UNSUPPORTED_FOG(); + break; + } + } +} + +// Get Matrix Data +GL_FUNC(glGetFloatv, void, (GLenum pname, GLfloat *params)); +void glGetFloatv(GLenum pname, GLfloat *params) { + switch (pname) { + case GL_MODELVIEW_MATRIX: { + memcpy((void *) params, gl_state.matrix_stacks.model_view.stack[gl_state.matrix_stacks.model_view.i].data, MATRIX_DATA_SIZE); + break; + } + case GL_PROJECTION_MATRIX: { + memcpy((void *) params, gl_state.matrix_stacks.projection.stack[gl_state.matrix_stacks.projection.i].data, MATRIX_DATA_SIZE); + break; + } + default: { + real_glGetFloatv()(pname, params); + break; + } + } +} diff --git a/media-layer/gles/src/compatibility-layer/state.h b/media-layer/gles/src/compatibility-layer/state.h new file mode 100644 index 00000000..7d364b2d --- /dev/null +++ b/media-layer/gles/src/compatibility-layer/state.h @@ -0,0 +1,50 @@ +#include + +#include "matrix.h" + +// Matrix Data +#define MATRIX_STACK_DEPTH 256 +typedef struct { + matrix_t stack[MATRIX_STACK_DEPTH]; + unsigned int i; +} matrix_stack_t; + +// Array Pointer Storage +typedef struct { + GLboolean enabled; + GLint size; + GLenum type; + GLsizei stride; + const void *pointer; +} array_pointer_t; + +// GL State +typedef struct { + struct { + GLfloat red; + GLfloat green; + GLfloat blue; + GLfloat alpha; + } color; + struct { + GLenum mode; + matrix_stack_t model_view; + matrix_stack_t projection; + matrix_stack_t texture; + } matrix_stacks; + struct { + array_pointer_t vertex; + array_pointer_t color; + array_pointer_t tex_coord; + } array_pointers; + GLboolean alpha_test; + GLboolean texture_2d; + struct { + GLboolean enabled; + GLfixed mode; + GLfloat color[4]; + GLfloat start; + GLfloat end; + } fog; +} gl_state_t; +extern gl_state_t gl_state; diff --git a/media-layer/stubs/src/GLESv1_CM.c b/media-layer/gles/src/stubs.c similarity index 73% rename from media-layer/stubs/src/GLESv1_CM.c rename to media-layer/gles/src/stubs.c index dcf08764..35bbead5 100644 --- a/media-layer/stubs/src/GLESv1_CM.c +++ b/media-layer/gles/src/stubs.c @@ -72,14 +72,48 @@ void glColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha void glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels) { } void glGenTextures(GLsizei n, GLuint *textures) { + static int i = 0; + for (int j = 0; j < n; j++) { + textures[j] = i++; + } } void glDeleteTextures(GLsizei n, const GLuint *textures) { } void glAlphaFunc(GLenum func, GLclampf ref) { } void glGetFloatv(GLenum pname, GLfloat *params) { + switch (pname) { + case GL_MODELVIEW_MATRIX: + case GL_PROJECTION_MATRIX: { + params[0] = 1; + params[1] = 0; + params[2] = 0; + params[3] = 0; + params[4] = 0; + params[5] = 1; + params[6] = 0; + params[7] = 0; + params[8] = 0; + params[9] = 0; + params[10] = 1; + params[11] = 0; + params[12] = 0; + params[13] = 0; + params[14] = 0; + params[15] = 1; + break; + } + default: { + params[0] = 0; + break; + } + } } +static GLuint current_texture = 0; void glBindTexture(GLenum target, GLuint texture) { + if (target == GL_TEXTURE_2D) { + current_texture = texture; + } } void glTranslatef(GLfloat x, GLfloat y, GLfloat z) { } @@ -101,6 +135,20 @@ GLboolean glIsEnabled(GLenum cap) { return GL_FALSE; } void glGetIntegerv(GLenum pname, GLint *data) { + switch (pname) { + case GL_TEXTURE_BINDING_2D: { + data[0] = current_texture; + break; + } + case GL_UNPACK_ALIGNMENT: { + data[0] = 1; + break; + } + default: { + data[0] = 0; + break; + } + } } void glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *data) { } diff --git a/media-layer/include/GLES/gl.h b/media-layer/include/GLES/gl.h index 77a0df14..10440043 100644 --- a/media-layer/include/GLES/gl.h +++ b/media-layer/include/GLES/gl.h @@ -5,10 +5,10 @@ extern "C" { #endif #define GL_FALSE 0 -#define GL_FOG_COLOR 0xb66 #define GL_ARRAY_BUFFER_BINDING 0x8894 #define GL_TEXTURE_BINDING_2D 0x8069 #define GL_UNSIGNED_BYTE 0x1401 +#define GL_FLOAT 0x1406 #define GL_RGB 0x1907 #define GL_RGBA 0x1908 #define GL_MODELVIEW_MATRIX 0xba6 @@ -19,6 +19,25 @@ extern "C" { #define GL_UNPACK_ALIGNMENT 0xcf5 #define GL_SRC_ALPHA 0x302 #define GL_ONE_MINUS_SRC_ALPHA 0x303 +#define GL_MODELVIEW 0x1700 +#define GL_PROJECTION 0x1701 +#define GL_TEXTURE 0x1702 +#define GL_VERTEX_ARRAY 0x8074 +#define GL_COLOR_ARRAY 0x8076 +#define GL_TEXTURE_COORD_ARRAY 0x8078 +#define GL_GREATER 0x204 +#define GL_ALPHA_TEST 0xbc0 +#define GL_TEXTURE_2D 0xde1 +#define GL_COLOR_MATERIAL 0xb57 +#define GL_PERSPECTIVE_CORRECTION_HINT 0xc50 +#define GL_FOG 0xb60 +#define GL_LINEAR 0x2601 +#define GL_EXP 0x800 +#define GL_FOG_DENSITY 0xb62 +#define GL_FOG_START 0xb63 +#define GL_FOG_END 0xb64 +#define GL_FOG_MODE 0xb65 +#define GL_FOG_COLOR 0xb66 #include #include @@ -34,6 +53,7 @@ typedef intptr_t GLintptr; typedef int32_t GLfixed; typedef unsigned int GLbitfield; typedef unsigned int GLenum; +typedef char GLchar; void glFogfv(GLenum pname, const GLfloat *params); void glVertexPointer(GLint size, GLenum type, GLsizei stride, const void *pointer); diff --git a/media-layer/include/media-layer/audio.h b/media-layer/include/media-layer/audio.h index 264437e9..7320196b 100644 --- a/media-layer/include/media-layer/audio.h +++ b/media-layer/include/media-layer/audio.h @@ -4,8 +4,15 @@ extern "C" { #endif +#ifndef MCPI_HEADLESS_MODE void media_audio_update(float volume, float x, float y, float z, float yaw); void media_audio_play(const char *source, const char *name, float x, float y, float z, float pitch, float volume, int is_ui); +#else +static inline void media_audio_update(__attribute__((unused)) float volume, __attribute__((unused)) float x, __attribute__((unused)) float y, __attribute__((unused)) float z, __attribute__((unused)) float yaw) { +} +static inline void media_audio_play(__attribute__((unused)) const char *source, __attribute__((unused)) const char *name, __attribute__((unused)) float x, __attribute__((unused)) float y, __attribute__((unused)) float z, __attribute__((unused)) float pitch, __attribute__((unused)) float volume, __attribute__((unused)) int is_ui) { +} +#endif #ifdef __cplusplus } diff --git a/media-layer/proxy/CMakeLists.txt b/media-layer/proxy/CMakeLists.txt index e421e91d..ecacb670 100644 --- a/media-layer/proxy/CMakeLists.txt +++ b/media-layer/proxy/CMakeLists.txt @@ -2,28 +2,26 @@ project(media-layer-proxy) # Configuration set(MEDIA_LAYER_PROXY_SRC src/common/common.c src/media-layer-core.c src/GLESv1_CM.c) # Media Layer Proxy Source -set(MEDIA_LAYER_PROXY_LIBS media-layer-core GLESv1_CM) # Media Layer Proxy Client Dependencies +# Build if(BUILD_NATIVE_COMPONENTS) - # Building Native Components - # Build Media Layer Proxy Client add_executable(media-layer-proxy-client src/client/client.cpp ${MEDIA_LAYER_PROXY_SRC}) - target_link_libraries(media-layer-proxy-client media-layer-headers reborn-util ${MEDIA_LAYER_PROXY_LIBS}) + target_link_libraries(media-layer-proxy-client media-layer-headers reborn-util media-layer-core-real GLESv1_CM) target_compile_definitions(media-layer-proxy-client PRIVATE -DMEDIA_LAYER_PROXY_CLIENT) # Install install(TARGETS media-layer-proxy-client DESTINATION "${MCPI_BIN_DIR}") endif() - if(BUILD_ARM_COMPONENTS) - # Building ARM Components - # Build Media Layer Proxy Server - add_library(media-layer-core SHARED src/server/server.cpp ${MEDIA_LAYER_PROXY_SRC}) - target_link_libraries(media-layer-core media-layer-headers reborn-util) - target_compile_definitions(media-layer-core PRIVATE -DMEDIA_LAYER_PROXY_SERVER) + add_library(media-layer-proxy-server SHARED src/server/server.cpp ${MEDIA_LAYER_PROXY_SRC}) + target_link_libraries(media-layer-proxy-server media-layer-headers reborn-util) + target_compile_definitions(media-layer-proxy-server PRIVATE -DMEDIA_LAYER_PROXY_SERVER) + set_target_properties(media-layer-proxy-server PROPERTIES OUTPUT_NAME "media-layer-core") # Symlink GLESv1_CM To Media Layer Proxy Server install_symlink("libmedia-layer-core.so" "${MCPI_LIB_DIR}/libGLESv1_CM.so.1") # Install - install(TARGETS media-layer-core DESTINATION "${MCPI_LIB_DIR}") + install(TARGETS media-layer-proxy-server DESTINATION "${MCPI_LIB_DIR}") + # Create Alias Target For Linking + add_library(media-layer-core ALIAS media-layer-proxy-server) endif() diff --git a/media-layer/proxy/src/GLESv1_CM.c b/media-layer/proxy/src/GLESv1_CM.c index c630b95e..d58f3f23 100644 --- a/media-layer/proxy/src/GLESv1_CM.c +++ b/media-layer/proxy/src/GLESv1_CM.c @@ -342,7 +342,7 @@ static int get_texture_size(GLsizei width, GLsizei height, GLenum format, GLenum break; } default: { - PROXY_ERR("Invalid Texture Format: %u", (unsigned int) format); + PROXY_ERR("Unsupported Texture Format: %u", (unsigned int) format); } } } else { @@ -858,7 +858,7 @@ static int get_glGetFloatv_params_size(GLenum pname) { return 16; } default: { - PROXY_ERR("Inavlid glGetFloatv Property: %u", pname); + PROXY_ERR("Unsupported glGetFloatv Property: %u", pname); } } } @@ -1104,7 +1104,7 @@ static int get_glGetIntegerv_params_size(GLenum pname) { return 1; } default: { - PROXY_ERR("Inavlid glGetIntegerv Property: %u", pname); + PROXY_ERR("Unsupported glGetIntegerv Property: %u", pname); } } } diff --git a/media-layer/stubs/CMakeLists.txt b/media-layer/stubs/CMakeLists.txt deleted file mode 100644 index 7f563f2e..00000000 --- a/media-layer/stubs/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -project(media-layer-stubs) - -# Add GLES1 Stubs For Linking -add_library(GLESv1_CM SHARED src/GLESv1_CM.c) -target_link_libraries(GLESv1_CM media-layer-headers) -set_target_properties(GLESv1_CM PROPERTIES SOVERSION "1") - -# Stubs Only Needed For ARM -if(BUILD_ARM_COMPONENTS) - # Install Fake GLESv1_CM Stubs In Server Mode - if(MCPI_HEADLESS_MODE) - install(TARGETS GLESv1_CM DESTINATION "${MCPI_LIB_DIR}") - endif() -endif() diff --git a/mods/CMakeLists.txt b/mods/CMakeLists.txt index 7bc0546b..6a3d7df7 100644 --- a/mods/CMakeLists.txt +++ b/mods/CMakeLists.txt @@ -54,15 +54,13 @@ else() target_link_libraries(override reborn-patch symbols dl home) add_library(textures SHARED src/textures/textures.cpp) - target_link_libraries(textures reborn-patch symbols media-layer-core feature) + target_link_libraries(textures reborn-patch symbols media-layer-core feature misc) add_library(atlas SHARED src/atlas/atlas.cpp) target_link_libraries(atlas reborn-patch symbols feature GLESv1_CM) - if(NOT MCPI_HEADLESS_MODE) - add_library(benchmark SHARED src/benchmark/benchmark.cpp) - target_link_libraries(benchmark reborn-patch symbols compat misc media-layer-core) - endif() + add_library(benchmark SHARED src/benchmark/benchmark.cpp) + target_link_libraries(benchmark reborn-patch symbols compat misc media-layer-core) endif() add_library(death SHARED src/death/death.cpp) @@ -85,10 +83,7 @@ target_link_libraries(init compat game-mode misc death options chat creative hom if(MCPI_SERVER_MODE) target_link_libraries(init server) else() - target_link_libraries(init multiplayer sound camera input sign touch textures atlas) - if(NOT MCPI_HEADLESS_MODE) - target_link_libraries(init benchmark) - endif() + target_link_libraries(init multiplayer sound camera input sign touch textures atlas benchmark) endif() ## Install Mods @@ -96,8 +91,5 @@ install(TARGETS init compat readdir feature game-mode misc death options chat cr if(MCPI_SERVER_MODE) install(TARGETS server DESTINATION "${MCPI_INSTALL_DIR}/mods") else() - install(TARGETS multiplayer sound override camera input sign touch textures atlas DESTINATION "${MCPI_INSTALL_DIR}/mods") - if(NOT MCPI_HEADLESS_MODE) - install(TARGETS benchmark DESTINATION "${MCPI_INSTALL_DIR}/mods") - endif() + install(TARGETS multiplayer sound override camera input sign touch textures atlas benchmark DESTINATION "${MCPI_INSTALL_DIR}/mods") endif() diff --git a/mods/src/benchmark/benchmark.cpp b/mods/src/benchmark/benchmark.cpp index 497dd944..62f75679 100644 --- a/mods/src/benchmark/benchmark.cpp +++ b/mods/src/benchmark/benchmark.cpp @@ -28,7 +28,7 @@ __attribute__((constructor)) static void _init_active(int argc, char *argv[]) { #define NANOSECONDS_IN_SECOND 1000000000ll // Config -#define BENCHMARK_GAME_MODE 1 // Creative Mode +#define BENCHMARK_GAME_MODE 0 // Survival Mode #define BENCHMARK_SEED 2048 // Random Number #define BENCHMARK_WORLD_NAME "_Benchmark" // Random Number #define BENCHMARK_LENGTH (180ll * NANOSECONDS_IN_SECOND) // 3 Minutes @@ -62,12 +62,20 @@ static void start_world(unsigned char *minecraft) { } // Track Frames +#ifndef MCPI_HEADLESS_MODE static unsigned long long int frames = 0; HOOK(media_swap_buffers, void, ()) { ensure_media_swap_buffers(); (*real_media_swap_buffers)(); frames++; } +#endif + +// Track Ticks +static unsigned long long int ticks = 0; +static void Minecraft_tick_injection(__attribute__((unused)) unsigned char *minecraft) { + ticks++; +} // Get Time static long long int get_time() { @@ -81,7 +89,13 @@ static long long int get_time() { // Store Time When World Loaded static int world_loaded = 0; static long long int world_loaded_time; +#ifndef MCPI_HEADLESS_MODE static unsigned long long int world_loaded_frames; +#endif +static unsigned long long int world_loaded_ticks; + +// Last Logged Status Update +static int32_t last_logged_status = -1; // Runs Every Tick static bool loaded = false; @@ -97,14 +111,30 @@ static void Minecraft_update_injection(unsigned char *minecraft) { if (!world_loaded && (*Minecraft_isLevelGenerated)(minecraft)) { world_loaded = 1; world_loaded_time = get_time(); +#ifndef MCPI_HEADLESS_MODE world_loaded_frames = frames; +#endif + world_loaded_ticks = ticks; } // Run Benchmark if (!exit_requested && world_loaded) { // Get Time long long int current_time = get_time() - world_loaded_time; +#ifndef MCPI_HEADLESS_MODE unsigned long long int current_frames = frames - world_loaded_frames; +#endif + unsigned long long int current_ticks = ticks - world_loaded_ticks; + + // Log + int32_t status = (((double) current_time) / ((double) BENCHMARK_LENGTH)) * 100; + if (status > 100) { + status = 100; + } + if (is_progress_difference_significant(status, last_logged_status)) { + INFO("Benchmark Status: %i%%", status); + last_logged_status = status; + } // Rotate Player static long long int rotate_point = BENCHMARK_ROTATION_INTERVAL; @@ -127,10 +157,17 @@ static void Minecraft_update_injection(unsigned char *minecraft) { // Disable Special Behavior After Requesting Exit exit_requested = true; - // Calculate FPS + // Calculate FPS & TPS + INFO("Benchmark Completed"); + INFO(" Total Time: %lld Nanoseconds", current_time); +#ifndef MCPI_HEADLESS_MODE static double frames_per_nanosecond = ((double) current_frames) / ((double) current_time); static double frames_per_second = frames_per_nanosecond * NANOSECONDS_IN_SECOND; - INFO("Benchmark Completed After %llu Frames In %lld Nanoseconds, Average FPS: %f", current_frames, current_time, frames_per_second); + INFO(" FPS: %f (%llu Total Frames)", frames_per_second, current_frames); +#endif + static double ticks_per_nanosecond = ((double) current_ticks) / ((double) current_time); + static double ticks_per_second = ticks_per_nanosecond * NANOSECONDS_IN_SECOND; + INFO(" TPS: %f (%llu Total Ticks)", ticks_per_second, current_ticks); } } } @@ -139,6 +176,8 @@ static void Minecraft_update_injection(unsigned char *minecraft) { void init_benchmark() { if (active) { misc_run_on_update(Minecraft_update_injection); + // Track Ticks + misc_run_on_tick(Minecraft_tick_injection); // Disable Interaction media_set_interactable(0); // Disable V-Sync diff --git a/mods/src/chat/chat.cpp b/mods/src/chat/chat.cpp index 5ade22e9..28b615ab 100644 --- a/mods/src/chat/chat.cpp +++ b/mods/src/chat/chat.cpp @@ -107,7 +107,7 @@ static void send_queued_messages(unsigned char *minecraft) { pthread_mutex_lock(&queue_mutex); // If Message Was Submitted, No Other Chat Windows Are Open, And The Game Is Not Paused, Then Re-Lock Cursor unsigned int new_chat_counter = chat_get_counter(); - if (old_chat_counter > new_chat_counter && new_chat_counter == 0 && (*(unsigned char **) (minecraft + Minecraft_screen_property_offset)) == NULL) { + if (old_chat_counter > new_chat_counter && new_chat_counter == 0) { // Unlock UI media_set_interactable(1); } diff --git a/mods/src/init/init.c b/mods/src/init/init.c index c1a0adbe..ccf156a8 100644 --- a/mods/src/init/init.c +++ b/mods/src/init/init.c @@ -26,7 +26,7 @@ __attribute__((constructor)) static void init() { init_options(); init_chat(); init_home(); -#if !defined(MCPI_SERVER_MODE) && !defined(MCPI_HEADLESS_MODE) +#ifndef MCPI_SERVER_MODE init_benchmark(); #endif } diff --git a/mods/src/init/init.h b/mods/src/init/init.h index db001036..0e3d3450 100644 --- a/mods/src/init/init.h +++ b/mods/src/init/init.h @@ -26,7 +26,7 @@ void init_death(); void init_options(); void init_chat(); void init_home(); -#if !defined(MCPI_SERVER_MODE) && !defined(MCPI_HEADLESS_MODE) +#ifndef MCPI_SERVER_MODE void init_benchmark(); #endif diff --git a/mods/src/misc/logging.cpp b/mods/src/misc/logging.cpp index 95a66f97..f930ba46 100644 --- a/mods/src/misc/logging.cpp +++ b/mods/src/misc/logging.cpp @@ -35,22 +35,6 @@ static void Gui_addMessage_injection(unsigned char *gui, std::string const& text free(new_message); } -// Check If Two Percentages Are Different Enough To Be Logged -#define SIGNIFICANT_PROGRESS 5 -static bool is_progress_difference_significant(int32_t new_val, int32_t old_val) { - if (new_val != old_val) { - if (new_val == -1 || old_val == -1) { - return true; - } else if (new_val == 0 || new_val == 100) { - return true; - } else { - return new_val - old_val >= SIGNIFICANT_PROGRESS; - } - } else { - return false; - } -} - // Print Progress Reports static int last_progress = -1; static const char *last_message = NULL; diff --git a/mods/src/misc/misc.cpp b/mods/src/misc/misc.cpp index 3bc10e47..a86367aa 100644 --- a/mods/src/misc/misc.cpp +++ b/mods/src/misc/misc.cpp @@ -25,7 +25,7 @@ static AppPlatform_readAssetFile_return_value AppPlatform_readAssetFile_injectio return ret; } -// Run Functions On Input Tick +// Run Functions On Update static std::vector &get_misc_update_functions() { static std::vector functions; return functions; @@ -33,18 +33,36 @@ static std::vector &get_misc_update_functions() { void misc_run_on_update(misc_update_function_t function) { get_misc_update_functions().push_back(function); } - // Handle Custom Update Behavior static void Minecraft_update_injection(unsigned char *minecraft) { // Call Original Method (*Minecraft_update)(minecraft); - // Run Input Tick Functions + // Run Functions for (misc_update_function_t function : get_misc_update_functions()) { (*function)(minecraft); } } +// Run Functions On Update +static std::vector &get_misc_tick_functions() { + static std::vector functions; + return functions; +} +void misc_run_on_tick(misc_update_function_t function) { + get_misc_tick_functions().push_back(function); +} +// Handle Custom Tick Behavior +static void Minecraft_tick_injection(unsigned char *minecraft, int32_t param_1, int32_t param_2) { + // Call Original Method + (*Minecraft_tick)(minecraft, param_1, param_2); + + // Run Functions + for (misc_update_function_t function : get_misc_tick_functions()) { + (*function)(minecraft); + } +} + // Add Missing Buttons To Pause Menu static void PauseScreen_init_injection(unsigned char *screen) { // Call Original Method @@ -88,6 +106,8 @@ void _init_misc_cpp() { // Handle Custom Update Behavior overwrite_calls((void *) Minecraft_update, (void *) Minecraft_update_injection); + // Handle Custom Tick Behavior + overwrite_calls((void *) Minecraft_tick, (void *) Minecraft_tick_injection); // Fix Pause Menu if (feature_has("Fix Pause Menu", server_disabled)) { diff --git a/mods/src/misc/misc.h b/mods/src/misc/misc.h index 4df9eb9d..66d1a44e 100644 --- a/mods/src/misc/misc.h +++ b/mods/src/misc/misc.h @@ -6,6 +6,7 @@ extern "C" { typedef void (*misc_update_function_t)(unsigned char *minecraft); void misc_run_on_update(misc_update_function_t function); +void misc_run_on_tick(misc_update_function_t function); void Level_saveLevelData_injection(unsigned char *level); diff --git a/mods/src/server/server.cpp b/mods/src/server/server.cpp index 01d475b9..f55487f6 100644 --- a/mods/src/server/server.cpp +++ b/mods/src/server/server.cpp @@ -224,6 +224,29 @@ static void handle_server_stop(unsigned char *minecraft) { } } +// Track TPS +#define NANOSECONDS_IN_SECOND 1000000000ll +static long long int get_time() { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC_RAW, &ts); + long long int a = (long long int) ts.tv_nsec; + long long int b = ((long long int) ts.tv_sec) * NANOSECONDS_IN_SECOND; + return a + b; +} +static bool is_last_tick_time_set = false; +static long long int last_tick_time; +static double tps = 0; +static void Minecraft_tick_injection(__attribute__((unused)) unsigned char *minecraft) { + long long int time = get_time(); + if (is_last_tick_time_set) { + long long int tick_time = time - last_tick_time; + tps = ((double) NANOSECONDS_IN_SECOND) / ((double) tick_time); + } else { + is_last_tick_time_set = true; + } + last_tick_time = time; +} + // Get ServerSideNetworkHandler From Minecraft static unsigned char *get_server_side_network_handler(unsigned char *minecraft) { return *(unsigned char **) (minecraft + Minecraft_network_handler_property_offset); @@ -233,28 +256,26 @@ static unsigned char *get_server_side_network_handler(unsigned char *minecraft) static volatile bool stdin_buffer_complete = false; static volatile char *stdin_buffer = NULL; static void *read_stdin_thread(__attribute__((unused)) void *data) { - // Check If STDIN Is A TTY - if (isatty(fileno(stdin))) { - // Loop - while (1) { - int bytes_available; - if (ioctl(fileno(stdin), FIONREAD, &bytes_available) == -1) { - bytes_available = 0; - } - for (int i = 0; i < bytes_available; i++) { - if (!stdin_buffer_complete) { - // Read Data - int x = fgetc(stdin); - if (x != EOF) { - if (x == '\n') { - if (stdin_buffer == NULL) { - stdin_buffer = strdup(""); - } - stdin_buffer_complete = true; - } else { - string_append((char **) &stdin_buffer, "%c", (char) x); - } + // Loop + while (1) { + int bytes_available; + if (ioctl(fileno(stdin), FIONREAD, &bytes_available) == -1) { + bytes_available = 0; + } + char buffer[bytes_available]; + bytes_available = read(fileno(stdin), (void *) buffer, bytes_available); + for (int i = 0; i < bytes_available; i++) { + if (!stdin_buffer_complete) { + // Read Data + char x = buffer[i]; + if (x == '\n') { + if (stdin_buffer == NULL) { + stdin_buffer = (volatile char *) malloc(1); + stdin_buffer[0] = '\0'; } + stdin_buffer_complete = true; + } else { + string_append((char **) &stdin_buffer, "%c", (char) x); } } } @@ -282,6 +303,7 @@ static void handle_commands(unsigned char *minecraft) { static std::string say_command("say "); static std::string kill_command("kill "); static std::string list_command("list"); + static std::string tps_command("tps"); static std::string stop_command("stop"); static std::string help_command("help"); if (!is_whitelist() && data.rfind(ban_command, 0) == 0) { @@ -301,6 +323,9 @@ static void handle_commands(unsigned char *minecraft) { // List Players INFO("All Players:"); find_players(minecraft, "", list_callback, true); + } else if (data == tps_command) { + // Print TPS + INFO("TPS: %f", tps); } else if (data == stop_command) { // Stop Server compat_request_exit(); @@ -312,6 +337,7 @@ static void handle_commands(unsigned char *minecraft) { INFO(" kill - Kill All Players With Specifed Username"); INFO(" say - Print Specified Message To Chat"); INFO(" list - List All Players"); + INFO(" tps - Print TPS"); INFO(" stop - Stop Server"); INFO(" help - Print This Message"); } else { @@ -523,6 +549,9 @@ static void server_init() { // Log IPs overwrite_call((void *) 0x75e54, (void *) ServerSideNetworkHandler_onReady_ClientGeneration_ServerSideNetworkHandler_popPendingPlayer_injection); + // Track TPS + misc_run_on_tick(Minecraft_tick_injection); + // Start Reading STDIN pthread_t read_stdin_thread_obj; pthread_create(&read_stdin_thread_obj, NULL, read_stdin_thread, NULL); diff --git a/mods/src/sign/sign.cpp b/mods/src/sign/sign.cpp index aa252352..dfb9fc19 100644 --- a/mods/src/sign/sign.cpp +++ b/mods/src/sign/sign.cpp @@ -42,13 +42,13 @@ static void TextEditScreen_updateEvents_injection(unsigned char *screen) { (*Screen_updateEvents)(screen); if (*(char *)(screen + 4) == '\0') { - uint32_t vtable = *((uint32_t *) screen); + unsigned char *vtable = *(unsigned char **) screen; for (char key : input) { if (key == BACKSPACE_KEY) { // Handle Backspace (*(Screen_keyPressed_t *) (vtable + Screen_keyPressed_vtable_offset))(screen, BACKSPACE_KEY); } else { - // Handle Nrmal Key + // Handle Normal Key (*(Screen_keyboardNewChar_t *) (vtable + Screen_keyboardNewChar_vtable_offset))(screen, key); } } diff --git a/mods/src/textures/textures.cpp b/mods/src/textures/textures.cpp index 0c379835..ee05d0db 100644 --- a/mods/src/textures/textures.cpp +++ b/mods/src/textures/textures.cpp @@ -7,14 +7,12 @@ #include #include +#include "../misc/misc.h" #include "../feature/feature.h" #include "../init/init.h" // Animated Water -static void Minecraft_tick_injection(unsigned char *minecraft, int32_t param_1, int32_t param_2) { - // Call Original Method - (*Minecraft_tick)(minecraft, param_1, param_2); - +static void Minecraft_tick_injection(unsigned char *minecraft) { // Tick Dynamic Textures unsigned char *textures = *(unsigned char **) (minecraft + Minecraft_textures_property_offset); if (textures != NULL) { @@ -77,7 +75,8 @@ static void get_texture_size(GLint id, GLsizei *width, GLsizei *height) { ++it; } // Not Found - ERR("Unable To Find Size Of Texture: %i", id); + *width = 0; + *height = 0; } // Scale Texture (Remember To Free) @@ -167,7 +166,7 @@ static void Textures_tick_glTexSubImage2D_injection(GLenum target, GLint level, void init_textures() { // Tick Dynamic Textures (Animated Water) if (feature_has("Animated Water", server_disabled)) { - overwrite_calls((void *) Minecraft_tick, (void *) Minecraft_tick_injection); + misc_run_on_tick(Minecraft_tick_injection); } // Scale Animated Textures diff --git a/scripts/tools/generate-appimage-builder-yaml.js b/scripts/tools/generate-appimage-builder-yaml.js index d74a150c..38a7b33a 100755 --- a/scripts/tools/generate-appimage-builder-yaml.js +++ b/scripts/tools/generate-appimage-builder-yaml.js @@ -108,18 +108,23 @@ const afterBundle = [ 'find ./AppDir/usr/bin -maxdepth 1 -name \'qemu-*\' -a ! -name \'qemu-arm\' -delete' ]; +// Environment +const env = { + APPDIR_MODULE_DIR: '/tmp/.minecraft-pi-patched' +}; +if (mode === 'client') { + // Make GTK Work (Zenity Uses GTK) + env.GTK_EXE_PREFIX = '${APPDIR}/usr'; + env.GTK_PATH = `\${APPDIR}/usr/lib/${triplet}/gtk-3.0`; + env.GTK_DATA_PREFIX = '${APPDIR}'; + env.GTK_THEME = 'Default'; + env.XDG_DATA_DIRS = '${APPDIR}/share:${APPDIR}/usr/share:/share:/usr/share'; + env.APPDIR_LIBRARY_PATH = `\${APPDIR}/usr/lib/${triplet}:\${APPDIR}/usr/${triplet}/lib:\${APPDIR}/lib/${triplet}:\${APPDIR}/usr/lib:\${APPDIR}/usr/lib/${triplet}/gdk-pixbuf-2.0/2.10.0/loaders`; +} + // Runtime const runtime = { - env: mode === 'client' ? { - // Make GTK Work (Zenity Uses GTK) - GTK_EXE_PREFIX: '${APPDIR}/usr', - GTK_PATH: `\${APPDIR}/usr/lib/${triplet}/gtk-3.0`, - GTK_DATA_PREFIX: '${APPDIR}', - GTK_THEME: 'Default', - XDG_DATA_DIRS: '${APPDIR}/share:${APPDIR}/usr/share:/share:/usr/share', - APPDIR_LIBRARY_PATH: `\${APPDIR}/usr/lib/${triplet}:\${APPDIR}/usr/${triplet}/lib:\${APPDIR}/lib/${triplet}:\${APPDIR}/usr/lib:\${APPDIR}/usr/lib/${triplet}/gdk-pixbuf-2.0/2.10.0/loaders`, - APPDIR_MODULE_DIR: '/tmp/.minecraft-pi-patched' - } : undefined, + env: env, preserve: arch !== 'armhf' ? [ // On non-ARM32 systems, an ARM32 linker is embedded, this // prevents AppImage-Builder from modifying ARM32 binaries