1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-10-05 19:31:04 +00:00

Make Luanti buildable for iOS (iPhoneSimulator) with ANGLE and basic CI.

This commit is contained in:
SFENCE 2024-11-18 13:21:02 +01:00
parent 5672b93007
commit 803870e0d2
28 changed files with 568 additions and 36 deletions

View file

@ -32,6 +32,7 @@ We aim to support these platforms:
* Windows via MinGW
* Linux (GL or GLES)
* macOS
* iOS (GLES)
* Android
This doesn't mean other platforms don't work or won't be supported, if you find something that doesn't work contributions are welcome.
@ -51,7 +52,7 @@ Driver (rows) vs Device (columns)
Notes:
* [1] `CIrrDeviceSDL`: supports Android, Linux, macOS, Windows
* [1] `CIrrDeviceSDL`: supports Android, Linux, macOS, iOS, Windows
* [2] `CIrrDeviceLinux`: supports Linux
* [3] `CIrrDeviceOSX`: supports macOS
* [4] `CIrrDeviceWin32`: supports Windows

View file

@ -22,7 +22,7 @@
#include <direct.h> // for _chdir
#include <io.h> // for _access
#include <tchar.h>
#elif (defined(_IRR_POSIX_API_) || defined(_IRR_OSX_PLATFORM_) || defined(_IRR_ANDROID_PLATFORM_))
#elif (defined(_IRR_POSIX_API_) || defined(_IRR_OSX_PLATFORM_) || defined(_IRR_IOS_PLATFORM_) || defined(_IRR_ANDROID_PLATFORM_) )
#include <cstdio>
#include <cstdlib>
#include <cstring>

View file

