GL: no contexts of the core's own, the frontend's libretro GL context alone

The OpenGL renderer draws on the context the frontend hands over
through libretro GL, and the core makes no context of its own: the
platform context code goes, EGL, EGLX11, EGL for Android, WGL, AGL,
BGL and the GLX window, with the shared contexts made for the shader
compiler's threads, and GLContext::Create makes the libretro context
alone. Programs are compiled on the frontend's context, so the OpenGL
backend reports no background compiling and its shader compiler runs
no threads; the shared-context compiler, its glFinish and VAO rebinds
and the driver bug that switched it off go with it. Android no longer
links libEGL.
This commit is contained in:
LibretroAdmin authored and libretroadmin committed 2026-11-07 01:36:34 +01:00
1 parent 94849325bd
commit a390228b51
26 files changed
+7 -2218

No files matched your search

+2 -2
View File
@@ -179,10 +179,10 @@ else ifneq (,$(filter android-arm64 android-x86_64,$(platform)))
CC = $(ANDROID_NDK_LLVM)/bin/clang --target=$(ANDROID_TRIPLE)
CXX = $(ANDROID_NDK_LLVM)/bin/clang++ --target=$(ANDROID_TRIPLE)
AR = $(ANDROID_NDK_LLVM)/bin/llvm-ar
FLAGS += -DANDROID -DHAVE_EGL=1 -I$(CORE_DIR)/Source/Android -funwind-tables
FLAGS += -DANDROID -I$(CORE_DIR)/Source/Android -funwind-tables
LDFLAGS += -static-libstdc++
HAVE_BUNDLED_ICONV := 1
LIBS += -lEGL -landroid -llog -latomic -lm -ldl
LIBS += -landroid -llog -latomic -lm -ldl
# Windows (MinGW-w64, native MSYS2 or cross)
else
-1
View File
@@ -1733,7 +1733,6 @@ ifeq ($(OS_ANDROID),1)
$(CORE_DIR)/Source/Android/jni/AndroidCommon/IDCache.cpp \
$(CORE_DIR)/Source/Core/Common/Logging/ConsoleListenerDroid.cpp \
$(CORE_DIR)/Source/Core/Common/MemArenaAndroid.cpp \
$(CORE_DIR)/Source/Core/Common/GL/GLInterface/EGL.cpp \
$(CORE_DIR)/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp
endif
ifeq ($(OS_APPLE),1)
-51
View File
@@ -5,27 +5,7 @@
#include "Common/GL/GLContext.h"
#if defined(__APPLE__) && !defined(__LIBRETRO__)
#include "Common/GL/GLInterface/AGL.h"
#endif
#if defined(_WIN32)
#include "Common/GL/GLInterface/WGL.h"
#endif
#if defined(__HAIKU__)
#include "Common/GL/GLInterface/BGL.h"
#endif
#if HAVE_EGL
#include "Common/GL/GLInterface/EGL.h"
#if HAVE_X11
#include "Common/GL/GLInterface/EGLX11.h"
#endif
#if defined(ANDROID) && !defined(__LIBRETRO__)
#include "Common/GL/GLInterface/EGLAndroid.h"
#endif
#endif
#if defined(__LIBRETRO__)
#include "DolphinLibretro/VideoContexts/GLContextLR.h"
#endif
const std::array<std::pair<int, int>, 9> GLContext::s_desktop_opengl_versions = {
{{4, 6}, {4, 5}, {4, 4}, {4, 3}, {4, 2}, {4, 1}, {4, 0}, {3, 3}, {3, 2}}};
@@ -42,11 +22,6 @@ bool GLContext::IsHeadless() const
return true;
}
std::unique_ptr<GLContext> GLContext::CreateSharedContext()
{
return nullptr;
}
bool GLContext::MakeCurrent()
{
return false;
@@ -82,34 +57,8 @@ std::unique_ptr<GLContext> GLContext::Create(const WindowSystemInfo& wsi, bool s
bool prefer_gles)
{
std::unique_ptr<GLContext> context;
#if defined(__APPLE__) && !defined(__LIBRETRO__)
if (wsi.type == WindowSystemType::MacOS || wsi.type == WindowSystemType::Headless)
context = std::make_unique<GLContextAGL>();
#endif
#if defined(_WIN32) && !defined(__LIBRETRO__)
if (wsi.type == WindowSystemType::Windows)
context = std::make_unique<GLContextWGL>();
#endif
#if defined(ANDROID) && !defined(__LIBRETRO__)
if (wsi.type == WindowSystemType::Android)
context = std::make_unique<GLContextEGLAndroid>();
#endif
#if defined(__HAIKU__)
if (wsi.type == WindowSystemType::Haiku)
context = std::make_unique<GLContextBGL>();
#endif
#if HAVE_EGL
#if HAVE_X11
if (wsi.type == WindowSystemType::X11)
context = std::make_unique<GLContextEGLX11>();
#endif
if (wsi.type == WindowSystemType::Headless || wsi.type == WindowSystemType::FBDev)
context = std::make_unique<GLContextEGL>();
#endif
#if defined(__LIBRETRO__)
if (wsi.type == WindowSystemType::Libretro)
context = std::make_unique<GLContextLR>();
#endif
if (!context)
return nullptr;
-3
View File
@@ -31,8 +31,6 @@ public:
virtual bool IsHeadless() const;
virtual std::unique_ptr<GLContext> CreateSharedContext();
virtual bool MakeCurrent();
virtual bool ClearCurrent();
@@ -57,7 +55,6 @@ protected:
// Window dimensions.
u32 m_backbuffer_width = 0;
u32 m_backbuffer_height = 0;
bool m_is_shared = false;
// A list of desktop OpenGL versions to attempt to create a context for.
// (4.6-3.2, geometry shaders is a minimum requirement since we're using core profile).
-42
View File
@@ -1,42 +0,0 @@
// Copyright 2008 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
// This define will silence all "OpenGL is deprecated, use Metal" warnings.
#define GL_SILENCE_DEPRECATION 1
#if defined(__APPLE__) && defined(__OBJC__)
#import <AppKit/AppKit.h>
#else
struct NSOpenGLContext;
struct NSOpenGLPixelFormat;
struct NSView;
#endif
#include "Common/GL/GLContext.h"
class GLContextAGL final : public GLContext
{
public:
~GLContextAGL() override;
bool IsHeadless() const override;
std::unique_ptr<GLContext> CreateSharedContext() override;
bool MakeCurrent() override;
bool ClearCurrent() override;
void Update() override;
void Swap() override;
void SwapInterval(int interval) override;
protected:
bool Initialize(const WindowSystemInfo& wsi, bool stereo, bool core) override;
NSView* m_view = nullptr;
NSOpenGLContext* m_context = nullptr;
NSOpenGLPixelFormat* m_pixel_format = nullptr;
};
-169
View File
@@ -1,169 +0,0 @@
// Copyright 2012 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "Common/GL/GLInterface/AGL.h"
#include "Common/Logging/Log.h"
// UpdateCachedDimensions and AttachContextToView contain calls to UI APIs, so they must only be
// called from the main thread or they risk crashing!
static bool UpdateCachedDimensions(NSView* view, u32* width, u32* height)
{
NSWindow* window = [view window];
NSSize size = [view frame].size;
const CGFloat scale = [window backingScaleFactor];
u32 new_width = static_cast<u32>(size.width * scale);
u32 new_height = static_cast<u32>(size.height * scale);
if (*width == new_width && *height == new_height)
return false;
*width = new_width;
*height = new_height;
return true;
}
static bool AttachContextToView(NSOpenGLContext* context, NSView* view, u32* width, u32* height)
{
// Enable high-resolution display support.
[view setWantsBestResolutionOpenGLSurface:YES];
NSWindow* window = [view window];
if (window == nil)
{
ERROR_LOG_FMT(VIDEO, "failed to get NSWindow");
return false;
}
(void)UpdateCachedDimensions(view, width, height);
[context setView:view];
return true;
}
GLContextAGL::~GLContextAGL()
{
if ([NSOpenGLContext currentContext] == m_context)
[NSOpenGLContext clearCurrentContext];
if (m_context)
{
[m_context clearDrawable];
[m_context release];
}
if (m_pixel_format)
[m_pixel_format release];
}
bool GLContextAGL::IsHeadless() const
{
return !m_view;
}
void GLContextAGL::Swap()
{
[m_context flushBuffer];
}
// Create rendering window.
// Call browser: Core.cpp:EmuThread() > main.cpp:Video_Initialize()
bool GLContextAGL::Initialize(const WindowSystemInfo& wsi, bool stereo, bool core)
{
NSOpenGLPixelFormatAttribute attr[] = {
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAOpenGLProfile,
core ? NSOpenGLProfileVersion3_2Core : NSOpenGLProfileVersionLegacy,
NSOpenGLPFAAccelerated,
stereo ? NSOpenGLPFAStereo : static_cast<NSOpenGLPixelFormatAttribute>(0),
0};
m_pixel_format = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr];
if (m_pixel_format == nil)
{
ERROR_LOG_FMT(VIDEO, "failed to create pixel format");
return false;
}
m_context = [[NSOpenGLContext alloc] initWithFormat:m_pixel_format shareContext:nil];
if (m_context == nil)
{
ERROR_LOG_FMT(VIDEO, "failed to create context");
return false;
}
if (!wsi.render_surface)
return true;
m_view = static_cast<NSView*>(wsi.render_surface);
m_opengl_mode = Mode::OpenGL;
__block bool success;
if ([NSThread isMainThread])
{
success = AttachContextToView(m_context, m_view, &m_backbuffer_width, &m_backbuffer_height);
}
else
{
dispatch_sync(dispatch_get_main_queue(), ^{
success = AttachContextToView(m_context, m_view, &m_backbuffer_width, &m_backbuffer_height);
});
}
if (!success)
{
return false;
}
[m_context makeCurrentContext];
return true;
}
std::unique_ptr<GLContext> GLContextAGL::CreateSharedContext()
{
NSOpenGLContext* new_agl_context = [[NSOpenGLContext alloc] initWithFormat:m_pixel_format
shareContext:m_context];
if (new_agl_context == nil)
{
ERROR_LOG_FMT(VIDEO, "failed to create shared context");
return nullptr;
}
std::unique_ptr<GLContextAGL> new_context = std::make_unique<GLContextAGL>();
new_context->m_context = new_agl_context;
new_context->m_pixel_format = m_pixel_format;
[new_context->m_pixel_format retain];
new_context->m_is_shared = true;
return new_context;
}
bool GLContextAGL::MakeCurrent()
{
[m_context makeCurrentContext];
return true;
}
bool GLContextAGL::ClearCurrent()
{
[NSOpenGLContext clearCurrentContext];
return true;
}
void GLContextAGL::Update()
{
if (!m_view)
return;
dispatch_sync(dispatch_get_main_queue(), ^{
if (UpdateCachedDimensions(m_view, &m_backbuffer_width, &m_backbuffer_height))
{
[m_context update];
}
});
}
void GLContextAGL::SwapInterval(int interval)
{
[m_context setValues:static_cast<GLint*>(&interval) forParameter:NSOpenGLCPSwapInterval];
}
-95
View File
@@ -1,95 +0,0 @@
// Copyright 2017 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "Common/GL/GLInterface/BGL.h"
#include <GLView.h>
#include <Size.h>
#include <Window.h>
#include "Common/Assert.h"
BGLView* GLContextBGL::s_current = nullptr;
GLContextBGL::~GLContextBGL()
{
if (!m_window)
delete m_gl;
}
bool GLContextBGL::Initialize(const WindowSystemInfo& wsi, bool stereo, bool core)
{
m_window = static_cast<BWindow*>(wsi.render_window);
m_gl = new BGLView(m_window ? m_window->Bounds() : BRect(), "GLContextBGL", B_FOLLOW_ALL_SIDES, 0,
BGL_RGB | BGL_DOUBLE | BGL_ALPHA);
if (m_window)
m_window->AddChild(m_gl);
m_opengl_mode = Mode::OpenGL;
m_gl->LockLooper();
BRect size = m_gl->Frame();
m_gl->UnlockLooper();
m_backbuffer_width = size.IntegerWidth();
m_backbuffer_height = size.IntegerHeight();
MakeCurrent();
return true;
}
bool GLContextBGL::IsHeadless() const
{
return m_window == nullptr;
}
bool GLContextBGL::MakeCurrent()
{
if (s_current)
s_current->UnlockGL();
m_gl->LockGL();
s_current = m_gl;
return true;
}
bool GLContextBGL::ClearCurrent()
{
if (!s_current)
return true;
ASSERT(m_gl == s_current);
s_current->UnlockGL();
s_current = nullptr;
return true;
}
void GLContextBGL::Swap()
{
m_gl->SwapBuffers();
}
void GLContextBGL::Update()
{
m_gl->LockLooper();
BRect size = m_gl->Frame();
if (m_backbuffer_width == size.IntegerWidth() && m_backbuffer_height == size.IntegerHeight())
{
m_gl->UnlockLooper();
return;
}
ClearCurrent();
m_gl->FrameResized(size.Width(), size.Height());
MakeCurrent();
m_gl->UnlockLooper();
m_backbuffer_width = size.IntegerWidth();
m_backbuffer_height = size.IntegerHeight();
}
void* GLContextBGL::GetFuncAddress(const std::string& name)
{
return m_gl->GetGLProcAddress(name.c_str());
}
-35
View File
@@ -1,35 +0,0 @@
// Copyright 2017 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "Common/GL/GLContext.h"
class BWindow;
class BGLView;
class GLContextBGL final : public GLContext
{
public:
~GLContextBGL() override;
bool IsHeadless() const override;
bool MakeCurrent() override;
bool ClearCurrent() override;
void Update() override;
void Swap() override;
void* GetFuncAddress(const std::string& name) override;
protected:
bool Initialize(const WindowSystemInfo& wsi, bool stereo, bool core) override;
private:
static BGLView* s_current;
BWindow* m_window;
BGLView* m_gl;
};
-377
View File
@@ -1,377 +0,0 @@
// Copyright 2012 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "Common/GL/GLInterface/EGL.h"
#include <array>
#include <sstream>
#include <vector>
#include "Common/Logging/Log.h"
#ifndef EGL_KHR_create_context
#define EGL_KHR_create_context 1
#define EGL_CONTEXT_MAJOR_VERSION_KHR 0x3098
#define EGL_CONTEXT_MINOR_VERSION_KHR 0x30FB
#define EGL_CONTEXT_FLAGS_KHR 0x30FC
#define EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR 0x30FD
#define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR 0x31BD
#define EGL_NO_RESET_NOTIFICATION_KHR 0x31BE
#define EGL_LOSE_CONTEXT_ON_RESET_KHR 0x31BF
#define EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR 0x00000001
#define EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR 0x00000002
#define EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR 0x00000004
#define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR 0x00000001
#define EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR 0x00000002
#define EGL_OPENGL_ES3_BIT_KHR 0x00000040
#endif /* EGL_KHR_create_context */
GLContextEGL::~GLContextEGL()
{
DestroyWindowSurface();
DestroyContext();
}
bool GLContextEGL::IsHeadless() const
{
return m_wsi.type == WindowSystemType::Headless;
}
void GLContextEGL::Swap()
{
if (m_egl_surface != EGL_NO_SURFACE)
eglSwapBuffers(m_egl_display, m_egl_surface);
}
void GLContextEGL::SwapInterval(int interval)
{
eglSwapInterval(m_egl_display, interval);
}
void* GLContextEGL::GetFuncAddress(const std::string& name)
{
return (void*)eglGetProcAddress(name.c_str());
}
void GLContextEGL::DetectMode()
{
EGLint num_configs;
bool supportsGL = false, supportsGLES3 = false;
std::array<int, 3> renderable_types{{EGL_OPENGL_BIT, EGL_OPENGL_ES3_BIT_KHR}};
for (auto renderable_type : renderable_types)
{
// attributes for a visual in RGBA format with at least
// 8 bits per color
int attribs[] = {EGL_RED_SIZE,
8,
EGL_GREEN_SIZE,
8,
EGL_BLUE_SIZE,
8,
EGL_RENDERABLE_TYPE,
renderable_type,
EGL_SURFACE_TYPE,
IsHeadless() ? 0 : EGL_WINDOW_BIT,
EGL_NONE};
// Get how many configs there are
if (!eglChooseConfig(m_egl_display, attribs, nullptr, 0, &num_configs))
{
INFO_LOG_FMT(VIDEO, "Error: couldn't get an EGL visual config");
continue;
}
EGLConfig* config = new EGLConfig[num_configs];
// Get all the configurations
if (!eglChooseConfig(m_egl_display, attribs, config, num_configs, &num_configs))
{
INFO_LOG_FMT(VIDEO, "Error: couldn't get an EGL visual config");
delete[] config;
continue;
}
for (int i = 0; i < num_configs; ++i)
{
EGLint attribVal;
bool ret;
ret = eglGetConfigAttrib(m_egl_display, config[i], EGL_RENDERABLE_TYPE, &attribVal);
if (ret)
{
if (attribVal & EGL_OPENGL_BIT)
supportsGL = true;
if (attribVal & EGL_OPENGL_ES3_BIT_KHR)
supportsGLES3 = true;
}
}
delete[] config;
}
if (supportsGL)
{
INFO_LOG_FMT(VIDEO, "Using OpenGL");
m_opengl_mode = Mode::OpenGL;
}
else if (supportsGLES3)
{
INFO_LOG_FMT(VIDEO, "Using OpenGL|ES");
m_opengl_mode = Mode::OpenGLES;
}
else
{
// Errored before we found a mode
ERROR_LOG_FMT(VIDEO, "Error: Failed to detect OpenGL flavour, falling back to OpenGL");
// This will fail to create a context, as it'll try to use the same attribs we just failed to
// find a matching config with
m_opengl_mode = Mode::OpenGL;
}
}
EGLDisplay GLContextEGL::OpenEGLDisplay()
{
return eglGetDisplay(static_cast<EGLNativeDisplayType>(m_wsi.render_surface));
}
EGLNativeWindowType GLContextEGL::GetEGLNativeWindow(EGLConfig config)
{
return reinterpret_cast<EGLNativeWindowType>(m_wsi.display_connection);
}
// Create rendering window.
// Call browser: Core.cpp:EmuThread() > main.cpp:Video_Initialize()
bool GLContextEGL::Initialize(const WindowSystemInfo& wsi, bool stereo, bool core)
{
EGLint egl_major, egl_minor;
bool supports_core_profile = false;
m_wsi = wsi;
m_egl_display = OpenEGLDisplay();
if (!m_egl_display)
{
INFO_LOG_FMT(VIDEO, "Error: eglGetDisplay() failed");
return false;
}
if (!eglInitialize(m_egl_display, &egl_major, &egl_minor))
{
INFO_LOG_FMT(VIDEO, "Error: eglInitialize() failed");
return false;
}
/* Detection code */
EGLint num_configs;
if (m_opengl_mode == Mode::Detect)
DetectMode();
// attributes for a visual in RGBA format with at least
// 8 bits per color
int attribs[] = {EGL_RENDERABLE_TYPE,
0,
EGL_RED_SIZE,
8,
EGL_GREEN_SIZE,
8,
EGL_BLUE_SIZE,
8,
EGL_SURFACE_TYPE,
IsHeadless() ? 0 : EGL_WINDOW_BIT,
EGL_NONE};
std::vector<EGLint> ctx_attribs;
switch (m_opengl_mode)
{
case Mode::OpenGL:
attribs[1] = EGL_OPENGL_BIT;
ctx_attribs = {EGL_NONE};
break;
case Mode::OpenGLES:
attribs[1] = EGL_OPENGL_ES3_BIT_KHR;
ctx_attribs = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE};
break;
default:
ERROR_LOG_FMT(VIDEO, "Unknown opengl mode set");
return false;
}
if (!eglChooseConfig(m_egl_display, attribs, &m_config, 1, &num_configs))
{
INFO_LOG_FMT(VIDEO, "Error: couldn't get an EGL visual config");
return false;
}
if (m_opengl_mode == Mode::OpenGL)
eglBindAPI(EGL_OPENGL_API);
else
eglBindAPI(EGL_OPENGL_ES_API);
std::string tmp;
std::istringstream buffer(eglQueryString(m_egl_display, EGL_EXTENSIONS));
while (buffer >> tmp)
{
if (tmp == "EGL_KHR_surfaceless_context")
m_supports_surfaceless = true;
else if (tmp == "EGL_KHR_create_context")
supports_core_profile = true;
}
if (supports_core_profile && core && m_opengl_mode == Mode::OpenGL)
{
for (const auto& version : s_desktop_opengl_versions)
{
std::vector<EGLint> core_attribs = {EGL_CONTEXT_MAJOR_VERSION_KHR,
version.first,
EGL_CONTEXT_MINOR_VERSION_KHR,
version.second,
EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR,
EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR,
EGL_CONTEXT_FLAGS_KHR,
EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR,
EGL_NONE};
m_egl_context = eglCreateContext(m_egl_display, m_config, EGL_NO_CONTEXT, &core_attribs[0]);
if (m_egl_context)
{
m_attribs = std::move(core_attribs);
break;
}
}
}
if (!m_egl_context)
{
m_egl_context = eglCreateContext(m_egl_display, m_config, EGL_NO_CONTEXT, &ctx_attribs[0]);
m_attribs = std::move(ctx_attribs);
}
if (!m_egl_context)
{
INFO_LOG_FMT(VIDEO, "Error: eglCreateContext failed");
return false;
}
if (!CreateWindowSurface())
{
ERROR_LOG_FMT(VIDEO, "Error: CreateWindowSurface failed {:#06x}", eglGetError());
return false;
}
return MakeCurrent();
}
std::unique_ptr<GLContext> GLContextEGL::CreateSharedContext()
{
eglBindAPI(m_opengl_mode == Mode::OpenGL ? EGL_OPENGL_API : EGL_OPENGL_ES_API);
EGLContext new_egl_context =
eglCreateContext(m_egl_display, m_config, m_egl_context, m_attribs.data());
if (!new_egl_context)
{
INFO_LOG_FMT(VIDEO, "Error: eglCreateContext failed {:#06x}", eglGetError());
return nullptr;
}
std::unique_ptr<GLContextEGL> new_context = std::make_unique<GLContextEGL>();
new_context->m_opengl_mode = m_opengl_mode;
new_context->m_egl_context = new_egl_context;
new_context->m_wsi.display_connection = m_wsi.display_connection;
new_context->m_egl_display = m_egl_display;
new_context->m_config = m_config;
new_context->m_supports_surfaceless = m_supports_surfaceless;
new_context->m_is_shared = true;
if (!new_context->CreateWindowSurface())
{
ERROR_LOG_FMT(VIDEO, "Error: CreateWindowSurface failed {:#06x}", eglGetError());
return nullptr;
}
return new_context;
}
bool GLContextEGL::CreateWindowSurface()
{
if (!IsHeadless())
{
EGLNativeWindowType native_window = GetEGLNativeWindow(m_config);
m_egl_surface = eglCreateWindowSurface(m_egl_display, m_config, native_window, nullptr);
if (!m_egl_surface)
{
INFO_LOG_FMT(VIDEO, "Error: eglCreateWindowSurface failed");
return false;
}
// Get dimensions from the surface.
EGLint surface_width = 1, surface_height = 1;
if (!eglQuerySurface(m_egl_display, m_egl_surface, EGL_WIDTH, &surface_width) ||
!eglQuerySurface(m_egl_display, m_egl_surface, EGL_HEIGHT, &surface_height))
{
WARN_LOG_FMT(VIDEO,
"Failed to get surface dimensions via eglQuerySurface. Size may be incorrect.");
}
m_backbuffer_width = static_cast<int>(surface_width);
m_backbuffer_height = static_cast<int>(surface_height);
}
else if (!m_supports_surfaceless)
{
EGLint attrib_list[] = {
EGL_NONE,
};
m_egl_surface = eglCreatePbufferSurface(m_egl_display, m_config, attrib_list);
if (!m_egl_surface)
{
INFO_LOG_FMT(VIDEO, "Error: eglCreatePbufferSurface failed");
return false;
}
}
else
{
m_egl_surface = EGL_NO_SURFACE;
}
return true;
}
void GLContextEGL::DestroyWindowSurface()
{
if (m_egl_surface == EGL_NO_SURFACE)
return;
if (eglGetCurrentSurface(EGL_DRAW) == m_egl_surface)
eglMakeCurrent(m_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (!eglDestroySurface(m_egl_display, m_egl_surface))
NOTICE_LOG_FMT(VIDEO, "Could not destroy window surface.");
m_egl_surface = EGL_NO_SURFACE;
}
bool GLContextEGL::MakeCurrent()
{
return eglMakeCurrent(m_egl_display, m_egl_surface, m_egl_surface, m_egl_context);
}
void GLContextEGL::UpdateSurface(void* window_handle)
{
m_wsi.render_surface = window_handle;
ClearCurrent();
DestroyWindowSurface();
CreateWindowSurface();
MakeCurrent();
}
bool GLContextEGL::ClearCurrent()
{
return eglMakeCurrent(m_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
}
// Close backend
void GLContextEGL::DestroyContext()
{
if (!m_egl_context)
return;
if (eglGetCurrentContext() == m_egl_context)
eglMakeCurrent(m_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (!eglDestroyContext(m_egl_display, m_egl_context))
NOTICE_LOG_FMT(VIDEO, "Could not destroy drawing context.");
if (!m_is_shared && !eglTerminate(m_egl_display))
NOTICE_LOG_FMT(VIDEO, "Could not destroy display connection.");
m_egl_context = EGL_NO_CONTEXT;
m_egl_display = EGL_NO_DISPLAY;
}
-52
View File
@@ -1,52 +0,0 @@
// Copyright 2008 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <string>
#include <vector>
#include "Common/GL/GLContext.h"
class GLContextEGL : public GLContext
{
public:
virtual ~GLContextEGL() override;
bool IsHeadless() const override;
std::unique_ptr<GLContext> CreateSharedContext() override;
bool MakeCurrent() override;
bool ClearCurrent() override;
void UpdateSurface(void* window_handle) override;
void Swap() override;
void SwapInterval(int interval) override;
void* GetFuncAddress(const std::string& name) override;
protected:
virtual EGLDisplay OpenEGLDisplay();
virtual EGLNativeWindowType GetEGLNativeWindow(EGLConfig config);
bool Initialize(const WindowSystemInfo& wsi, bool stereo, bool core) override;
bool CreateWindowSurface();
void DestroyWindowSurface();
void DetectMode();
void DestroyContext();
WindowSystemInfo m_wsi = {};
EGLConfig m_config;
bool m_supports_surfaceless = false;
std::vector<int> m_attribs;
EGLSurface m_egl_surface = EGL_NO_SURFACE;
EGLContext m_egl_context = EGL_NO_CONTEXT;
EGLDisplay m_egl_display = EGL_NO_DISPLAY;
};
@@ -1,21 +0,0 @@
// Copyright 2014 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "Common/GL/GLInterface/EGLAndroid.h"
#include <android/native_window.h>
EGLDisplay GLContextEGLAndroid::OpenEGLDisplay()
{
return eglGetDisplay(EGL_DEFAULT_DISPLAY);
}
EGLNativeWindowType GLContextEGLAndroid::GetEGLNativeWindow(EGLConfig config)
{
EGLint format;
eglGetConfigAttrib(m_egl_display, config, EGL_NATIVE_VISUAL_ID, &format);
ANativeWindow_setBuffersGeometry(static_cast<ANativeWindow*>(m_wsi.render_surface), 0, 0, format);
m_backbuffer_width = ANativeWindow_getWidth(static_cast<ANativeWindow*>(m_wsi.render_surface));
m_backbuffer_height = ANativeWindow_getHeight(static_cast<ANativeWindow*>(m_wsi.render_surface));
return static_cast<EGLNativeWindowType>(m_wsi.render_surface);
}
@@ -1,13 +0,0 @@
// Copyright 2014 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "Common/GL/GLInterface/EGL.h"
class GLContextEGLAndroid final : public GLContextEGL
{
protected:
EGLDisplay OpenEGLDisplay() override;
EGLNativeWindowType GetEGLNativeWindow(EGLConfig config) override;
};
@@ -1,66 +0,0 @@
// Copyright 2014 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "Common/GL/GLInterface/EGLX11.h"
#include <cstdlib>
#include "Common/StringUtil.h"
GLContextEGLX11::GLContextEGLX11()
{
// HACK: If EGL_PLATFORM is set to "wayland", Dolphin will still use GLContextEGLX11 due to our
// lack of proper Wayland support, and crash.
// Some distros have started aggressively setting that environment variable, bypassing the usual
// runtime detection. Thus, clear that environment variable if it forces wayland.
const char* current_egl_platform = getenv("EGL_PLATFORM");
const bool replace_egl_platform =
current_egl_platform != nullptr &&
Common::CaseInsensitiveContains(current_egl_platform, "wayland");
setenv("EGL_PLATFORM", "", replace_egl_platform);
}
GLContextEGLX11::~GLContextEGLX11()
{
// The context must be destroyed before the window.
DestroyWindowSurface();
DestroyContext();
m_render_window.reset();
}
void GLContextEGLX11::Update()
{
m_render_window->UpdateDimensions();
m_backbuffer_width = m_render_window->GetWidth();
m_backbuffer_height = m_render_window->GetHeight();
}
EGLDisplay GLContextEGLX11::OpenEGLDisplay()
{
return eglGetDisplay(static_cast<Display*>(m_wsi.display_connection));
}
EGLNativeWindowType GLContextEGLX11::GetEGLNativeWindow(EGLConfig config)
{
EGLint vid;
eglGetConfigAttrib(m_egl_display, config, EGL_NATIVE_VISUAL_ID, &vid);
XVisualInfo visTemplate = {};
visTemplate.visualid = vid;
int nVisuals;
XVisualInfo* vi = XGetVisualInfo(static_cast<Display*>(m_wsi.display_connection), VisualIDMask,
&visTemplate, &nVisuals);
if (m_render_window)
m_render_window.reset();
m_render_window = GLX11Window::Create(static_cast<Display*>(m_wsi.display_connection),
reinterpret_cast<Window>(m_wsi.render_surface), vi);
m_backbuffer_width = m_render_window->GetWidth();
m_backbuffer_height = m_render_window->GetHeight();
XFree(vi);
return reinterpret_cast<EGLNativeWindowType>(m_render_window->GetWindow());
}
@@ -1,24 +0,0 @@
// Copyright 2014 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <X11/Xlib.h>
#include "Common/GL/GLInterface/EGL.h"
#include "Common/GL/GLX11Window.h"
class GLContextEGLX11 final : public GLContextEGL
{
public:
GLContextEGLX11();
~GLContextEGLX11() override;
void Update() override;
protected:
EGLDisplay OpenEGLDisplay() override;
EGLNativeWindowType GetEGLNativeWindow(EGLConfig config) override;
std::unique_ptr<GLX11Window> m_render_window;
};
-491
View File
@@ -1,491 +0,0 @@
// Copyright 2012 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "Common/GL/GLInterface/WGL.h"
#include <windows.h>
#include <array>
#include <string>
#include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"
// from wglext.h
#ifndef WGL_ARB_pbuffer
#define WGL_ARB_pbuffer 1
DECLARE_HANDLE(HPBUFFERARB);
#define WGL_DRAW_TO_PBUFFER_ARB 0x202D
#define WGL_MAX_PBUFFER_PIXELS_ARB 0x202E
#define WGL_MAX_PBUFFER_WIDTH_ARB 0x202F
#define WGL_MAX_PBUFFER_HEIGHT_ARB 0x2030
#define WGL_PBUFFER_LARGEST_ARB 0x2033
#define WGL_PBUFFER_WIDTH_ARB 0x2034
#define WGL_PBUFFER_HEIGHT_ARB 0x2035
#define WGL_PBUFFER_LOST_ARB 0x2036
typedef HPBUFFERARB(WINAPI* PFNWGLCREATEPBUFFERARBPROC)(HDC hDC, int iPixelFormat, int iWidth,
int iHeight, const int* piAttribList);
typedef HDC(WINAPI* PFNWGLGETPBUFFERDCARBPROC)(HPBUFFERARB hPbuffer);
typedef int(WINAPI* PFNWGLRELEASEPBUFFERDCARBPROC)(HPBUFFERARB hPbuffer, HDC hDC);
typedef BOOL(WINAPI* PFNWGLDESTROYPBUFFERARBPROC)(HPBUFFERARB hPbuffer);
typedef BOOL(WINAPI* PFNWGLQUERYPBUFFERARBPROC)(HPBUFFERARB hPbuffer, int iAttribute, int* piValue);
#endif /* WGL_ARB_pbuffer */
#ifndef WGL_ARB_pixel_format
#define WGL_ARB_pixel_format 1
#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000
#define WGL_DRAW_TO_WINDOW_ARB 0x2001
#define WGL_DRAW_TO_BITMAP_ARB 0x2002
#define WGL_ACCELERATION_ARB 0x2003
#define WGL_NEED_PALETTE_ARB 0x2004
#define WGL_NEED_SYSTEM_PALETTE_ARB 0x2005
#define WGL_SWAP_LAYER_BUFFERS_ARB 0x2006
#define WGL_SWAP_METHOD_ARB 0x2007
#define WGL_NUMBER_OVERLAYS_ARB 0x2008
#define WGL_NUMBER_UNDERLAYS_ARB 0x2009
#define WGL_TRANSPARENT_ARB 0x200A
#define WGL_TRANSPARENT_RED_VALUE_ARB 0x2037
#define WGL_TRANSPARENT_GREEN_VALUE_ARB 0x2038
#define WGL_TRANSPARENT_BLUE_VALUE_ARB 0x2039
#define WGL_TRANSPARENT_ALPHA_VALUE_ARB 0x203A
#define WGL_TRANSPARENT_INDEX_VALUE_ARB 0x203B
#define WGL_SHARE_DEPTH_ARB 0x200C
#define WGL_SHARE_STENCIL_ARB 0x200D
#define WGL_SHARE_ACCUM_ARB 0x200E
#define WGL_SUPPORT_GDI_ARB 0x200F
#define WGL_SUPPORT_OPENGL_ARB 0x2010
#define WGL_DOUBLE_BUFFER_ARB 0x2011
#define WGL_STEREO_ARB 0x2012
#define WGL_PIXEL_TYPE_ARB 0x2013
#define WGL_COLOR_BITS_ARB 0x2014
#define WGL_RED_BITS_ARB 0x2015
#define WGL_RED_SHIFT_ARB 0x2016
#define WGL_GREEN_BITS_ARB 0x2017
#define WGL_GREEN_SHIFT_ARB 0x2018
#define WGL_BLUE_BITS_ARB 0x2019
#define WGL_BLUE_SHIFT_ARB 0x201A
#define WGL_ALPHA_BITS_ARB 0x201B
#define WGL_ALPHA_SHIFT_ARB 0x201C
#define WGL_ACCUM_BITS_ARB 0x201D
#define WGL_ACCUM_RED_BITS_ARB 0x201E
#define WGL_ACCUM_GREEN_BITS_ARB 0x201F
#define WGL_ACCUM_BLUE_BITS_ARB 0x2020
#define WGL_ACCUM_ALPHA_BITS_ARB 0x2021
#define WGL_DEPTH_BITS_ARB 0x2022
#define WGL_STENCIL_BITS_ARB 0x2023
#define WGL_AUX_BUFFERS_ARB 0x2024
#define WGL_NO_ACCELERATION_ARB 0x2025
#define WGL_GENERIC_ACCELERATION_ARB 0x2026
#define WGL_FULL_ACCELERATION_ARB 0x2027
#define WGL_SWAP_EXCHANGE_ARB 0x2028
#define WGL_SWAP_COPY_ARB 0x2029
#define WGL_SWAP_UNDEFINED_ARB 0x202A
#define WGL_TYPE_RGBA_ARB 0x202B
#define WGL_TYPE_COLORINDEX_ARB 0x202C
typedef BOOL(WINAPI* PFNWGLGETPIXELFORMATATTRIBIVARBPROC)(HDC hdc, int iPixelFormat,
int iLayerPlane, UINT nAttributes,
const int* piAttributes, int* piValues);
typedef BOOL(WINAPI* PFNWGLGETPIXELFORMATATTRIBFVARBPROC)(HDC hdc, int iPixelFormat,
int iLayerPlane, UINT nAttributes,
const int* piAttributes, FLOAT* pfValues);
typedef BOOL(WINAPI* PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC hdc, const int* piAttribIList,
const FLOAT* pfAttribFList, UINT nMaxFormats,
int* piFormats, UINT* nNumFormats);
#endif /* WGL_ARB_pixel_format */
#ifndef WGL_ARB_create_context
#define WGL_ARB_create_context 1
#define WGL_CONTEXT_DEBUG_BIT_ARB 0x00000001
#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002
#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
#define WGL_CONTEXT_LAYER_PLANE_ARB 0x2093
#define WGL_CONTEXT_FLAGS_ARB 0x2094
#define ERROR_INVALID_VERSION_ARB 0x2095
typedef HGLRC(WINAPI* PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hDC, HGLRC hShareContext,
const int* attribList);
#endif /* WGL_ARB_create_context */
#ifndef WGL_ARB_create_context_profile
#define WGL_ARB_create_context_profile 1
#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
#define ERROR_INVALID_PROFILE_ARB 0x2096
#endif /* WGL_ARB_create_context_profile */
#ifndef WGL_EXT_swap_control
#define WGL_EXT_swap_control 1
typedef BOOL(WINAPI* PFNWGLSWAPINTERVALEXTPROC)(int interval);
typedef int(WINAPI* PFNWGLGETSWAPINTERVALEXTPROC)(void);
#endif /* WGL_EXT_swap_control */
// Persistent pointers
static PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = nullptr;
static PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = nullptr;
static PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = nullptr;
static PFNWGLCREATEPBUFFERARBPROC wglCreatePbufferARB = nullptr;
static PFNWGLGETPBUFFERDCARBPROC wglGetPbufferDCARB = nullptr;
static PFNWGLRELEASEPBUFFERDCARBPROC wglReleasePbufferDCARB = nullptr;
static PFNWGLDESTROYPBUFFERARBPROC wglDestroyPbufferARB = nullptr;
static void LoadWGLExtensions()
{
wglSwapIntervalEXT =
reinterpret_cast<PFNWGLSWAPINTERVALEXTPROC>(wglGetProcAddress("wglSwapIntervalEXT"));
wglCreateContextAttribsARB = reinterpret_cast<PFNWGLCREATECONTEXTATTRIBSARBPROC>(
wglGetProcAddress("wglCreateContextAttribsARB"));
wglChoosePixelFormatARB = reinterpret_cast<PFNWGLCHOOSEPIXELFORMATARBPROC>(
wglGetProcAddress("wglChoosePixelFormatARB"));
wglCreatePbufferARB =
reinterpret_cast<PFNWGLCREATEPBUFFERARBPROC>(wglGetProcAddress("wglCreatePbufferARB"));
wglGetPbufferDCARB =
reinterpret_cast<PFNWGLGETPBUFFERDCARBPROC>(wglGetProcAddress("wglGetPbufferDCARB"));
wglReleasePbufferDCARB =
reinterpret_cast<PFNWGLRELEASEPBUFFERDCARBPROC>(wglGetProcAddress("wglReleasePbufferDCARB"));
wglDestroyPbufferARB =
reinterpret_cast<PFNWGLDESTROYPBUFFERARBPROC>(wglGetProcAddress("wglDestroyPbufferARB"));
}
static void ClearWGLExtensionPointers()
{
wglSwapIntervalEXT = nullptr;
wglCreateContextAttribsARB = nullptr;
wglChoosePixelFormatARB = nullptr;
wglCreatePbufferARB = nullptr;
wglGetPbufferDCARB = nullptr;
wglReleasePbufferDCARB = nullptr;
wglDestroyPbufferARB = nullptr;
}
GLContextWGL::~GLContextWGL()
{
if (m_rc)
{
if (wglGetCurrentContext() == m_rc && !wglMakeCurrent(m_dc, nullptr))
NOTICE_LOG_FMT(VIDEO, "Could not release drawing context.");
if (!wglDeleteContext(m_rc))
ERROR_LOG_FMT(VIDEO, "Attempt to release rendering context failed.");
m_rc = nullptr;
}
if (m_dc)
{
if (m_pbuffer_handle)
{
wglReleasePbufferDCARB(static_cast<HPBUFFERARB>(m_pbuffer_handle), m_dc);
m_dc = nullptr;
wglDestroyPbufferARB(static_cast<HPBUFFERARB>(m_pbuffer_handle));
m_pbuffer_handle = nullptr;
}
else
{
if (!ReleaseDC(m_window_handle, m_dc))
ERROR_LOG_FMT(VIDEO, "Attempt to release device context failed.");
m_dc = nullptr;
}
}
}
bool GLContextWGL::IsHeadless() const
{
return !m_window_handle;
}
void GLContextWGL::SwapInterval(int interval)
{
if (wglSwapIntervalEXT)
{
wglSwapIntervalEXT(interval);
}
else
{
ERROR_LOG_FMT(VIDEO,
"No support for SwapInterval (framerate clamped to monitor refresh rate).");
}
}
void GLContextWGL::Swap()
{
SwapBuffers(m_dc);
}
void* GLContextWGL::GetFuncAddress(const std::string& name)
{
FARPROC func = wglGetProcAddress(name.c_str());
if (func == nullptr)
{
// Using GetModuleHandle here is okay, since we import functions from opengl32.dll, it's
// guaranteed to be loaded.
HMODULE opengl_module = GetModuleHandle(TEXT("opengl32.dll"));
func = GetProcAddress(opengl_module, name.c_str());
}
return reinterpret_cast<void*>(func);
}
// Create rendering window.
// Call browser: Core.cpp:EmuThread() > main.cpp:Video_Initialize()
bool GLContextWGL::Initialize(const WindowSystemInfo& wsi, bool stereo, bool core)
{
if (!wsi.render_surface)
return false;
RECT window_rect = {};
m_window_handle = static_cast<HWND>(wsi.render_surface);
if (!GetClientRect(m_window_handle, &window_rect))
return false;
// Clear extension function pointers before creating the first context.
ClearWGLExtensionPointers();
// Control window size and picture scaling
int twidth = window_rect.right - window_rect.left;
int theight = window_rect.bottom - window_rect.top;
m_backbuffer_width = twidth;
m_backbuffer_height = theight;
const DWORD stereo_flag = stereo ? PFD_STEREO : 0;
// clang-format off
static const PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor
1, // Version Number
PFD_DRAW_TO_WINDOW | // Format Must Support Window
PFD_SUPPORT_OPENGL | // Format Must Support OpenGL
PFD_DOUBLEBUFFER | // Must Support Double Buffering
stereo_flag, // Could Support Quad Buffering
PFD_TYPE_RGBA, // Request An RGBA Format
32, // Select Our Color Depth
0,
0, 0, 0, 0, 0, // Color Bits Ignored
0, // 8bit Alpha Buffer
0, // Shift Bit Ignored
0, // No Accumulation Buffer
0, 0, 0, 0, // Accumulation Bits Ignored
0, // 0Bit Z-Buffer (Depth Buffer)
0, // 0bit Stencil Buffer
0, // No Auxiliary Buffer
PFD_MAIN_PLANE, // Main Drawing Layer
0, // Reserved
0, 0, 0 // Layer Masks Ignored
};
// clang-format on
m_dc = GetDC(m_window_handle);
if (!m_dc)
{
PanicAlertFmt("(1) Can't create an OpenGL Device context. Fail.");
return false;
}
const int pixel_format = ChoosePixelFormat(m_dc, &pfd);
if (pixel_format == 0)
{
PanicAlertFmt("(2) Can't find a suitable PixelFormat.");
return false;
}
if (!SetPixelFormat(m_dc, pixel_format, &pfd))
{
PanicAlertFmt("(3) Can't set the PixelFormat.");
return false;
}
m_rc = wglCreateContext(m_dc);
if (!m_rc)
{
PanicAlertFmt("(4) Can't create an OpenGL rendering context.");
return false;
}
// WGL only supports desktop GL, for now.
m_opengl_mode = Mode::OpenGL;
if (core)
{
// Make the fallback context current, temporarily.
// This is because we need an active context to use wglCreateContextAttribsARB.
if (!wglMakeCurrent(m_dc, m_rc))
{
PanicAlertFmt("(5) Can't make dummy WGL context current.");
return false;
}
// Load WGL extension function pointers.
LoadWGLExtensions();
// Attempt creating a core context.
HGLRC core_context = CreateCoreContext(m_dc, nullptr);
// Switch out the temporary context before continuing, regardless of whether we got a core
// context. If we didn't get a core context, the caller expects that the context is not current.
if (!wglMakeCurrent(m_dc, nullptr))
{
PanicAlertFmt("(6) Failed to switch out temporary context");
return false;
}
// If we got a core context, destroy and replace the temporary context.
if (core_context)
{
wglDeleteContext(m_rc);
m_rc = core_context;
}
else
{
WARN_LOG_FMT(VIDEO, "Failed to create a core OpenGL context. Using fallback context.");
}
}
return MakeCurrent();
}
std::unique_ptr<GLContext> GLContextWGL::CreateSharedContext()
{
// WGL does not support surfaceless contexts, so we use a 1x1 pbuffer instead.
HANDLE pbuffer;
HDC dc;
if (!CreatePBuffer(m_dc, 1, 1, &pbuffer, &dc))
return nullptr;
HGLRC rc = CreateCoreContext(dc, m_rc);
if (!rc)
{
wglReleasePbufferDCARB(static_cast<HPBUFFERARB>(pbuffer), dc);
wglDestroyPbufferARB(static_cast<HPBUFFERARB>(pbuffer));
return nullptr;
}
std::unique_ptr<GLContextWGL> context = std::make_unique<GLContextWGL>();
context->m_pbuffer_handle = pbuffer;
context->m_dc = dc;
context->m_rc = rc;
context->m_opengl_mode = m_opengl_mode;
context->m_is_shared = true;
return context;
}
HGLRC GLContextWGL::CreateCoreContext(HDC dc, HGLRC share_context)
{
if (!wglCreateContextAttribsARB)
{
WARN_LOG_FMT(VIDEO, "Missing WGL_ARB_create_context extension");
return nullptr;
}
for (const auto& version : s_desktop_opengl_versions)
{
// Construct list of attributes. Prefer a forward-compatible, core context.
std::array<int, 5 * 2> attribs = {WGL_CONTEXT_PROFILE_MASK_ARB,
WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
#ifdef _DEBUG
WGL_CONTEXT_FLAGS_ARB,
WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB |
WGL_CONTEXT_DEBUG_BIT_ARB,
#else
WGL_CONTEXT_FLAGS_ARB,
WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
#endif
WGL_CONTEXT_MAJOR_VERSION_ARB,
version.first,
WGL_CONTEXT_MINOR_VERSION_ARB,
version.second,
0,
0};
// Attempt creating this context.
HGLRC core_context = wglCreateContextAttribsARB(dc, share_context, attribs.data());
if (core_context)
{
INFO_LOG_FMT(VIDEO, "WGL: Created a GL {}.{} core context", version.first, version.second);
return core_context;
}
}
WARN_LOG_FMT(VIDEO, "Unable to create a core OpenGL context of an acceptable version");
return nullptr;
}
bool GLContextWGL::CreatePBuffer(HDC onscreen_dc, int width, int height, HANDLE* pbuffer_handle,
HDC* pbuffer_dc)
{
if (!wglChoosePixelFormatARB || !wglCreatePbufferARB || !wglGetPbufferDCARB ||
!wglReleasePbufferDCARB || !wglDestroyPbufferARB)
{
ERROR_LOG_FMT(VIDEO, "Missing WGL_ARB_pbuffer extension");
return false;
}
static constexpr std::array<int, 7 * 2> pf_iattribs = {
WGL_DRAW_TO_PBUFFER_ARB,
1, // Pixel format compatible with pbuffer rendering
WGL_RED_BITS_ARB,
0,
WGL_GREEN_BITS_ARB,
0,
WGL_BLUE_BITS_ARB,
0,
WGL_DEPTH_BITS_ARB,
0,
WGL_STENCIL_BITS_ARB,
0,
0,
0};
static constexpr std::array<float, 1 * 2> pf_fattribs = {};
int pixel_format;
UINT num_pixel_formats;
if (!wglChoosePixelFormatARB(onscreen_dc, pf_iattribs.data(), pf_fattribs.data(), 1,
&pixel_format, &num_pixel_formats))
{
ERROR_LOG_FMT(VIDEO, "Failed to obtain a compatible pbuffer pixel format");
return false;
}
static constexpr std::array<int, 1 * 2> pb_attribs = {};
HPBUFFERARB pbuffer =
wglCreatePbufferARB(onscreen_dc, pixel_format, width, height, pb_attribs.data());
if (!pbuffer)
{
ERROR_LOG_FMT(VIDEO, "Failed to create pbuffer");
return false;
}
HDC dc = wglGetPbufferDCARB(pbuffer);
if (!dc)
{
ERROR_LOG_FMT(VIDEO, "Failed to get drawing context from pbuffer");
wglDestroyPbufferARB(pbuffer);
return false;
}
*pbuffer_handle = pbuffer;
*pbuffer_dc = dc;
return true;
}
bool GLContextWGL::MakeCurrent()
{
return wglMakeCurrent(m_dc, m_rc) == TRUE;
}
bool GLContextWGL::ClearCurrent()
{
return wglMakeCurrent(m_dc, nullptr) == TRUE;
}
// Update window width, size and etc. Called from Render.cpp
void GLContextWGL::Update()
{
RECT rcWindow;
GetClientRect(m_window_handle, &rcWindow);
// Get the new window width and height
m_backbuffer_width = rcWindow.right - rcWindow.left;
m_backbuffer_height = rcWindow.bottom - rcWindow.top;
}
-40
View File
@@ -1,40 +0,0 @@
// Copyright 2008 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <windows.h>
#include <string>
#include "Common/GL/GLContext.h"
class GLContextWGL final : public GLContext
{
public:
~GLContextWGL() override;
bool IsHeadless() const override;
std::unique_ptr<GLContext> CreateSharedContext() override;
bool MakeCurrent() override;
bool ClearCurrent() override;
void Update() override;
void Swap() override;
void SwapInterval(int interval) override;
void* GetFuncAddress(const std::string& name) override;
protected:
bool Initialize(const WindowSystemInfo& wsi, bool stereo, bool core) override;
static HGLRC CreateCoreContext(HDC dc, HGLRC share_context);
static bool CreatePBuffer(HDC onscreen_dc, int width, int height, HANDLE* pbuffer_handle,
HDC* pbuffer_dc);
HWND m_window_handle = nullptr;
HANDLE m_pbuffer_handle = nullptr;
HDC m_dc = nullptr;
HGLRC m_rc = nullptr;
};
-51
View File
@@ -1,51 +0,0 @@
// Copyright 2012 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "Common/GL/GLX11Window.h"
GLX11Window::GLX11Window(Display* display, Window parent_window, Colormap color_map, Window window,
int width, int height)
: m_display(display), m_parent_window(parent_window), m_color_map(color_map), m_window(window),
m_width(width), m_height(height)
{
}
GLX11Window::~GLX11Window()
{
XUnmapWindow(m_display, m_window);
XDestroyWindow(m_display, m_window);
XFreeColormap(m_display, m_color_map);
}
void GLX11Window::UpdateDimensions()
{
XWindowAttributes attribs;
XGetWindowAttributes(m_display, m_parent_window, &attribs);
XResizeWindow(m_display, m_window, attribs.width, attribs.height);
m_width = attribs.width;
m_height = attribs.height;
}
std::unique_ptr<GLX11Window> GLX11Window::Create(Display* display, Window parent_window,
XVisualInfo* vi)
{
// Set color map for the new window based on the visual.
Colormap color_map = XCreateColormap(display, parent_window, vi->visual, AllocNone);
XSetWindowAttributes attribs = {};
attribs.colormap = color_map;
// Get the dimensions from the parent window.
XWindowAttributes parent_attribs = {};
XGetWindowAttributes(display, parent_window, &parent_attribs);
// Create the window
Window window =
XCreateWindow(display, parent_window, 0, 0, parent_attribs.width, parent_attribs.height, 0,
vi->depth, InputOutput, vi->visual, CWColormap, &attribs);
XSelectInput(display, parent_window, StructureNotifyMask);
XMapWindow(display, window);
XSync(display, True);
return std::make_unique<GLX11Window>(display, parent_window, color_map, window,
parent_attribs.width, parent_attribs.height);
}
-38
View File
@@ -1,38 +0,0 @@
// Copyright 2008 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#include <memory>
#include <thread>
class GLX11Window
{
public:
GLX11Window(Display* display, Window parent_window, Colormap color_map, Window window, int width,
int height);
~GLX11Window();
Display* GetDisplay() const { return m_display; }
Window GetParentWindow() const { return m_parent_window; }
Window GetWindow() const { return m_window; }
int GetWidth() const { return m_width; }
int GetHeight() const { return m_height; }
void UpdateDimensions();
static std::unique_ptr<GLX11Window> Create(Display* display, Window parent_window,
XVisualInfo* vi);
private:
Display* m_display;
Window m_parent_window;
Colormap m_color_map;
Window m_window;
int m_width;
int m_height;
};
@@ -7,555 +7,7 @@
#include "DolphinLibretro/VideoContexts/ContextStatus.h"
#include "DolphinLibretro/Video.h"
#include <cstring>
#include <memory>
#include <vector>
#include "Common/GL/GLExtensions/GLExtensions.h"
/* Shared contexts for the shader compiler's threads, made from the
* frontend's: EGL, then GLX, on every Unix but Apple's, and WGL on
* Windows, all loaded at runtime from whichever libraries the frontend
* has, so the core links none of them. */
#if (defined(__unix__) || defined(__ANDROID__)) && !defined(__APPLE__)
#define LR_SHARED_EGL 1
#if !defined(__ANDROID__)
#define LR_SHARED_GLX 1
#endif
#include "Common/DynamicLibrary.h"
#endif
#if defined(_WIN32)
#define LR_SHARED_WGL 1
#include <windows.h>
#endif
namespace
{
// Version/profile of the frontend context, so worker contexts are created to match it.
struct CurrentGLVersion
{
GLint major = 0;
GLint minor = 0;
bool core_profile = false;
};
CurrentGLVersion QueryCurrentGLVersion(bool gles)
{
CurrentGLVersion v;
glGetIntegerv(GL_MAJOR_VERSION, &v.major);
glGetIntegerv(GL_MINOR_VERSION, &v.minor);
if (!gles && (v.major > 3 || (v.major == 3 && v.minor >= 2)))
{
GLint mask = 0;
glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &mask);
v.core_profile = (mask & GL_CONTEXT_CORE_PROFILE_BIT) != 0;
}
return v;
}
#if defined(LR_SHARED_EGL)
// EGL is loaded at runtime, from the library the frontend already has loaded.
namespace EGL
{
using EGLDisplay = void*;
using EGLContext = void*;
using EGLSurface = void*;
using EGLConfig = void*;
using EGLint = int;
using EGLBoolean = unsigned int;
using EGLenum = unsigned int;
constexpr EGLint EGL_TRUE = 1;
constexpr EGLint EGL_NONE = 0x3038;
constexpr EGLint EGL_CONFIG_ID = 0x3028;
constexpr EGLint EGL_EXTENSIONS = 0x3055;
constexpr EGLint EGL_HEIGHT = 0x3056;
constexpr EGLint EGL_WIDTH = 0x3057;
constexpr EGLint EGL_CONTEXT_CLIENT_VERSION = 0x3098;
constexpr EGLint EGL_CONTEXT_MAJOR_VERSION_KHR = 0x3098;
constexpr EGLint EGL_CONTEXT_MINOR_VERSION_KHR = 0x30FB;
constexpr EGLint EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR = 0x30FD;
constexpr EGLint EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR = 0x1;
constexpr EGLint EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR = 0x2;
constexpr EGLenum EGL_OPENGL_ES_API = 0x30A0;
constexpr EGLenum EGL_OPENGL_API = 0x30A2;
EGLBoolean (*BindAPI)(EGLenum);
EGLContext (*GetCurrentContext)();
EGLDisplay (*GetCurrentDisplay)();
EGLBoolean (*QueryContext)(EGLDisplay, EGLContext, EGLint, EGLint*);
EGLBoolean (*ChooseConfig)(EGLDisplay, const EGLint*, EGLConfig*, EGLint, EGLint*);
EGLContext (*CreateContext)(EGLDisplay, EGLConfig, EGLContext, const EGLint*);
EGLint (*GetError)();
const char* (*QueryString)(EGLDisplay, EGLint);
EGLSurface (*CreatePbufferSurface)(EGLDisplay, EGLConfig, const EGLint*);
EGLBoolean (*DestroyContext)(EGLDisplay, EGLContext);
EGLBoolean (*DestroySurface)(EGLDisplay, EGLSurface);
EGLBoolean (*MakeCurrent)(EGLDisplay, EGLSurface, EGLSurface, EGLContext);
bool Load()
{
static Common::DynamicLibrary libegl;
static const bool loaded = [] {
if (!libegl.Open("libEGL.so.1") && !libegl.Open("libEGL.so"))
return false;
return libegl.GetSymbol("eglBindAPI", &BindAPI) &&
libegl.GetSymbol("eglGetCurrentContext", &GetCurrentContext) &&
libegl.GetSymbol("eglGetCurrentDisplay", &GetCurrentDisplay) &&
libegl.GetSymbol("eglQueryContext", &QueryContext) &&
libegl.GetSymbol("eglChooseConfig", &ChooseConfig) &&
libegl.GetSymbol("eglCreateContext", &CreateContext) &&
libegl.GetSymbol("eglGetError", &GetError) &&
libegl.GetSymbol("eglQueryString", &QueryString) &&
libegl.GetSymbol("eglCreatePbufferSurface", &CreatePbufferSurface) &&
libegl.GetSymbol("eglDestroyContext", &DestroyContext) &&
libegl.GetSymbol("eglDestroySurface", &DestroySurface) &&
libegl.GetSymbol("eglMakeCurrent", &MakeCurrent);
}();
return loaded;
}
} // namespace EGL
class GLContextLRSharedEGL final : public GLContext
{
public:
GLContextLRSharedEGL(Mode mode, EGL::EGLDisplay display, EGL::EGLContext context,
EGL::EGLSurface surface)
: m_display(display), m_context(context), m_surface(surface)
{
m_opengl_mode = mode;
m_is_shared = true;
}
~GLContextLRSharedEGL() override
{
if (EGL::GetCurrentContext() == m_context)
ClearCurrent();
if (m_surface)
EGL::DestroySurface(m_display, m_surface);
EGL::DestroyContext(m_display, m_context);
}
bool IsHeadless() const override { return true; }
bool MakeCurrent() override
{
EGL::BindAPI(m_opengl_mode == Mode::OpenGLES ? EGL::EGL_OPENGL_ES_API : EGL::EGL_OPENGL_API);
return EGL::MakeCurrent(m_display, m_surface, m_surface, m_context) ==
static_cast<EGL::EGLBoolean>(EGL::EGL_TRUE);
}
bool ClearCurrent() override
{
return EGL::MakeCurrent(m_display, nullptr, nullptr, nullptr) ==
static_cast<EGL::EGLBoolean>(EGL::EGL_TRUE);
}
void* GetFuncAddress(const std::string& name) override
{
return reinterpret_cast<void*>(Libretro::Video::hw_render.get_proc_address(name.c_str()));
}
private:
EGL::EGLDisplay m_display;
EGL::EGLContext m_context;
EGL::EGLSurface m_surface;
};
std::unique_ptr<GLContext> CreateSharedEGL(GLContext::Mode mode)
{
if (!EGL::Load())
return nullptr;
const EGL::EGLContext share = EGL::GetCurrentContext();
const EGL::EGLDisplay display = EGL::GetCurrentDisplay();
if (!share || !display)
return nullptr;
const bool gles = mode == GLContext::Mode::OpenGLES;
EGL::BindAPI(gles ? EGL::EGL_OPENGL_ES_API : EGL::EGL_OPENGL_API);
EGL::EGLint config_id = 0;
EGL::QueryContext(display, share, EGL::EGL_CONFIG_ID, &config_id);
const EGL::EGLint config_attribs[] = {EGL::EGL_CONFIG_ID, config_id, EGL::EGL_NONE};
EGL::EGLConfig config = nullptr;
EGL::EGLint num_configs = 0;
if (!EGL::ChooseConfig(display, config_attribs, &config, 1, &num_configs) || num_configs < 1)
config = nullptr; // EGL_NO_CONFIG_KHR, frontend may have used EGL_KHR_no_config_context
const CurrentGLVersion ver = QueryCurrentGLVersion(gles);
EGL::EGLint ctx_attribs[7];
int n = 0;
ctx_attribs[n++] = EGL::EGL_CONTEXT_MAJOR_VERSION_KHR;
ctx_attribs[n++] = ver.major;
ctx_attribs[n++] = EGL::EGL_CONTEXT_MINOR_VERSION_KHR;
ctx_attribs[n++] = ver.minor;
if (!gles)
{
ctx_attribs[n++] = EGL::EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR;
ctx_attribs[n++] = ver.core_profile ? EGL::EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR :
EGL::EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR;
}
ctx_attribs[n] = EGL::EGL_NONE;
EGL::EGLContext context = EGL::CreateContext(display, config, share, ctx_attribs);
if (!context)
{
// No EGL_KHR_create_context, fall back to the basic attributes.
const EGL::EGLint es_attribs[] = {EGL::EGL_CONTEXT_CLIENT_VERSION, 3, EGL::EGL_NONE};
const EGL::EGLint gl_attribs[] = {EGL::EGL_NONE};
context = EGL::CreateContext(display, config, share, gles ? es_attribs : gl_attribs);
}
if (!context)
{
WARN_LOG_FMT(VIDEO, "Shared context: eglCreateContext failed {:#06x}", EGL::GetError());
return nullptr;
}
// Worker contexts never draw, so avoid a surface when possible.
EGL::EGLSurface surface = nullptr;
const char* exts = EGL::QueryString(display, EGL::EGL_EXTENSIONS);
if (!exts || !strstr(exts, "EGL_KHR_surfaceless_context"))
{
const EGL::EGLint pbuffer_attribs[] = {EGL::EGL_WIDTH, 1, EGL::EGL_HEIGHT, 1, EGL::EGL_NONE};
surface = config ? EGL::CreatePbufferSurface(display, config, pbuffer_attribs) : nullptr;
if (!surface)
{
WARN_LOG_FMT(VIDEO, "Shared context: no surfaceless support and pbuffer creation failed");
EGL::DestroyContext(display, context);
return nullptr;
}
}
INFO_LOG_FMT(VIDEO, "Created EGL shared context for shader compiling");
return std::make_unique<GLContextLRSharedEGL>(mode, display, context, surface);
}
#endif // LR_SHARED_EGL
#if defined(LR_SHARED_GLX)
// GLX is loaded at runtime (rather than including GL/glx.h, which conflicts with Dolphin's GL
// loader, and linking libGL), using whichever libGL the frontend already has loaded.
namespace GLX
{
using Display = void;
using GLXContext = void*;
using GLXFBConfig = void*;
using GLXDrawable = unsigned long;
using XErrorHandler = int (*)(Display*, void*);
constexpr int GLX_SCREEN = 0x800C;
constexpr int GLX_FBCONFIG_ID = 0x8013;
constexpr int GLX_CONTEXT_MAJOR_VERSION_ARB = 0x2091;
constexpr int GLX_CONTEXT_MINOR_VERSION_ARB = 0x2092;
constexpr int GLX_CONTEXT_PROFILE_MASK_ARB = 0x9126;
constexpr int GLX_CONTEXT_CORE_PROFILE_BIT_ARB = 0x1;
constexpr int GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB = 0x2;
constexpr int GLX_CONTEXT_ES2_PROFILE_BIT_EXT = 0x4;
GLXContext (*GetCurrentContext)();
Display* (*GetCurrentDisplay)();
int (*QueryContext)(Display*, GLXContext, int, int*);
GLXFBConfig* (*ChooseFBConfig)(Display*, int, const int*, int*);
GLXFBConfig* (*GetFBConfigs)(Display*, int, int*);
int (*GetFBConfigAttrib)(Display*, GLXFBConfig, int, int*);
void* (*GetProcAddressARB)(const unsigned char*);
int (*MakeContextCurrent)(Display*, GLXDrawable, GLXDrawable, GLXContext);
void (*DestroyContext)(Display*, GLXContext);
GLXContext (*CreateContextAttribsARB)(Display*, GLXFBConfig, GLXContext, int, const int*);
int (*XFree)(void*);
int (*XSync)(Display*, int);
XErrorHandler (*XSetErrorHandler)(XErrorHandler);
bool Load()
{
static Common::DynamicLibrary libgl;
static Common::DynamicLibrary libx11;
static const bool loaded = [] {
if (!libgl.Open("libGL.so.1") && !libgl.Open("libGL.so"))
return false;
if (!libx11.Open("libX11.so.6") && !libx11.Open("libX11.so"))
return false;
return libgl.GetSymbol("glXGetCurrentContext", &GetCurrentContext) &&
libgl.GetSymbol("glXGetCurrentDisplay", &GetCurrentDisplay) &&
libgl.GetSymbol("glXQueryContext", &QueryContext) &&
libgl.GetSymbol("glXChooseFBConfig", &ChooseFBConfig) &&
libgl.GetSymbol("glXGetFBConfigs", &GetFBConfigs) &&
libgl.GetSymbol("glXGetFBConfigAttrib", &GetFBConfigAttrib) &&
libgl.GetSymbol("glXGetProcAddressARB", &GetProcAddressARB) &&
libgl.GetSymbol("glXMakeContextCurrent", &MakeContextCurrent) &&
libgl.GetSymbol("glXDestroyContext", &DestroyContext) &&
libx11.GetSymbol("XFree", &XFree) && libx11.GetSymbol("XSync", &XSync) &&
libx11.GetSymbol("XSetErrorHandler", &XSetErrorHandler) &&
(CreateContextAttribsARB = reinterpret_cast<decltype(CreateContextAttribsARB)>(
GetProcAddressARB(reinterpret_cast<const unsigned char*>(
"glXCreateContextAttribsARB")))) != nullptr;
}();
return loaded;
}
int IgnoreXError(Display*, void*)
{
return 0;
}
} // namespace GLX
class GLContextLRSharedGLX final : public GLContext
{
public:
GLContextLRSharedGLX(Mode mode, GLX::Display* display, GLX::GLXContext context)
: m_display(display), m_context(context)
{
m_opengl_mode = mode;
m_is_shared = true;
}
~GLContextLRSharedGLX() override
{
if (GLX::GetCurrentContext() == m_context)
ClearCurrent();
GLX::DestroyContext(m_display, m_context);
}
bool IsHeadless() const override { return true; }
// GLX_ARB_create_context allows GL 3.0+ contexts to be made current without a drawable.
bool MakeCurrent() override { return GLX::MakeContextCurrent(m_display, 0, 0, m_context) != 0; }
bool ClearCurrent() override { return GLX::MakeContextCurrent(m_display, 0, 0, nullptr) != 0; }
void* GetFuncAddress(const std::string& name) override
{
return reinterpret_cast<void*>(Libretro::Video::hw_render.get_proc_address(name.c_str()));
}
private:
GLX::Display* m_display;
GLX::GLXContext m_context;
};
std::unique_ptr<GLContext> CreateSharedGLX(GLContext::Mode mode)
{
if (!GLX::Load())
return nullptr;
GLX::GLXContext share = GLX::GetCurrentContext();
GLX::Display* display = GLX::GetCurrentDisplay();
if (!share || !display)
return nullptr;
int screen = 0;
int fbconfig_id = 0;
GLX::QueryContext(display, share, GLX::GLX_SCREEN, &screen);
GLX::QueryContext(display, share, GLX::GLX_FBCONFIG_ID, &fbconfig_id);
const int fb_attribs[] = {GLX::GLX_FBCONFIG_ID, fbconfig_id, 0};
int num_configs = 0;
GLX::GLXFBConfig config = nullptr;
GLX::GLXFBConfig* configs = GLX::ChooseFBConfig(display, screen, fb_attribs, &num_configs);
if (configs && num_configs > 0)
config = configs[0];
else
{
/* Mesa keeps its default attributes beside the ID and may match none:
* the screen's configs, for the one with the ID */
int i;
if (configs)
GLX::XFree(configs);
configs = GLX::GetFBConfigs(display, screen, &num_configs);
for (i = 0; configs && i < num_configs && !config; i++)
{
int id = 0;
if (GLX::GetFBConfigAttrib(display, configs[i], GLX::GLX_FBCONFIG_ID, &id) == 0 &&
id == fbconfig_id)
{
config = configs[i];
}
}
}
if (configs)
GLX::XFree(configs);
if (!config)
{
WARN_LOG_FMT(VIDEO, "Shared context: no GLXFBConfig matching frontend context");
return nullptr;
}
const bool gles = mode == GLContext::Mode::OpenGLES;
const CurrentGLVersion ver = QueryCurrentGLVersion(gles);
const int profile = gles ? GLX::GLX_CONTEXT_ES2_PROFILE_BIT_EXT :
ver.core_profile ? GLX::GLX_CONTEXT_CORE_PROFILE_BIT_ARB :
GLX::GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
const int ctx_attribs[] = {GLX::GLX_CONTEXT_MAJOR_VERSION_ARB, ver.major,
GLX::GLX_CONTEXT_MINOR_VERSION_ARB, ver.minor,
GLX::GLX_CONTEXT_PROFILE_MASK_ARB, profile,
0};
// A failed glXCreateContextAttribsARB raises an X error, and the default handler would
// terminate the frontend, so swallow errors while creating.
GLX::XSync(display, 0);
const GLX::XErrorHandler old_handler = GLX::XSetErrorHandler(GLX::IgnoreXError);
GLX::GLXContext context = GLX::CreateContextAttribsARB(display, config, share, 1, ctx_attribs);
GLX::XSync(display, 0);
GLX::XSetErrorHandler(old_handler);
if (!context)
{
WARN_LOG_FMT(VIDEO, "Shared context: glXCreateContextAttribsARB failed");
return nullptr;
}
INFO_LOG_FMT(VIDEO, "Created GLX shared context for shader compiling");
return std::make_unique<GLContextLRSharedGLX>(mode, display, context);
}
#endif // LR_SHARED_GLX
#if defined(LR_SHARED_WGL)
namespace WGL
{
constexpr int WGL_CONTEXT_MAJOR_VERSION_ARB = 0x2091;
constexpr int WGL_CONTEXT_MINOR_VERSION_ARB = 0x2092;
constexpr int WGL_CONTEXT_PROFILE_MASK_ARB = 0x9126;
constexpr int WGL_CONTEXT_CORE_PROFILE_BIT_ARB = 0x1;
constexpr int WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB = 0x2;
constexpr int WGL_CONTEXT_ES2_PROFILE_BIT_EXT = 0x4;
HGLRC(WINAPI* GetCurrentContext)();
HDC(WINAPI* GetCurrentDC)();
BOOL(WINAPI* MakeCurrent)(HDC, HGLRC);
BOOL(WINAPI* DeleteContext)(HGLRC);
PROC(WINAPI* GetExtProc)(LPCSTR);
HGLRC(WINAPI* CreateContextAttribsARB)(HDC, HGLRC, const int*);
HANDLE(WINAPI* CreatePbufferARB)(HDC, int, int, int, const int*);
HDC(WINAPI* GetPbufferDCARB)(HANDLE);
int(WINAPI* ReleasePbufferDCARB)(HANDLE, HDC);
BOOL(WINAPI* DestroyPbufferARB)(HANDLE);
/* opengl32 from the frontend, and the extensions with its context current */
bool Load()
{
HMODULE gl = GetModuleHandleA("opengl32.dll");
if (!gl)
return false;
GetCurrentContext = reinterpret_cast<decltype(GetCurrentContext)>(
reinterpret_cast<void*>(GetProcAddress(gl, "wglGetCurrentContext")));
GetCurrentDC = reinterpret_cast<decltype(GetCurrentDC)>(
reinterpret_cast<void*>(GetProcAddress(gl, "wglGetCurrentDC")));
MakeCurrent = reinterpret_cast<decltype(MakeCurrent)>(
reinterpret_cast<void*>(GetProcAddress(gl, "wglMakeCurrent")));
DeleteContext = reinterpret_cast<decltype(DeleteContext)>(
reinterpret_cast<void*>(GetProcAddress(gl, "wglDeleteContext")));
GetExtProc = reinterpret_cast<decltype(GetExtProc)>(
reinterpret_cast<void*>(GetProcAddress(gl, "wglGetProcAddress")));
if (!GetCurrentContext || !GetCurrentDC || !MakeCurrent || !DeleteContext || !GetExtProc)
return false;
CreateContextAttribsARB = reinterpret_cast<decltype(CreateContextAttribsARB)>(
reinterpret_cast<void*>(GetExtProc("wglCreateContextAttribsARB")));
CreatePbufferARB = reinterpret_cast<decltype(CreatePbufferARB)>(
reinterpret_cast<void*>(GetExtProc("wglCreatePbufferARB")));
GetPbufferDCARB = reinterpret_cast<decltype(GetPbufferDCARB)>(
reinterpret_cast<void*>(GetExtProc("wglGetPbufferDCARB")));
ReleasePbufferDCARB = reinterpret_cast<decltype(ReleasePbufferDCARB)>(
reinterpret_cast<void*>(GetExtProc("wglReleasePbufferDCARB")));
DestroyPbufferARB = reinterpret_cast<decltype(DestroyPbufferARB)>(
reinterpret_cast<void*>(GetExtProc("wglDestroyPbufferARB")));
return CreateContextAttribsARB != nullptr;
}
} // namespace WGL
class GLContextLRSharedWGL final : public GLContext
{
public:
GLContextLRSharedWGL(Mode mode, HDC dc, HGLRC context, HANDLE pbuffer)
: m_dc(dc), m_context(context), m_pbuffer(pbuffer)
{
m_opengl_mode = mode;
m_is_shared = true;
}
~GLContextLRSharedWGL() override
{
if (WGL::GetCurrentContext() == m_context)
ClearCurrent();
WGL::DeleteContext(m_context);
if (m_pbuffer)
{
WGL::ReleasePbufferDCARB(m_pbuffer, m_dc);
WGL::DestroyPbufferARB(m_pbuffer);
}
}
bool IsHeadless() const override { return true; }
bool MakeCurrent() override { return WGL::MakeCurrent(m_dc, m_context) != FALSE; }
bool ClearCurrent() override { return WGL::MakeCurrent(nullptr, nullptr) != FALSE; }
void* GetFuncAddress(const std::string& name) override
{
return reinterpret_cast<void*>(Libretro::Video::hw_render.get_proc_address(name.c_str()));
}
private:
HDC m_dc;
HGLRC m_context;
HANDLE m_pbuffer;
};
/* A context sharing the frontend's, current on a 1x1 pbuffer of the
* frontend's pixel format, or on the frontend's own DC without
* WGL_ARB_pbuffer. */
std::unique_ptr<GLContext> CreateSharedWGL(GLContext::Mode mode)
{
if (!WGL::Load())
return nullptr;
const HGLRC share = WGL::GetCurrentContext();
const HDC frontend_dc = WGL::GetCurrentDC();
if (!share || !frontend_dc)
return nullptr;
const bool gles = mode == GLContext::Mode::OpenGLES;
const CurrentGLVersion ver = QueryCurrentGLVersion(gles);
const int profile = gles ? WGL::WGL_CONTEXT_ES2_PROFILE_BIT_EXT :
ver.core_profile ? WGL::WGL_CONTEXT_CORE_PROFILE_BIT_ARB :
WGL::WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
const int ctx_attribs[] = {WGL::WGL_CONTEXT_MAJOR_VERSION_ARB, ver.major,
WGL::WGL_CONTEXT_MINOR_VERSION_ARB, ver.minor,
WGL::WGL_CONTEXT_PROFILE_MASK_ARB, profile,
0};
HANDLE pbuffer = nullptr;
HDC dc = frontend_dc;
if (WGL::CreatePbufferARB && WGL::GetPbufferDCARB && WGL::ReleasePbufferDCARB &&
WGL::DestroyPbufferARB)
{
const int pbuffer_attribs[] = {0};
pbuffer = WGL::CreatePbufferARB(frontend_dc, GetPixelFormat(frontend_dc), 1, 1,
pbuffer_attribs);
HDC pbuffer_dc = pbuffer ? WGL::GetPbufferDCARB(pbuffer) : nullptr;
if (pbuffer_dc)
dc = pbuffer_dc;
else if (pbuffer)
{
WGL::DestroyPbufferARB(pbuffer);
pbuffer = nullptr;
}
}
const HGLRC context = WGL::CreateContextAttribsARB(dc, share, ctx_attribs);
if (!context)
{
WARN_LOG_FMT(VIDEO, "Shared context: wglCreateContextAttribsARB failed {:#x}", GetLastError());
if (pbuffer)
{
WGL::ReleasePbufferDCARB(pbuffer, dc);
WGL::DestroyPbufferARB(pbuffer);
}
return nullptr;
}
INFO_LOG_FMT(VIDEO, "Created WGL shared context for shader compiling ({})",
pbuffer ? "pbuffer" : "frontend DC");
return std::make_unique<GLContextLRSharedWGL>(mode, dc, context, pbuffer);
}
#endif // LR_SHARED_WGL
} // namespace
GLContextLR::GLContextLR()
{
@@ -605,24 +57,6 @@ void GLContextLR::Swap()
}
std::unique_ptr<GLContext> GLContextLR::CreateSharedContext()
{
// Must be called on the thread the frontend's context is current on.
#if defined(LR_SHARED_EGL)
if (auto context = CreateSharedEGL(m_opengl_mode))
return context;
#endif
#if defined(LR_SHARED_GLX)
if (auto context = CreateSharedGLX(m_opengl_mode))
return context;
#endif
#if defined(LR_SHARED_WGL)
if (auto context = CreateSharedWGL(m_opengl_mode))
return context;
#endif
return nullptr;
}
void GLContextLR::Shutdown()
{
m_initialized = false;
@@ -17,10 +17,6 @@ public:
bool IsInitialized() const { return m_initialized; }
void Shutdown();
// libretro has no API for additional contexts, so this creates one directly through
// EGL/GLX, sharing with whatever frontend context is current on the calling thread.
std::unique_ptr<GLContext> CreateSharedContext() override;
private:
bool m_initialized = false;
};
+2 -3
View File
@@ -699,9 +699,8 @@ bool PopulateConfig(GLContext* m_main_gl_context)
g_backend_info.bSupportsComputeShaders &&
g_ogl_config.bSupportsImageLoadStore;
// Background compiling is supported only when shared contexts aren't broken.
g_backend_info.bSupportsBackgroundCompiling =
!DriverDetails::HasBug(DriverDetails::BUG_SHARED_CONTEXT_SHADER_COMPILATION);
// Programs are compiled on the frontend's context alone: the core makes no contexts of its own.
g_backend_info.bSupportsBackgroundCompiling = false;
// Program binaries are supported on GL4.1+, ARB_get_program_binary, or ES3.
if (supports_glsl_cache)
+1 -1
View File
@@ -671,7 +671,7 @@ void OGLGfx::UnbindTexture(const AbstractTexture* texture)
std::unique_ptr<VideoCommon::AsyncShaderCompiler> OGLGfx::CreateAsyncShaderCompiler()
{
return std::make_unique<SharedContextAsyncShaderCompiler>();
return std::make_unique<VideoCommon::AsyncShaderCompiler>();
}
bool OGLGfx::IsGLES() const
@@ -52,7 +52,6 @@ atomic_slot_map_t* ProgramShaderCache::s_pipeline_programs;
quiesce_t ProgramShaderCache::s_pipeline_program_quiesce;
static std::string s_glsl_header;
static retro_atomic_64_t s_shader_counter;
static thread_local bool s_is_shared_context = false;
static std::string GetGLSLVersionString()
{
@@ -586,7 +585,7 @@ PipelineProgram* ProgramShaderCache::GetPipelineProgram(const GLVertexFormat* ve
// We temporarily change the vertex array to the pipeline's vertex format.
// This can prevent the NVIDIA OpenGL driver from recompiling on first use.
GLuint vao = vertex_format ? vertex_format->VAO : s_attributeless_VAO;
if (s_is_shared_context || vao != s_last_VAO)
if (vao != s_last_VAO)
glBindVertexArray(vao);
// Attach shaders.
@@ -608,7 +607,7 @@ PipelineProgram* ProgramShaderCache::GetPipelineProgram(const GLVertexFormat* ve
glLinkProgram(prog->shader.glprogid);
// Restore VAO binding after linking.
if (!s_is_shared_context && vao != s_last_VAO)
if (vao != s_last_VAO)
glBindVertexArray(s_last_VAO);
if (!CheckProgramLinkResult(prog->shader.glprogid,
@@ -625,11 +624,6 @@ PipelineProgram* ProgramShaderCache::GetPipelineProgram(const GLVertexFormat* ve
// This is only needed for drivers which don't support binding layout.
prog->shader.SetProgramVariables();
// If this is a shared context, ensure we sync before we return the program to
// the main thread. If we don't do this, some driver can lock up (e.g. AMD).
if (s_is_shared_context)
glFinish();
// Publish it, unless another thread made the same program meanwhile; a dead program in the
// slot is simply replaced.
for (;;)
@@ -915,48 +909,4 @@ u64 ProgramShaderCache::GenerateShaderID()
return static_cast<u64>(retro_atomic_fetch_add_64(&s_shader_counter, 1));
}
bool SharedContextAsyncShaderCompiler::WorkerThreadInitMainThread(void** param)
{
std::unique_ptr<GLContext> context = GetOGLGfx()->GetMainGLContext()->CreateSharedContext();
if (!context)
{
PanicAlertFmt("Failed to create shared context for shader compiling.");
return false;
}
*param = context.release();
return true;
}
bool SharedContextAsyncShaderCompiler::WorkerThreadInitWorkerThread(void* param)
{
GLContext* context = static_cast<GLContext*>(param);
if (!context->MakeCurrent())
return false;
s_is_shared_context = true;
// Make the state match the main context to have a better chance of avoiding recompiles.
if (!context->IsGLES())
glEnable(GL_PROGRAM_POINT_SIZE);
if (g_backend_info.bSupportsClipControl)
glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
if (g_backend_info.bSupportsDepthClamp)
{
glEnable(GL_CLIP_DISTANCE0);
glEnable(GL_CLIP_DISTANCE1);
glEnable(GL_DEPTH_CLAMP);
}
if (g_backend_info.bSupportsPrimitiveRestart)
GLUtil::EnablePrimitiveRestart(context);
return true;
}
void SharedContextAsyncShaderCompiler::WorkerThreadExit(void* param)
{
GLContext* context = static_cast<GLContext*>(param);
context->ClearCurrent();
delete context;
}
} // namespace OGL
@@ -121,12 +121,4 @@ private:
static GLuint s_last_VAO;
};
class SharedContextAsyncShaderCompiler : public VideoCommon::AsyncShaderCompiler
{
protected:
bool WorkerThreadInitMainThread(void** param) override;
bool WorkerThreadInitWorkerThread(void* param) override;
void WorkerThreadExit(void* param) override;
};
} // namespace OGL
@@ -106,10 +106,6 @@ constexpr BugInfo m_known_bugs[] = {
-1.0, true},
{API_VULKAN, OS_ALL, VENDOR_ARM, DRIVER_ARM, Family::UNKNOWN, BUG_PRIMITIVE_RESTART, -1.0, -1.0,
true},
{API_OPENGL, OS_LINUX, VENDOR_MESA, DRIVER_I965, Family::UNKNOWN,
BUG_SHARED_CONTEXT_SHADER_COMPILATION, -1.0, -1.0, true},
{API_OPENGL, OS_LINUX, VENDOR_MESA, DRIVER_NOUVEAU, Family::UNKNOWN,
BUG_SHARED_CONTEXT_SHADER_COMPILATION, -1.0, -1.0, true},
{API_VULKAN, OS_ALL, VENDOR_NVIDIA, DRIVER_NVIDIA, Family::UNKNOWN, BUG_BROKEN_MSAA_CLEAR, -1.0,
-1.0, true},
{API_VULKAN, OS_ALL, VENDOR_IMGTEC, DRIVER_IMGTEC, Family::UNKNOWN,
-9
View File
@@ -245,15 +245,6 @@ enum Bug
// the negated value to a temporary variable then using that in the bitwise op.
BUG_BROKEN_BITWISE_OP_NEGATION,
// BUG: The GPU shader code appears to be context-specific on Mesa/i965.
// This means that if we compiled the ubershaders asynchronously, they will be recompiled
// on the main thread the first time they are used, causing stutter. For now, disable
// asynchronous compilation on Mesa i965. On nouveau, our use of glFinish() can cause
// crashes and/or lockups.
// Started version: -1
// Ended Version: -1
BUG_SHARED_CONTEXT_SHADER_COMPILATION,
// Bug: Fast clears on a MSAA framebuffer can cause NVIDIA GPU resets/lockups.
// Started version: -1
// Ended version: -1