Apply clang-format-15

This commit is contained in:
Reg Tiangha 2024-06-10 22:29:24 -06:00
parent 3193775201
commit 99b9cec967
No known key found for this signature in database
GPG key ID: 00D437798B1C2970
21 changed files with 1623 additions and 1779 deletions

View file

@ -7,7 +7,7 @@
#define ALWAYS_INLINE __attribute__((always_inline)) inline #define ALWAYS_INLINE __attribute__((always_inline)) inline
#define AssertMsg(cond, msg) assert(cond && msg) #define AssertMsg(cond, msg) assert(cond&& msg)
#define Assert(cond) assert(cond) #define Assert(cond) assert(cond)
#define Panic(msg) assert(false && msg) #define Panic(msg) assert(false && msg)

View file

@ -1,9 +1,9 @@
#include "context.h"
#include "../log.h"
#include "loader.h"
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include <stdlib.h> #include <stdlib.h>
#include "../log.h"
#include "context.h"
#include "loader.h"
Log_SetChannel(GL::Context); Log_SetChannel(GL::Context);
#if defined(_WIN32) #if defined(_WIN32)
@ -20,16 +20,15 @@ Log_SetChannel(GL::Context);
namespace GL { namespace GL {
static bool ShouldPreferESContext() static bool ShouldPreferESContext() {
{
#ifndef _MSC_VER #ifndef _MSC_VER
const char* value = std::getenv("PREFER_GLES_CONTEXT"); const char* value = std::getenv("PREFER_GLES_CONTEXT");
return (value && strcmp(value, "1") == 0); return (value && strcmp(value, "1") == 0);
#else #else
char buffer[2] = {}; char buffer[2] = {};
size_t buffer_size = sizeof(buffer); size_t buffer_size = sizeof(buffer);
getenv_s(&buffer_size, buffer, "PREFER_GLES_CONTEXT"); getenv_s(&buffer_size, buffer, "PREFER_GLES_CONTEXT");
return (std::strcmp(buffer, "1") == 0); return (std::strcmp(buffer, "1") == 0);
#endif #endif
} }
@ -37,139 +36,131 @@ Context::Context(const WindowInfo& wi) : m_wi(wi) {}
Context::~Context() = default; Context::~Context() = default;
std::vector<Context::FullscreenModeInfo> Context::EnumerateFullscreenModes() std::vector<Context::FullscreenModeInfo> Context::EnumerateFullscreenModes() {
{ return {};
return {};
} }
std::unique_ptr<GL::Context> Context::Create(const WindowInfo& wi, const Version* versions_to_try, std::unique_ptr<GL::Context> Context::Create(const WindowInfo& wi, const Version* versions_to_try,
size_t num_versions_to_try) size_t num_versions_to_try) {
{ if (ShouldPreferESContext()) {
if (ShouldPreferESContext()) // move ES versions to the front
{ Version* new_versions_to_try =
// move ES versions to the front static_cast<Version*>(alloca(sizeof(Version) * num_versions_to_try));
Version* new_versions_to_try = static_cast<Version*>(alloca(sizeof(Version) * num_versions_to_try)); size_t count = 0;
size_t count = 0; for (size_t i = 0; i < num_versions_to_try; i++) {
for (size_t i = 0; i < num_versions_to_try; i++) if (versions_to_try[i].profile == Profile::ES)
{ new_versions_to_try[count++] = versions_to_try[i];
if (versions_to_try[i].profile == Profile::ES) }
new_versions_to_try[count++] = versions_to_try[i]; for (size_t i = 0; i < num_versions_to_try; i++) {
if (versions_to_try[i].profile != Profile::ES)
new_versions_to_try[count++] = versions_to_try[i];
}
versions_to_try = new_versions_to_try;
} }
for (size_t i = 0; i < num_versions_to_try; i++)
{
if (versions_to_try[i].profile != Profile::ES)
new_versions_to_try[count++] = versions_to_try[i];
}
versions_to_try = new_versions_to_try;
}
std::unique_ptr<Context> context; std::unique_ptr<Context> context;
#if defined(_WIN32) #if defined(_WIN32)
context = ContextWGL::Create(wi, versions_to_try, num_versions_to_try); context = ContextWGL::Create(wi, versions_to_try, num_versions_to_try);
#elif defined(__APPLE__) #elif defined(__APPLE__)
context = ContextAGL::Create(wi, versions_to_try, num_versions_to_try); context = ContextAGL::Create(wi, versions_to_try, num_versions_to_try);
#else #else
if (wi.type == WindowInfo::Type::X11) if (wi.type == WindowInfo::Type::X11) {
{ const char* use_egl_x11 = std::getenv("USE_EGL_X11");
const char* use_egl_x11 = std::getenv("USE_EGL_X11"); if (use_egl_x11 && std::strcmp(use_egl_x11, "1") == 0)
if (use_egl_x11 && std::strcmp(use_egl_x11, "1") == 0) context = ContextEGLX11::Create(wi, versions_to_try, num_versions_to_try);
context = ContextEGLX11::Create(wi, versions_to_try, num_versions_to_try); else
else context = ContextGLX::Create(wi, versions_to_try, num_versions_to_try);
context = ContextGLX::Create(wi, versions_to_try, num_versions_to_try); }
}
#ifdef WAYLAND_ENABLED #ifdef WAYLAND_ENABLED
if (wi.type == WindowInfo::Type::Wayland) if (wi.type == WindowInfo::Type::Wayland)
context = ContextEGLWayland::Create(wi, versions_to_try, num_versions_to_try); context = ContextEGLWayland::Create(wi, versions_to_try, num_versions_to_try);
#endif #endif
#endif #endif
if (!context) if (!context)
return nullptr; return nullptr;
Log_InfoPrintf("Created a %s context", context->IsGLES() ? "OpenGL ES" : "OpenGL"); Log_InfoPrintf("Created a %s context", context->IsGLES() ? "OpenGL ES" : "OpenGL");
// TODO: Not thread-safe. // TODO: Not thread-safe.
static Context* context_being_created; static Context* context_being_created;
context_being_created = context.get(); context_being_created = context.get();
if (!gladLoadGLLoader([](const char* name) { return context_being_created->GetProcAddress(name); })) if (!gladLoadGLLoader(
{ [](const char* name) { return context_being_created->GetProcAddress(name); })) {
Log_ErrorPrintf("Failed to load GL functions for GLAD"); Log_ErrorPrintf("Failed to load GL functions for GLAD");
return nullptr; return nullptr;
} }
const char* gl_vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR)); const char* gl_vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
const char* gl_renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER)); const char* gl_renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER));
const char* gl_version = reinterpret_cast<const char*>(glGetString(GL_VERSION)); const char* gl_version = reinterpret_cast<const char*>(glGetString(GL_VERSION));
const char* gl_shading_language_version = reinterpret_cast<const char*>(glGetString(GL_SHADING_LANGUAGE_VERSION)); const char* gl_shading_language_version =
Log_InfoPrintf("GL_VENDOR: %s", gl_vendor); reinterpret_cast<const char*>(glGetString(GL_SHADING_LANGUAGE_VERSION));
Log_InfoPrintf("GL_RENDERER: %s", gl_renderer); Log_InfoPrintf("GL_VENDOR: %s", gl_vendor);
Log_InfoPrintf("GL_VERSION: %s", gl_version); Log_InfoPrintf("GL_RENDERER: %s", gl_renderer);
Log_InfoPrintf("GL_SHADING_LANGUAGE_VERSION: %s", gl_shading_language_version); Log_InfoPrintf("GL_VERSION: %s", gl_version);
Log_InfoPrintf("GL_SHADING_LANGUAGE_VERSION: %s", gl_shading_language_version);
return context; return context;
} }
const std::array<Context::Version, 11>& Context::GetAllDesktopVersionsList() const std::array<Context::Version, 11>& Context::GetAllDesktopVersionsList() {
{ static constexpr std::array<Version, 11> vlist = {{{Profile::Core, 4, 6},
static constexpr std::array<Version, 11> vlist = {{{Profile::Core, 4, 6}, {Profile::Core, 4, 5},
{Profile::Core, 4, 5}, {Profile::Core, 4, 4},
{Profile::Core, 4, 4}, {Profile::Core, 4, 3},
{Profile::Core, 4, 3}, {Profile::Core, 4, 2},
{Profile::Core, 4, 2}, {Profile::Core, 4, 1},
{Profile::Core, 4, 1}, {Profile::Core, 4, 0},
{Profile::Core, 4, 0}, {Profile::Core, 3, 3},
{Profile::Core, 3, 3}, {Profile::Core, 3, 2},
{Profile::Core, 3, 2}, {Profile::Core, 3, 1},
{Profile::Core, 3, 1}, {Profile::Core, 3, 0}}};
{Profile::Core, 3, 0}}}; return vlist;
return vlist;
} }
const std::array<Context::Version, 12>& Context::GetAllDesktopVersionsListWithFallback() const std::array<Context::Version, 12>& Context::GetAllDesktopVersionsListWithFallback() {
{ static constexpr std::array<Version, 12> vlist = {{{Profile::Core, 4, 6},
static constexpr std::array<Version, 12> vlist = {{{Profile::Core, 4, 6}, {Profile::Core, 4, 5},
{Profile::Core, 4, 5}, {Profile::Core, 4, 4},
{Profile::Core, 4, 4}, {Profile::Core, 4, 3},
{Profile::Core, 4, 3}, {Profile::Core, 4, 2},
{Profile::Core, 4, 2}, {Profile::Core, 4, 1},
{Profile::Core, 4, 1}, {Profile::Core, 4, 0},
{Profile::Core, 4, 0}, {Profile::Core, 3, 3},
{Profile::Core, 3, 3}, {Profile::Core, 3, 2},
{Profile::Core, 3, 2}, {Profile::Core, 3, 1},
{Profile::Core, 3, 1}, {Profile::Core, 3, 0},
{Profile::Core, 3, 0}, {Profile::NoProfile, 0, 0}}};
{Profile::NoProfile, 0, 0}}}; return vlist;
return vlist;
} }
const std::array<Context::Version, 4>& Context::GetAllESVersionsList() const std::array<Context::Version, 4>& Context::GetAllESVersionsList() {
{ static constexpr std::array<Version, 4> vlist = {
static constexpr std::array<Version, 4> vlist = { {{Profile::ES, 3, 2}, {Profile::ES, 3, 1}, {Profile::ES, 3, 0}, {Profile::ES, 2, 0}}};
{{Profile::ES, 3, 2}, {Profile::ES, 3, 1}, {Profile::ES, 3, 0}, {Profile::ES, 2, 0}}}; return vlist;
return vlist;
} }
const std::array<Context::Version, 16>& Context::GetAllVersionsList() const std::array<Context::Version, 16>& Context::GetAllVersionsList() {
{ static constexpr std::array<Version, 16> vlist = {{{Profile::Core, 4, 6},
static constexpr std::array<Version, 16> vlist = {{{Profile::Core, 4, 6}, {Profile::Core, 4, 5},
{Profile::Core, 4, 5}, {Profile::Core, 4, 4},
{Profile::Core, 4, 4}, {Profile::Core, 4, 3},
{Profile::Core, 4, 3}, {Profile::Core, 4, 2},
{Profile::Core, 4, 2}, {Profile::Core, 4, 1},
{Profile::Core, 4, 1}, {Profile::Core, 4, 0},
{Profile::Core, 4, 0}, {Profile::Core, 3, 3},
{Profile::Core, 3, 3}, {Profile::Core, 3, 2},
{Profile::Core, 3, 2}, {Profile::Core, 3, 1},
{Profile::Core, 3, 1}, {Profile::Core, 3, 0},
{Profile::Core, 3, 0}, {Profile::ES, 3, 2},
{Profile::ES, 3, 2}, {Profile::ES, 3, 1},
{Profile::ES, 3, 1}, {Profile::ES, 3, 0},
{Profile::ES, 3, 0}, {Profile::ES, 2, 0},
{Profile::ES, 2, 0}, {Profile::NoProfile, 0, 0}}};
{Profile::NoProfile, 0, 0}}}; return vlist;
return vlist;
} }
} // namespace GL } // namespace GL

View file

