diff --git a/CMakeLists.txt b/CMakeLists.txt index 99a49d3..e4a922d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -182,7 +182,7 @@ add_library(fennec STATIC include/fennec/platform/interface/fwd.h include/fennec/platform/interface/platform.h source/platform/interface/platform.cpp - include/fennec/platform/interface/display.h + include/fennec/platform/interface/display.h source/platform/interface/display.cpp include/fennec/platform/interface/gfxcontext.h diff --git a/cmake/linux.cmake b/cmake/linux.cmake index 262bbe1..7b49d1a 100644 --- a/cmake/linux.cmake +++ b/cmake/linux.cmake @@ -37,6 +37,7 @@ macro(fennec_check_platform) include("${FENNEC_SOURCE_DIR}/cmake/wayland.cmake") fennec_check_wayland() + fennec_init_graphics() endif() endmacro() \ No newline at end of file diff --git a/cmake/opengl.cmake b/cmake/opengl.cmake index 5c1250e..da9e597 100644 --- a/cmake/opengl.cmake +++ b/cmake/opengl.cmake @@ -16,7 +16,12 @@ # along with this program. If not, see . # ====================================================================================================================== -find_package(OpenGL) +if(FENNEC_GRAPHICS_WANT_EGL) + find_package(OpenGL REQUIRED COMPONENTS EGL) + message(STATUS "EGL Requested") +else() + find_package(OpenGL) +endif() if(TARGET OpenGL::GL) # Core OpenGL Desktop Profile list(APPEND FENNEC_LINK_LIBRARIES OpenGL::OpenGL) @@ -42,5 +47,6 @@ if(FENNEC_GRAPHICS_WANT_EGL) list(APPEND FENNEC_LINK_LIBRARIES OpenGL::EGL) list(APPEND FENNEC_COMPILE_DEFINITIONS FENNEC_GRAPHICS_EGL=1) list(APPEND FENNEC_EXTRA_SOURCES + include/fennec/platform/opengl/egl/context.h source/platform/opengl/egl/context.cpp ) endif() \ No newline at end of file diff --git a/cmake/platform.cmake b/cmake/platform.cmake index 24067c3..3b933b8 100644 --- a/cmake/platform.cmake +++ b/cmake/platform.cmake @@ -22,13 +22,13 @@ message(STATUS "OS: ${CMAKE_SYSTEM_NAME}") include("${FENNEC_SOURCE_DIR}/cmake/default_user.cmake") +# Graphics APIs +macro(fennec_init_graphics) + include("${FENNEC_SOURCE_DIR}/cmake/opengl.cmake") +endmacro() + # Check for Linux if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") set(FENNEC_PLATFORM "Linux") include("${FENNEC_SOURCE_DIR}/cmake/linux.cmake") -endif () - -# Graphics APIs -if(FENNEC_USER_CLIENT) - include("${FENNEC_SOURCE_DIR}/cmake/opengl.cmake") -endif() \ No newline at end of file +endif () \ No newline at end of file diff --git a/include/fennec/containers/list.h b/include/fennec/containers/list.h index 82af851..38fd1c0 100644 --- a/include/fennec/containers/list.h +++ b/include/fennec/containers/list.h @@ -62,6 +62,7 @@ public: static constexpr size_t npos = -1; class iterator; + class const_iterator; private: struct node; @@ -100,7 +101,7 @@ public: size_t n = it._n; size_t p = _next_free(); - _table[p].data = fennec::forward(x); + fennec::construct(&_table[p].data, fennec::forward(x)); if (n == npos) { if (empty()) { _root = p; @@ -256,6 +257,54 @@ public: } }; + class const_iterator { + public: + ~const_iterator() { + _list = nullptr; + } + + // prefix operator + constexpr friend const_iterator& operator++(const_iterator& rhs) { + if (rhs._list->_next(rhs._n) < rhs._list->capacity()) { + return rhs; + } + rhs._n = npos; + return rhs; + } + + constexpr friend const_iterator operator++(const_iterator& lhs, int) { + const_iterator prev = lhs; + ++lhs; + return prev; + } + + constexpr const value_t& operator*() { + return *(_list->_table[_n].data); + } + + constexpr const value_t* operator->() { + return &*(_list->_table[_n].data); + } + + constexpr bool operator==(const const_iterator& it) { + return _list == it._list and _n == it._n; + } + + constexpr bool operator!=(const const_iterator& it) { + return _list != it._list or _n != it._n; + } + + private: + const list* _list; + size_t _n; + friend list; + + const_iterator(const list* ls, size_t n) + : _list(ls) + , _n(n) { + } + }; + constexpr iterator begin() { return iterator(this, _root); } @@ -264,6 +313,14 @@ public: return iterator(this, npos); } + constexpr const_iterator begin() const { + return const_iterator(this, _root); + } + + constexpr const_iterator end() const { + return const_iterator(this, npos); + } + private: allocation _table; dynarray _freed; @@ -306,15 +363,15 @@ private: } }; - size_t _next(size_t n) { + size_t _next(size_t n) const { return _table[n].next; } - size_t _prev(size_t n) { + size_t _prev(size_t n) const { return _table[n].prev; } - size_t _walk(size_t i) { + size_t _walk(size_t i) const { size_t n = _root; if (n == npos) return n; while (i > 0 && n != npos) { diff --git a/include/fennec/platform/interface/display.h b/include/fennec/platform/interface/display.h index c0a112e..d650b6b 100644 --- a/include/fennec/platform/interface/display.h +++ b/include/fennec/platform/interface/display.h @@ -19,7 +19,9 @@ #ifndef FENNEC_PLATFORM_INTERFACE_DISPLAY_H #define FENNEC_PLATFORM_INTERFACE_DISPLAY_H +#include #include +#include #include namespace fennec @@ -38,7 +40,7 @@ public: }; virtual bool connected() const = 0; - virtual ~display() = default; + virtual ~display(); virtual window* create_window() = 0; @@ -46,23 +48,25 @@ public: return _config.format; } - template - ContextT* get_context() { - return static_cast(_context); - } + virtual void select_context(); + virtual void* get_native_handle() = 0; - template - PlatformT* get_platform() { - return static_cast(_platform); - } + platform* get_platform() { return _platform; } + gfxcontext* get_context() { return _context; } + + const string name; + const uint64_t uuid; protected: platform* _platform; gfxcontext* _context; config _config; - explicit display(platform* platform) - : _platform(platform) + template + explicit display(platform* platform, const cstring& name, DisplayT*) + : name(name) + , uuid(typeuuid()) + , _platform(platform) , _context(nullptr) , _config { .format = { diff --git a/include/fennec/platform/interface/fwd.h b/include/fennec/platform/interface/fwd.h index d1d7802..e16b063 100644 --- a/include/fennec/platform/interface/fwd.h +++ b/include/fennec/platform/interface/fwd.h @@ -27,7 +27,7 @@ class display; // Handles display protocols class window; // Handles window surfaces of the display protocol class inputdevice; // Handles input devices class gfxcontext; // Handle Driver Contexts, i.e. EGL, WGL, VkWayland, etc. -class gfxsurface; // Handle +class gfxsurface; // Handle surface targets for windows } diff --git a/include/fennec/platform/interface/gfxcontext.h b/include/fennec/platform/interface/gfxcontext.h index 29daa0b..2c06868 100644 --- a/include/fennec/platform/interface/gfxcontext.h +++ b/include/fennec/platform/interface/gfxcontext.h @@ -19,4 +19,34 @@ #ifndef FENNEC_PLATFORM_INTERFACE_GFXCONTEXT_H #define FENNEC_PLATFORM_INTERFACE_GFXCONTEXT_H +#include +#include +#include + +namespace fennec +{ + +class gfxcontext { +public: + const string name; + const uint64_t uuid; + + virtual bool connected() = 0; + virtual void make_current(gfxsurface* surface) = 0; + + virtual ~gfxcontext() = default; + +protected: + display* _display; + + template + gfxcontext(display* display, const cstring& name, ContextT*) + : name(name) + , uuid(typeuuid()) + , _display(display) { + } +}; + +} + #endif // FENNEC_PLATFORM_INTERFACE_GFXCONTEXT_H diff --git a/include/fennec/platform/interface/platform.h b/include/fennec/platform/interface/platform.h index f946e60..9a7ce12 100644 --- a/include/fennec/platform/interface/platform.h +++ b/include/fennec/platform/interface/platform.h @@ -22,6 +22,7 @@ #include #include #include +#include #include /* @@ -31,7 +32,7 @@ * We want everything to be type agnostic from the top level down * * The general structure is: - * Platform -> Display Protocol -> GFX API -> GFX Context + * Platform -> Display Protocol -> GFX Context -> GFX API * -> Window -> GFX Surface * -> Input Protocol * @@ -64,6 +65,7 @@ public: using symbol = void*; const string name; + const uint64_t uuid; virtual ~platform() = default; @@ -78,8 +80,10 @@ public: display* get_display() { return _display; } protected: - explicit platform(const cstring& name) - : name(name) { + template + explicit platform(const cstring& name, PlatformT*) + : name(name) + , uuid(typeuuid()) { auto& globals = _get_globals(); assertf(globals.singleton == nullptr, "Conflicting Platform Definitions."); globals.singleton = this; @@ -105,11 +109,6 @@ public: ctor constructor; }; - static void add_driver(display_ctor ctor, int priority); - static void add_driver(input_ctor ctor, int priority); - static void add_driver(gfxctx_ctor ctor, int priority); - -private: struct global_context { platform* singleton; list> displays; @@ -121,6 +120,11 @@ private: } }; + static void add_driver(display_ctor ctor, int priority); + static void add_driver(input_ctor ctor, int priority); + static void add_driver(gfxctx_ctor ctor, int priority); + +private: static global_context& _get_globals(); public: diff --git a/include/fennec/platform/linux/platform.h b/include/fennec/platform/linux/platform.h index 5d4c8e1..e55ef57 100644 --- a/include/fennec/platform/linux/platform.h +++ b/include/fennec/platform/linux/platform.h @@ -26,7 +26,7 @@ namespace fennec class linux_platform : public unix_platform { public: linux_platform() - : unix_platform("linux") { + : unix_platform("linux", this) { } void initialize() override; diff --git a/include/fennec/platform/linux/wayland/display.h b/include/fennec/platform/linux/wayland/display.h index 9896dc9..f4b06e6 100644 --- a/include/fennec/platform/linux/wayland/display.h +++ b/include/fennec/platform/linux/wayland/display.h @@ -31,6 +31,7 @@ public: ~wayland_display() override; bool connected() const override; + void* get_native_handle() override { return _handle; } window* create_window() override; diff --git a/include/fennec/platform/opengl/egl/context.h b/include/fennec/platform/opengl/egl/context.h new file mode 100644 index 0000000..896c5c7 --- /dev/null +++ b/include/fennec/platform/opengl/egl/context.h @@ -0,0 +1,49 @@ +// ===================================================================================================================== +// fennec, a free and open source game engine +// Copyright © 2025 Medusa Slockbower +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// ===================================================================================================================== + +#ifndef FENNEC_PLATFORM_OPENGL_EGL_CONTEXT_H +#define FENNEC_PLATFORM_OPENGL_EGL_CONTEXT_H + +#include +#include + +namespace fennec +{ + +class eglcontext : public gfxcontext { +public: + eglcontext(display* display); + + ~eglcontext() override; + + bool connected() override; + + void make_current(gfxsurface* surface) override; + +private: + EGLDisplay _egldisplay; + EGLContext _eglcontext; + EGLConfig _eglconfig; + EGLint _eglvmajor, _eglvminor; + + void cleanup(); +}; + +} + +#endif // FENNEC_PLATFORM_OPENGL_EGL_CONTEXT_H diff --git a/include/fennec/platform/unix/platform.h b/include/fennec/platform/unix/platform.h index 525a156..bada8cc 100644 --- a/include/fennec/platform/unix/platform.h +++ b/include/fennec/platform/unix/platform.h @@ -18,6 +18,7 @@ #ifndef FENNEC_PLATFORM_UNIX_UNIX_PLATFORM_H #define FENNEC_PLATFORM_UNIX_UNIX_PLATFORM_H + #include namespace fennec @@ -25,8 +26,9 @@ namespace fennec class unix_platform : public platform { public: - explicit unix_platform(const cstring& name) - : platform(name) { + template + explicit unix_platform(const cstring& name, PlatformT*) + : platform(name, (PlatformT*)(nullptr)) { } shared_object* load_object(const cstring& file) override; diff --git a/source/platform/interface/display.cpp b/source/platform/interface/display.cpp new file mode 100644 index 0000000..b1afc33 --- /dev/null +++ b/source/platform/interface/display.cpp @@ -0,0 +1,42 @@ +// ===================================================================================================================== +// fennec, a free and open source game engine +// Copyright © 2025 Medusa Slockbower +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// ===================================================================================================================== + +#include +#include +#include + +namespace fennec +{ + +display::~display() { + delete _context; +} + +void display::select_context() { + if (_context != nullptr) return; + + const platform::global_context& globals = platform::get_globals(); + for (const auto& ctx : globals.graphics) { + _context = ctx.constructor(this); + if (_context) { + return; + } + } +} + +} diff --git a/source/platform/interface/platform.cpp b/source/platform/interface/platform.cpp index 90926d1..4c0144a 100644 --- a/source/platform/interface/platform.cpp +++ b/source/platform/interface/platform.cpp @@ -23,21 +23,22 @@ namespace fennec { template -static constexpr void insert_driver(list>& list, CtorT ctor, int priority) { - using list_t = fennec::list>; +static constexpr void insert_driver(list>& drvrs, CtorT ctor, int priority) { + using list_t = list>; using iter_t = typename list_t::iterator; - iter_t it = list.begin(); - while (it != list.end()) { + iter_t it = drvrs.begin(); + while (it != drvrs.end()) { if (priority > it->priority) { break; } } - list.insert(it, { priority, ctor }); + drvrs.insert(it, { priority, ctor }); } void platform::initialize() { load_display(); + _display->select_context(); } void platform::shutdown() { diff --git a/source/platform/linux/platform.cpp b/source/platform/linux/platform.cpp index d9c31ae..f8772aa 100644 --- a/source/platform/linux/platform.cpp +++ b/source/platform/linux/platform.cpp @@ -25,8 +25,7 @@ namespace fennec { STATIC_CONSTRUCTOR(_init_linux) { - static linux_platform* platform = new linux_platform(); - file::cout().write(platform, sizeof(platform), 1); + static linux_platform platform; } void linux_platform::initialize() { @@ -34,7 +33,7 @@ void linux_platform::initialize() { } void linux_platform::shutdown() { - + platform::shutdown(); } } diff --git a/source/platform/linux/wayland/display.cpp b/source/platform/linux/wayland/display.cpp index cb1271b..02433d8 100644 --- a/source/platform/linux/wayland/display.cpp +++ b/source/platform/linux/wayland/display.cpp @@ -38,7 +38,7 @@ STATIC_CONSTRUCTOR(_wayland_init) { } wayland_display::wayland_display(platform* platform) - : display(platform) + : display(platform, "wayland", this) , _handle(nullptr) , _registry(nullptr) , _compositor(nullptr) @@ -56,22 +56,23 @@ wayland_display::wayland_display(platform* platform) // Get handles _handle = wl_display_connect(nullptr); - if (not _handle) { cleanup(); return; } + // Get the wayland registry _registry = wl_display_get_registry(_handle); - if (not _registry) { cleanup(); return; } + // Add listener for interfaces wl_registry_add_listener(_registry, &listener, this); wl_display_roundtrip(_handle); + // Check for fifo v1, wayland's builtin fifo only allows one frame to be in flight at a time if (not _fifo) { assert(_fifo, "Compositor does not support fifo-v1 protocol, falling back to other display protocols."); cleanup(); @@ -111,7 +112,7 @@ void wayland_display::cleanup() { libwayland::unload_symbols(_platform); } - void wayland_display::listen_global(void* data, wl_registry* registry, uint32_t name, const char* itfc, uint32_t version) { +void wayland_display::listen_global(void* data, wl_registry* registry, uint32_t name, const char* itfc, uint32_t version) { static constexpr wl_seat_listener seat_listener = { listen_seat, nullptr }; @@ -132,10 +133,10 @@ void wayland_display::cleanup() { } - void wayland_display::listen_global_remove(void*, wl_registry*, uint32_t) { +void wayland_display::listen_global_remove(void*, wl_registry*, uint32_t) { } - void wayland_display::listen_seat(void*, wl_seat*, uint32_t) { +void wayland_display::listen_seat(void*, wl_seat*, uint32_t) { } diff --git a/source/platform/opengl/egl/context.cpp b/source/platform/opengl/egl/context.cpp new file mode 100644 index 0000000..2769e8d --- /dev/null +++ b/source/platform/opengl/egl/context.cpp @@ -0,0 +1,136 @@ +// ===================================================================================================================== +// fennec, a free and open source game engine +// Copyright © 2025 Medusa Slockbower +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// ===================================================================================================================== + +#include +#include +#include +#include + +namespace fennec +{ + +static gfxcontext* _create_egl_context(display* display) { + eglcontext* ctx = new eglcontext(display); + if (not ctx->connected()) { + delete ctx, ctx = nullptr; + } + return ctx; +} + +STATIC_CONSTRUCTOR(_egl_init) { + platform::add_driver(_create_egl_context, 1); +} + +eglcontext::eglcontext(display* display) + : gfxcontext(display, "EGL", this) { + + const display::pixel_format& fmt = _display->get_color_format(); + + EGLint context_attrs[] = { +#if FENNEC_GRAPHICS_OPENGL + EGL_CONTEXT_MAJOR_VERSION, 4, + EGL_CONTEXT_MINOR_VERSION, 3, +#elif FENNEC_GRAPHICS_GLES3 + EGL_CONTEXT_MAJOR_VERSION, 3, + EGL_CONTEXT_MINOR_VERSION, 2, +#elif FENNEC_GRAPHICS_GLES2 + EGL_CONTEXT_MAJOR_VERSION, 2, + EGL_CONTEXT_MINOR_VERSION, 0, +#endif + EGL_NONE + }; + + EGLint config_attrs[] = { + EGL_SURFACE_TYPE, + EGL_WINDOW_BIT, + EGL_DEPTH_SIZE, fmt.depth, + EGL_RED_SIZE, fmt.r, + EGL_RED_SIZE, fmt.g, + EGL_RED_SIZE, fmt.b, + EGL_RENDERABLE_TYPE, + +#if FENNEC_GRAPHICS_OPENGL + EGL_OPENGL_BIT, +#elif FENNEC_GRAPHICS_GLES3 + EGL_OPENGL_ES3_BIT, +#elif FENNEC_GRAPHICS_GLES2 + EGL_OPENGL_ES2_BIT, +#endif + EGL_NONE + }; + + + _egldisplay = eglGetDisplay(_display->get_native_handle()); + if (_egldisplay == nullptr) { + cleanup(); + return; + } + + if (not eglInitialize(_egldisplay, &_eglvmajor, &_eglvminor)) { + cleanup(); + return; + } + +#if FENNEC_GRAPHICS_OPENGL + if (not eglBindAPI(EGL_OPENGL_API)) { +#elif FENNEC_GRAPHICS_GLES3 or FENNEC_GRAPHICS_GLES2 + if (not eglBindAPI(EGL_OPENGL_ES_API)) { +#endif + cleanup(); + return; + } + + EGLint n; + if (not eglChooseConfig(_egldisplay, config_attrs, &_eglconfig, 1, &n)) { + cleanup(); + return; + } + + _eglcontext = eglCreateContext(_egldisplay, _eglconfig, EGL_NO_CONTEXT, context_attrs); + if (_eglcontext == nullptr) { + cleanup(); + return; + } +} + +eglcontext::~eglcontext() { + cleanup(); +} + +bool eglcontext::connected() { + return _eglcontext != nullptr; +} + +void eglcontext::make_current(gfxsurface*) { + +} + +void eglcontext::cleanup() { + eglMakeCurrent(_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroyContext(_display, _eglcontext); + eglTerminate(_display); + eglReleaseThread(); + + _egldisplay = nullptr; + _eglconfig = nullptr; + _eglcontext = nullptr; + _eglvmajor = 0; + _eglvminor = 0; +} + +} diff --git a/test/tests/test_platform.h b/test/tests/test_platform.h index 27d6e55..bc275e8 100644 --- a/test/tests/test_platform.h +++ b/test/tests/test_platform.h @@ -18,6 +18,8 @@ #ifndef FENNEC_TEST_PLATFORM_H #define FENNEC_TEST_PLATFORM_H + +#include #include @@ -30,6 +32,7 @@ inline void fennec_test_platform() { instance->initialize(); fennec_test_run(instance->get_display() != nullptr, true); + fennec_test_run(instance->get_display()->get_context() != nullptr, true); instance->shutdown(); }