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();
}