@ -1,77 +1,81 @@
#pragma once #pragma once
#include "../duckstation_compat.h"
#include "../window_info.h"
#include <array> #include <array>
#include <memory> #include <memory>
#include <vector> #include <vector>
#include "../duckstation_compat.h"
#include "../window_info.h"
namespace GL { namespace GL {
using namespace citra; using namespace citra;
class Context class Context {
{
public: public:
Context(const WindowInfo& wi); Context(const WindowInfo& wi);
virtual ~Context(); virtual ~Context();
enum class Profile enum class Profile { NoProfile, Core, ES };
{
NoProfile,
Core,
ES
};
struct Version struct Version {
{ Profile profile;
Profile profile; int major_version;
int major_version; int minor_version;
int minor_version; };
};
struct FullscreenModeInfo struct FullscreenModeInfo {
{ u32 width;
u32 width; u32 height;
u32 height; float refresh_rate;
float refresh_rate; };
};
ALWAYS_INLINE const WindowInfo& GetWindowInfo() const { return m_wi; } ALWAYS_INLINE const WindowInfo& GetWindowInfo() const {
ALWAYS_INLINE bool IsGLES() const { return (m_version.profile == Profile::ES); } return m_wi;
ALWAYS_INLINE u32 GetSurfaceWidth() const { return m_wi.surface_width; } }
ALWAYS_INLINE u32 GetSurfaceHeight() const { return m_wi.surface_height; } ALWAYS_INLINE bool IsGLES() const {
ALWAYS_INLINE WindowInfo::SurfaceFormat GetSurfaceFormat() const { return m_wi.surface_format; } return (m_version.profile == Profile::ES);
}
ALWAYS_INLINE u32 GetSurfaceWidth() const {
return m_wi.surface_width;
}
ALWAYS_INLINE u32 GetSurfaceHeight() const {
return m_wi.surface_height;
}
ALWAYS_INLINE WindowInfo::SurfaceFormat GetSurfaceFormat() const {
return m_wi.surface_format;
}
virtual void* GetProcAddress(const char* name) = 0; virtual void* GetProcAddress(const char* name) = 0;
virtual bool ChangeSurface(const WindowInfo& new_wi) = 0; virtual bool ChangeSurface(const WindowInfo& new_wi) = 0;
virtual void ResizeSurface(u32 new_surface_width = 0, u32 new_surface_height = 0) = 0; virtual void ResizeSurface(u32 new_surface_width = 0, u32 new_surface_height = 0) = 0;
virtual bool SwapBuffers() = 0; virtual bool SwapBuffers() = 0;
virtual bool MakeCurrent() = 0; virtual bool MakeCurrent() = 0;
virtual bool DoneCurrent() = 0; virtual bool DoneCurrent() = 0;
virtual bool SetSwapInterval(s32 interval) = 0; virtual bool SetSwapInterval(s32 interval) = 0;
virtual std::unique_ptr<Context> CreateSharedContext(const WindowInfo& wi) = 0; virtual std::unique_ptr<Context> CreateSharedContext(const WindowInfo& wi) = 0;
virtual std::vector<FullscreenModeInfo> EnumerateFullscreenModes(); virtual std::vector<FullscreenModeInfo> EnumerateFullscreenModes();
static std::unique_ptr<Context> Create(const WindowInfo& wi, const Version* versions_to_try, static std::unique_ptr<Context> Create(const WindowInfo& wi, const Version* versions_to_try,
size_t num_versions_to_try); size_t num_versions_to_try);
template<size_t N> template <size_t N>
static std::unique_ptr<Context> Create(const WindowInfo& wi, const std::array<Version, N>& versions_to_try) static std::unique_ptr<Context> Create(const WindowInfo& wi,
{ const std::array<Version, N>& versions_to_try) {
return Create(wi, versions_to_try.data(), versions_to_try.size()); return Create(wi, versions_to_try.data(), versions_to_try.size());
} }
static std::unique_ptr<Context> Create(const WindowInfo& wi) { return Create(wi, GetAllVersionsList()); } static std::unique_ptr<Context> Create(const WindowInfo& wi) {
return Create(wi, GetAllVersionsList());
}
static const std::array<Version, 11>& GetAllDesktopVersionsList(); static const std::array<Version, 11>& GetAllDesktopVersionsList();
static const std::array<Version, 12>& GetAllDesktopVersionsListWithFallback(); static const std::array<Version, 12>& GetAllDesktopVersionsListWithFallback();
static const std::array<Version, 4>& GetAllESVersionsList(); static const std::array<Version, 4>& GetAllESVersionsList();
static const std::array<Version, 16>& GetAllVersionsList(); static const std::array<Version, 16>& GetAllVersionsList();
protected: protected:
#ifdef _WIN32 #ifdef _WIN32
#endif #endif
WindowInfo m_wi; WindowInfo m_wi;
Version m_version = {}; Version m_version = {};
}; };
} // namespace GL } // namespace GL

View file

@ -14,37 +14,38 @@ struct NSView;
namespace GL { namespace GL {
using namespace melonDS; using namespace melonDS;
class ContextAGL final : public Context class ContextAGL final : public Context {
{
public: public:
ContextAGL(const WindowInfo& wi); ContextAGL(const WindowInfo& wi);
~ContextAGL() override; ~ContextAGL() override;
static std::unique_ptr<Context> Create(const WindowInfo& wi, const Version* versions_to_try, static std::unique_ptr<Context> Create(const WindowInfo& wi, const Version* versions_to_try,
size_t num_versions_to_try); size_t num_versions_to_try);
void* GetProcAddress(const char* name) override; void* GetProcAddress(const char* name) override;
bool ChangeSurface(const WindowInfo& new_wi) override; bool ChangeSurface(const WindowInfo& new_wi) override;
void ResizeSurface(u32 new_surface_width = 0, u32 new_surface_height = 0) override; void ResizeSurface(u32 new_surface_width = 0, u32 new_surface_height = 0) override;
bool SwapBuffers() override; bool SwapBuffers() override;
bool MakeCurrent() override; bool MakeCurrent() override;
bool DoneCurrent() override; bool DoneCurrent() override;
bool SetSwapInterval(s32 interval) override; bool SetSwapInterval(s32 interval) override;
std::unique_ptr<Context> CreateSharedContext(const WindowInfo& wi) override; std::unique_ptr<Context> CreateSharedContext(const WindowInfo& wi) override;
private: private:
ALWAYS_INLINE NSView* GetView() const { return static_cast<NSView*>((__bridge NSView*)m_wi.window_handle); } ALWAYS_INLINE NSView* GetView() const {
return static_cast<NSView*>((__bridge NSView*)m_wi.window_handle);
}
bool Initialize(const Version* versions_to_try, size_t num_versions_to_try); bool Initialize(const Version* versions_to_try, size_t num_versions_to_try);
bool CreateContext(NSOpenGLContext* share_context, int profile, bool make_current); bool CreateContext(NSOpenGLContext* share_context, int profile, bool make_current);
void BindContextToView(); void BindContextToView();
// returns true if dimensions have changed // returns true if dimensions have changed
bool UpdateDimensions(); bool UpdateDimensions();
NSOpenGLContext* m_context = nullptr; NSOpenGLContext* m_context = nullptr;
NSOpenGLPixelFormat* m_pixel_format = nullptr; NSOpenGLPixelFormat* m_pixel_format = nullptr;
void* m_opengl_module_handle = nullptr; void* m_opengl_module_handle = nullptr;
}; };
} // namespace GL } // namespace GL

View file

@ -1,432 +1,386 @@
#include "context_egl.h" #include <cstring>
#include "../log.h"
#include "../duckstation_compat.h"
#include <optional> #include <optional>
#include <vector> #include <vector>
#include <cstring> #include "../duckstation_compat.h"
#include "../log.h"
#include "context_egl.h"
Log_SetChannel(GL::ContextEGL); Log_SetChannel(GL::ContextEGL);
namespace GL { namespace GL {
ContextEGL::ContextEGL(const WindowInfo& wi) : Context(wi) {} ContextEGL::ContextEGL(const WindowInfo& wi) : Context(wi) {}
ContextEGL::~ContextEGL() ContextEGL::~ContextEGL() {
{ DestroySurface();
DestroySurface(); DestroyContext();
DestroyContext();
} }
std::unique_ptr<Context> ContextEGL::Create(const WindowInfo& wi, const Version* versions_to_try, std::unique_ptr<Context> ContextEGL::Create(const WindowInfo& wi, const Version* versions_to_try,
size_t num_versions_to_try) size_t num_versions_to_try) {
{ std::unique_ptr<ContextEGL> context = std::make_unique<ContextEGL>(wi);
std::unique_ptr<ContextEGL> context = std::make_unique<ContextEGL>(wi); if (!context->Initialize(versions_to_try, num_versions_to_try))
if (!context->Initialize(versions_to_try, num_versions_to_try)) return nullptr;
return nullptr;
return context; return context;
} }
bool ContextEGL::Initialize(const Version* versions_to_try, size_t num_versions_to_try) bool ContextEGL::Initialize(const Version* versions_to_try, size_t num_versions_to_try) {
{ if (!gladLoadEGL()) {
if (!gladLoadEGL()) Log_ErrorPrintf("Loading GLAD EGL functions failed");
{ return false;
Log_ErrorPrintf("Loading GLAD EGL functions failed"); }
if (!SetDisplay())
return false;
int egl_major, egl_minor;
if (!eglInitialize(m_display, &egl_major, &egl_minor)) {
Log_ErrorPrintf("eglInitialize() failed: %d", eglGetError());
return false;
}
Log_InfoPrintf("EGL Version: %d.%d", egl_major, egl_minor);
const char* extensions = eglQueryString(m_display, EGL_EXTENSIONS);
if (extensions) {
Log_InfoPrintf("EGL Extensions: %s", extensions);
m_supports_surfaceless = std::strstr(extensions, "EGL_KHR_surfaceless_context") != nullptr;
}
if (!m_supports_surfaceless)
Log_WarningPrint(
"EGL implementation does not support surfaceless contexts, emulating with pbuffers");
for (size_t i = 0; i < num_versions_to_try; i++) {
if (CreateContextAndSurface(versions_to_try[i], nullptr, true))
return true;
}
return false; return false;
}
if (!SetDisplay())
return false;
int egl_major, egl_minor;
if (!eglInitialize(m_display, &egl_major, &egl_minor))
{
Log_ErrorPrintf("eglInitialize() failed: %d", eglGetError());
return false;
}
Log_InfoPrintf("EGL Version: %d.%d", egl_major, egl_minor);
const char* extensions = eglQueryString(m_display, EGL_EXTENSIONS);
if (extensions)
{
Log_InfoPrintf("EGL Extensions: %s", extensions);
m_supports_surfaceless = std::strstr(extensions, "EGL_KHR_surfaceless_context") != nullptr;
}
if (!m_supports_surfaceless)
Log_WarningPrint("EGL implementation does not support surfaceless contexts, emulating with pbuffers");
for (size_t i = 0; i < num_versions_to_try; i++)
{
if (CreateContextAndSurface(versions_to_try[i], nullptr, true))
return true;
}
return false;
} }
bool ContextEGL::SetDisplay() bool ContextEGL::SetDisplay() {
{ m_display = eglGetDisplay(static_cast<EGLNativeDisplayType>(m_wi.display_connection));
m_display = eglGetDisplay(static_cast<EGLNativeDisplayType>(m_wi.display_connection)); if (!m_display) {
if (!m_display) Log_ErrorPrintf("eglGetDisplay() failed: %d", eglGetError());
{ return false;
Log_ErrorPrintf("eglGetDisplay() failed: %d", eglGetError()); }
return false;
}
return true; return true;
} }
void* ContextEGL::GetProcAddress(const char* name) void* ContextEGL::GetProcAddress(const char* name) {
{ return reinterpret_cast<void*>(eglGetProcAddress(name));
return reinterpret_cast<void*>(eglGetProcAddress(name));
} }
bool ContextEGL::ChangeSurface(const WindowInfo& new_wi) bool ContextEGL::ChangeSurface(const WindowInfo& new_wi) {
{ const bool was_current = (eglGetCurrentContext() == m_context);
const bool was_current = (eglGetCurrentContext() == m_context); if (was_current)
if (was_current) eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (m_surface != EGL_NO_SURFACE) if (m_surface != EGL_NO_SURFACE) {
{ eglDestroySurface(m_display, m_surface);
eglDestroySurface(m_display, m_surface); m_surface = EGL_NO_SURFACE;
m_surface = EGL_NO_SURFACE; }
}
m_wi = new_wi; m_wi = new_wi;
if (!CreateSurface()) if (!CreateSurface())
return false; return false;
if (was_current && !eglMakeCurrent(m_display, m_surface, m_surface, m_context)) if (was_current && !eglMakeCurrent(m_display, m_surface, m_surface, m_context)) {
{ Log_ErrorPrintf("Failed to make context current again after surface change");
Log_ErrorPrintf("Failed to make context current again after surface change"); return false;
return false; }
}
return true; return true;
} }
void ContextEGL::ResizeSurface(u32 new_surface_width /*= 0*/, u32 new_surface_height /*= 0*/) void ContextEGL::ResizeSurface(u32 new_surface_width /*= 0*/, u32 new_surface_height /*= 0*/) {
{ if (new_surface_width == 0 && new_surface_height == 0) {
if (new_surface_width == 0 && new_surface_height == 0) EGLint surface_width, surface_height;
{ if (eglQuerySurface(m_display, m_surface, EGL_WIDTH, &surface_width) &&
eglQuerySurface(m_display, m_surface, EGL_HEIGHT, &surface_height)) {
m_wi.surface_width = static_cast<u32>(surface_width);
m_wi.surface_height = static_cast<u32>(surface_height);
return;
} else {
Log_ErrorPrintf("eglQuerySurface() failed: %d", eglGetError());
}
}
m_wi.surface_width = new_surface_width;
m_wi.surface_height = new_surface_height;
}
bool ContextEGL::SwapBuffers() {
return eglSwapBuffers(m_display, m_surface);
}
bool ContextEGL::MakeCurrent() {
if (!eglMakeCurrent(m_display, m_surface, m_surface, m_context)) {
Log_ErrorPrintf("eglMakeCurrent() failed: %d", eglGetError());
return false;
}
return true;
}
bool ContextEGL::DoneCurrent() {
return eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
}
bool ContextEGL::SetSwapInterval(s32 interval) {
return eglSwapInterval(m_display, interval);
}
std::unique_ptr<Context> ContextEGL::CreateSharedContext(const WindowInfo& wi) {
std::unique_ptr<ContextEGL> context = std::make_unique<ContextEGL>(wi);
context->m_display = m_display;
context->m_supports_surfaceless = m_supports_surfaceless;
if (!context->CreateContextAndSurface(m_version, m_context, false))
return nullptr;
return context;
}
EGLNativeWindowType ContextEGL::GetNativeWindow(EGLConfig config) {
return {};
}
bool ContextEGL::CreateSurface() {
if (m_wi.type == WindowInfo::Type::Surfaceless) {
if (m_supports_surfaceless)
return true;
else
return CreatePBufferSurface();
}
EGLNativeWindowType native_window = GetNativeWindow(m_config);
m_surface = eglCreateWindowSurface(m_display, m_config, native_window, nullptr);
if (!m_surface) {
Log_ErrorPrintf("eglCreateWindowSurface() failed: %d", eglGetError());
return false;
}
// Some implementations may require the size to be queried at runtime.
EGLint surface_width, surface_height; EGLint surface_width, surface_height;
if (eglQuerySurface(m_display, m_surface, EGL_WIDTH, &surface_width) && if (eglQuerySurface(m_display, m_surface, EGL_WIDTH, &surface_width) &&
eglQuerySurface(m_display, m_surface, EGL_HEIGHT, &surface_height)) eglQuerySurface(m_display, m_surface, EGL_HEIGHT, &surface_height)) {
{ m_wi.surface_width = static_cast<u32>(surface_width);
m_wi.surface_width = static_cast<u32>(surface_width); m_wi.surface_height = static_cast<u32>(surface_height);
m_wi.surface_height = static_cast<u32>(surface_height); } else {
return; Log_ErrorPrintf("eglQuerySurface() failed: %d", eglGetError());
} }
else
{ return true;
Log_ErrorPrintf("eglQuerySurface() failed: %d", eglGetError()); }
bool ContextEGL::CreatePBufferSurface() {
const u32 width = std::max<u32>(m_wi.surface_width, 1);
const u32 height = std::max<u32>(m_wi.surface_height, 1);
// TODO: Format
EGLint attrib_list[] = {
EGL_WIDTH, static_cast<EGLint>(width), EGL_HEIGHT, static_cast<EGLint>(height), EGL_NONE,
};
m_surface = eglCreatePbufferSurface(m_display, m_config, attrib_list);
if (!m_surface) {
Log_ErrorPrintf("eglCreatePbufferSurface() failed: %d", eglGetError());
return false;
} }
}
m_wi.surface_width = new_surface_width; Log_DevPrintf("Created %ux%u pbuffer surface", width, height);
m_wi.surface_height = new_surface_height; return true;
} }
bool ContextEGL::SwapBuffers() bool ContextEGL::CheckConfigSurfaceFormat(EGLConfig config,
{ WindowInfo::SurfaceFormat format) const {
return eglSwapBuffers(m_display, m_surface); int red_size, green_size, blue_size, alpha_size;
} if (!eglGetConfigAttrib(m_display, config, EGL_RED_SIZE, &red_size) ||
!eglGetConfigAttrib(m_display, config, EGL_GREEN_SIZE, &green_size) ||
!eglGetConfigAttrib(m_display, config, EGL_BLUE_SIZE, &blue_size) ||
!eglGetConfigAttrib(m_display, config, EGL_ALPHA_SIZE, &alpha_size)) {
return false;
}
bool ContextEGL::MakeCurrent() switch (format) {
{
if (!eglMakeCurrent(m_display, m_surface, m_surface, m_context))
{
Log_ErrorPrintf("eglMakeCurrent() failed: %d", eglGetError());
return false;
}
return true;
}
bool ContextEGL::DoneCurrent()
{
return eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
}
bool ContextEGL::SetSwapInterval(s32 interval)
{
return eglSwapInterval(m_display, interval);
}
std::unique_ptr<Context> ContextEGL::CreateSharedContext(const WindowInfo& wi)
{
std::unique_ptr<ContextEGL> context = std::make_unique<ContextEGL>(wi);
context->m_display = m_display;
context->m_supports_surfaceless = m_supports_surfaceless;
if (!context->CreateContextAndSurface(m_version, m_context, false))
return nullptr;
return context;
}
EGLNativeWindowType ContextEGL::GetNativeWindow(EGLConfig config)
{
return {};
}
bool ContextEGL::CreateSurface()
{
if (m_wi.type == WindowInfo::Type::Surfaceless)
{
if (m_supports_surfaceless)
return true;
else
return CreatePBufferSurface();
}
EGLNativeWindowType native_window = GetNativeWindow(m_config);
m_surface = eglCreateWindowSurface(m_display, m_config, native_window, nullptr);
if (!m_surface)
{
Log_ErrorPrintf("eglCreateWindowSurface() failed: %d", eglGetError());
return false;
}
// Some implementations may require the size to be queried at runtime.
EGLint surface_width, surface_height;
if (eglQuerySurface(m_display, m_surface, EGL_WIDTH, &surface_width) &&
eglQuerySurface(m_display, m_surface, EGL_HEIGHT, &surface_height))
{
m_wi.surface_width = static_cast<u32>(surface_width);
m_wi.surface_height = static_cast<u32>(surface_height);
}
else
{
Log_ErrorPrintf("eglQuerySurface() failed: %d", eglGetError());
}
return true;
}
bool ContextEGL::CreatePBufferSurface()
{
const u32 width = std::max<u32>(m_wi.surface_width, 1);
const u32 height = std::max<u32>(m_wi.surface_height, 1);
// TODO: Format
EGLint attrib_list[] = {
EGL_WIDTH, static_cast<EGLint>(width), EGL_HEIGHT, static_cast<EGLint>(height), EGL_NONE,
};
m_surface = eglCreatePbufferSurface(m_display, m_config, attrib_list);
if (!m_surface)
{
Log_ErrorPrintf("eglCreatePbufferSurface() failed: %d", eglGetError());
return false;
}
Log_DevPrintf("Created %ux%u pbuffer surface", width, height);
return true;
}
bool ContextEGL::CheckConfigSurfaceFormat(EGLConfig config, WindowInfo::SurfaceFormat format) const
{
int red_size, green_size, blue_size, alpha_size;
if (!eglGetConfigAttrib(m_display, config, EGL_RED_SIZE, &red_size) ||
!eglGetConfigAttrib(m_display, config, EGL_GREEN_SIZE, &green_size) ||
!eglGetConfigAttrib(m_display, config, EGL_BLUE_SIZE, &blue_size) ||
!eglGetConfigAttrib(m_display, config, EGL_ALPHA_SIZE, &alpha_size))
{
return false;
}
switch (format)
{
case WindowInfo::SurfaceFormat::Auto: case WindowInfo::SurfaceFormat::Auto:
return true; return true;
case WindowInfo::SurfaceFormat::RGB8: case WindowInfo::SurfaceFormat::RGB8:
return (red_size == 8 && green_size == 8 && blue_size == 8); return (red_size == 8 && green_size == 8 && blue_size == 8);
case WindowInfo::SurfaceFormat::RGBA8: case WindowInfo::SurfaceFormat::RGBA8:
return (red_size == 8 && green_size == 8 && blue_size == 8 && alpha_size == 8); return (red_size == 8 && green_size == 8 && blue_size == 8 && alpha_size == 8);
case WindowInfo::SurfaceFormat::RGB565: case WindowInfo::SurfaceFormat::RGB565:
return (red_size == 5 && green_size == 6 && blue_size == 5); return (red_size == 5 && green_size == 6 && blue_size == 5);
default: default:
return false; return false;
} }
} }
void ContextEGL::DestroyContext() void ContextEGL::DestroyContext() {
{ if (eglGetCurrentContext() == m_context)
if (eglGetCurrentContext() == m_context) eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (m_context != EGL_NO_CONTEXT) if (m_context != EGL_NO_CONTEXT) {
{ eglDestroyContext(m_display, m_context);
eglDestroyContext(m_display, m_context); m_context = EGL_NO_CONTEXT;
m_context = EGL_NO_CONTEXT; }
}
} }
void ContextEGL::DestroySurface() void ContextEGL::DestroySurface() {
{ if (eglGetCurrentSurface(EGL_DRAW) == m_surface)
if (eglGetCurrentSurface(EGL_DRAW) == m_surface) eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (m_surface != EGL_NO_SURFACE) if (m_surface != EGL_NO_SURFACE) {
{ eglDestroySurface(m_display, m_surface);
eglDestroySurface(m_display, m_surface); m_surface = EGL_NO_SURFACE;
m_surface = EGL_NO_SURFACE; }
}
} }
bool ContextEGL::CreateContext(const Version& version, EGLContext share_context) bool ContextEGL::CreateContext(const Version& version, EGLContext share_context) {
{ Log_DevPrintf("Trying version %u.%u (%s)", version.major_version, version.minor_version,
Log_DevPrintf( version.profile == Context::Profile::ES
"Trying version %u.%u (%s)", version.major_version, version.minor_version, ? "ES"
version.profile == Context::Profile::ES ? "ES" : (version.profile == Context::Profile::Core ? "Core" : "None")); : (version.profile == Context::Profile::Core ? "Core" : "None"));
int surface_attribs[16] = { int surface_attribs[16] = {
EGL_RENDERABLE_TYPE, EGL_RENDERABLE_TYPE,
(version.profile == Profile::ES) ? (version.profile == Profile::ES)
((version.major_version >= 3) ? EGL_OPENGL_ES3_BIT : ? ((version.major_version >= 3)
((version.major_version == 2) ? EGL_OPENGL_ES2_BIT : EGL_OPENGL_ES_BIT)) : ? EGL_OPENGL_ES3_BIT
EGL_OPENGL_BIT, : ((version.major_version == 2) ? EGL_OPENGL_ES2_BIT : EGL_OPENGL_ES_BIT))
EGL_SURFACE_TYPE, : EGL_OPENGL_BIT,
(m_wi.type != WindowInfo::Type::Surfaceless) ? EGL_WINDOW_BIT : 0, EGL_SURFACE_TYPE,
}; (m_wi.type != WindowInfo::Type::Surfaceless) ? EGL_WINDOW_BIT : 0,
int nsurface_attribs = 4; };
int nsurface_attribs = 4;
switch (m_wi.surface_format) switch (m_wi.surface_format) {
{
case WindowInfo::SurfaceFormat::RGB8: case WindowInfo::SurfaceFormat::RGB8:
surface_attribs[nsurface_attribs++] = EGL_RED_SIZE; surface_attribs[nsurface_attribs++] = EGL_RED_SIZE;
surface_attribs[nsurface_attribs++] = 8; surface_attribs[nsurface_attribs++] = 8;
surface_attribs[nsurface_attribs++] = EGL_GREEN_SIZE; surface_attribs[nsurface_attribs++] = EGL_GREEN_SIZE;
surface_attribs[nsurface_attribs++] = 8; surface_attribs[nsurface_attribs++] = 8;
surface_attribs[nsurface_attribs++] = EGL_BLUE_SIZE; surface_attribs[nsurface_attribs++] = EGL_BLUE_SIZE;
surface_attribs[nsurface_attribs++] = 8; surface_attribs[nsurface_attribs++] = 8;
break; break;
case WindowInfo::SurfaceFormat::RGBA8: case WindowInfo::SurfaceFormat::RGBA8:
surface_attribs[nsurface_attribs++] = EGL_RED_SIZE; surface_attribs[nsurface_attribs++] = EGL_RED_SIZE;
surface_attribs[nsurface_attribs++] = 8; surface_attribs[nsurface_attribs++] = 8;
surface_attribs[nsurface_attribs++] = EGL_GREEN_SIZE; surface_attribs[nsurface_attribs++] = EGL_GREEN_SIZE;
surface_attribs[nsurface_attribs++] = 8; surface_attribs[nsurface_attribs++] = 8;
surface_attribs[nsurface_attribs++] = EGL_BLUE_SIZE; surface_attribs[nsurface_attribs++] = EGL_BLUE_SIZE;
surface_attribs[nsurface_attribs++] = 8; surface_attribs[nsurface_attribs++] = 8;
surface_attribs[nsurface_attribs++] = EGL_ALPHA_SIZE; surface_attribs[nsurface_attribs++] = EGL_ALPHA_SIZE;
surface_attribs[nsurface_attribs++] = 8; surface_attribs[nsurface_attribs++] = 8;
break; break;
case WindowInfo::SurfaceFormat::RGB565: case WindowInfo::SurfaceFormat::RGB565:
surface_attribs[nsurface_attribs++] = EGL_RED_SIZE; surface_attribs[nsurface_attribs++] = EGL_RED_SIZE;
surface_attribs[nsurface_attribs++] = 5; surface_attribs[nsurface_attribs++] = 5;
surface_attribs[nsurface_attribs++] = EGL_GREEN_SIZE; surface_attribs[nsurface_attribs++] = EGL_GREEN_SIZE;
surface_attribs[nsurface_attribs++] = 6; surface_attribs[nsurface_attribs++] = 6;
surface_attribs[nsurface_attribs++] = EGL_BLUE_SIZE; surface_attribs[nsurface_attribs++] = EGL_BLUE_SIZE;
surface_attribs[nsurface_attribs++] = 5; surface_attribs[nsurface_attribs++] = 5;
break; break;
case WindowInfo::SurfaceFormat::Auto: case WindowInfo::SurfaceFormat::Auto:
break; break;
default: default:
UnreachableCode(); UnreachableCode();
break; break;
}
surface_attribs[nsurface_attribs++] = EGL_NONE;
surface_attribs[nsurface_attribs++] = 0;
EGLint num_configs;
if (!eglChooseConfig(m_display, surface_attribs, nullptr, 0, &num_configs) || num_configs == 0)
{
Log_ErrorPrintf("eglChooseConfig() failed: %d", eglGetError());
return false;
}
std::vector<EGLConfig> configs(static_cast<u32>(num_configs));
if (!eglChooseConfig(m_display, surface_attribs, configs.data(), num_configs, &num_configs))
{
Log_ErrorPrintf("eglChooseConfig() failed: %d", eglGetError());
return false;
}
configs.resize(static_cast<u32>(num_configs));
std::optional<EGLConfig> config;
for (EGLConfig check_config : configs)
{
if (CheckConfigSurfaceFormat(check_config, m_wi.surface_format))
{
config = check_config;
break;
} }
}
if (!config.has_value()) surface_attribs[nsurface_attribs++] = EGL_NONE;
{ surface_attribs[nsurface_attribs++] = 0;
Log_WarningPrintf("No EGL configs matched exactly, using first.");
config = configs.front();
}
int attribs[8]; EGLint num_configs;
int nattribs = 0; if (!eglChooseConfig(m_display, surface_attribs, nullptr, 0, &num_configs) ||
if (version.profile != Profile::NoProfile) num_configs == 0) {
{ Log_ErrorPrintf("eglChooseConfig() failed: %d", eglGetError());
attribs[nattribs++] = EGL_CONTEXT_MAJOR_VERSION; return false;
attribs[nattribs++] = version.major_version; }
attribs[nattribs++] = EGL_CONTEXT_MINOR_VERSION;
attribs[nattribs++] = version.minor_version;
}
attribs[nattribs++] = EGL_NONE;
attribs[nattribs++] = 0;
if (!eglBindAPI((version.profile == Profile::ES) ? EGL_OPENGL_ES_API : EGL_OPENGL_API)) std::vector<EGLConfig> configs(static_cast<u32>(num_configs));
{ if (!eglChooseConfig(m_display, surface_attribs, configs.data(), num_configs, &num_configs)) {
Log_ErrorPrintf("eglBindAPI(%s) failed", (version.profile == Profile::ES) ? "EGL_OPENGL_ES_API" : "EGL_OPENGL_API"); Log_ErrorPrintf("eglChooseConfig() failed: %d", eglGetError());
return false; return false;
} }
configs.resize(static_cast<u32>(num_configs));
m_context = eglCreateContext(m_display, config.value(), share_context, attribs); std::optional<EGLConfig> config;
if (!m_context) for (EGLConfig check_config : configs) {
{ if (CheckConfigSurfaceFormat(check_config, m_wi.surface_format)) {
Log_ErrorPrintf("eglCreateContext() failed: %d", eglGetError()); config = check_config;
return false; break;
} }
}
Log_InfoPrintf( if (!config.has_value()) {
"Got version %u.%u (%s)", version.major_version, version.minor_version, Log_WarningPrintf("No EGL configs matched exactly, using first.");
version.profile == Context::Profile::ES ? "ES" : (version.profile == Context::Profile::Core ? "Core" : "None")); config = configs.front();
}
m_config = config.value(); int attribs[8];
m_version = version; int nattribs = 0;
return true; if (version.profile != Profile::NoProfile) {
attribs[nattribs++] = EGL_CONTEXT_MAJOR_VERSION;
attribs[nattribs++] = version.major_version;
attribs[nattribs++] = EGL_CONTEXT_MINOR_VERSION;
attribs[nattribs++] = version.minor_version;
}
attribs[nattribs++] = EGL_NONE;
attribs[nattribs++] = 0;
if (!eglBindAPI((version.profile == Profile::ES) ? EGL_OPENGL_ES_API : EGL_OPENGL_API)) {
Log_ErrorPrintf("eglBindAPI(%s) failed",
(version.profile == Profile::ES) ? "EGL_OPENGL_ES_API" : "EGL_OPENGL_API");
return false;
}
m_context = eglCreateContext(m_display, config.value(), share_context, attribs);
if (!m_context) {
Log_ErrorPrintf("eglCreateContext() failed: %d", eglGetError());
return false;
}
Log_InfoPrintf("Got version %u.%u (%s)", version.major_version, version.minor_version,
version.profile == Context::Profile::ES
? "ES"
: (version.profile == Context::Profile::Core ? "Core" : "None"));
m_config = config.value();
m_version = version;
return true;
} }
bool ContextEGL::CreateContextAndSurface(const Version& version, EGLContext share_context, bool make_current) bool ContextEGL::CreateContextAndSurface(const Version& version, EGLContext share_context,
{ bool make_current) {
if (!CreateContext(version, share_context)) if (!CreateContext(version, share_context))
return false; return false;
if (!CreateSurface()) if (!CreateSurface()) {
{ Log_ErrorPrintf("Failed to create surface for context");
Log_ErrorPrintf("Failed to create surface for context"); eglDestroyContext(m_display, m_context);
eglDestroyContext(m_display, m_context); m_context = EGL_NO_CONTEXT;
m_context = EGL_NO_CONTEXT; return false;
return false;
}
if (make_current && !eglMakeCurrent(m_display, m_surface, m_surface, m_context))
{
Log_ErrorPrintf("eglMakeCurrent() failed: %d", eglGetError());
if (m_surface != EGL_NO_SURFACE)
{
eglDestroySurface(m_display, m_surface);
m_surface = EGL_NO_SURFACE;
} }
eglDestroyContext(m_display, m_context);
m_context = EGL_NO_CONTEXT;
return false;
}
return true; if (make_current && !eglMakeCurrent(m_display, m_surface, m_surface, m_context)) {
Log_ErrorPrintf("eglMakeCurrent() failed: %d", eglGetError());
if (m_surface != EGL_NO_SURFACE) {
eglDestroySurface(m_display, m_surface);
m_surface = EGL_NO_SURFACE;
}
eglDestroyContext(m_display, m_context);
m_context = EGL_NO_CONTEXT;
return false;
}
return true;
} }
} // namespace GL } // namespace GL

View file

@ -1,48 +1,48 @@
#pragma once #pragma once
#include "context.h"
#include "../../../../../externals/glad/src/glad_egl.h" #include "../../../../../externals/glad/src/glad_egl.h"
#include "context.h"
namespace GL { namespace GL {
class ContextEGL : public Context class ContextEGL : public Context {
{
public: public:
ContextEGL(const WindowInfo& wi); ContextEGL(const WindowInfo& wi);
~ContextEGL() override; ~ContextEGL() override;
static std::unique_ptr<Context> Create(const WindowInfo& wi, const Version* versions_to_try, static std::unique_ptr<Context> Create(const WindowInfo& wi, const Version* versions_to_try,
size_t num_versions_to_try); size_t num_versions_to_try);
void* GetProcAddress(const char* name) override; void* GetProcAddress(const char* name) override;
virtual bool ChangeSurface(const WindowInfo& new_wi) override; virtual bool ChangeSurface(const WindowInfo& new_wi) override;
virtual void ResizeSurface(u32 new_surface_width = 0, u32 new_surface_height = 0) override; virtual void ResizeSurface(u32 new_surface_width = 0, u32 new_surface_height = 0) override;
bool SwapBuffers() override; bool SwapBuffers() override;
bool MakeCurrent() override; bool MakeCurrent() override;
bool DoneCurrent() override; bool DoneCurrent() override;
bool SetSwapInterval(s32 interval) override; bool SetSwapInterval(s32 interval) override;
virtual std::unique_ptr<Context> CreateSharedContext(const WindowInfo& wi) override; virtual std::unique_ptr<Context> CreateSharedContext(const WindowInfo& wi) override;
protected: protected:
virtual bool SetDisplay(); virtual bool SetDisplay();
virtual EGLNativeWindowType GetNativeWindow(EGLConfig config); virtual EGLNativeWindowType GetNativeWindow(EGLConfig config);
bool Initialize(const Version* versions_to_try, size_t num_versions_to_try); bool Initialize(const Version* versions_to_try, size_t num_versions_to_try);
bool CreateDisplay(); bool CreateDisplay();
bool CreateContext(const Version& version, EGLContext share_context); bool CreateContext(const Version& version, EGLContext share_context);
bool CreateContextAndSurface(const Version& version, EGLContext share_context, bool make_current); bool CreateContextAndSurface(const Version& version, EGLContext share_context,
bool CreateSurface(); bool make_current);
bool CreatePBufferSurface(); bool CreateSurface();
bool CheckConfigSurfaceFormat(EGLConfig config, WindowInfo::SurfaceFormat format) const; bool CreatePBufferSurface();
void DestroyContext(); bool CheckConfigSurfaceFormat(EGLConfig config, WindowInfo::SurfaceFormat format) const;
void DestroySurface(); void DestroyContext();
void DestroySurface();
EGLDisplay m_display = EGL_NO_DISPLAY; EGLDisplay m_display = EGL_NO_DISPLAY;
EGLSurface m_surface = EGL_NO_SURFACE; EGLSurface m_surface = EGL_NO_SURFACE;
EGLContext m_context = EGL_NO_CONTEXT; EGLContext m_context = EGL_NO_CONTEXT;
EGLConfig m_config = {}; EGLConfig m_config = {};
bool m_supports_surfaceless = false; bool m_supports_surfaceless = false;
}; };
} // namespace GL } // namespace GL

View file

@ -1,86 +1,78 @@
#include "context_egl_wayland.h"
#include "../log.h"
#include <dlfcn.h> #include <dlfcn.h>
#include "../log.h"
#include "context_egl_wayland.h"
Log_SetChannel(ContextEGLWayland); Log_SetChannel(ContextEGLWayland);
namespace GL { namespace GL {
static const char* WAYLAND_EGL_MODNAME = "libwayland-egl.so.1"; static const char* WAYLAND_EGL_MODNAME = "libwayland-egl.so.1";
ContextEGLWayland::ContextEGLWayland(const WindowInfo& wi) : ContextEGL(wi) {} ContextEGLWayland::ContextEGLWayland(const WindowInfo& wi) : ContextEGL(wi) {}
ContextEGLWayland::~ContextEGLWayland() ContextEGLWayland::~ContextEGLWayland() {
{ if (m_wl_window)
if (m_wl_window) m_wl_egl_window_destroy(m_wl_window);
m_wl_egl_window_destroy(m_wl_window); if (m_wl_module)
if (m_wl_module) dlclose(m_wl_module);
dlclose(m_wl_module);
} }
std::unique_ptr<Context> ContextEGLWayland::Create(const WindowInfo& wi, const Version* versions_to_try, std::unique_ptr<Context> ContextEGLWayland::Create(const WindowInfo& wi,
size_t num_versions_to_try) const Version* versions_to_try,
{ size_t num_versions_to_try) {
std::unique_ptr<ContextEGLWayland> context = std::make_unique<ContextEGLWayland>(wi); std::unique_ptr<ContextEGLWayland> context = std::make_unique<ContextEGLWayland>(wi);
if (!context->LoadModule() || !context->Initialize(versions_to_try, num_versions_to_try)) if (!context->LoadModule() || !context->Initialize(versions_to_try, num_versions_to_try))
return nullptr; return nullptr;
return context; return context;
} }
std::unique_ptr<Context> ContextEGLWayland::CreateSharedContext(const WindowInfo& wi) std::unique_ptr<Context> ContextEGLWayland::CreateSharedContext(const WindowInfo& wi) {
{ std::unique_ptr<ContextEGLWayland> context = std::make_unique<ContextEGLWayland>(wi);
std::unique_ptr<ContextEGLWayland> context = std::make_unique<ContextEGLWayland>(wi); context->m_display = m_display;
context->m_display = m_display;
if (!context->LoadModule() || !context->CreateContextAndSurface(m_version, m_context, false)) if (!context->LoadModule() || !context->CreateContextAndSurface(m_version, m_context, false))
return nullptr; return nullptr;
return context; return context;
} }
void ContextEGLWayland::ResizeSurface(u32 new_surface_width, u32 new_surface_height) void ContextEGLWayland::ResizeSurface(u32 new_surface_width, u32 new_surface_height) {
{ if (m_wl_window)
if (m_wl_window) m_wl_egl_window_resize(m_wl_window, new_surface_width, new_surface_height, 0, 0);
m_wl_egl_window_resize(m_wl_window, new_surface_width, new_surface_height, 0, 0);
ContextEGL::ResizeSurface(new_surface_width, new_surface_height); ContextEGL::ResizeSurface(new_surface_width, new_surface_height);
} }
EGLNativeWindowType ContextEGLWayland::GetNativeWindow(EGLConfig config) EGLNativeWindowType ContextEGLWayland::GetNativeWindow(EGLConfig config) {
{ if (m_wl_window) {
if (m_wl_window) m_wl_egl_window_destroy(m_wl_window);
{ m_wl_window = nullptr;
m_wl_egl_window_destroy(m_wl_window); }
m_wl_window = nullptr;
}
m_wl_window = m_wl_window = m_wl_egl_window_create(static_cast<wl_surface*>(m_wi.window_handle),
m_wl_egl_window_create(static_cast<wl_surface*>(m_wi.window_handle), m_wi.surface_width, m_wi.surface_height); m_wi.surface_width, m_wi.surface_height);
if (!m_wl_window) if (!m_wl_window)
return {}; return {};
return reinterpret_cast<EGLNativeWindowType>(m_wl_window); return reinterpret_cast<EGLNativeWindowType>(m_wl_window);
} }
bool ContextEGLWayland::LoadModule() bool ContextEGLWayland::LoadModule() {
{ m_wl_module = dlopen(WAYLAND_EGL_MODNAME, RTLD_NOW | RTLD_GLOBAL);
m_wl_module = dlopen(WAYLAND_EGL_MODNAME, RTLD_NOW | RTLD_GLOBAL); if (!m_wl_module) {
if (!m_wl_module) Log_ErrorPrintf("Failed to load %s.", WAYLAND_EGL_MODNAME);
{ return false;
Log_ErrorPrintf("Failed to load %s.", WAYLAND_EGL_MODNAME); }
return false;
}
m_wl_egl_window_create = m_wl_egl_window_create = reinterpret_cast<decltype(m_wl_egl_window_create)>(
reinterpret_cast<decltype(m_wl_egl_window_create)>(dlsym(m_wl_module, "wl_egl_window_create")); dlsym(m_wl_module, "wl_egl_window_create"));
m_wl_egl_window_destroy = m_wl_egl_window_destroy = reinterpret_cast<decltype(m_wl_egl_window_destroy)>(
reinterpret_cast<decltype(m_wl_egl_window_destroy)>(dlsym(m_wl_module, "wl_egl_window_destroy")); dlsym(m_wl_module, "wl_egl_window_destroy"));
m_wl_egl_window_resize = m_wl_egl_window_resize = reinterpret_cast<decltype(m_wl_egl_window_resize)>(
reinterpret_cast<decltype(m_wl_egl_window_resize)>(dlsym(m_wl_module, "wl_egl_window_resize")); dlsym(m_wl_module, "wl_egl_window_resize"));
if (!m_wl_egl_window_create || !m_wl_egl_window_destroy || !m_wl_egl_window_resize) if (!m_wl_egl_window_create || !m_wl_egl_window_destroy || !m_wl_egl_window_resize) {
{ Log_ErrorPrintf("Failed to load one or more functions from %s.", WAYLAND_EGL_MODNAME);
Log_ErrorPrintf("Failed to load one or more functions from %s.", WAYLAND_EGL_MODNAME); return false;
return false; }
}
return true; return true;
} }
} // namespace GL } // namespace GL

View file

@ -1,33 +1,33 @@
#pragma once #pragma once
#include "context_egl.h"
#include <wayland-egl.h> #include <wayland-egl.h>
#include "context_egl.h"
namespace GL { namespace GL {
class ContextEGLWayland final : public ContextEGL class ContextEGLWayland final : public ContextEGL {
{
public: public:
ContextEGLWayland(const WindowInfo& wi); ContextEGLWayland(const WindowInfo& wi);
~ContextEGLWayland() override; ~ContextEGLWayland() override;
static std::unique_ptr<Context> Create(const WindowInfo& wi, const Version* versions_to_try, static std::unique_ptr<Context> Create(const WindowInfo& wi, const Version* versions_to_try,
size_t num_versions_to_try); size_t num_versions_to_try);
std::unique_ptr<Context> CreateSharedContext(const WindowInfo& wi) override; std::unique_ptr<Context> CreateSharedContext(const WindowInfo& wi) override;
void ResizeSurface(u32 new_surface_width = 0, u32 new_surface_height = 0) override; void ResizeSurface(u32 new_surface_width = 0, u32 new_surface_height = 0) override;
protected: protected:
EGLNativeWindowType GetNativeWindow(EGLConfig config) override; EGLNativeWindowType GetNativeWindow(EGLConfig config) override;
private: private:
bool LoadModule(); bool LoadModule();
wl_egl_window* m_wl_window = nullptr; wl_egl_window* m_wl_window = nullptr;
void* m_wl_module = nullptr; void* m_wl_module = nullptr;
wl_egl_window* (*m_wl_egl_window_create)(struct wl_surface* surface, int width, int height); wl_egl_window* (*m_wl_egl_window_create)(struct wl_surface* surface, int width, int height);
void (*m_wl_egl_window_destroy)(struct wl_egl_window* egl_window); void (*m_wl_egl_window_destroy)(struct wl_egl_window* egl_window);
void (*m_wl_egl_window_resize)(struct wl_egl_window* egl_window, int width, int height, int dx, int dy); void (*m_wl_egl_window_resize)(struct wl_egl_window* egl_window, int width, int height, int dx,
int dy);
}; };
} // namespace GL } // namespace GL

View file

@ -1,5 +1,5 @@
#include "context_egl_x11.h"
#include "../log.h" #include "../log.h"
#include "context_egl_x11.h"
Log_SetChannel(GL::ContextEGLX11); Log_SetChannel(GL::ContextEGLX11);
namespace GL { namespace GL {
@ -7,63 +7,59 @@ ContextEGLX11::ContextEGLX11(const WindowInfo& wi) : ContextEGL(wi) {}
ContextEGLX11::~ContextEGLX11() = default; ContextEGLX11::~ContextEGLX11() = default;
std::unique_ptr<Context> ContextEGLX11::Create(const WindowInfo& wi, const Version* versions_to_try, std::unique_ptr<Context> ContextEGLX11::Create(const WindowInfo& wi, const Version* versions_to_try,
size_t num_versions_to_try) size_t num_versions_to_try) {
{ std::unique_ptr<ContextEGLX11> context = std::make_unique<ContextEGLX11>(wi);
std::unique_ptr<ContextEGLX11> context = std::make_unique<ContextEGLX11>(wi); if (!context->Initialize(versions_to_try, num_versions_to_try))
if (!context->Initialize(versions_to_try, num_versions_to_try)) return nullptr;
return nullptr;
return context; return context;
} }
std::unique_ptr<Context> ContextEGLX11::CreateSharedContext(const WindowInfo& wi) std::unique_ptr<Context> ContextEGLX11::CreateSharedContext(const WindowInfo& wi) {
{ std::unique_ptr<ContextEGLX11> context = std::make_unique<ContextEGLX11>(wi);
std::unique_ptr<ContextEGLX11> context = std::make_unique<ContextEGLX11>(wi); context->m_display = m_display;
context->m_display = m_display;
if (!context->CreateContextAndSurface(m_version, m_context, false)) if (!context->CreateContextAndSurface(m_version, m_context, false))
return nullptr; return nullptr;
return context; return context;
} }
void ContextEGLX11::ResizeSurface(u32 new_surface_width, u32 new_surface_height) void ContextEGLX11::ResizeSurface(u32 new_surface_width, u32 new_surface_height) {
{ m_window.Resize();
m_window.Resize(); ContextEGL::ResizeSurface(new_surface_width, new_surface_height);
ContextEGL::ResizeSurface(new_surface_width, new_surface_height);
} }
EGLNativeWindowType ContextEGLX11::GetNativeWindow(EGLConfig config) EGLNativeWindowType ContextEGLX11::GetNativeWindow(EGLConfig config) {
{ X11InhibitErrors ei;
X11InhibitErrors ei;
EGLint native_visual_id = 0; EGLint native_visual_id = 0;
if (!eglGetConfigAttrib(m_display, m_config, EGL_NATIVE_VISUAL_ID, &native_visual_id)) if (!eglGetConfigAttrib(m_display, m_config, EGL_NATIVE_VISUAL_ID, &native_visual_id)) {
{ Log_ErrorPrintf("Failed to get X11 visual ID");
Log_ErrorPrintf("Failed to get X11 visual ID"); return false;
return false; }
}
XVisualInfo vi_query = {}; XVisualInfo vi_query = {};
vi_query.visualid = native_visual_id; vi_query.visualid = native_visual_id;
int num_vis; int num_vis;
XVisualInfo* vi = XGetVisualInfo(static_cast<Display*>(m_wi.display_connection), VisualIDMask, &vi_query, &num_vis); XVisualInfo* vi = XGetVisualInfo(static_cast<Display*>(m_wi.display_connection), VisualIDMask,
if (num_vis <= 0 || !vi) &vi_query, &num_vis);
{ if (num_vis <= 0 || !vi) {
Log_ErrorPrintf("Failed to query visual from X11"); Log_ErrorPrintf("Failed to query visual from X11");
return false; return false;
} }
m_window.Destroy();
if (!m_window.Create(GetDisplay(),
static_cast<Window>(reinterpret_cast<uintptr_t>(m_wi.window_handle)),
vi)) {
Log_ErrorPrintf("Faild to create X11 child window");
XFree(vi);
return false;
}
m_window.Destroy();
if (!m_window.Create(GetDisplay(), static_cast<Window>(reinterpret_cast<uintptr_t>(m_wi.window_handle)), vi))
{
Log_ErrorPrintf("Faild to create X11 child window");
XFree(vi); XFree(vi);
return false; return static_cast<EGLNativeWindowType>(m_window.GetWindow());
}
XFree(vi);
return static_cast<EGLNativeWindowType>(m_window.GetWindow());
} }
} // namespace GL } // namespace GL

View file

@ -4,25 +4,26 @@
namespace GL { namespace GL {
class ContextEGLX11 final : public ContextEGL class ContextEGLX11 final : public ContextEGL {
{
public: public:
ContextEGLX11(const WindowInfo& wi); ContextEGLX11(const WindowInfo& wi);
~ContextEGLX11() override; ~ContextEGLX11() override;
static std::unique_ptr<Context> Create(const WindowInfo& wi, const Version* versions_to_try, static std::unique_ptr<Context> Create(const WindowInfo& wi, const Version* versions_to_try,
size_t num_versions_to_try); size_t num_versions_to_try);
std::unique_ptr<Context> CreateSharedContext(const WindowInfo& wi) override; std::unique_ptr<Context> CreateSharedContext(const WindowInfo& wi) override;
void ResizeSurface(u32 new_surface_width = 0, u32 new_surface_height = 0) override; void ResizeSurface(u32 new_surface_width = 0, u32 new_surface_height = 0) override;
protected: protected:
EGLNativeWindowType GetNativeWindow(EGLConfig config) override; EGLNativeWindowType GetNativeWindow(EGLConfig config) override;
private: private:
ALWAYS_INLINE Display* GetDisplay() const { return static_cast<Display*>(m_wi.display_connection); } ALWAYS_INLINE Display* GetDisplay() const {
return static_cast<Display*>(m_wi.display_connection);
}
X11Window m_window; X11Window m_window;
}; };
} // namespace GL } // namespace GL

View file

@ -1,328 +1,281 @@
#include "context_glx.h" #include <dlfcn.h>
#include "../duckstation_compat.h" #include "../duckstation_compat.h"
#include "../log.h" #include "../log.h"
#include <dlfcn.h> #include "context_glx.h"
Log_SetChannel(GL::ContextGLX); Log_SetChannel(GL::ContextGLX);
namespace GL { namespace GL {
ContextGLX::ContextGLX(const WindowInfo& wi) : Context(wi) {} ContextGLX::ContextGLX(const WindowInfo& wi) : Context(wi) {}
ContextGLX::~ContextGLX() ContextGLX::~ContextGLX() {
{ if (glXGetCurrentContext() == m_context)
if (glXGetCurrentContext() == m_context) glXMakeCurrent(GetDisplay(), None, nullptr);
glXMakeCurrent(GetDisplay(), None, nullptr);
if (m_context) if (m_context)
glXDestroyContext(GetDisplay(), m_context); glXDestroyContext(GetDisplay(), m_context);
if (m_vi) if (m_vi)
XFree(m_vi); XFree(m_vi);
if (m_libGL_handle) if (m_libGL_handle)
dlclose(m_libGL_handle); dlclose(m_libGL_handle);
} }
std::unique_ptr<Context> ContextGLX::Create(const WindowInfo& wi, const Version* versions_to_try, std::unique_ptr<Context> ContextGLX::Create(const WindowInfo& wi, const Version* versions_to_try,
size_t num_versions_to_try) size_t num_versions_to_try) {
{ std::unique_ptr<ContextGLX> context = std::make_unique<ContextGLX>(wi);
std::unique_ptr<ContextGLX> context = std::make_unique<ContextGLX>(wi); if (!context->Initialize(versions_to_try, num_versions_to_try))
if (!context->Initialize(versions_to_try, num_versions_to_try)) return nullptr;
return nullptr;
return context; return context;
} }
bool ContextGLX::Initialize(const Version* versions_to_try, size_t num_versions_to_try) bool ContextGLX::Initialize(const Version* versions_to_try, size_t num_versions_to_try) {
{ // We need libGL loaded, because GLAD loads its own, then releases it.
// We need libGL loaded, because GLAD loads its own, then releases it. m_libGL_handle = dlopen("libGL.so.1", RTLD_NOW | RTLD_GLOBAL);
m_libGL_handle = dlopen("libGL.so.1", RTLD_NOW | RTLD_GLOBAL); if (!m_libGL_handle) {
if (!m_libGL_handle) m_libGL_handle = dlopen("libGL.so", RTLD_NOW | RTLD_GLOBAL);
{ if (!m_libGL_handle) {
m_libGL_handle = dlopen("libGL.so", RTLD_NOW | RTLD_GLOBAL); Log_ErrorPrintf("Failed to load libGL.so: %s", dlerror());
if (!m_libGL_handle) return false;
{ }
Log_ErrorPrintf("Failed to load libGL.so: %s", dlerror());
return false;
} }
}
const int screen = DefaultScreen(GetDisplay());
if (!gladLoadGLX(GetDisplay(), screen))
{
Log_ErrorPrintf("Loading GLAD GLX functions failed");
return false;
}
if (m_wi.type == WindowInfo::Type::X11)
{
if (!CreateWindow(screen))
return false;
}
else
{
Panic("Create pbuffer");
}
for (size_t i = 0; i < num_versions_to_try; i++)
{
const Version& cv = versions_to_try[i];
if (cv.profile == Profile::NoProfile && CreateAnyContext(nullptr, true))
{
m_version = cv;
return true;
}
else if (cv.profile != Profile::NoProfile && CreateVersionContext(cv, nullptr, true))
{
m_version = cv;
return true;
}
}
return false;
}
void* ContextGLX::GetProcAddress(const char* name)
{
return reinterpret_cast<void*>(glXGetProcAddress(reinterpret_cast<const GLubyte*>(name)));
}
bool ContextGLX::ChangeSurface(const WindowInfo& new_wi)
{
const bool was_current = (glXGetCurrentContext() == m_context);
if (was_current)
glXMakeCurrent(GetDisplay(), None, nullptr);
m_window.Destroy();
m_wi = new_wi;
if (new_wi.type == WindowInfo::Type::X11)
{
const int screen = DefaultScreen(GetDisplay()); const int screen = DefaultScreen(GetDisplay());
if (!CreateWindow(screen)) if (!gladLoadGLX(GetDisplay(), screen)) {
return false; Log_ErrorPrintf("Loading GLAD GLX functions failed");
} return false;
}
if (m_wi.type == WindowInfo::Type::X11) {
if (!CreateWindow(screen))
return false;
} else {
Panic("Create pbuffer");
}
for (size_t i = 0; i < num_versions_to_try; i++) {
const Version& cv = versions_to_try[i];
if (cv.profile == Profile::NoProfile && CreateAnyContext(nullptr, true)) {
m_version = cv;
return true;
} else if (cv.profile != Profile::NoProfile && CreateVersionContext(cv, nullptr, true)) {
m_version = cv;
return true;
}
}
if (was_current && !glXMakeCurrent(GetDisplay(), GetDrawable(), m_context))
{
Log_ErrorPrintf("Failed to make context current again after surface change");
return false; return false;
}
return true;
} }
void ContextGLX::ResizeSurface(u32 new_surface_width /*= 0*/, u32 new_surface_height /*= 0*/) void* ContextGLX::GetProcAddress(const char* name) {
{ return reinterpret_cast<void*>(glXGetProcAddress(reinterpret_cast<const GLubyte*>(name)));
m_window.Resize(new_surface_width, new_surface_height);
m_wi.surface_width = m_window.GetWidth();
m_wi.surface_height = m_window.GetHeight();
} }
bool ContextGLX::SwapBuffers() bool ContextGLX::ChangeSurface(const WindowInfo& new_wi) {
{ const bool was_current = (glXGetCurrentContext() == m_context);
glXSwapBuffers(GetDisplay(), GetDrawable()); if (was_current)
return true; glXMakeCurrent(GetDisplay(), None, nullptr);
}
bool ContextGLX::MakeCurrent() m_window.Destroy();
{ m_wi = new_wi;
return (glXMakeCurrent(GetDisplay(), GetDrawable(), m_context) == True);
}
bool ContextGLX::DoneCurrent() if (new_wi.type == WindowInfo::Type::X11) {
{ const int screen = DefaultScreen(GetDisplay());
return (glXMakeCurrent(GetDisplay(), None, nullptr) == True); if (!CreateWindow(screen))
} return false;
}
if (was_current && !glXMakeCurrent(GetDisplay(), GetDrawable(), m_context)) {
Log_ErrorPrintf("Failed to make context current again after surface change");
return false;
}
bool ContextGLX::SetSwapInterval(s32 interval)
{
if (GLAD_GLX_EXT_swap_control)
{
glXSwapIntervalEXT(GetDisplay(), GetDrawable(), interval);
return true; return true;
}
else if (GLAD_GLX_MESA_swap_control)
{
return (glXSwapIntervalMESA(static_cast<u32>(std::max(interval, 0))) != 0);
}
else if (GLAD_GLX_SGI_swap_control)
{
return (glXSwapIntervalSGI(interval) != 0);
}
else
{
return false;
}
} }
std::unique_ptr<Context> ContextGLX::CreateSharedContext(const WindowInfo& wi) void ContextGLX::ResizeSurface(u32 new_surface_width /*= 0*/, u32 new_surface_height /*= 0*/) {
{ m_window.Resize(new_surface_width, new_surface_height);
std::unique_ptr<ContextGLX> context = std::make_unique<ContextGLX>(wi); m_wi.surface_width = m_window.GetWidth();
if (wi.type == WindowInfo::Type::X11) m_wi.surface_height = m_window.GetHeight();
{
const int screen = DefaultScreen(context->GetDisplay());
if (!context->CreateWindow(screen))
return nullptr;
}
else
{
Panic("Create pbuffer");
}
if (m_version.profile == Profile::NoProfile)
{
if (!context->CreateAnyContext(m_context, false))
return nullptr;
}
else
{
if (!context->CreateVersionContext(m_version, m_context, false))
return nullptr;
}
context->m_version = m_version;
return context;
} }
bool ContextGLX::CreateWindow(int screen) bool ContextGLX::SwapBuffers() {
{ glXSwapBuffers(GetDisplay(), GetDrawable());
int attribs[32] = {GLX_X_RENDERABLE, True, GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, return true;
GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR, GLX_DOUBLEBUFFER, True}; }
int nattribs = 8;
switch (m_wi.surface_format) bool ContextGLX::MakeCurrent() {
{ return (glXMakeCurrent(GetDisplay(), GetDrawable(), m_context) == True);
}
bool ContextGLX::DoneCurrent() {
return (glXMakeCurrent(GetDisplay(), None, nullptr) == True);
}
bool ContextGLX::SetSwapInterval(s32 interval) {
if (GLAD_GLX_EXT_swap_control) {
glXSwapIntervalEXT(GetDisplay(), GetDrawable(), interval);
return true;
} else if (GLAD_GLX_MESA_swap_control) {
return (glXSwapIntervalMESA(static_cast<u32>(std::max(interval, 0))) != 0);
} else if (GLAD_GLX_SGI_swap_control) {
return (glXSwapIntervalSGI(interval) != 0);
} else {
return false;
}
}
std::unique_ptr<Context> ContextGLX::CreateSharedContext(const WindowInfo& wi) {
std::unique_ptr<ContextGLX> context = std::make_unique<ContextGLX>(wi);
if (wi.type == WindowInfo::Type::X11) {
const int screen = DefaultScreen(context->GetDisplay());
if (!context->CreateWindow(screen))
return nullptr;
} else {
Panic("Create pbuffer");
}
if (m_version.profile == Profile::NoProfile) {
if (!context->CreateAnyContext(m_context, false))
return nullptr;
} else {
if (!context->CreateVersionContext(m_version, m_context, false))
return nullptr;
}
context->m_version = m_version;
return context;
}
bool ContextGLX::CreateWindow(int screen) {
int attribs[32] = {GLX_X_RENDERABLE, True, GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR, GLX_DOUBLEBUFFER, True};
int nattribs = 8;
switch (m_wi.surface_format) {
case WindowInfo::SurfaceFormat::RGB8: case WindowInfo::SurfaceFormat::RGB8:
attribs[nattribs++] = GLX_RED_SIZE; attribs[nattribs++] = GLX_RED_SIZE;
attribs[nattribs++] = 8; attribs[nattribs++] = 8;
attribs[nattribs++] = GLX_GREEN_SIZE; attribs[nattribs++] = GLX_GREEN_SIZE;
attribs[nattribs++] = 8; attribs[nattribs++] = 8;
attribs[nattribs++] = GLX_BLUE_SIZE; attribs[nattribs++] = GLX_BLUE_SIZE;
attribs[nattribs++] = 8; attribs[nattribs++] = 8;
break; break;
case WindowInfo::SurfaceFormat::RGBA8: case WindowInfo::SurfaceFormat::RGBA8:
attribs[nattribs++] = GLX_RED_SIZE; attribs[nattribs++] = GLX_RED_SIZE;
attribs[nattribs++] = 8; attribs[nattribs++] = 8;
attribs[nattribs++] = GLX_GREEN_SIZE; attribs[nattribs++] = GLX_GREEN_SIZE;
attribs[nattribs++] = 8; attribs[nattribs++] = 8;
attribs[nattribs++] = GLX_BLUE_SIZE; attribs[nattribs++] = GLX_BLUE_SIZE;
attribs[nattribs++] = 8; attribs[nattribs++] = 8;
attribs[nattribs++] = GLX_ALPHA_SIZE; attribs[nattribs++] = GLX_ALPHA_SIZE;
attribs[nattribs++] = 8; attribs[nattribs++] = 8;
break; break;
case WindowInfo::SurfaceFormat::RGB565: case WindowInfo::SurfaceFormat::RGB565:
attribs[nattribs++] = GLX_RED_SIZE; attribs[nattribs++] = GLX_RED_SIZE;
attribs[nattribs++] = 5; attribs[nattribs++] = 5;
attribs[nattribs++] = GLX_GREEN_SIZE; attribs[nattribs++] = GLX_GREEN_SIZE;
attribs[nattribs++] = 6; attribs[nattribs++] = 6;
attribs[nattribs++] = GLX_BLUE_SIZE; attribs[nattribs++] = GLX_BLUE_SIZE;
attribs[nattribs++] = 5; attribs[nattribs++] = 5;
break; break;
case WindowInfo::SurfaceFormat::Auto: case WindowInfo::SurfaceFormat::Auto:
break; break;
default: default:
UnreachableCode(); UnreachableCode();
break; break;
} }
attribs[nattribs++] = None; attribs[nattribs++] = None;
attribs[nattribs++] = 0; attribs[nattribs++] = 0;
int fbcount = 0; int fbcount = 0;
GLXFBConfig* fbc = glXChooseFBConfig(GetDisplay(), screen, attribs, &fbcount); GLXFBConfig* fbc = glXChooseFBConfig(GetDisplay(), screen, attribs, &fbcount);
if (!fbc || !fbcount) if (!fbc || !fbcount) {
{ Log_ErrorPrintf("glXChooseFBConfig() failed");
Log_ErrorPrintf("glXChooseFBConfig() failed"); return false;
return false; }
} m_fb_config = *fbc;
m_fb_config = *fbc; XFree(fbc);
XFree(fbc);
if (!GLAD_GLX_VERSION_1_3) if (!GLAD_GLX_VERSION_1_3) {
{ Log_ErrorPrintf("GLX Version 1.3 is required");
Log_ErrorPrintf("GLX Version 1.3 is required"); return false;
return false; }
}
m_vi = glXGetVisualFromFBConfig(GetDisplay(), m_fb_config); m_vi = glXGetVisualFromFBConfig(GetDisplay(), m_fb_config);
if (!m_vi) if (!m_vi) {
{ Log_ErrorPrintf("glXGetVisualFromFBConfig() failed");
Log_ErrorPrintf("glXGetVisualFromFBConfig() failed"); return false;
return false; }
}
return m_window.Create(GetDisplay(), static_cast<Window>(reinterpret_cast<uintptr_t>(m_wi.window_handle)), m_vi); return m_window.Create(
GetDisplay(), static_cast<Window>(reinterpret_cast<uintptr_t>(m_wi.window_handle)), m_vi);
} }
bool ContextGLX::CreateAnyContext(GLXContext share_context, bool make_current) bool ContextGLX::CreateAnyContext(GLXContext share_context, bool make_current) {
{ X11InhibitErrors ie;
X11InhibitErrors ie;
m_context = glXCreateContext(GetDisplay(), m_vi, share_context, True); m_context = glXCreateContext(GetDisplay(), m_vi, share_context, True);
if (!m_context || ie.HadError()) if (!m_context || ie.HadError()) {
{ Log_ErrorPrintf("glxCreateContext() failed");
Log_ErrorPrintf("glxCreateContext() failed"); return false;
return false;
}
if (make_current)
{
if (!glXMakeCurrent(GetDisplay(), GetDrawable(), m_context))
{
Log_ErrorPrintf("glXMakeCurrent() failed");
return false;
} }
}
return true; if (make_current) {
if (!glXMakeCurrent(GetDisplay(), GetDrawable(), m_context)) {
Log_ErrorPrintf("glXMakeCurrent() failed");
return false;
}
}
return true;
} }
bool ContextGLX::CreateVersionContext(const Version& version, GLXContext share_context, bool make_current) bool ContextGLX::CreateVersionContext(const Version& version, GLXContext share_context,
{ bool make_current) {
// we need create context attribs // we need create context attribs
if (!GLAD_GLX_VERSION_1_3) if (!GLAD_GLX_VERSION_1_3) {
{ Log_ErrorPrint("Missing GLX version 1.3.");
Log_ErrorPrint("Missing GLX version 1.3."); return false;
return false;
}
int attribs[32];
int nattribs = 0;
attribs[nattribs++] = GLX_CONTEXT_PROFILE_MASK_ARB;
attribs[nattribs++] =
((version.profile == Profile::ES) ?
((version.major_version >= 2) ? GLX_CONTEXT_ES2_PROFILE_BIT_EXT : GLX_CONTEXT_ES_PROFILE_BIT_EXT) :
GLX_CONTEXT_CORE_PROFILE_BIT_ARB);
attribs[nattribs++] = GLX_CONTEXT_MAJOR_VERSION_ARB;
attribs[nattribs++] = version.major_version;
attribs[nattribs++] = GLX_CONTEXT_MINOR_VERSION_ARB;
attribs[nattribs++] = version.minor_version;
attribs[nattribs++] = None;
attribs[nattribs++] = 0;
X11InhibitErrors ie;
m_context = glXCreateContextAttribsARB(GetDisplay(), m_fb_config, share_context, True, attribs);
XSync(GetDisplay(), False);
if (ie.HadError())
m_context = nullptr;
if (!m_context)
return false;
if (make_current)
{
if (!glXMakeCurrent(GetDisplay(), GetDrawable(), m_context))
{
Log_ErrorPrint("glXMakeCurrent() failed");
glXDestroyContext(GetDisplay(), m_context);
m_context = nullptr;
return false;
} }
}
return true; int attribs[32];
int nattribs = 0;
attribs[nattribs++] = GLX_CONTEXT_PROFILE_MASK_ARB;
attribs[nattribs++] = ((version.profile == Profile::ES)
? ((version.major_version >= 2) ? GLX_CONTEXT_ES2_PROFILE_BIT_EXT
: GLX_CONTEXT_ES_PROFILE_BIT_EXT)
: GLX_CONTEXT_CORE_PROFILE_BIT_ARB);
attribs[nattribs++] = GLX_CONTEXT_MAJOR_VERSION_ARB;
attribs[nattribs++] = version.major_version;
attribs[nattribs++] = GLX_CONTEXT_MINOR_VERSION_ARB;
attribs[nattribs++] = version.minor_version;
attribs[nattribs++] = None;
attribs[nattribs++] = 0;
X11InhibitErrors ie;
m_context = glXCreateContextAttribsARB(GetDisplay(), m_fb_config, share_context, True, attribs);
XSync(GetDisplay(), False);
if (ie.HadError())
m_context = nullptr;
if (!m_context)
return false;
if (make_current) {
if (!glXMakeCurrent(GetDisplay(), GetDrawable(), m_context)) {
Log_ErrorPrint("glXMakeCurrent() failed");
glXDestroyContext(GetDisplay(), m_context);
m_context = nullptr;
return false;
}
}
return true;
} }
} // namespace GL } // namespace GL

View file

@ -1,44 +1,47 @@
#pragma once #pragma once
#include "context.h"
#include "../../../../../externals/glad/src/glad_glx.h" #include "../../../../../externals/glad/src/glad_glx.h"
#include "context.h"
#include "x11_window.h" #include "x11_window.h"
namespace GL { namespace GL {
class ContextGLX final : public Context class ContextGLX final : public Context {
{
public: public:
ContextGLX(const WindowInfo& wi); ContextGLX(const WindowInfo& wi);
~ContextGLX() override; ~ContextGLX() override;
static std::unique_ptr<Context> Create(const WindowInfo& wi, const Version* versions_to_try, static std::unique_ptr<Context> Create(const WindowInfo& wi, const Version* versions_to_try,
size_t num_versions_to_try); size_t num_versions_to_try);
void* GetProcAddress(const char* name) override; void* GetProcAddress(const char* name) override;
bool ChangeSurface(const WindowInfo& new_wi) override; bool ChangeSurface(const WindowInfo& new_wi) override;
void ResizeSurface(u32 new_surface_width = 0, u32 new_surface_height = 0) override; void ResizeSurface(u32 new_surface_width = 0, u32 new_surface_height = 0) override;
bool SwapBuffers() override; bool SwapBuffers() override;
bool MakeCurrent() override; bool MakeCurrent() override;
bool DoneCurrent() override; bool DoneCurrent() override;
bool SetSwapInterval(s32 interval) override; bool SetSwapInterval(s32 interval) override;
std::unique_ptr<Context> CreateSharedContext(const WindowInfo& wi) override; std::unique_ptr<Context> CreateSharedContext(const WindowInfo& wi) override;
private: private:
ALWAYS_INLINE Display* GetDisplay() const { return static_cast<Display*>(m_wi.display_connection); } ALWAYS_INLINE Display* GetDisplay() const {
ALWAYS_INLINE GLXDrawable GetDrawable() const { return static_cast<GLXDrawable>(m_window.GetWindow()); } return static_cast<Display*>(m_wi.display_connection);
}
ALWAYS_INLINE GLXDrawable GetDrawable() const {
return static_cast<GLXDrawable>(m_window.GetWindow());
}
bool Initialize(const Version* versions_to_try, size_t num_versions_to_try); bool Initialize(const Version* versions_to_try, size_t num_versions_to_try);
bool CreateWindow(int screen); bool CreateWindow(int screen);
bool CreateAnyContext(GLXContext share_context, bool make_current); bool CreateAnyContext(GLXContext share_context, bool make_current);
bool CreateVersionContext(const Version& version, GLXContext share_context, bool make_current); bool CreateVersionContext(const Version& version, GLXContext share_context, bool make_current);
GLXContext m_context = nullptr; GLXContext m_context = nullptr;
GLXFBConfig m_fb_config = {}; GLXFBConfig m_fb_config = {};
XVisualInfo* m_vi = nullptr; XVisualInfo* m_vi = nullptr;
X11Window m_window; X11Window m_window;
// GLAD releases its reference to libGL.so, so we need to maintain our own. // GLAD releases its reference to libGL.so, so we need to maintain our own.
void* m_libGL_handle = nullptr; void* m_libGL_handle = nullptr;
}; };
} // namespace GL } // namespace GL

View file

@ -1,7 +1,7 @@
#include "context_wgl.h"
#include "../duckstation_compat.h" #include "../duckstation_compat.h"
#include "../log.h" #include "../log.h"
#include "../scoped_guard.h" #include "../scoped_guard.h"
#include "context_wgl.h"
#include "loader.h" #include "loader.h"
using namespace melonDS; using namespace melonDS;
Log_SetChannel(GL::ContextWGL); Log_SetChannel(GL::ContextWGL);
@ -9,445 +9,390 @@ Log_SetChannel(GL::ContextWGL);
// TODO: get rid of this // TODO: get rid of this
#pragma comment(lib, "opengl32.lib") #pragma comment(lib, "opengl32.lib")
static void* GetProcAddressCallback(const char* name) static void* GetProcAddressCallback(const char* name) {
{ void* addr = reinterpret_cast<void*>(wglGetProcAddress(name));
void* addr = reinterpret_cast<void*>(wglGetProcAddress(name)); if (addr)
if (addr) return addr;
return addr;
// try opengl32.dll // try opengl32.dll
return reinterpret_cast<void*>(::GetProcAddress(GetModuleHandleA("opengl32.dll"), name)); return reinterpret_cast<void*>(::GetProcAddress(GetModuleHandleA("opengl32.dll"), name));
} }
namespace GL { namespace GL {
ContextWGL::ContextWGL(const WindowInfo& wi) : Context(wi) {} ContextWGL::ContextWGL(const WindowInfo& wi) : Context(wi) {}
ContextWGL::~ContextWGL() ContextWGL::~ContextWGL() {
{ if (wglGetCurrentContext() == m_rc)
if (wglGetCurrentContext() == m_rc) wglMakeCurrent(m_dc, nullptr);
wglMakeCurrent(m_dc, nullptr);
if (m_rc) if (m_rc)
wglDeleteContext(m_rc); wglDeleteContext(m_rc);
ReleaseDC(); ReleaseDC();
} }
std::unique_ptr<Context> ContextWGL::Create(const WindowInfo& wi, const Version* versions_to_try, std::unique_ptr<Context> ContextWGL::Create(const WindowInfo& wi, const Version* versions_to_try,
size_t num_versions_to_try) size_t num_versions_to_try) {
{ std::unique_ptr<ContextWGL> context = std::make_unique<ContextWGL>(wi);
std::unique_ptr<ContextWGL> context = std::make_unique<ContextWGL>(wi); if (!context->Initialize(versions_to_try, num_versions_to_try))
if (!context->Initialize(versions_to_try, num_versions_to_try)) return nullptr;
return nullptr;
return context; return context;
} }
bool ContextWGL::Initialize(const Version* versions_to_try, size_t num_versions_to_try) bool ContextWGL::Initialize(const Version* versions_to_try, size_t num_versions_to_try) {
{ if (m_wi.type == WindowInfo::Type::Win32) {
if (m_wi.type == WindowInfo::Type::Win32) if (!InitializeDC())
{ return false;
} else {
Log_ErrorPrint("ContextWGL must always start with a valid surface.");
return false;
}
// Everything including core/ES requires a dummy profile to load the WGL extensions.
if (!CreateAnyContext(nullptr, true))
return false;
for (size_t i = 0; i < num_versions_to_try; i++) {
const Version& cv = versions_to_try[i];
if (cv.profile == Profile::NoProfile) {
// we already have the dummy context, so just use that
m_version = cv;
return true;
} else if (CreateVersionContext(cv, nullptr, true)) {
m_version = cv;
return true;
}
}
return false;
}
void* ContextWGL::GetProcAddress(const char* name) {
return GetProcAddressCallback(name);
}
bool ContextWGL::ChangeSurface(const WindowInfo& new_wi) {
const bool was_current = (wglGetCurrentContext() == m_rc);
ReleaseDC();
m_wi = new_wi;
if (!InitializeDC()) if (!InitializeDC())
return false; return false;
}
else
{
Log_ErrorPrint("ContextWGL must always start with a valid surface.");
return false;
}
// Everything including core/ES requires a dummy profile to load the WGL extensions. if (was_current && !wglMakeCurrent(m_dc, m_rc)) {
if (!CreateAnyContext(nullptr, true)) Log_ErrorPrintf("Failed to make context current again after surface change: 0x%08X",
return false; GetLastError());
return false;
for (size_t i = 0; i < num_versions_to_try; i++)
{
const Version& cv = versions_to_try[i];
if (cv.profile == Profile::NoProfile)
{
// we already have the dummy context, so just use that
m_version = cv;
return true;
}
else if (CreateVersionContext(cv, nullptr, true))
{
m_version = cv;
return true;
}
}
return false;
}
void* ContextWGL::GetProcAddress(const char* name)
{
return GetProcAddressCallback(name);
}
bool ContextWGL::ChangeSurface(const WindowInfo& new_wi)
{
const bool was_current = (wglGetCurrentContext() == m_rc);
ReleaseDC();
m_wi = new_wi;
if (!InitializeDC())
return false;
if (was_current && !wglMakeCurrent(m_dc, m_rc))
{
Log_ErrorPrintf("Failed to make context current again after surface change: 0x%08X", GetLastError());
return false;
}
return true;
}
void ContextWGL::ResizeSurface(u32 new_surface_width /*= 0*/, u32 new_surface_height /*= 0*/)
{
RECT client_rc = {};
GetClientRect(GetHWND(), &client_rc);
m_wi.surface_width = static_cast<u32>(client_rc.right - client_rc.left);
m_wi.surface_height = static_cast<u32>(client_rc.bottom - client_rc.top);
}
bool ContextWGL::SwapBuffers()
{
return ::SwapBuffers(m_dc);
}
bool ContextWGL::MakeCurrent()
{
if (!wglMakeCurrent(m_dc, m_rc))
{
Log_ErrorPrintf("wglMakeCurrent() failed: 0x%08X", GetLastError());
return false;
}
return true;
}
bool ContextWGL::DoneCurrent()
{
return wglMakeCurrent(m_dc, nullptr);
}
bool ContextWGL::SetSwapInterval(s32 interval)
{
if (!GLAD_WGL_EXT_swap_control)
return false;
return wglSwapIntervalEXT(interval);
}
std::unique_ptr<Context> ContextWGL::CreateSharedContext(const WindowInfo& wi)
{
std::unique_ptr<ContextWGL> context = std::make_unique<ContextWGL>(wi);
if (wi.type == WindowInfo::Type::Win32)
{
if (!context->InitializeDC())
return nullptr;
}
else
{
Log_ErrorPrint("PBuffer not implemented");
return nullptr;
}
if (m_version.profile == Profile::NoProfile)
{
if (!context->CreateAnyContext(m_rc, false))
return nullptr;
}
else
{
if (!context->CreateVersionContext(m_version, m_rc, false))
return nullptr;
}
context->m_version = m_version;
return context;
}
HDC ContextWGL::GetDCAndSetPixelFormat(HWND hwnd)
{
PIXELFORMATDESCRIPTOR pfd = {};
pfd.nSize = sizeof(pfd);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.dwLayerMask = PFD_MAIN_PLANE;
pfd.cRedBits = 8;
pfd.cGreenBits = 8;
pfd.cBlueBits = 8;
pfd.cColorBits = 24;
HDC hDC = ::GetDC(hwnd);
if (!hDC)
{
Log_ErrorPrintf("GetDC() failed: 0x%08X", GetLastError());
return {};
}
if (!m_pixel_format.has_value())
{
const int pf = ChoosePixelFormat(hDC, &pfd);
if (pf == 0)
{
Log_ErrorPrintf("ChoosePixelFormat() failed: 0x%08X", GetLastError());
::ReleaseDC(hwnd, hDC);
return {};
}
m_pixel_format = pf;
}
if (!SetPixelFormat(hDC, m_pixel_format.value(), &pfd))
{
Log_ErrorPrintf("SetPixelFormat() failed: 0x%08X", GetLastError());
::ReleaseDC(hwnd, hDC);
return {};
}
return hDC;
}
bool ContextWGL::InitializeDC()
{
if (m_wi.type == WindowInfo::Type::Win32)
{
m_dc = GetDCAndSetPixelFormat(GetHWND());
if (!m_dc)
{
Log_ErrorPrint("Failed to get DC for window");
return false;
} }
return true; return true;
}
else if (m_wi.type == WindowInfo::Type::Surfaceless)
{
return CreatePBuffer();
}
else
{
Log_ErrorPrintf("Unknown window info type %u", static_cast<unsigned>(m_wi.type));
return false;
}
} }
void ContextWGL::ReleaseDC() void ContextWGL::ResizeSurface(u32 new_surface_width /*= 0*/, u32 new_surface_height /*= 0*/) {
{ RECT client_rc = {};
if (m_pbuffer) GetClientRect(GetHWND(), &client_rc);
{ m_wi.surface_width = static_cast<u32>(client_rc.right - client_rc.left);
wglReleasePbufferDCARB(m_pbuffer, m_dc); m_wi.surface_height = static_cast<u32>(client_rc.bottom - client_rc.top);
m_dc = {};
wglDestroyPbufferARB(m_pbuffer);
m_pbuffer = {};
::ReleaseDC(m_dummy_window, m_dummy_dc);
m_dummy_dc = {};
DestroyWindow(m_dummy_window);
m_dummy_window = {};
}
else if (m_dc)
{
::ReleaseDC(GetHWND(), m_dc);
m_dc = {};
}
} }
bool ContextWGL::CreatePBuffer() bool ContextWGL::SwapBuffers() {
{ return ::SwapBuffers(m_dc);
static bool window_class_registered = false; }
static const wchar_t* window_class_name = L"ContextWGLPBuffer";
if (!window_class_registered) bool ContextWGL::MakeCurrent() {
{ if (!wglMakeCurrent(m_dc, m_rc)) {
WNDCLASSEXW wc = {}; Log_ErrorPrintf("wglMakeCurrent() failed: 0x%08X", GetLastError());
wc.cbSize = sizeof(WNDCLASSEXW); return false;
wc.style = 0;
wc.lpfnWndProc = DefWindowProcW;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = GetModuleHandle(nullptr);
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = window_class_name;
wc.hIconSm = NULL;
if (!RegisterClassExW(&wc))
{
Log_ErrorPrint("(ContextWGL::CreatePBuffer) RegisterClassExW() failed");
return false;
} }
window_class_registered = true; return true;
}
HWND hwnd = CreateWindowExW(0, window_class_name, window_class_name, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
if (!hwnd)
{
Log_ErrorPrint("(ContextWGL::CreatePBuffer) CreateWindowEx() failed");
return false;
}
ScopedGuard hwnd_guard([hwnd]() { DestroyWindow(hwnd); });
HDC hdc = GetDCAndSetPixelFormat(hwnd);
if (!hdc)
return false;
ScopedGuard hdc_guard([hdc, hwnd]() { ::ReleaseDC(hwnd, hdc); });
static constexpr const int pb_attribs[] = {0, 0};
AssertMsg(m_pixel_format.has_value(), "Has pixel format for pbuffer");
HPBUFFERARB pbuffer = wglCreatePbufferARB(hdc, m_pixel_format.value(), 1, 1, pb_attribs);
if (!pbuffer)
{
Log_ErrorPrint("(ContextWGL::CreatePBuffer) wglCreatePbufferARB() failed");
return false;
}
ScopedGuard pbuffer_guard([pbuffer]() { wglDestroyPbufferARB(pbuffer); });
m_dc = wglGetPbufferDCARB(pbuffer);
if (!m_dc)
{
Log_ErrorPrint("(ContextWGL::CreatePbuffer) wglGetPbufferDCARB() failed");
return false;
}
m_dummy_window = hwnd;
m_dummy_dc = hdc;
m_pbuffer = pbuffer;
pbuffer_guard.Cancel();
hdc_guard.Cancel();
hwnd_guard.Cancel();
return true;
} }
bool ContextWGL::CreateAnyContext(HGLRC share_context, bool make_current) bool ContextWGL::DoneCurrent() {
{ return wglMakeCurrent(m_dc, nullptr);
m_rc = wglCreateContext(m_dc);
if (!m_rc)
{
Log_ErrorPrintf("wglCreateContext() failed: 0x%08X", GetLastError());
return false;
}
if (make_current)
{
if (!wglMakeCurrent(m_dc, m_rc))
{
Log_ErrorPrintf("wglMakeCurrent() failed: 0x%08X", GetLastError());
return false;
}
// re-init glad-wgl
if (!gladLoadWGLLoader([](const char* name) -> void* { return reinterpret_cast<void*>(wglGetProcAddress(name)); }, m_dc))
{
Log_ErrorPrint("Loading GLAD WGL functions failed");
return false;
}
}
if (share_context && !wglShareLists(share_context, m_rc))
{
Log_ErrorPrintf("wglShareLists() failed: 0x%08X", GetLastError());
return false;
}
return true;
} }
bool ContextWGL::CreateVersionContext(const Version& version, HGLRC share_context, bool make_current) bool ContextWGL::SetSwapInterval(s32 interval) {
{ if (!GLAD_WGL_EXT_swap_control)
// we need create context attribs return false;
if (!GLAD_WGL_ARB_create_context)
{
Log_ErrorPrint("Missing GLAD_WGL_ARB_create_context.");
return false;
}
HGLRC new_rc; return wglSwapIntervalEXT(interval);
if (version.profile == Profile::Core) }
{
const int attribs[] = {WGL_CONTEXT_PROFILE_MASK_ARB, std::unique_ptr<Context> ContextWGL::CreateSharedContext(const WindowInfo& wi) {
WGL_CONTEXT_CORE_PROFILE_BIT_ARB, std::unique_ptr<ContextWGL> context = std::make_unique<ContextWGL>(wi);
WGL_CONTEXT_MAJOR_VERSION_ARB, if (wi.type == WindowInfo::Type::Win32) {
version.major_version, if (!context->InitializeDC())
WGL_CONTEXT_MINOR_VERSION_ARB, return nullptr;
version.minor_version, } else {
Log_ErrorPrint("PBuffer not implemented");
return nullptr;
}
if (m_version.profile == Profile::NoProfile) {
if (!context->CreateAnyContext(m_rc, false))
return nullptr;
} else {
if (!context->CreateVersionContext(m_version, m_rc, false))
return nullptr;
}
context->m_version = m_version;
return context;
}
HDC ContextWGL::GetDCAndSetPixelFormat(HWND hwnd) {
PIXELFORMATDESCRIPTOR pfd = {};
pfd.nSize = sizeof(pfd);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.dwLayerMask = PFD_MAIN_PLANE;
pfd.cRedBits = 8;
pfd.cGreenBits = 8;
pfd.cBlueBits = 8;
pfd.cColorBits = 24;
HDC hDC = ::GetDC(hwnd);
if (!hDC) {
Log_ErrorPrintf("GetDC() failed: 0x%08X", GetLastError());
return {};
}
if (!m_pixel_format.has_value()) {
const int pf = ChoosePixelFormat(hDC, &pfd);
if (pf == 0) {
Log_ErrorPrintf("ChoosePixelFormat() failed: 0x%08X", GetLastError());
::ReleaseDC(hwnd, hDC);
return {};
}
m_pixel_format = pf;
}
if (!SetPixelFormat(hDC, m_pixel_format.value(), &pfd)) {
Log_ErrorPrintf("SetPixelFormat() failed: 0x%08X", GetLastError());
::ReleaseDC(hwnd, hDC);
return {};
}
return hDC;
}
bool ContextWGL::InitializeDC() {
if (m_wi.type == WindowInfo::Type::Win32) {
m_dc = GetDCAndSetPixelFormat(GetHWND());
if (!m_dc) {
Log_ErrorPrint("Failed to get DC for window");
return false;
}
return true;
} else if (m_wi.type == WindowInfo::Type::Surfaceless) {
return CreatePBuffer();
} else {
Log_ErrorPrintf("Unknown window info type %u", static_cast<unsigned>(m_wi.type));
return false;
}
}
void ContextWGL::ReleaseDC() {
if (m_pbuffer) {
wglReleasePbufferDCARB(m_pbuffer, m_dc);
m_dc = {};
wglDestroyPbufferARB(m_pbuffer);
m_pbuffer = {};
::ReleaseDC(m_dummy_window, m_dummy_dc);
m_dummy_dc = {};
DestroyWindow(m_dummy_window);
m_dummy_window = {};
} else if (m_dc) {
::ReleaseDC(GetHWND(), m_dc);
m_dc = {};
}
}
bool ContextWGL::CreatePBuffer() {
static bool window_class_registered = false;
static const wchar_t* window_class_name = L"ContextWGLPBuffer";
if (!window_class_registered) {
WNDCLASSEXW wc = {};
wc.cbSize = sizeof(WNDCLASSEXW);
wc.style = 0;
wc.lpfnWndProc = DefWindowProcW;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = GetModuleHandle(nullptr);
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = window_class_name;
wc.hIconSm = NULL;
if (!RegisterClassExW(&wc)) {
Log_ErrorPrint("(ContextWGL::CreatePBuffer) RegisterClassExW() failed");
return false;
}
window_class_registered = true;
}
HWND hwnd = CreateWindowExW(0, window_class_name, window_class_name, 0, 0, 0, 0, 0, NULL, NULL,
NULL, NULL);
if (!hwnd) {
Log_ErrorPrint("(ContextWGL::CreatePBuffer) CreateWindowEx() failed");
return false;
}
ScopedGuard hwnd_guard([hwnd]() { DestroyWindow(hwnd); });
HDC hdc = GetDCAndSetPixelFormat(hwnd);
if (!hdc)
return false;
ScopedGuard hdc_guard([hdc, hwnd]() { ::ReleaseDC(hwnd, hdc); });
static constexpr const int pb_attribs[] = {0, 0};
AssertMsg(m_pixel_format.has_value(), "Has pixel format for pbuffer");
HPBUFFERARB pbuffer = wglCreatePbufferARB(hdc, m_pixel_format.value(), 1, 1, pb_attribs);
if (!pbuffer) {
Log_ErrorPrint("(ContextWGL::CreatePBuffer) wglCreatePbufferARB() failed");
return false;
}
ScopedGuard pbuffer_guard([pbuffer]() { wglDestroyPbufferARB(pbuffer); });
m_dc = wglGetPbufferDCARB(pbuffer);
if (!m_dc) {
Log_ErrorPrint("(ContextWGL::CreatePbuffer) wglGetPbufferDCARB() failed");
return false;
}
m_dummy_window = hwnd;
m_dummy_dc = hdc;
m_pbuffer = pbuffer;
pbuffer_guard.Cancel();
hdc_guard.Cancel();
hwnd_guard.Cancel();
return true;
}
bool ContextWGL::CreateAnyContext(HGLRC share_context, bool make_current) {
m_rc = wglCreateContext(m_dc);
if (!m_rc) {
Log_ErrorPrintf("wglCreateContext() failed: 0x%08X", GetLastError());
return false;
}
if (make_current) {
if (!wglMakeCurrent(m_dc, m_rc)) {
Log_ErrorPrintf("wglMakeCurrent() failed: 0x%08X", GetLastError());
return false;
}
// re-init glad-wgl
if (!gladLoadWGLLoader(
[](const char* name) -> void* {
return reinterpret_cast<void*>(wglGetProcAddress(name));
},
m_dc)) {
Log_ErrorPrint("Loading GLAD WGL functions failed");
return false;
}
}
if (share_context && !wglShareLists(share_context, m_rc)) {
Log_ErrorPrintf("wglShareLists() failed: 0x%08X", GetLastError());
return false;
}
return true;
}
bool ContextWGL::CreateVersionContext(const Version& version, HGLRC share_context,
bool make_current) {
// we need create context attribs
if (!GLAD_WGL_ARB_create_context) {
Log_ErrorPrint("Missing GLAD_WGL_ARB_create_context.");
return false;
}
HGLRC new_rc;
if (version.profile == Profile::Core) {
const int attribs[] = {WGL_CONTEXT_PROFILE_MASK_ARB,
WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
WGL_CONTEXT_MAJOR_VERSION_ARB,
version.major_version,
WGL_CONTEXT_MINOR_VERSION_ARB,
version.minor_version,
#ifdef _DEBUG #ifdef _DEBUG
WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FLAGS_ARB,
WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB | WGL_CONTEXT_DEBUG_BIT_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB | WGL_CONTEXT_DEBUG_BIT_ARB,
#else #else
WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FLAGS_ARB,
WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
#endif #endif
0, 0,
0}; 0};
new_rc = wglCreateContextAttribsARB(m_dc, share_context, attribs); new_rc = wglCreateContextAttribsARB(m_dc, share_context, attribs);
} } else if (version.profile == Profile::ES) {
else if (version.profile == Profile::ES) if ((version.major_version >= 2 && !GLAD_WGL_EXT_create_context_es2_profile) ||
{ (version.major_version < 2 && !GLAD_WGL_EXT_create_context_es_profile)) {
if ((version.major_version >= 2 && !GLAD_WGL_EXT_create_context_es2_profile) || Log_ErrorPrint("WGL_EXT_create_context_es_profile not supported");
(version.major_version < 2 && !GLAD_WGL_EXT_create_context_es_profile)) return false;
{ }
Log_ErrorPrint("WGL_EXT_create_context_es_profile not supported");
return false; const int attribs[] = {WGL_CONTEXT_PROFILE_MASK_ARB,
((version.major_version >= 2) ? WGL_CONTEXT_ES2_PROFILE_BIT_EXT
: WGL_CONTEXT_ES_PROFILE_BIT_EXT),
WGL_CONTEXT_MAJOR_VERSION_ARB,
version.major_version,
WGL_CONTEXT_MINOR_VERSION_ARB,
version.minor_version,
0,
0};
new_rc = wglCreateContextAttribsARB(m_dc, share_context, attribs);
} else {
Log_ErrorPrint("Unknown profile");
return false;
} }
const int attribs[] = { if (!new_rc)
WGL_CONTEXT_PROFILE_MASK_ARB, return false;
((version.major_version >= 2) ? WGL_CONTEXT_ES2_PROFILE_BIT_EXT : WGL_CONTEXT_ES_PROFILE_BIT_EXT),
WGL_CONTEXT_MAJOR_VERSION_ARB,
version.major_version,
WGL_CONTEXT_MINOR_VERSION_ARB,
version.minor_version,
0,
0};
new_rc = wglCreateContextAttribsARB(m_dc, share_context, attribs); // destroy and swap contexts
} if (m_rc) {
else if (!wglMakeCurrent(m_dc, make_current ? new_rc : nullptr)) {
{ Log_ErrorPrintf("wglMakeCurrent() failed: 0x%08X", GetLastError());
Log_ErrorPrint("Unknown profile"); wglDeleteContext(new_rc);
return false; return false;
} }
if (!new_rc) // re-init glad-wgl
return false; if (make_current && !gladLoadWGLLoader(
[](const char* name) -> void* {
return reinterpret_cast<void*>(wglGetProcAddress(name));
},
m_dc)) {
Log_ErrorPrint("Loading GLAD WGL functions failed");
return false;
}
// destroy and swap contexts wglDeleteContext(m_rc);
if (m_rc)
{
if (!wglMakeCurrent(m_dc, make_current ? new_rc : nullptr))
{
Log_ErrorPrintf("wglMakeCurrent() failed: 0x%08X", GetLastError());
wglDeleteContext(new_rc);
return false;
} }
// re-init glad-wgl m_rc = new_rc;
if (make_current && !gladLoadWGLLoader([](const char* name) -> void* { return reinterpret_cast<void*>(wglGetProcAddress(name)); }, m_dc)) return true;
{
Log_ErrorPrint("Loading GLAD WGL functions failed");
return false;
}
wglDeleteContext(m_rc);
}
m_rc = new_rc;
return true;
} }
} // namespace GL } // namespace GL

View file

@ -1,53 +1,54 @@
#pragma once #pragma once
#include "../windows_headers.h" #include "../windows_headers.h"
#include "context.h"
#include "../../../../../externals/glad/src/glad_wgl.h"
#include "loader.h"
#include <optional> #include <optional>
#include "../../../../../externals/glad/src/glad_wgl.h"
#include "context.h"
#include "loader.h"
namespace GL { namespace GL {
class ContextWGL final : public Context class ContextWGL final : public Context {
{
public: public:
ContextWGL(const WindowInfo& wi); ContextWGL(const WindowInfo& wi);
~ContextWGL() override; ~ContextWGL() override;
static std::unique_ptr<Context> Create(const WindowInfo& wi, const Version* versions_to_try, static std::unique_ptr<Context> Create(const WindowInfo& wi, const Version* versions_to_try,
size_t num_versions_to_try); size_t num_versions_to_try);
void* GetProcAddress(const char* name) override; void* GetProcAddress(const char* name) override;
bool ChangeSurface(const WindowInfo& new_wi) override; bool ChangeSurface(const WindowInfo& new_wi) override;
void ResizeSurface(u32 new_surface_width = 0, u32 new_surface_height = 0) override; void ResizeSurface(u32 new_surface_width = 0, u32 new_surface_height = 0) override;
bool SwapBuffers() override; bool SwapBuffers() override;
bool MakeCurrent() override; bool MakeCurrent() override;
bool DoneCurrent() override; bool DoneCurrent() override;
bool SetSwapInterval(s32 interval) override; bool SetSwapInterval(s32 interval) override;
std::unique_ptr<Context> CreateSharedContext(const WindowInfo& wi) override; std::unique_ptr<Context> CreateSharedContext(const WindowInfo& wi) override;
private: private:
ALWAYS_INLINE HWND GetHWND() const { return static_cast<HWND>(m_wi.window_handle); } ALWAYS_INLINE HWND GetHWND() const {
return static_cast<HWND>(m_wi.window_handle);
}
HDC GetDCAndSetPixelFormat(HWND hwnd); HDC GetDCAndSetPixelFormat(HWND hwnd);
bool Initialize(const Version* versions_to_try, size_t num_versions_to_try); bool Initialize(const Version* versions_to_try, size_t num_versions_to_try);
bool InitializeDC(); bool InitializeDC();
void ReleaseDC(); void ReleaseDC();
bool CreatePBuffer(); bool CreatePBuffer();
bool CreateAnyContext(HGLRC share_context, bool make_current); bool CreateAnyContext(HGLRC share_context, bool make_current);
bool CreateVersionContext(const Version& version, HGLRC share_context, bool make_current); bool CreateVersionContext(const Version& version, HGLRC share_context, bool make_current);
HDC m_dc = {}; HDC m_dc = {};
HGLRC m_rc = {}; HGLRC m_rc = {};
// Can't change pixel format once it's set for a RC. // Can't change pixel format once it's set for a RC.
std::optional<int> m_pixel_format; std::optional<int> m_pixel_format;
// Dummy window for creating a PBuffer off when we're surfaceless. // Dummy window for creating a PBuffer off when we're surfaceless.
HWND m_dummy_window = {}; HWND m_dummy_window = {};
HDC m_dummy_dc = {}; HDC m_dummy_dc = {};
HPBUFFERARB m_pbuffer = {}; HPBUFFERARB m_pbuffer = {};
}; };
} // namespace GL } // namespace GL

View file

@ -1,101 +1,89 @@
#include "x11_window.h"
#include "../log.h"
#include "../duckstation_compat.h"
#include <cstdio> #include <cstdio>
#include "../duckstation_compat.h"
#include "../log.h"
#include "x11_window.h"
Log_SetChannel(X11Window); Log_SetChannel(X11Window);
namespace GL { namespace GL {
X11Window::X11Window() = default; X11Window::X11Window() = default;
X11Window::~X11Window() X11Window::~X11Window() {
{ Destroy();
Destroy();
} }
bool X11Window::Create(Display* display, Window parent_window, const XVisualInfo* vi) bool X11Window::Create(Display* display, Window parent_window, const XVisualInfo* vi) {
{ m_display = display;
m_display = display; m_parent_window = parent_window;
m_parent_window = parent_window; XSync(m_display, True);
XSync(m_display, True);
XWindowAttributes parent_wa = {};
XGetWindowAttributes(m_display, m_parent_window, &parent_wa);
m_width = static_cast<u32>(parent_wa.width);
m_height = static_cast<u32>(parent_wa.height);
// Failed X calls terminate the process so no need to check for errors.
// We could swap the error handler out here as well.
m_colormap = XCreateColormap(m_display, m_parent_window, vi->visual, AllocNone);
XSetWindowAttributes wa = {};
wa.colormap = m_colormap;
m_window = XCreateWindow(m_display, m_parent_window, 0, 0, m_width, m_height, 0, vi->depth, InputOutput, vi->visual,
CWColormap, &wa);
XMapWindow(m_display, m_window);
XSync(m_display, True);
return true;
}
void X11Window::Destroy()
{
if (m_window)
{
XUnmapWindow(m_display, m_window);
XDestroyWindow(m_display, m_window);
m_window = {};
}
if (m_colormap)
{
XFreeColormap(m_display, m_colormap);
m_colormap = {};
}
}
void X11Window::Resize(u32 width, u32 height)
{
if (width != 0 && height != 0)
{
m_width = width;
m_height = height;
}
else
{
XWindowAttributes parent_wa = {}; XWindowAttributes parent_wa = {};
XGetWindowAttributes(m_display, m_parent_window, &parent_wa); XGetWindowAttributes(m_display, m_parent_window, &parent_wa);
m_width = static_cast<u32>(parent_wa.width); m_width = static_cast<u32>(parent_wa.width);
m_height = static_cast<u32>(parent_wa.height); m_height = static_cast<u32>(parent_wa.height);
}
XResizeWindow(m_display, m_window, m_width, m_height); // Failed X calls terminate the process so no need to check for errors.
// We could swap the error handler out here as well.
m_colormap = XCreateColormap(m_display, m_parent_window, vi->visual, AllocNone);
XSetWindowAttributes wa = {};
wa.colormap = m_colormap;
m_window = XCreateWindow(m_display, m_parent_window, 0, 0, m_width, m_height, 0, vi->depth,
InputOutput, vi->visual, CWColormap, &wa);
XMapWindow(m_display, m_window);
XSync(m_display, True);
return true;
}
void X11Window::Destroy() {
if (m_window) {
XUnmapWindow(m_display, m_window);
XDestroyWindow(m_display, m_window);
m_window = {};
}
if (m_colormap) {
XFreeColormap(m_display, m_colormap);
m_colormap = {};
}
}
void X11Window::Resize(u32 width, u32 height) {
if (width != 0 && height != 0) {
m_width = width;
m_height = height;
} else {
XWindowAttributes parent_wa = {};
XGetWindowAttributes(m_display, m_parent_window, &parent_wa);
m_width = static_cast<u32>(parent_wa.width);
m_height = static_cast<u32>(parent_wa.height);
}
XResizeWindow(m_display, m_window, m_width, m_height);
} }
static X11InhibitErrors* s_current_error_inhibiter; static X11InhibitErrors* s_current_error_inhibiter;
X11InhibitErrors::X11InhibitErrors() X11InhibitErrors::X11InhibitErrors() {
{ Assert(!s_current_error_inhibiter);
Assert(!s_current_error_inhibiter); m_old_handler = XSetErrorHandler(ErrorHandler);
m_old_handler = XSetErrorHandler(ErrorHandler); s_current_error_inhibiter = this;
s_current_error_inhibiter = this;
} }
X11InhibitErrors::~X11InhibitErrors() X11InhibitErrors::~X11InhibitErrors() {
{ Assert(s_current_error_inhibiter == this);
Assert(s_current_error_inhibiter == this); s_current_error_inhibiter = nullptr;
s_current_error_inhibiter = nullptr; XSetErrorHandler(m_old_handler);
XSetErrorHandler(m_old_handler);
} }
int X11InhibitErrors::ErrorHandler(Display* display, XErrorEvent* ee) int X11InhibitErrors::ErrorHandler(Display* display, XErrorEvent* ee) {
{ char error_string[256] = {};
char error_string[256] = {}; XGetErrorText(display, ee->error_code, error_string, sizeof(error_string));
XGetErrorText(display, ee->error_code, error_string, sizeof(error_string)); Log_WarningPrintf("X11 Error: %s (Error %u Minor %u Request %u)", error_string, ee->error_code,
Log_WarningPrintf("X11 Error: %s (Error %u Minor %u Request %u)", error_string, ee->error_code, ee->minor_code, ee->minor_code, ee->request_code);
ee->request_code);
s_current_error_inhibiter->m_had_error = true; s_current_error_inhibiter->m_had_error = true;
return 0; return 0;
} }
} // namespace GL } // namespace GL

View file

@ -1,49 +1,55 @@
#pragma once #pragma once
#include "../duckstation_compat.h"
#include <X11/Xlib.h> #include <X11/Xlib.h>
#include <X11/Xutil.h> #include <X11/Xutil.h>
#include "../duckstation_compat.h"
namespace GL { namespace GL {
using namespace citra; using namespace citra;
class X11Window class X11Window {
{
public: public:
X11Window(); X11Window();
~X11Window(); ~X11Window();
ALWAYS_INLINE Window GetWindow() const { return m_window; } ALWAYS_INLINE Window GetWindow() const {
ALWAYS_INLINE u32 GetWidth() const { return m_width; } return m_window;
ALWAYS_INLINE u32 GetHeight() const { return m_height; } }
ALWAYS_INLINE u32 GetWidth() const {
return m_width;
}
ALWAYS_INLINE u32 GetHeight() const {
return m_height;
}
bool Create(Display* display, Window parent_window, const XVisualInfo* vi); bool Create(Display* display, Window parent_window, const XVisualInfo* vi);
void Destroy(); void Destroy();
// Setting a width/height of 0 will use parent dimensions. // Setting a width/height of 0 will use parent dimensions.
void Resize(u32 width = 0, u32 height = 0); void Resize(u32 width = 0, u32 height = 0);
private: private:
Display* m_display = nullptr; Display* m_display = nullptr;
Window m_parent_window = {}; Window m_parent_window = {};
Window m_window = {}; Window m_window = {};
Colormap m_colormap = {}; Colormap m_colormap = {};
u32 m_width = 0; u32 m_width = 0;
u32 m_height = 0; u32 m_height = 0;
}; };
// Helper class for managing X errors // Helper class for managing X errors
class X11InhibitErrors class X11InhibitErrors {
{
public: public:
X11InhibitErrors(); X11InhibitErrors();
~X11InhibitErrors(); ~X11InhibitErrors();
ALWAYS_INLINE bool HadError() const { return m_had_error; } ALWAYS_INLINE bool HadError() const {
return m_had_error;
}
private: private:
static int ErrorHandler(Display* display, XErrorEvent* ee); static int ErrorHandler(Display* display, XErrorEvent* ee);
XErrorHandler m_old_handler = {}; XErrorHandler m_old_handler = {};
bool m_had_error = false; bool m_had_error = false;
}; };
} // namespace GL } // namespace GL

View file

@ -5,42 +5,74 @@
#define Log_SetChannel(ChannelName) #define Log_SetChannel(ChannelName)
#define Log_ErrorPrint(msg) puts(msg "\n"); #define Log_ErrorPrint(msg) puts(msg "\n");
#define Log_ErrorPrintf(...) do { printf(__VA_ARGS__); putchar('\n'); } while (0) #define Log_ErrorPrintf(...) \
do { \
printf(__VA_ARGS__); \
putchar('\n'); \
} while (0)
#define Log_WarningPrint(msg) puts(msg) #define Log_WarningPrint(msg) puts(msg)
#define Log_WarningPrintf(...) do { printf(__VA_ARGS__); putchar('\n'); } while (0) #define Log_WarningPrintf(...) \
do { \
printf(__VA_ARGS__); \
putchar('\n'); \
} while (0)
#define Log_PerfPrint(msg) puts(msg) #define Log_PerfPrint(msg) puts(msg)
#define Log_PerfPrintf(...) do { printf(__VA_ARGS__); putchar('\n'); } while (0) #define Log_PerfPrintf(...) \
do { \
printf(__VA_ARGS__); \
putchar('\n'); \
} while (0)
#define Log_InfoPrint(msg) puts(msg) #define Log_InfoPrint(msg) puts(msg)
#define Log_InfoPrintf(...) do { printf(__VA_ARGS__); putchar('\n'); } while (0) #define Log_InfoPrintf(...) \
do { \
printf(__VA_ARGS__); \
putchar('\n'); \
} while (0)
#define Log_VerbosePrint(msg) puts(msg) #define Log_VerbosePrint(msg) puts(msg)
#define Log_VerbosePrintf(...) do { printf(__VA_ARGS__); putchar('\n'); } while (0) #define Log_VerbosePrintf(...) \
do { \
printf(__VA_ARGS__); \
putchar('\n'); \
} while (0)
#define Log_DevPrint(msg) puts(msg) #define Log_DevPrint(msg) puts(msg)
#define Log_DevPrintf(...) do { printf(__VA_ARGS__); putchar('\n'); } while (0) #define Log_DevPrintf(...) \
do { \
printf(__VA_ARGS__); \
putchar('\n'); \
} while (0)
#define Log_ProfilePrint(msg) puts(msg) #define Log_ProfilePrint(msg) puts(msg)
#define Log_ProfilePrintf(...) do { printf(__VA_ARGS__); putchar('\n'); } while (0) #define Log_ProfilePrintf(...) \
do { \
printf(__VA_ARGS__); \
putchar('\n'); \
} while (0)
#ifdef _DEBUG #ifdef _DEBUG
#define Log_DebugPrint(msg) puts(msg) #define Log_DebugPrint(msg) puts(msg)
#define Log_DebugPrintf(...) do { printf(__VA_ARGS__); putchar('\n'); } while (0) #define Log_DebugPrintf(...) \
do { \
printf(__VA_ARGS__); \
putchar('\n'); \
} while (0)
#define Log_TracePrint(msg) puts(msg) #define Log_TracePrint(msg) puts(msg)
#define Log_TracePrintf(...) do { printf(__VA_ARGS__); putchar('\n'); } while (0) #define Log_TracePrintf(...) \
do { \
printf(__VA_ARGS__); \
putchar('\n'); \
} while (0)
#else #else
#define Log_DebugPrint(msg) \ #define Log_DebugPrint(msg) \
do \ do { \
{ \ } while (0)
} while (0) #define Log_DebugPrintf(...) \
#define Log_DebugPrintf(...) \ do { \
do \ } while (0)
{ \ #define Log_TracePrint(msg) \
} while (0) do { \
#define Log_TracePrint(msg) \ } while (0)
do \ #define Log_TracePrintf(...) \
{ \ do { \
} while (0) } while (0)
#define Log_TracePrintf(...) \
do \
{ \
} while (0)
#endif #endif
#endif #endif

View file

@ -5,30 +5,34 @@
/// ScopedGuard provides an object which runs a function (usually a lambda) when /// ScopedGuard provides an object which runs a function (usually a lambda) when
/// it goes out of scope. This can be useful for releasing resources or handles /// it goes out of scope. This can be useful for releasing resources or handles
/// which do not normally have C++ types to automatically release. /// which do not normally have C++ types to automatically release.
template<typename T> template <typename T>
class ScopedGuard final class ScopedGuard final {
{
public: public:
ALWAYS_INLINE ScopedGuard(T&& func) : m_func(std::forward<T>(func)) {} ALWAYS_INLINE ScopedGuard(T&& func) : m_func(std::forward<T>(func)) {}
ALWAYS_INLINE ScopedGuard(ScopedGuard&& other) : m_func(std::move(other.m_func)) { other.m_func = nullptr; } ALWAYS_INLINE ScopedGuard(ScopedGuard&& other) : m_func(std::move(other.m_func)) {
ALWAYS_INLINE ~ScopedGuard() { Invoke(); } other.m_func = nullptr;
}
ALWAYS_INLINE ~ScopedGuard() {
Invoke();
}
ScopedGuard(const ScopedGuard&) = delete; ScopedGuard(const ScopedGuard&) = delete;
void operator=(const ScopedGuard&) = delete; void operator=(const ScopedGuard&) = delete;
/// Prevents the function from being invoked when we go out of scope. /// Prevents the function from being invoked when we go out of scope.
ALWAYS_INLINE void Cancel() { m_func.reset(); } ALWAYS_INLINE void Cancel() {
m_func.reset();
}
/// Explicitly fires the function. /// Explicitly fires the function.
ALWAYS_INLINE void Invoke() ALWAYS_INLINE void Invoke() {
{ if (!m_func.has_value())
if (!m_func.has_value()) return;
return;
m_func.value()(); m_func.value()();
m_func.reset(); m_func.reset();
} }
private: private:
std::optional<T> m_func; std::optional<T> m_func;
}; };

View file

@ -1,191 +1,175 @@
#include "window_info.h"
#include "common/log.h" #include "common/log.h"
#include "window_info.h"
Log_SetChannel(WindowInfo); Log_SetChannel(WindowInfo);
#if defined(_WIN32) #if defined(_WIN32)
#include "common/windows_headers.h"
#include <dwmapi.h> #include <dwmapi.h>
#include "common/windows_headers.h"
static bool GetRefreshRateFromDWM(HWND hwnd, float* refresh_rate) static bool GetRefreshRateFromDWM(HWND hwnd, float* refresh_rate) {
{ static HMODULE dwm_module = nullptr;
static HMODULE dwm_module = nullptr; static HRESULT(STDAPICALLTYPE * is_composition_enabled)(BOOL * pfEnabled) = nullptr;
static HRESULT(STDAPICALLTYPE * is_composition_enabled)(BOOL * pfEnabled) = nullptr; static HRESULT(STDAPICALLTYPE * get_timing_info)(HWND hwnd, DWM_TIMING_INFO * pTimingInfo) =
static HRESULT(STDAPICALLTYPE * get_timing_info)(HWND hwnd, DWM_TIMING_INFO * pTimingInfo) = nullptr; nullptr;
static bool load_tried = false; static bool load_tried = false;
if (!load_tried) if (!load_tried) {
{ load_tried = true;
load_tried = true; dwm_module = LoadLibrary("dwmapi.dll");
dwm_module = LoadLibrary("dwmapi.dll"); if (dwm_module) {
if (dwm_module) std::atexit([]() {
{ FreeLibrary(dwm_module);
std::atexit([]() { dwm_module = nullptr;
FreeLibrary(dwm_module); });
dwm_module = nullptr; is_composition_enabled = reinterpret_cast<decltype(is_composition_enabled)>(
}); GetProcAddress(dwm_module, "DwmIsCompositionEnabled"));
is_composition_enabled = get_timing_info = reinterpret_cast<decltype(get_timing_info)>(
reinterpret_cast<decltype(is_composition_enabled)>(GetProcAddress(dwm_module, "DwmIsCompositionEnabled")); GetProcAddress(dwm_module, "DwmGetCompositionTimingInfo"));
get_timing_info = }
reinterpret_cast<decltype(get_timing_info)>(GetProcAddress(dwm_module, "DwmGetCompositionTimingInfo")); }
BOOL composition_enabled;
if (!is_composition_enabled ||
FAILED(is_composition_enabled(&composition_enabled) || !get_timing_info))
return false;
DWM_TIMING_INFO ti = {};
ti.cbSize = sizeof(ti);
HRESULT hr = get_timing_info(nullptr, &ti);
if (SUCCEEDED(hr)) {
if (ti.rateRefresh.uiNumerator == 0 || ti.rateRefresh.uiDenominator == 0)
return false;
*refresh_rate = static_cast<float>(ti.rateRefresh.uiNumerator) /
static_cast<float>(ti.rateRefresh.uiDenominator);
return true;
} }
}
BOOL composition_enabled;
if (!is_composition_enabled || FAILED(is_composition_enabled(&composition_enabled) || !get_timing_info))
return false; return false;
DWM_TIMING_INFO ti = {};
ti.cbSize = sizeof(ti);
HRESULT hr = get_timing_info(nullptr, &ti);
if (SUCCEEDED(hr))
{
if (ti.rateRefresh.uiNumerator == 0 || ti.rateRefresh.uiDenominator == 0)
return false;
*refresh_rate = static_cast<float>(ti.rateRefresh.uiNumerator) / static_cast<float>(ti.rateRefresh.uiDenominator);
return true;
}
return false;
} }
static bool GetRefreshRateFromMonitor(HWND hwnd, float* refresh_rate) static bool GetRefreshRateFromMonitor(HWND hwnd, float* refresh_rate) {
{ HMONITOR mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
HMONITOR mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); if (!mon)
if (!mon) return false;
return false;
MONITORINFOEXW mi = {}; MONITORINFOEXW mi = {};
mi.cbSize = sizeof(mi); mi.cbSize = sizeof(mi);
if (GetMonitorInfoW(mon, &mi)) if (GetMonitorInfoW(mon, &mi)) {
{ DEVMODEW dm = {};
DEVMODEW dm = {}; dm.dmSize = sizeof(dm);
dm.dmSize = sizeof(dm);
// 0/1 are reserved for "defaults". // 0/1 are reserved for "defaults".
if (EnumDisplaySettingsW(mi.szDevice, ENUM_CURRENT_SETTINGS, &dm) && dm.dmDisplayFrequency > 1) if (EnumDisplaySettingsW(mi.szDevice, ENUM_CURRENT_SETTINGS, &dm) &&
{ dm.dmDisplayFrequency > 1) {
*refresh_rate = static_cast<float>(dm.dmDisplayFrequency); *refresh_rate = static_cast<float>(dm.dmDisplayFrequency);
return true; return true;
}
} }
}
return false; return false;
} }
bool WindowInfo::QueryRefreshRateForWindow(const WindowInfo& wi, float* refresh_rate) bool WindowInfo::QueryRefreshRateForWindow(const WindowInfo& wi, float* refresh_rate) {
{ if (wi.type != Type::Win32 || !wi.window_handle)
if (wi.type != Type::Win32 || !wi.window_handle) return false;
return false;
// Try DWM first, then fall back to integer values. // Try DWM first, then fall back to integer values.
const HWND hwnd = static_cast<HWND>(wi.window_handle); const HWND hwnd = static_cast<HWND>(wi.window_handle);
return GetRefreshRateFromDWM(hwnd, refresh_rate) || GetRefreshRateFromMonitor(hwnd, refresh_rate); return GetRefreshRateFromDWM(hwnd, refresh_rate) ||
GetRefreshRateFromMonitor(hwnd, refresh_rate);
} }
#else #else
#ifdef USE_X11 #ifdef USE_X11
#include <X11/extensions/Xrandr.h>
#include "common/scoped_guard.h" #include "common/scoped_guard.h"
#include "gl/x11_window.h" #include "gl/x11_window.h"
#include <X11/extensions/Xrandr.h>
static bool GetRefreshRateFromXRandR(const WindowInfo& wi, float* refresh_rate) static bool GetRefreshRateFromXRandR(const WindowInfo& wi, float* refresh_rate) {
{ Display* display = static_cast<Display*>(wi.display_connection);
Display* display = static_cast<Display*>(wi.display_connection); Window window = static_cast<Window>(reinterpret_cast<uintptr_t>(wi.window_handle));
Window window = static_cast<Window>(reinterpret_cast<uintptr_t>(wi.window_handle)); if (!display || !window)
if (!display || !window) return false;
return false;
GL::X11InhibitErrors inhibiter; GL::X11InhibitErrors inhibiter;
XRRScreenResources* res = XRRGetScreenResources(display, window); XRRScreenResources* res = XRRGetScreenResources(display, window);
if (!res) if (!res) {
{ Log_ErrorPrint("XRRGetScreenResources() failed");
Log_ErrorPrint("XRRGetScreenResources() failed"); return false;
return false;
}
ScopedGuard res_guard([res]() { XRRFreeScreenResources(res); });
int num_monitors;
XRRMonitorInfo* mi = XRRGetMonitors(display, window, True, &num_monitors);
if (num_monitors < 0)
{
Log_ErrorPrint("XRRGetMonitors() failed");
return false;
}
else if (num_monitors > 1)
{
Log_WarningPrintf("XRRGetMonitors() returned %d monitors, using first", num_monitors);
}
ScopedGuard mi_guard([mi]() { XRRFreeMonitors(mi); });
if (mi->noutput <= 0)
{
Log_ErrorPrint("Monitor has no outputs");
return false;
}
else if (mi->noutput > 1)
{
Log_WarningPrintf("Monitor has %d outputs, using first", mi->noutput);
}
XRROutputInfo* oi = XRRGetOutputInfo(display, res, mi->outputs[0]);
if (!oi)
{
Log_ErrorPrint("XRRGetOutputInfo() failed");
return false;
}
ScopedGuard oi_guard([oi]() { XRRFreeOutputInfo(oi); });
XRRCrtcInfo* ci = XRRGetCrtcInfo(display, res, oi->crtc);
if (!ci)
{
Log_ErrorPrint("XRRGetCrtcInfo() failed");
return false;
}
ScopedGuard ci_guard([ci]() { XRRFreeCrtcInfo(ci); });
XRRModeInfo* mode = nullptr;
for (int i = 0; i < res->nmode; i++)
{
if (res->modes[i].id == ci->mode)
{
mode = &res->modes[i];
break;
} }
}
if (!mode)
{
Log_ErrorPrintf("Failed to look up mode %d (of %d)", static_cast<int>(ci->mode), res->nmode);
return false;
}
if (mode->dotClock == 0 || mode->hTotal == 0 || mode->vTotal == 0) ScopedGuard res_guard([res]() { XRRFreeScreenResources(res); });
{
Log_ErrorPrintf("Modeline is invalid: %ld/%d/%d", mode->dotClock, mode->hTotal, mode->vTotal);
return false;
}
*refresh_rate = int num_monitors;
static_cast<double>(mode->dotClock) / (static_cast<double>(mode->hTotal) * static_cast<double>(mode->vTotal)); XRRMonitorInfo* mi = XRRGetMonitors(display, window, True, &num_monitors);
return true; if (num_monitors < 0) {
Log_ErrorPrint("XRRGetMonitors() failed");
return false;
} else if (num_monitors > 1) {
Log_WarningPrintf("XRRGetMonitors() returned %d monitors, using first", num_monitors);
}
ScopedGuard mi_guard([mi]() { XRRFreeMonitors(mi); });
if (mi->noutput <= 0) {
Log_ErrorPrint("Monitor has no outputs");
return false;
} else if (mi->noutput > 1) {
Log_WarningPrintf("Monitor has %d outputs, using first", mi->noutput);
}
XRROutputInfo* oi = XRRGetOutputInfo(display, res, mi->outputs[0]);
if (!oi) {
Log_ErrorPrint("XRRGetOutputInfo() failed");
return false;
}
ScopedGuard oi_guard([oi]() { XRRFreeOutputInfo(oi); });
XRRCrtcInfo* ci = XRRGetCrtcInfo(display, res, oi->crtc);
if (!ci) {
Log_ErrorPrint("XRRGetCrtcInfo() failed");
return false;
}
ScopedGuard ci_guard([ci]() { XRRFreeCrtcInfo(ci); });
XRRModeInfo* mode = nullptr;
for (int i = 0; i < res->nmode; i++) {
if (res->modes[i].id == ci->mode) {
mode = &res->modes[i];
break;
}
}
if (!mode) {
Log_ErrorPrintf("Failed to look up mode %d (of %d)", static_cast<int>(ci->mode),
res->nmode);
return false;
}
if (mode->dotClock == 0 || mode->hTotal == 0 || mode->vTotal == 0) {
Log_ErrorPrintf("Modeline is invalid: %ld/%d/%d", mode->dotClock, mode->hTotal,
mode->vTotal);
return false;
}
*refresh_rate = static_cast<double>(mode->dotClock) /
(static_cast<double>(mode->hTotal) * static_cast<double>(mode->vTotal));
return true;
} }
#endif // USE_X11 #endif // USE_X11
bool WindowInfo::QueryRefreshRateForWindow(const WindowInfo& wi, float* refresh_rate) bool WindowInfo::QueryRefreshRateForWindow(const WindowInfo& wi, float* refresh_rate) {
{
#if defined(USE_X11) #if defined(USE_X11)
if (wi.type == WindowInfo::Type::X11) if (wi.type == WindowInfo::Type::X11)
return GetRefreshRateFromXRandR(wi, refresh_rate); return GetRefreshRateFromXRandR(wi, refresh_rate);
#endif #endif
return false; return false;
} }
#endif #endif

View file

@ -2,42 +2,32 @@
#include "../types.h" #include "../types.h"
// Contains the information required to create a graphics context in a window. // Contains the information required to create a graphics context in a window.
struct WindowInfo struct WindowInfo {
{ enum class Type {
enum class Type Surfaceless,
{ Win32,
Surfaceless, X11,
Win32, Wayland,
X11, MacOS,
Wayland, Android,
MacOS, Display,
Android, };
Display,
};
enum class SurfaceFormat enum class SurfaceFormat { None, Auto, RGB8, RGBA8, RGB565, Count };
{
None,
Auto,
RGB8,
RGBA8,
RGB565,
Count
};
Type type = Type::Surfaceless; Type type = Type::Surfaceless;
void* display_connection = nullptr; void* display_connection = nullptr;
void* window_handle = nullptr; void* window_handle = nullptr;
citra::u32 surface_width = 0; citra::u32 surface_width = 0;
citra::u32 surface_height = 0; citra::u32 surface_height = 0;
float surface_refresh_rate = 0.0f; float surface_refresh_rate = 0.0f;
float surface_scale = 1.0f; float surface_scale = 1.0f;
SurfaceFormat surface_format = SurfaceFormat::RGB8; SurfaceFormat surface_format = SurfaceFormat::RGB8;
// Needed for macOS. // Needed for macOS.
#ifdef __APPLE__ #ifdef __APPLE__
void* surface_handle = nullptr; void* surface_handle = nullptr;
#endif #endif
static bool QueryRefreshRateForWindow(const WindowInfo& wi, float* refresh_rate); static bool QueryRefreshRateForWindow(const WindowInfo& wi, float* refresh_rate);
}; };

View file

@ -19,21 +19,20 @@
#ifndef TYPES_H #ifndef TYPES_H
#define TYPES_H #define TYPES_H
#include <stdint.h>
#include <array> #include <array>
#include <stdint.h>
namespace citra namespace citra {
{ typedef uint8_t u8;
typedef uint8_t u8; typedef uint16_t u16;
typedef uint16_t u16; typedef uint32_t u32;
typedef uint32_t u32; typedef uint64_t u64;
typedef uint64_t u64; typedef int8_t s8;
typedef int8_t s8; typedef int16_t s16;
typedef int16_t s16; typedef int32_t s32;
typedef int32_t s32; typedef int64_t s64;
typedef int64_t s64;
template<class T, std::size_t A, std::size_t B> template <class T, std::size_t A, std::size_t B>
using array2d = std::array<std::array<T, B>, A>; using array2d = std::array<std::array<T, B>, A>;
} } // namespace citra
#endif // TYPES_H #endif // TYPES_H