@ -1,4 +1,5 @@
// Copyright (C) 2002-2012 Nikolaus Gebhardt
// Copyright (C) 2025 Luanti contributors
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h
@ -29,6 +30,13 @@
#include "CSDLManager.h"
#ifdef _IRR_COMPILE_WITH_ANGLE_
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
#include <GLES3/gl3.h>
#endif
// Since SDL doesn't have mouse keys as keycodes we need to fall back to EKEY_CODE in some cases.
static inline bool is_fake_key(EKEY_CODE key) {
switch (key) {
@ -409,13 +417,32 @@ CIrrDeviceSDL::~CIrrDeviceSDL()
for (u32 i = 0; i < numJoysticks; ++i)
SDL_JoystickClose(Joysticks[i]);
#endif
#ifndef _IRR_COMPILE_WITH_ANGLE_
if (Window && Context) {
SDL_GL_MakeCurrent(Window, NULL);
SDL_GL_DeleteContext(Context);
}
#else
if (Display != EGL_NO_DISPLAY) {
eglMakeCurrent(Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
}
if (Surface != EGL_NO_SURFACE) {
eglDestroySurface(Display, Surface);
}
if (Context != EGL_NO_CONTEXT) {
eglDestroyContext(Display, Context);
}
if (Display != EGL_NO_DISPLAY) {
eglTerminate(Display);
}
if (View) {
SDL_Metal_DestroyView(View);
}
if (Window) {
SDL_DestroyWindow(Window);
}
#endif
if (--SDLDeviceInstances == 0) {
SDL_Quit();
@ -529,21 +556,9 @@ bool CIrrDeviceSDL::createWindow()
return false;
}
bool CIrrDeviceSDL::createWindowWithContext()
{
u32 SDL_Flags = 0;
SDL_Flags |= SDL_WINDOW_ALLOW_HIGHDPI;
SDL_Flags |= getFullscreenFlag(CreationParams.Fullscreen);
if (Resizable)
SDL_Flags |= SDL_WINDOW_RESIZABLE;
if (CreationParams.WindowMaximized)
SDL_Flags |= SDL_WINDOW_MAXIMIZED;
SDL_Flags |= SDL_WINDOW_OPENGL;
SDL_GL_ResetAttributes();
#ifdef _IRR_EMSCRIPTEN_PLATFORM_
bool CIrrDeviceSDL::createWindowWithContextEmscripten()
{
if (Width != 0 || Height != 0)
emscripten_set_canvas_size(Width, Height);
else {
@ -581,7 +596,11 @@ bool CIrrDeviceSDL::createWindowWithContext()
emscripten_set_mouseleave_callback("#canvas", (void *)this, false, MouseLeaveCallback);
return true;
#else // !_IRR_EMSCRIPTEN_PLATFORM_
}
#else // _IRR_EMSCRIPTEN_PLATFORM_
#ifndef _IRR_COMPILE_WITH_ANGLE_
bool CIrrDeviceSDL::createWindowWithContextSDL()
{
switch (CreationParams.DriverType) {
case video::EDT_OPENGL:
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
@ -629,7 +648,12 @@ bool CIrrDeviceSDL::createWindowWithContext()
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
}
Window = SDL_CreateWindow("", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, Width, Height, SDL_Flags);
if (!SDL_GL_LoadLibrary(NULL)) {
os::Printer::log("Could not load OpenGL ES library", SDL_GetError(), ELL_WARNING);
return false;
}
Window = SDL_CreateWindow("", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, Width, Height, SDL_WINDOW_OPENGL | SDL_WINDOW_ALLOW_HIGHDPI);
if (!Window) {
os::Printer::log("Could not create window", SDL_GetError(), ELL_WARNING);
return false;
@ -643,6 +667,121 @@ bool CIrrDeviceSDL::createWindowWithContext()
return false;
}
int major, minor;
SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &major);
SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &minor);
os::Printer::log("OpenGL ES version", std::to_string(major) + "." + std::to_string(minor), ELL_INFORMATION);
const char* error = SDL_GetError();
if (*error != '\0') {
os::Printer::log("SDL Error", error, ELL_WARNING);
SDL_ClearError();
}
}
#else // _IRR_COMPILE_WITH_ANGLE_
bool CIrrDeviceSDL::createWindowWithContextANGLE()
{
Window = SDL_CreateWindow("", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, Width, Height, SDL_WINDOW_METAL | SDL_WINDOW_ALLOW_HIGHDPI);
if (!Window) {
os::Printer::log("Could not create window", SDL_GetError(), ELL_WARNING);
return false;
}
auto metal_view = SDL_Metal_CreateView(Window);
auto metal_layer = SDL_Metal_GetLayer(metal_view);
EGLAttrib egl_display_attribs[] = {
EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE,
EGL_POWER_PREFERENCE_ANGLE, EGL_HIGH_POWER_ANGLE,
EGL_NONE
};
Display = eglGetPlatformDisplay(EGL_PLATFORM_ANGLE_ANGLE, (void*) EGL_DEFAULT_DISPLAY, egl_display_attribs);
if (Display == EGL_NO_DISPLAY)
{
os::Printer::log("Failed to get EGL display");
return false;
}
if (eglInitialize(Display, NULL, NULL) == false)
{
os::Printer::log("Failed to initialize EGL");
return false;
}
EGLint egl_config_attribs[] = {
EGL_RED_SIZE, (CreationParams.Bits == 16 ? 5 : 8),
EGL_GREEN_SIZE, (CreationParams.Bits == 16 ? 5 : 8),
EGL_BLUE_SIZE, (CreationParams.Bits == 16 ? 5 : 8),
EGL_ALPHA_SIZE, (CreationParams.WithAlphaChannel ? (CreationParams.Bits == 16 ? 1 : 8) : 0),
EGL_DEPTH_SIZE, CreationParams.ZBufferBits,
EGL_STENCIL_SIZE, CreationParams.Stencilbuffer ? 8 : 0,
EGL_SAMPLE_BUFFERS, CreationParams.AntiAlias > 1 ? 1 : 0,
EGL_SAMPLES, CreationParams.AntiAlias > 1 ? CreationParams.AntiAlias : 0,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,
EGL_NONE
};
EGLConfig config;
EGLint configs_count;
if (!eglChooseConfig(Display, egl_config_attribs, &config, 1, &configs_count))
{
os::Printer::log("Failed to choose EGL config");
return false;
}
EGLint egl_context_attribs[] = {
EGL_CONTEXT_CLIENT_VERSION, 3,
EGL_NONE
};
Context = eglCreateContext(Display, config, EGL_NO_CONTEXT, egl_context_attribs);
if (Context == EGL_NO_CONTEXT) {
os::Printer::log("Failed to create EGL context");
return false;
}
Surface = eglCreateWindowSurface(Display, config, metal_layer, NULL);
if (Surface == EGL_NO_SURFACE)
{
os::Printer::log("Failed to create EGL surface");
return false;
}
if (!eglMakeCurrent(Display, Surface, Surface, Context))
{
os::Printer::log("Failed to make EGL context current");
return false;
}
}
#endif // _IRR_COMPILE_WITH_ANGLE_
#endif // _IRR_EMSCRIPTEN_PLATFORM_
bool CIrrDeviceSDL::createWindowWithContext()
{
u32 SDL_Flags = 0;
SDL_Flags |= SDL_WINDOW_ALLOW_HIGHDPI;
SDL_Flags |= getFullscreenFlag(CreationParams.Fullscreen);
if (Resizable)
SDL_Flags |= SDL_WINDOW_RESIZABLE;
if (CreationParams.WindowMaximized)
SDL_Flags |= SDL_WINDOW_MAXIMIZED;
SDL_Flags |= SDL_WINDOW_OPENGL;
//SDL_Flags |= SDL_WINDOW_METAL;
SDL_GL_ResetAttributes();
#ifdef _IRR_EMSCRIPTEN_PLATFORM_
createWindowWithContextEmscripten();
#else // !_IRR_EMSCRIPTEN_PLATFORM_
#ifndef _IRR_COMPILE_WITH_ANGLE_
createWindowWithContextSDL();
#else
createWindowWithContextANGLE();
#endif
updateSizeAndScale();
if (ScaleX != 1.0f || ScaleY != 1.0f) {
// The given window size is in pixels, not in screen coordinates.
@ -1161,7 +1300,11 @@ float CIrrDeviceSDL::getDisplayDensity() const
void CIrrDeviceSDL::SwapWindow()
{
#ifndef _IRR_COMPILE_WITH_ANGLE_
SDL_GL_SwapWindow(Window);
#else
eglSwapBuffers(Display, Surface);
#endif
}
//! pause execution temporarily

View file

@ -23,6 +23,10 @@
#undef SDL_VIDEO_DRIVER_DIRECTFB
#include <SDL_syswm.h>
#ifdef _IRR_COMPILE_WITH_ANGLE_
#include <EGL/egl.h>
#endif
#include <memory>
#include <unordered_map>
@ -302,13 +306,30 @@ private:
void createDriver();
bool createWindow();
#ifdef _IRR_EMSCRIPTEN_PLATFORM_
bool createWindowWithContextEmscripten();
#else // _IRR_EMSCRIPTEN_PLATFORM_
#ifndef _IRR_COMPILE_WITH_ANGLE_
bool createWindowWithContextSDL();
#else // _IRR_COMPILE_WITH_ANGLE_
bool createWindowWithContextANGLE();
#endif // _IRR_COMPILE_WITH_ANGLE_
#endif // _IRR_EMSCRIPTEN_PLATFORM_
bool createWindowWithContext();
void createKeyMap();
void logAttributes();
SDL_GLContext Context;
SDL_Window *Window;
#ifndef _IRR_COMPILE_WITH_ANGLE_
SDL_GLContext Context;
#else
SDL_MetalView View;
EGLSurface Surface;
EGLContext Context;
EGLDisplay Display;
#endif
#if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_)
core::array<SDL_Joystick *> Joysticks;
#endif

View file

@ -1,6 +1,11 @@
set(DEFAULT_SDL2 ON)
if(APPLE AND CMAKE_SYSTEM_NAME STREQUAL "iOS")
set(DEFAULT_ANGLE ON)
endif()
option(USE_SDL2 "Use the SDL2 backend" ${DEFAULT_SDL2})
option(USE_ANGLE "Use the Google ANGLE EGL and OpenGL ES" ${DEFAULT_ANGLE})
option(USE_SDL2_STATIC "Link with SDL2 static libraries" FALSE)
@ -64,8 +69,17 @@ if(WIN32)
add_compile_definitions(_IRR_WINDOWS_ _IRR_WINDOWS_API_)
set(DEVICE "WINDOWS")
elseif(APPLE)
add_compile_definitions(_IRR_OSX_PLATFORM_)
set(DEVICE "OSX")
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
add_compile_definitions(-D_IRR_OSX_PLATFORM_)
set(DEVICE "OSX")
elseif(CMAKE_SYSTEM_NAME STREQUAL "iOS")
add_compile_definitions(-D_IRR_IOS_PLATFORM_)
if(NOT USE_SDL2)
message(FATAL_ERROR "The iOS build requires SDL2")
endif()
else()
message(FATAL_ERROR "Not supported Apple device.")
endif()
elseif(ANDROID)
add_compile_definitions(_IRR_ANDROID_PLATFORM_)
if(NOT USE_SDL2)
@ -100,6 +114,10 @@ endif()
add_compile_definitions("_IRR_COMPILE_WITH_${DEVICE}_DEVICE_")
if(USE_ANGLE)
add_definitions("-D_IRR_COMPILE_WITH_ANGLE_")
endif()
# X11
if(DEVICE STREQUAL "X11")
@ -125,7 +143,7 @@ endif()
# OpenGL
if(USE_SDL2)
if(NOT ANDROID)
if(NOT ANDROID AND NOT USE_ANGLE)
set(DEFAULT_OPENGL3 TRUE)
endif()
else()
@ -133,17 +151,17 @@ else()
endif()
option(ENABLE_OPENGL3 "Enable OpenGL 3+" ${DEFAULT_OPENGL3})
if(ANDROID OR EMSCRIPTEN)
if(ANDROID OR EMSCRIPTEN OR USE_ANGLE)
set(ENABLE_OPENGL FALSE)
else()
option(ENABLE_OPENGL "Enable OpenGL" TRUE)
endif()
if(APPLE)
if(APPLE AND NOT CMAKE_SYSTEM_NAME STREQUAL "iOS")
set(ENABLE_GLES2 FALSE)
set(ENABLE_WEBGL1 FALSE)
else()
if(ANDROID OR EMSCRIPTEN)
if(ANDROID OR EMSCRIPTEN OR USE_ANGLE)
set(DEFAULT_GLES2 TRUE)
endif()
if(EMSCRIPTEN)
@ -231,10 +249,10 @@ if(USE_SDL2)
if(NOT USE_SDL2_STATIC)
set(USE_SDL2_SHARED TRUE)
endif()
if(NOT ANDROID)
if(NOT ANDROID AND NOT CMAKE_SYSTEM_NAME STREQUAL "iOS")
find_package(SDL2 REQUIRED)
else()
# provided by AndroidLibs.cmake
# provided by AndroidLibs.cmake or from cmake call
endif()
message(STATUS "Found SDL2: ${SDL2_LIBRARIES}")
@ -280,7 +298,7 @@ endif()
if(ANDROID)
enable_language(C)
elseif(APPLE)
elseif(APPLE AND NOT CMAKE_SYSTEM_NAME STREQUAL "iOS")
find_library(COCOA_LIB Cocoa REQUIRED)
find_library(IOKIT_LIB IOKit REQUIRED)
@ -600,6 +618,26 @@ target_link_libraries(IrrlichtMt PRIVATE
"$<$<BOOL:${USE_X11}>:${X11_Xi_LIB}>"
)
if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
target_link_libraries(IrrlichtMt PRIVATE
"-framework UIKit"
"-framework Foundation"
"-framework CoreGraphics"
"-framework QuartzCore"
"-framework AudioToolbox"
"-framework AVFoundation"
"-framework CoreAudio"
"-framework CoreMotion"
"-framework IOSurface"
${OPENGLES2_LIBRARY}
${SDL2_LIBRARIES}
)
else()
target_link_libraries(IrrlichtMt PRIVATE
"$<$<BOOL:${USE_SDL2}>:SDL2::SDL2>"
)
endif()
if(WIN32)
target_compile_definitions(IrrlichtMt INTERFACE _IRR_WINDOWS_API_)
endif()

View file

@ -11,7 +11,7 @@
#include <unistd.h>
#ifndef _IRR_ANDROID_PLATFORM_
#include <sys/types.h>
#ifdef _IRR_OSX_PLATFORM_
#if defined(_IRR_OSX_PLATFORM_) || defined(_IRR_IOS_PLATFORM_)
#include <sys/sysctl.h>
#endif
#endif

View file

@ -33,7 +33,11 @@ bool CSDLManager::activateContext(const SExposedVideoData &videoData, bool resto
void *CSDLManager::getProcAddress(const std::string &procName)
{
#ifndef _IRR_COMPILE_WITH_ANGLE_
return SDL_GL_GetProcAddress(procName.c_str());
#else
return (void *)eglGetProcAddress(procName.c_str());
#endif
}
bool CSDLManager::swapBuffers()

View file

@ -7,7 +7,7 @@
#include "irrTypes.h"
// even though we have mt_opengl.h our driver code still uses GL_* constants
#if defined(_IRR_COMPILE_WITH_SDL_DEVICE_)
#if defined(_IRR_COMPILE_WITH_SDL_DEVICE_) && !defined(_IRR_COMPILE_WITH_ANGLE_)
#include <SDL_video.h>
#include <SDL_opengl.h>
#else

View file

@ -23,6 +23,8 @@
#include "mt_opengl.h"
#include <iostream>
namespace video
{
@ -214,14 +216,20 @@ void COpenGL3DriverBase::initQuadsIndices(u32 max_vertex_count)
void COpenGL3DriverBase::initVersion()
{
TEST_GL_ERROR(this);
Name = GL.GetString(GL_VERSION);
TEST_GL_ERROR(this);
printVersion();
// print renderer information
TEST_GL_ERROR(this);
VendorName = GL.GetString(GL_RENDERER);
TEST_GL_ERROR(this);
os::Printer::log("Renderer", VendorName.c_str(), ELL_INFORMATION);
TEST_GL_ERROR(this);
Version = getVersionFromOpenGL();
TEST_GL_ERROR(this);
}
bool COpenGL3DriverBase::isVersionAtLeast(int major, int minor) const noexcept
@ -235,9 +243,13 @@ bool COpenGL3DriverBase::isVersionAtLeast(int major, int minor) const noexcept
bool COpenGL3DriverBase::genericDriverInit(const core::dimension2d<u32> &screenSize, bool stencilBuffer)
{
TEST_GL_ERROR(this);
initVersion();
TEST_GL_ERROR(this);
initFeatures();
TEST_GL_ERROR(this);
printTextureFormats();
TEST_GL_ERROR(this);
if (EnableErrorTest) {
if (KHRDebugSupported) {
@ -259,6 +271,7 @@ bool COpenGL3DriverBase::genericDriverInit(const core::dimension2d<u32> &screenS
StencilBuffer = stencilBuffer;
TEST_GL_ERROR(this);
GL.PixelStorei(GL_PACK_ALIGNMENT, 1);
for (s32 i = 0; i < ETS_COUNT; ++i)
@ -266,15 +279,19 @@ bool COpenGL3DriverBase::genericDriverInit(const core::dimension2d<u32> &screenS
GL.ClearDepthf(1.0f);
TEST_GL_ERROR(this);
GL.FrontFace(GL_CW);
// create material renderers
TEST_GL_ERROR(this);
createMaterialRenderers();
TEST_GL_ERROR(this);
// set the renderstates
setRenderStates3DMode();
// set fog mode
TEST_GL_ERROR(this);
setFog(FogColor, FogType, FogStart, FogEnd, FogDensity, PixelFog, RangeFog);
// We need to reset once more at the beginning of the first rendering.
@ -725,6 +742,7 @@ void COpenGL3DriverBase::draw2DImage(const video::ITexture *texture, const core:
const core::rect<s32> &sourceRect, const core::rect<s32> *clipRect,
const video::SColor *const colors, bool useAlphaChannelOfTexture)
{
TEST_GL_ERROR(this);
if (!texture)
return;
@ -896,6 +914,7 @@ void COpenGL3DriverBase::draw2DImageBatch(const video::ITexture *texture,
if (clipRect)
GL.Disable(GL_SCISSOR_TEST);
TEST_GL_ERROR(this);
}
//! draw a 2d rectangle
@ -976,13 +995,16 @@ void COpenGL3DriverBase::drawArrays(GLenum primitiveType, const VertexType &vert
beginDraw(vertexType, reinterpret_cast<uintptr_t>(vertices));
GL.DrawArrays(primitiveType, 0, vertexCount);
endDraw(vertexType);
TEST_GL_ERROR(this);
}
void COpenGL3DriverBase::drawElements(GLenum primitiveType, const VertexType &vertexType, const void *vertices, int vertexCount, const u16 *indices, int indexCount)
{
TEST_GL_ERROR(this);
beginDraw(vertexType, reinterpret_cast<uintptr_t>(vertices));
GL.DrawRangeElements(primitiveType, 0, vertexCount - 1, indexCount, GL_UNSIGNED_SHORT, indices);
endDraw(vertexType);
TEST_GL_ERROR(this);
}
void COpenGL3DriverBase::drawGeneric(const void *vertices, const void *indexList,
@ -1189,6 +1211,7 @@ void COpenGL3DriverBase::setRenderStates3DMode()
//! Can be called by an IMaterialRenderer to make its work easier.
void COpenGL3DriverBase::setBasicRenderStates(const SMaterial &material, const SMaterial &lastmaterial, bool resetAllRenderStates)
{
TEST_GL_ERROR(this);
// ZBuffer
switch (material.ZBuffer) {
case ECFN_DISABLED:
@ -1341,6 +1364,7 @@ void COpenGL3DriverBase::setBasicRenderStates(const SMaterial &material, const S
// Texture parameters
setTextureRenderStates(material, resetAllRenderStates);
TEST_GL_ERROR(this);
}
//! Compare in SMaterial doesn't check texture parameters, so we should call this on each OnRender call.
@ -1491,6 +1515,7 @@ void COpenGL3DriverBase::setRenderStates2DMode(bool alpha, bool texture, bool al
}
MaterialRenderer2DActive->OnRender(this, video::EVT_STANDARD);
TEST_GL_ERROR(this);
}
void COpenGL3DriverBase::chooseMaterial2D()

View file

@ -163,6 +163,8 @@ public:
inline void irrGlObjectLabel(GLenum identifier, GLuint name, const char *label)
{
// KHR_debug implements ObjectLabelKHR in OpenGL ES
#ifndef _IRR_COMPILE_WITH_OGLES2_
if (KHRDebugSupported) {
u32 len = static_cast<u32>(strlen(label));
// Since our texture strings can get quite long we also truncate
@ -170,6 +172,7 @@ public:
len = std::min(len, std::min(MaxLabelLength, 82U));
GL.ObjectLabel(identifier, name, len, label);
}
#endif
}
bool LODBiasSupported = false;

View file

@ -8,6 +8,8 @@
#include "mt_opengl.h"
#include "CColorConverter.h"
#include <iostream>
namespace video
{