Common: Add GL context wrappers

This commit is contained in:
Connor McLaughlin 2021-07-08 23:35:21 +10:00 committed by refractionpcsx2
parent 5848efe03b
commit 198fc2629e
17 changed files with 2097 additions and 7 deletions

View File

@ -18,6 +18,8 @@ target_sources(common PRIVATE
Exceptions.cpp
FastFormatString.cpp
FastJmp.cpp
GL/Context.cpp
GL/StreamBuffer.cpp
IniInterface.cpp
Mutex.cpp
Misc.cpp
@ -67,6 +69,8 @@ target_sources(common PRIVATE
Exceptions.h
FastJmp.h
General.h
GL/Context.h
GL/StreamBuffer.h
MemcpyFast.h
MemsetFast.inl
Path.h
@ -123,14 +127,50 @@ if(WIN32)
enable_language(ASM_MASM)
target_sources(common PRIVATE FastJmp.asm)
target_link_libraries(common PUBLIC pthreads4w Winmm.lib)
elseif(NOT APPLE)
if(TARGET PkgConfig::XRANDR)
target_link_libraries(common PRIVATE PkgConfig::XRANDR)
target_compile_definitions(common PRIVATE "HAS_XRANDR=1")
endif()
target_sources(common PRIVATE
FastJmp.asm
GL/ContextWGL.cpp
GL/ContextWGL.h
)
target_link_libraries(common PUBLIC pthreads4w Winmm.lib opengl32.lib)
elseif(APPLE)
target_sources(common PRIVATE
GL/ContextAGL.mm
GL/ContextAGL.h
)
set_source_files_properties(GL/ContextAGL.mm PROPERTIES SKIP_PRECOMPILE_HEADERS ON)
target_compile_options(common PUBLIC -fobjc-arc)
target_link_options(common PUBLIC -fobjc-link-runtime)
else()
if(X11_API OR WAYLAND_API)
target_sources(common PRIVATE
GL/ContextEGL.cpp
GL/ContextEGL.h
)
target_link_libraries(common PRIVATE PkgConfig::EGL)
endif()
if(X11_API)
target_sources(common PRIVATE
GL/ContextEGLX11.cpp
GL/ContextEGLX11.h
)
if(TARGET PkgConfig::XRANDR)
target_link_libraries(common PRIVATE PkgConfig::XRANDR)
target_compile_definitions(common PRIVATE "HAS_XRANDR=1")
endif()
endif()
if(WAYLAND_API)
target_sources(common PRIVATE
GL/ContextEGLWayland.cpp
GL/ContextEGLWayland.h
)
target_link_libraries(common PRIVATE ${WAYLAND_EGL_LIBRARIES})
endif()
endif()
target_link_libraries(common PRIVATE ${LIBC_LIBRARIES} PUBLIC wxWidgets::all)
target_link_libraries(common PRIVATE ${LIBC_LIBRARIES} PUBLIC wxWidgets::all glad)
target_compile_features(common PUBLIC cxx_std_17)
target_include_directories(common PUBLIC ../3rdparty/include ../)
target_compile_definitions(common PUBLIC "${PCSX2_DEFS}")

182
common/GL/Context.cpp Normal file
View File

@ -0,0 +1,182 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2021 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "common/PrecompiledHeader.h"
#include "common/Console.h"
#include "common/GL/Context.h"
#include "glad.h"
#include <cstdio>
#include <cstdlib>
#ifdef __APPLE__
#include <stdlib.h>
#else
#include <malloc.h>
#endif
#if defined(_WIN32) && !defined(_M_ARM64)
#include "common/GL/ContextWGL.h"
#elif defined(__APPLE__)
#include "common/GL/ContextAGL.h"
#else
#ifdef X11_API
#include "common/GL/ContextEGLX11.h"
#endif
#ifdef WAYLAND_API
#include "common/GL/ContextEGLWayland.h"
#endif
#endif
namespace GL
{
static bool ShouldPreferESContext()
{
#ifndef _MSC_VER
const char* value = std::getenv("PREFER_GLES_CONTEXT");
return (value && std::strcmp(value, "1") == 0);
#else
char buffer[2] = {};
size_t buffer_size = sizeof(buffer);
getenv_s(&buffer_size, buffer, "PREFER_GLES_CONTEXT");
return (std::strcmp(buffer, "1") == 0);
#endif
}
static void DisableBrokenExtensions(const char* gl_vendor, const char* gl_renderer)
{
if (std::strstr(gl_vendor, "ARM"))
{
// GL_{EXT,OES}_copy_image seem to be implemented on the CPU in the Mali drivers...
Console.Warning("Mali driver detected, disabling GL_{EXT,OES}_copy_image");
GLAD_GL_EXT_copy_image = 0;
GLAD_GL_OES_copy_image = 0;
}
}
Context::Context(const WindowInfo& wi)
: m_wi(wi)
{
}
Context::~Context() = default;
std::vector<Context::FullscreenModeInfo> Context::EnumerateFullscreenModes()
{
return {};
}
std::unique_ptr<GL::Context> Context::Create(const WindowInfo& wi, const Version* versions_to_try,
size_t num_versions_to_try)
{
if (ShouldPreferESContext())
{
// move ES versions to the front
Version* new_versions_to_try = static_cast<Version*>(alloca(sizeof(Version) * num_versions_to_try));
size_t count = 0;
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];
}
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;
#if defined(_WIN32) && !defined(_M_ARM64)
context = ContextWGL::Create(wi, versions_to_try, num_versions_to_try);
#elif defined(__APPLE__)
context = ContextAGL::Create(wi, versions_to_try, num_versions_to_try);
#endif
#if defined(X11_API)
if (wi.type == WindowInfo::Type::X11)
context = ContextEGLX11::Create(wi, versions_to_try, num_versions_to_try);
#endif
#if defined(WAYLAND_API)
if (wi.type == WindowInfo::Type::Wayland)
context = ContextEGLWayland::Create(wi, versions_to_try, num_versions_to_try);
#endif
if (!context)
return nullptr;
Console.WriteLn("Created a %s context", context->IsGLES() ? "OpenGL ES" : "OpenGL");
// NOTE: Not thread-safe. But this is okay, since we're not going to be creating more than one context at a time.
static Context* context_being_created;
context_being_created = context.get();
// load up glad
if (!context->IsGLES())
{
if (!gladLoadGLLoader([](const char* name) { return context_being_created->GetProcAddress(name); }))
{
Console.Error("Failed to load GL functions for GLAD");
return nullptr;
}
}
else
{
if (!gladLoadGLES2Loader([](const char* name) { return context_being_created->GetProcAddress(name); }))
{
Console.Error("Failed to load GLES functions for GLAD");
return nullptr;
}
}
context_being_created = nullptr;
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_version = reinterpret_cast<const char*>(glGetString(GL_VERSION));
const char* gl_shading_language_version = reinterpret_cast<const char*>(glGetString(GL_SHADING_LANGUAGE_VERSION));
Console.WriteLn(Color_Magenta, "GL_VENDOR: %s", gl_vendor);
Console.WriteLn(Color_Magenta, "GL_RENDERER: %s", gl_renderer);
Console.WriteLn(Color_Magenta, "GL_VERSION: %s", gl_version);
Console.WriteLn(Color_Magenta, "GL_SHADING_LANGUAGE_VERSION: %s", gl_shading_language_version);
DisableBrokenExtensions(gl_vendor, gl_renderer);
return context;
}
const std::array<Context::Version, 16>& Context::GetAllVersionsList()
{
static constexpr std::array<Version, 16> vlist = {{{Profile::Core, 4, 6},
{Profile::Core, 4, 5},
{Profile::Core, 4, 4},
{Profile::Core, 4, 3},
{Profile::Core, 4, 2},
{Profile::Core, 4, 1},
{Profile::Core, 4, 0},
{Profile::Core, 3, 3},
{Profile::Core, 3, 2},
{Profile::Core, 3, 1},
{Profile::Core, 3, 0},
{Profile::ES, 3, 2},
{Profile::ES, 3, 1},
{Profile::ES, 3, 0},
{Profile::ES, 2, 0},
{Profile::NoProfile, 0, 0}}};
return vlist;
}
} // namespace GL

86
common/GL/Context.h Normal file
View File

@ -0,0 +1,86 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2021 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "common/Pcsx2Defs.h"
#include "common/WindowInfo.h"
#include <array>
#include <memory>
#include <vector>
namespace GL {
class Context
{
public:
Context(const WindowInfo& wi);
virtual ~Context();
enum class Profile
{
NoProfile,
Core,
ES
};
struct Version
{
Profile profile;
int major_version;
int minor_version;
};
struct FullscreenModeInfo
{
u32 width;
u32 height;
float refresh_rate;
};
__fi const WindowInfo& GetWindowInfo() const { return m_wi; }
__fi bool IsGLES() const { return (m_version.profile == Profile::ES); }
__fi u32 GetSurfaceWidth() const { return m_wi.surface_width; }
__fi u32 GetSurfaceHeight() const { return m_wi.surface_height; }
virtual void* GetProcAddress(const char* name) = 0;
virtual bool ChangeSurface(const WindowInfo& new_wi) = 0;
virtual void ResizeSurface(u32 new_surface_width = 0, u32 new_surface_height = 0) = 0;
virtual bool SwapBuffers() = 0;
virtual bool MakeCurrent() = 0;
virtual bool DoneCurrent() = 0;
virtual bool SetSwapInterval(s32 interval) = 0;
virtual std::unique_ptr<Context> CreateSharedContext(const WindowInfo& wi) = 0;
virtual std::vector<FullscreenModeInfo> EnumerateFullscreenModes();
static std::unique_ptr<Context> Create(const WindowInfo& wi, const Version* versions_to_try,
size_t num_versions_to_try);
template<size_t N>
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());
}
static std::unique_ptr<Context> Create(const WindowInfo& wi) { return Create(wi, GetAllVersionsList()); }
static const std::array<Version, 16>& GetAllVersionsList();
protected:
WindowInfo m_wi;
Version m_version = {};
};
} // namespace GL

64
common/GL/ContextAGL.h Normal file
View File

@ -0,0 +1,64 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2021 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "common/GL/Context.h"
#include "glad.h"
#if defined(__APPLE__) && defined(__OBJC__)
#import <AppKit/AppKit.h>
#else
struct NSOpenGLContext;
struct NSOpenGLPixelFormat;
#endif
namespace GL
{
class ContextAGL final : public Context
{
public:
ContextAGL(const WindowInfo& wi);
~ContextAGL() override;
static std::unique_ptr<Context> Create(const WindowInfo& wi, const Version* versions_to_try,
size_t num_versions_to_try);
void* GetProcAddress(const char* name) override;
bool ChangeSurface(const WindowInfo& new_wi) override;
void ResizeSurface(u32 new_surface_width = 0, u32 new_surface_height = 0) override;
bool SwapBuffers() override;
bool MakeCurrent() override;
bool DoneCurrent() override;
bool SetSwapInterval(s32 interval) override;
std::unique_ptr<Context> CreateSharedContext(const WindowInfo& wi) override;
private:
#if defined(__APPLE__) && defined(__OBJC__)
__fi NSView* GetView() const { return (__bridge NSView*)m_wi.window_handle; }
#endif
bool Initialize(const Version* versions_to_try, size_t num_versions_to_try);
bool CreateContext(NSOpenGLContext* share_context, int profile, bool make_current);
void BindContextToView();
// returns true if dimensions have changed
bool UpdateDimensions();
NSOpenGLContext* m_context = nullptr;
NSOpenGLPixelFormat* m_pixel_format = nullptr;
void* m_opengl_module_handle = nullptr;
};
} // namespace GL

222
common/GL/ContextAGL.mm Normal file
View File

@ -0,0 +1,222 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2021 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "common/GL/ContextAGL.h"
#include "common/Assertions.h"
#include "common/Console.h"
#include "glad.h"
#include <dlfcn.h>
#if ! __has_feature(objc_arc)
#error "Compile this with -fobjc-arc"
#endif
namespace GL
{
ContextAGL::ContextAGL(const WindowInfo& wi)
: Context(wi)
{
m_opengl_module_handle = dlopen("/System/Library/Frameworks/OpenGL.framework/Versions/Current/OpenGL", RTLD_NOW);
if (!m_opengl_module_handle)
Console.Error("Could not open OpenGL.framework, function lookups will probably fail");
}
ContextAGL::~ContextAGL()
{
if ([NSOpenGLContext currentContext] == m_context)
[NSOpenGLContext clearCurrentContext];
if (m_opengl_module_handle)
dlclose(m_opengl_module_handle);
}
std::unique_ptr<Context> ContextAGL::Create(const WindowInfo& wi, const Version* versions_to_try,
size_t num_versions_to_try)
{
std::unique_ptr<ContextAGL> context = std::make_unique<ContextAGL>(wi);
if (!context->Initialize(versions_to_try, num_versions_to_try))
return nullptr;
return context;
}
bool ContextAGL::Initialize(const Version* versions_to_try, size_t num_versions_to_try)
{
for (size_t i = 0; i < num_versions_to_try; i++)
{
const Version& cv = versions_to_try[i];
if (cv.profile == Profile::NoProfile && CreateContext(nullptr, NSOpenGLProfileVersionLegacy, true))
{
// we already have the dummy context, so just use that
m_version = cv;
return true;
}
else if (cv.profile == Profile::Core)
{
if (cv.major_version > 4 || cv.minor_version > 1)
continue;
const NSOpenGLPixelFormatAttribute profile = (cv.major_version > 3 || cv.minor_version > 2) ? NSOpenGLProfileVersion4_1Core : NSOpenGLProfileVersion3_2Core;
if (CreateContext(nullptr, static_cast<int>(profile), true))
{
m_version = cv;
return true;
}
}
}
return false;
}
void* ContextAGL::GetProcAddress(const char* name)
{
void* addr = m_opengl_module_handle ? dlsym(m_opengl_module_handle, name) : nullptr;
if (addr)
return addr;
return dlsym(RTLD_NEXT, name);
}
bool ContextAGL::ChangeSurface(const WindowInfo& new_wi)
{
m_wi = new_wi;
BindContextToView();
return true;
}
void ContextAGL::ResizeSurface(u32 new_surface_width /*= 0*/, u32 new_surface_height /*= 0*/)
{
UpdateDimensions();
}
bool ContextAGL::UpdateDimensions()
{
const NSSize window_size = [GetView() frame].size;
const CGFloat window_scale = [[GetView() window] backingScaleFactor];
const u32 new_width = static_cast<u32>(static_cast<CGFloat>(window_size.width) * window_scale);
const u32 new_height = static_cast<u32>(static_cast<CGFloat>(window_size.height) * window_scale);
if (m_wi.surface_width == new_width && m_wi.surface_height == new_height)
return false;
m_wi.surface_width = new_width;
m_wi.surface_height = new_height;
dispatch_block_t block = ^{
[m_context update];
};
if ([NSThread isMainThread])
block();
else
dispatch_sync(dispatch_get_main_queue(), block);
return true;
}
bool ContextAGL::SwapBuffers()
{
[m_context flushBuffer];
return true;
}
bool ContextAGL::MakeCurrent()
{
[m_context makeCurrentContext];
return true;
}
bool ContextAGL::DoneCurrent()
{
[NSOpenGLContext clearCurrentContext];
return true;
}
bool ContextAGL::SetSwapInterval(s32 interval)
{
GLint gl_interval = static_cast<GLint>(interval);
[m_context setValues:&gl_interval forParameter:NSOpenGLCPSwapInterval];
return true;
}
std::unique_ptr<Context> ContextAGL::CreateSharedContext(const WindowInfo& wi)
{
std::unique_ptr<ContextAGL> context = std::make_unique<ContextAGL>(wi);
context->m_context = [[NSOpenGLContext alloc] initWithFormat:m_pixel_format shareContext:m_context];
if (context->m_context == nil)
return nullptr;
context->m_version = m_version;
context->m_pixel_format = m_pixel_format;
if (wi.type == WindowInfo::Type::MacOS)
context->BindContextToView();
return context;
}
bool ContextAGL::CreateContext(NSOpenGLContext* share_context, int profile, bool make_current)
{
if (m_context)
m_context = nullptr;
const NSOpenGLPixelFormatAttribute attribs[] = {
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAOpenGLProfile, static_cast<NSOpenGLPixelFormatAttribute>(profile),
NSOpenGLPFAAccelerated,
0
};
m_pixel_format = [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs];
if (m_pixel_format == nil)
{
Console.Error("(ContextAGL) Failed to initialize pixel format");
return false;
}
m_context = [[NSOpenGLContext alloc] initWithFormat:m_pixel_format shareContext:nil];
if (m_context == nil)
return false;
if (m_wi.type == WindowInfo::Type::MacOS)
BindContextToView();
if (make_current)
[m_context makeCurrentContext];
return true;
}
void ContextAGL::BindContextToView()
{
NSView* const view = GetView();
NSWindow* const window = [view window];
[view setWantsBestResolutionOpenGLSurface:YES];
UpdateDimensions();
dispatch_block_t block = ^{
[window makeFirstResponder:view];
[m_context setView:view];
[window makeKeyAndOrderFront:nil];
};
if ([NSThread isMainThread])
block();
else
dispatch_sync(dispatch_get_main_queue(), block);
}
} // namespace GL

399
common/GL/ContextEGL.cpp Normal file
View File

@ -0,0 +1,399 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2021 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "common/PrecompiledHeader.h"
#include "common/Console.h"
#include "ContextEGL.h"
#include <optional>
#include <vector>
namespace GL
{
ContextEGL::ContextEGL(const WindowInfo& wi)
: Context(wi)
{
}
ContextEGL::~ContextEGL()
{
DestroySurface();
DestroyContext();
}
std::unique_ptr<Context> ContextEGL::Create(const WindowInfo& wi, const Version* versions_to_try,
size_t num_versions_to_try)
{
std::unique_ptr<ContextEGL> context = std::make_unique<ContextEGL>(wi);
if (!context->Initialize(versions_to_try, num_versions_to_try))
return nullptr;
return context;
}
bool ContextEGL::Initialize(const Version* versions_to_try, size_t num_versions_to_try)
{
if (!gladLoadEGL())
{
Console.Error("Loading GLAD EGL functions failed");
return false;
}
if (!SetDisplay())
return false;
int egl_major, egl_minor;
if (!eglInitialize(m_display, &egl_major, &egl_minor))
{
Console.Error("eglInitialize() failed: %d", eglGetError());
return false;
}
Console.WriteLn("EGL Version: %d.%d", egl_major, egl_minor);
const char* extensions = eglQueryString(m_display, EGL_EXTENSIONS);
if (extensions)
{
Console.WriteLn("EGL Extensions: %s", extensions);
m_supports_surfaceless = std::strstr(extensions, "EGL_KHR_surfaceless_context") != nullptr;
}
if (!m_supports_surfaceless)
Console.Warning("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()
{
m_display = eglGetDisplay(static_cast<EGLNativeDisplayType>(m_wi.display_connection));
if (!m_display)
{
Console.Error("eglGetDisplay() failed: %d", eglGetError());
return false;
}
return true;
}
void* ContextEGL::GetProcAddress(const char* name)
{
return reinterpret_cast<void*>(eglGetProcAddress(name));
}
bool ContextEGL::ChangeSurface(const WindowInfo& new_wi)
{
const bool was_current = (eglGetCurrentContext() == m_context);
if (was_current)
eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (m_surface != EGL_NO_SURFACE)
{
eglDestroySurface(m_display, m_surface);
m_surface = EGL_NO_SURFACE;
}
m_wi = new_wi;
if (!CreateSurface())
return false;
if (was_current && !eglMakeCurrent(m_display, m_surface, m_surface, m_context))
{
Console.Error("Failed to make context current again after surface change");
return false;
}
return true;
}
void ContextEGL::ResizeSurface(u32 new_surface_width /*= 0*/, u32 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
{
Console.Error("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))
{
Console.Error("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)
{
Console.Error("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
{
Console.Error("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);
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)
{
Console.Error("eglCreatePbufferSurface() failed: %d", eglGetError());
return false;
}
Console.WriteLn("Created %ux%u pbuffer surface", width, height);
return true;
}
bool ContextEGL::CheckConfigSurfaceFormat(EGLConfig config) const
{
int red_size, green_size, blue_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))
{
return false;
}
return (red_size == 8 && green_size == 8 && blue_size == 8);
}
void ContextEGL::DestroyContext()
{
if (eglGetCurrentContext() == m_context)
eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (m_context != EGL_NO_CONTEXT)
{
eglDestroyContext(m_display, m_context);
m_context = EGL_NO_CONTEXT;
}
}
void ContextEGL::DestroySurface()
{
if (eglGetCurrentSurface(EGL_DRAW) == m_surface)
eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (m_surface != EGL_NO_SURFACE)
{
eglDestroySurface(m_display, m_surface);
m_surface = EGL_NO_SURFACE;
}
}
bool ContextEGL::CreateContext(const Version& version, EGLContext share_context)
{
Console.WriteLn(
"Trying version %u.%u (%s)", version.major_version, version.minor_version,
version.profile == Context::Profile::ES ? "ES" : (version.profile == Context::Profile::Core ? "Core" : "None"));
int surface_attribs[16];
int nsurface_attribs = 0;
surface_attribs[nsurface_attribs++] = EGL_RENDERABLE_TYPE;
surface_attribs[nsurface_attribs++] = (version.profile == Profile::ES) ?
((version.major_version >= 3) ? EGL_OPENGL_ES3_BIT :
((version.major_version == 2) ? EGL_OPENGL_ES2_BIT : EGL_OPENGL_ES_BIT)) :
EGL_OPENGL_BIT;
surface_attribs[nsurface_attribs++] = EGL_SURFACE_TYPE;
surface_attribs[nsurface_attribs++] = (m_wi.type != WindowInfo::Type::Surfaceless) ? EGL_WINDOW_BIT : 0;
surface_attribs[nsurface_attribs++] = EGL_RED_SIZE;
surface_attribs[nsurface_attribs++] = 8;
surface_attribs[nsurface_attribs++] = EGL_GREEN_SIZE;
surface_attribs[nsurface_attribs++] = 8;
surface_attribs[nsurface_attribs++] = EGL_BLUE_SIZE;
surface_attribs[nsurface_attribs++] = 8;
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)
{
Console.Error("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))
{
Console.Error("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))
{
config = check_config;
break;
}
}
if (!config.has_value())
{
Console.Warning("No EGL configs matched exactly, using first.");
config = configs.front();
}
int attribs[8];
int nattribs = 0;
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))
{
Console.Error("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)
{
Console.Error("eglCreateContext() failed: %d", eglGetError());
return false;
}
Console.WriteLn(
"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)
{
if (!CreateContext(version, share_context))
return false;
if (!CreateSurface())
{
Console.Error("Failed to create surface for context");
eglDestroyContext(m_display, m_context);
m_context = EGL_NO_CONTEXT;
return false;
}
if (make_current && !eglMakeCurrent(m_display, m_surface, m_surface, m_context))
{
Console.Error("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

64
common/GL/ContextEGL.h Normal file
View File

@ -0,0 +1,64 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2021 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "common/GL/Context.h"
#include "glad_egl.h"
namespace GL
{
class ContextEGL : public Context
{
public:
ContextEGL(const WindowInfo& wi);
~ContextEGL() override;
static std::unique_ptr<Context> Create(const WindowInfo& wi, const Version* versions_to_try,
size_t num_versions_to_try);
void* GetProcAddress(const char* name) override;
virtual bool ChangeSurface(const WindowInfo& new_wi) override;
virtual void ResizeSurface(u32 new_surface_width = 0, u32 new_surface_height = 0) override;
bool SwapBuffers() override;
bool MakeCurrent() override;
bool DoneCurrent() override;
bool SetSwapInterval(s32 interval) override;
virtual std::unique_ptr<Context> CreateSharedContext(const WindowInfo& wi) override;
protected:
virtual bool SetDisplay();
virtual EGLNativeWindowType GetNativeWindow(EGLConfig config);
bool Initialize(const Version* versions_to_try, size_t num_versions_to_try);
bool CreateDisplay();
bool CreateContext(const Version& version, EGLContext share_context);
bool CreateContextAndSurface(const Version& version, EGLContext share_context, bool make_current);
bool CreateSurface();
bool CreatePBufferSurface();
bool CheckConfigSurfaceFormat(EGLConfig config) const;
void DestroyContext();
void DestroySurface();
EGLDisplay m_display = EGL_NO_DISPLAY;
EGLSurface m_surface = EGL_NO_SURFACE;
EGLContext m_context = EGL_NO_CONTEXT;
EGLConfig m_config = {};
bool m_supports_surfaceless = false;
};
} // namespace GL

View File

@ -0,0 +1,77 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2021 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "common/PrecompiledHeader.h"
#include "ContextEGLWayland.h"
#include <wayland-egl.h>
namespace GL
{
ContextEGLWayland::ContextEGLWayland(const WindowInfo& wi)
: ContextEGL(wi)
{
}
ContextEGLWayland::~ContextEGLWayland()
{
if (m_wl_window)
wl_egl_window_destroy(m_wl_window);
}
std::unique_ptr<Context> ContextEGLWayland::Create(const WindowInfo& wi, const Version* versions_to_try,
size_t num_versions_to_try)
{
std::unique_ptr<ContextEGLWayland> context = std::make_unique<ContextEGLWayland>(wi);
if (!context->Initialize(versions_to_try, num_versions_to_try))
return nullptr;
return context;
}
std::unique_ptr<Context> ContextEGLWayland::CreateSharedContext(const WindowInfo& wi)
{
std::unique_ptr<ContextEGLWayland> context = std::make_unique<ContextEGLWayland>(wi);
context->m_display = m_display;
if (!context->CreateContextAndSurface(m_version, m_context, false))
return nullptr;
return context;
}
void ContextEGLWayland::ResizeSurface(u32 new_surface_width, u32 new_surface_height)
{
if (m_wl_window)
wl_egl_window_resize(m_wl_window, new_surface_width, new_surface_height, 0, 0);
ContextEGL::ResizeSurface(new_surface_width, new_surface_height);
}
EGLNativeWindowType ContextEGLWayland::GetNativeWindow(EGLConfig config)
{
if (m_wl_window)
{
wl_egl_window_destroy(m_wl_window);
m_wl_window = nullptr;
}
m_wl_window =
wl_egl_window_create(static_cast<wl_surface*>(m_wi.window_handle), m_wi.surface_width, m_wi.surface_height);
if (!m_wl_window)
return {};
return reinterpret_cast<EGLNativeWindowType>(m_wl_window);
}
} // namespace GL

View File

@ -0,0 +1,43 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2021 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "common/GL/ContextEGL.h"
#include <wayland-egl.h>
namespace GL
{
class ContextEGLWayland final : public ContextEGL
{
public:
ContextEGLWayland(const WindowInfo& wi);
~ContextEGLWayland() override;
static std::unique_ptr<Context> Create(const WindowInfo& wi, const Version* versions_to_try,
size_t num_versions_to_try);
std::unique_ptr<Context> CreateSharedContext(const WindowInfo& wi) override;
void ResizeSurface(u32 new_surface_width = 0, u32 new_surface_height = 0) override;
protected:
EGLNativeWindowType GetNativeWindow(EGLConfig config) override;
private:
wl_egl_window* m_wl_window = nullptr;
};
} // namespace GL

View File

@ -0,0 +1,60 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2021 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "common/PrecompiledHeader.h"
#include "common/GL/ContextEGLX11.h"
#include <X11/Xlib.h>
namespace GL
{
ContextEGLX11::ContextEGLX11(const WindowInfo& wi)
: ContextEGL(wi)
{
}
ContextEGLX11::~ContextEGLX11() = default;
std::unique_ptr<Context> ContextEGLX11::Create(const WindowInfo& wi, const Version* versions_to_try,
size_t num_versions_to_try)
{
std::unique_ptr<ContextEGLX11> context = std::make_unique<ContextEGLX11>(wi);
if (!context->Initialize(versions_to_try, num_versions_to_try))
return nullptr;
return context;
}
std::unique_ptr<Context> ContextEGLX11::CreateSharedContext(const WindowInfo& wi)
{
std::unique_ptr<ContextEGLX11> context = std::make_unique<ContextEGLX11>(wi);
context->m_display = m_display;
if (!context->CreateContextAndSurface(m_version, m_context, false))
return nullptr;
return context;
}
void ContextEGLX11::ResizeSurface(u32 new_surface_width, u32 new_surface_height)
{
ContextEGL::ResizeSurface(new_surface_width, new_surface_height);
}
EGLNativeWindowType ContextEGLX11::GetNativeWindow(EGLConfig config)
{
return (EGLNativeWindowType)reinterpret_cast<Window>(m_wi.window_handle);
}
} // namespace GL

37
common/GL/ContextEGLX11.h Normal file
View File

@ -0,0 +1,37 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2021 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "common/GL/ContextEGL.h"
namespace GL
{
class ContextEGLX11 final : public ContextEGL
{
public:
ContextEGLX11(const WindowInfo& wi);
~ContextEGLX11() override;
static std::unique_ptr<Context> Create(const WindowInfo& wi, const Version* versions_to_try,
size_t num_versions_to_try);
std::unique_ptr<Context> CreateSharedContext(const WindowInfo& wi) override;
void ResizeSurface(u32 new_surface_width = 0, u32 new_surface_height = 0) override;
protected:
EGLNativeWindowType GetNativeWindow(EGLConfig config) override;
};
} // namespace GL

334
common/GL/ContextWGL.cpp Normal file
View File

@ -0,0 +1,334 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2021 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "common/PrecompiledHeader.h"
#include "common/Console.h"
#include "ContextWGL.h"
static void* GetProcAddressCallback(const char* name)
{
void* addr = wglGetProcAddress(name);
if (addr)
return addr;
// try opengl32.dll
return ::GetProcAddress(GetModuleHandleA("opengl32.dll"), name);
}
namespace GL
{
ContextWGL::ContextWGL(const WindowInfo& wi)
: Context(wi)
{
}
ContextWGL::~ContextWGL()
{
if (wglGetCurrentContext() == m_rc)
wglMakeCurrent(m_dc, nullptr);
if (m_rc)
wglDeleteContext(m_rc);
if (m_dc)
ReleaseDC(GetHWND(), m_dc);
}
std::unique_ptr<Context> ContextWGL::Create(const WindowInfo& wi, const Version* versions_to_try,
size_t num_versions_to_try)
{
std::unique_ptr<ContextWGL> context = std::make_unique<ContextWGL>(wi);
if (!context->Initialize(versions_to_try, num_versions_to_try))
return nullptr;
return context;
}
bool ContextWGL::Initialize(const Version* versions_to_try, size_t num_versions_to_try)
{
if (m_wi.type == WindowInfo::Type::Win32)
{
if (!InitializeDC())
return false;
}
else
{
Console.Error("PBuffer not implemented");
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);
if (m_dc)
{
ReleaseDC(GetHWND(), m_dc);
m_dc = {};
}
m_wi = new_wi;
if (!InitializeDC())
return false;
if (was_current && !wglMakeCurrent(m_dc, m_rc))
{
Console.Error("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))
{
Console.Error("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
{
Console.Error("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;
}
bool ContextWGL::InitializeDC()
{
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;
m_dc = GetDC(GetHWND());
if (!m_dc)
{
Console.Error("GetDC() failed: 0x%08X", GetLastError());
return false;
}
const int pf = ChoosePixelFormat(m_dc, &pfd);
if (pf == 0)
{
Console.Error("ChoosePixelFormat() failed: 0x%08X", GetLastError());
return false;
}
if (!SetPixelFormat(m_dc, pf, &pfd))
{
Console.Error("SetPixelFormat() failed: 0x%08X", GetLastError());
return false;
}
return true;
}
bool ContextWGL::CreateAnyContext(HGLRC share_context, bool make_current)
{
m_rc = wglCreateContext(m_dc);
if (!m_rc)
{
Console.Error("wglCreateContext() failed: 0x%08X", GetLastError());
return false;
}
if (make_current)
{
if (!wglMakeCurrent(m_dc, m_rc))
{
Console.Error("wglMakeCurrent() failed: 0x%08X", GetLastError());
return false;
}
// re-init glad-wgl
if (!gladLoadWGLLoader([](const char* name) -> void* { return wglGetProcAddress(name); }, m_dc))
{
Console.Error("Loading GLAD WGL functions failed");
return false;
}
}
if (share_context && !wglShareLists(share_context, m_rc))
{
Console.Error("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)
{
Console.Error("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
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
0, 0};
new_rc = wglCreateContextAttribsARB(m_dc, share_context, attribs);
}
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))
{
Console.Error("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
{
Console.Error("Unknown profile");
return false;
}
if (!new_rc)
return false;
// destroy and swap contexts
if (m_rc)
{
if (!wglMakeCurrent(m_dc, make_current ? new_rc : nullptr))
{
Console.Error("wglMakeCurrent() failed: 0x%08X", GetLastError());
wglDeleteContext(new_rc);
return false;
}
// re-init glad-wgl
if (make_current && !gladLoadWGLLoader([](const char* name) -> void* { return wglGetProcAddress(name); }, m_dc))
{
Console.Error("Loading GLAD WGL functions failed");
return false;
}
wglDeleteContext(m_rc);
}
m_rc = new_rc;
return true;
}
} // namespace GL

59
common/GL/ContextWGL.h Normal file
View File

@ -0,0 +1,59 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2021 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "common/GL/Context.h"
#include "glad_wgl.h"
#include "glad.h"
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
namespace GL
{
class ContextWGL final : public Context
{
public:
ContextWGL(const WindowInfo& wi);
~ContextWGL() override;
static std::unique_ptr<Context> Create(const WindowInfo& wi, const Version* versions_to_try,
size_t num_versions_to_try);
void* GetProcAddress(const char* name) override;
bool ChangeSurface(const WindowInfo& new_wi) override;
void ResizeSurface(u32 new_surface_width = 0, u32 new_surface_height = 0) override;
bool SwapBuffers() override;
bool MakeCurrent() override;
bool DoneCurrent() override;
bool SetSwapInterval(s32 interval) override;
std::unique_ptr<Context> CreateSharedContext(const WindowInfo& wi) override;
private:
__fi HWND GetHWND() const { return static_cast<HWND>(m_wi.window_handle); }
bool Initialize(const Version* versions_to_try, size_t num_versions_to_try);
bool InitializeDC();
bool CreateAnyContext(HGLRC share_context, bool make_current);
bool CreateVersionContext(const Version& version, HGLRC share_context, bool make_current);
HDC m_dc = {};
HGLRC m_rc = {};
};
} // namespace GL

331
common/GL/StreamBuffer.cpp Normal file
View File

@ -0,0 +1,331 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2021 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "StreamBuffer.h"
#include "common/Align.h"
#include "common/Assertions.h"
#include <array>
#include <cstring>
namespace GL
{
StreamBuffer::StreamBuffer(GLenum target, GLuint buffer_id, u32 size)
: m_target(target)
, m_buffer_id(buffer_id)
, m_size(size)
{
}
StreamBuffer::~StreamBuffer()
{
glDeleteBuffers(1, &m_buffer_id);
}
void StreamBuffer::Bind()
{
glBindBuffer(m_target, m_buffer_id);
}
void StreamBuffer::Unbind()
{
glBindBuffer(m_target, 0);
}
namespace detail
{
// Uses glBufferSubData() to update. Preferred for drivers which don't support {ARB,EXT}_buffer_storage.
class BufferSubDataStreamBuffer final : public StreamBuffer
{
public:
~BufferSubDataStreamBuffer() override = default;
MappingResult Map(u32 alignment, u32 min_size) override
{
return MappingResult{static_cast<void*>(m_cpu_buffer.data()), 0, 0, m_size / alignment};
}
void Unmap(u32 used_size) override
{
if (used_size == 0)
return;
glBindBuffer(m_target, m_buffer_id);
glBufferSubData(m_target, 0, used_size, m_cpu_buffer.data());
}
static std::unique_ptr<StreamBuffer> Create(GLenum target, u32 size)
{
glGetError();
GLuint buffer_id;
glGenBuffers(1, &buffer_id);
glBindBuffer(target, buffer_id);
glBufferData(target, size, nullptr, GL_STREAM_DRAW);
GLenum err = glGetError();
if (err != GL_NO_ERROR)
{
glBindBuffer(target, 0);
glDeleteBuffers(1, &buffer_id);
return {};
}
return std::unique_ptr<StreamBuffer>(new BufferSubDataStreamBuffer(target, buffer_id, size));
}
private:
BufferSubDataStreamBuffer(GLenum target, GLuint buffer_id, u32 size)
: StreamBuffer(target, buffer_id, size)
, m_cpu_buffer(size)
{
}
std::vector<u8> m_cpu_buffer;
};
// Uses BufferData() to orphan the buffer after every update. Used on Mali where BufferSubData forces a sync.
class BufferDataStreamBuffer final : public StreamBuffer
{
public:
~BufferDataStreamBuffer() override = default;
MappingResult Map(u32 alignment, u32 min_size) override
{
return MappingResult{static_cast<void*>(m_cpu_buffer.data()), 0, 0, m_size / alignment};
}
void Unmap(u32 used_size) override
{
if (used_size == 0)
return;
glBindBuffer(m_target, m_buffer_id);
glBufferData(m_target, used_size, m_cpu_buffer.data(), GL_STREAM_DRAW);
}
static std::unique_ptr<StreamBuffer> Create(GLenum target, u32 size)
{
glGetError();
GLuint buffer_id;
glGenBuffers(1, &buffer_id);
glBindBuffer(target, buffer_id);
glBufferData(target, size, nullptr, GL_STREAM_DRAW);
GLenum err = glGetError();
if (err != GL_NO_ERROR)
{
glBindBuffer(target, 0);
glDeleteBuffers(1, &buffer_id);
return {};
}
return std::unique_ptr<StreamBuffer>(new BufferDataStreamBuffer(target, buffer_id, size));
}
private:
BufferDataStreamBuffer(GLenum target, GLuint buffer_id, u32 size)
: StreamBuffer(target, buffer_id, size)
, m_cpu_buffer(size)
{
}
std::vector<u8> m_cpu_buffer;
};
// Base class for implementations which require syncing.
class SyncingStreamBuffer : public StreamBuffer
{
public:
enum : u32
{
NUM_SYNC_POINTS = 16
};
virtual ~SyncingStreamBuffer() override
{
for (u32 i = m_available_block_index; i <= m_used_block_index; i++)
{
pxAssert(m_sync_objects[i]);
glDeleteSync(m_sync_objects[i]);
}
}
protected:
SyncingStreamBuffer(GLenum target, GLuint buffer_id, u32 size)
: StreamBuffer(target, buffer_id, size)
, m_bytes_per_block((size + (NUM_SYNC_POINTS)-1) / NUM_SYNC_POINTS)
{
}
__fi u32 GetSyncIndexForOffset(u32 offset) { return offset / m_bytes_per_block; }
__fi void AddSyncsForOffset(u32 offset)
{
const u32 end = GetSyncIndexForOffset(offset);
for (; m_used_block_index < end; m_used_block_index++)
{
pxAssert(!m_sync_objects[m_used_block_index]);
m_sync_objects[m_used_block_index] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
}
}
__fi void WaitForSync(GLsync& sync)
{
glClientWaitSync(sync, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED);
glDeleteSync(sync);
sync = nullptr;
}
__fi void EnsureSyncsWaitedForOffset(u32 offset)
{
const u32 end = std::min<u32>(GetSyncIndexForOffset(offset) + 1, NUM_SYNC_POINTS);
for (; m_available_block_index < end; m_available_block_index++)
{
pxAssert(m_sync_objects[m_available_block_index]);
WaitForSync(m_sync_objects[m_available_block_index]);
}
}
void AllocateSpace(u32 size)
{
// add sync objects for writes since the last allocation
AddSyncsForOffset(m_position);
// wait for sync objects for the space we want to use
EnsureSyncsWaitedForOffset(m_position + size);
// wrap-around?
if ((m_position + size) > m_size)
{
// current position ... buffer end
AddSyncsForOffset(m_size);
// rewind, and try again
m_position = 0;
// wait for the sync at the start of the buffer
WaitForSync(m_sync_objects[0]);
m_available_block_index = 1;
// and however much more we need to satisfy the allocation
EnsureSyncsWaitedForOffset(size);
m_used_block_index = 0;
}
}
u32 m_position = 0;
u32 m_used_block_index = 0;
u32 m_available_block_index = NUM_SYNC_POINTS;
u32 m_bytes_per_block;
std::array<GLsync, NUM_SYNC_POINTS> m_sync_objects{};
};
class BufferStorageStreamBuffer : public SyncingStreamBuffer
{
public:
~BufferStorageStreamBuffer() override
{
glBindBuffer(m_target, m_buffer_id);
glUnmapBuffer(m_target);
glBindBuffer(m_target, 0);
}
MappingResult Map(u32 alignment, u32 min_size) override
{
if (m_position > 0)
m_position = Common::AlignUp(m_position, alignment);
AllocateSpace(min_size);
pxAssert((m_position + min_size) <= (m_available_block_index * m_bytes_per_block));
const u32 free_space_in_block = ((m_available_block_index * m_bytes_per_block) - m_position);
return MappingResult{static_cast<void*>(m_mapped_ptr + m_position), m_position, m_position / alignment,
free_space_in_block / alignment};
}
void Unmap(u32 used_size) override
{
pxAssert((m_position + used_size) <= m_size);
if (!m_coherent)
{
Bind();
glFlushMappedBufferRange(m_target, m_position, used_size);
}
m_position += used_size;
}
static std::unique_ptr<StreamBuffer> Create(GLenum target, u32 size, bool coherent = true)
{
glGetError();
GLuint buffer_id;
glGenBuffers(1, &buffer_id);
glBindBuffer(target, buffer_id);
const u32 flags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | (coherent ? GL_MAP_COHERENT_BIT : 0);
const u32 map_flags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | (coherent ? 0 : GL_MAP_FLUSH_EXPLICIT_BIT);
if (GLAD_GL_VERSION_4_4 || GLAD_GL_ARB_buffer_storage)
glBufferStorage(target, size, nullptr, flags);
else if (GLAD_GL_EXT_buffer_storage)
glBufferStorageEXT(target, size, nullptr, flags);
GLenum err = glGetError();
if (err != GL_NO_ERROR)
{
glBindBuffer(target, 0);
glDeleteBuffers(1, &buffer_id);
return {};
}
u8* mapped_ptr = static_cast<u8*>(glMapBufferRange(target, 0, size, map_flags));
pxAssertRel(mapped_ptr, "Persistent buffer was mapped");
return std::unique_ptr<StreamBuffer>(new BufferStorageStreamBuffer(target, buffer_id, size, mapped_ptr, coherent));
}
private:
BufferStorageStreamBuffer(GLenum target, GLuint buffer_id, u32 size, u8* mapped_ptr, bool coherent)
: SyncingStreamBuffer(target, buffer_id, size)
, m_mapped_ptr(mapped_ptr)
, m_coherent(coherent)
{
}
u8* m_mapped_ptr;
bool m_coherent;
};
} // namespace detail
std::unique_ptr<StreamBuffer> StreamBuffer::Create(GLenum target, u32 size)
{
std::unique_ptr<StreamBuffer> buf;
if (GLAD_GL_VERSION_4_4 || GLAD_GL_ARB_buffer_storage || GLAD_GL_EXT_buffer_storage)
{
buf = detail::BufferStorageStreamBuffer::Create(target, size);
if (buf)
return buf;
}
// BufferSubData is slower on all drivers except NVIDIA...
const char* vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
if (std::strcmp(vendor, "NVIDIA") == 0)
return detail::BufferSubDataStreamBuffer::Create(target, size);
else
return detail::BufferDataStreamBuffer::Create(target, size);
}
} // namespace GL

58
common/GL/StreamBuffer.h Normal file
View File

@ -0,0 +1,58 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2021 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "common/Pcsx2Defs.h"
#include "glad.h"
#include <memory>
#include <tuple>
#include <vector>
namespace GL
{
/// Provides a buffer for streaming data to the GPU, ideally in write-combined memory.
class StreamBuffer
{
public:
virtual ~StreamBuffer();
__fi GLuint GetGLBufferId() const { return m_buffer_id; }
__fi GLenum GetGLTarget() const { return m_target; }
__fi u32 GetSize() const { return m_size; }
void Bind();
void Unbind();
struct MappingResult
{
void* pointer;
u32 buffer_offset;
u32 index_aligned; // offset / alignment, suitable for base vertex
u32 space_aligned; // remaining space / alignment
};
virtual MappingResult Map(u32 alignment, u32 min_size) = 0;
virtual void Unmap(u32 used_size) = 0;
static std::unique_ptr<StreamBuffer> Create(GLenum target, u32 size);
protected:
StreamBuffer(GLenum target, GLuint buffer_id, u32 size);
GLenum m_target;
GLuint m_buffer_id;
u32 m_size;
};
} // namespace GL

View File

@ -33,6 +33,7 @@
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\glad\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<ExceptionHandling>Async</ExceptionHandling>
<PrecompiledHeader>Use</PrecompiledHeader>
<ForcedIncludeFiles>PrecompiledHeader.h</ForcedIncludeFiles>
@ -48,6 +49,9 @@
<ClCompile Include="FastJmp.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="GL\Context.cpp" />
<ClCompile Include="GL\ContextWGL.cpp" />
<ClCompile Include="GL\StreamBuffer.cpp" />
<ClCompile Include="IniInterface.cpp" />
<ClCompile Include="pxStreams.cpp" />
<ClCompile Include="pxTranslate.cpp" />
@ -99,6 +103,9 @@
<ClInclude Include="EmbeddedImage.h" />
<ClInclude Include="boost_spsc_queue.hpp" />
<ClInclude Include="FastJmp.h" />
<ClInclude Include="GL\Context.h" />
<ClInclude Include="GL\ContextWGL.h" />
<ClInclude Include="GL\StreamBuffer.h" />
<ClInclude Include="ScopedAlloc.h" />
<ClInclude Include="ScopedGuard.h" />
<ClInclude Include="StringUtil.h" />
@ -150,6 +157,9 @@
<ClInclude Include="emitter\implement\simd_shufflepack.h" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\3rdparty\glad\glad.vcxproj">
<Project>{c0293b32-5acf-40f0-aa6c-e6da6f3bf33a}</Project>
</ProjectReference>
<ProjectReference Include="..\3rdparty\pthreads4w\build\pthreads4w.vcxproj">
<Project>{0fae817d-9a32-4830-857e-81da57246e16}</Project>
</ProjectReference>

View File

@ -121,6 +121,15 @@
<ClCompile Include="WindowInfo.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="GL\ContextWGL.cpp">
<Filter>Source Files\GL</Filter>
</ClCompile>
<ClCompile Include="GL\Context.cpp">
<Filter>Source Files\GL</Filter>
</ClCompile>
<ClCompile Include="GL\StreamBuffer.cpp">
<Filter>Source Files\GL</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Assertions.h">
@ -282,6 +291,15 @@
<ClInclude Include="WindowInfo.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="GL\Context.h">
<Filter>Header Files\GL</Filter>
</ClInclude>
<ClInclude Include="GL\ContextWGL.h">
<Filter>Header Files\GL</Filter>
</ClInclude>
<ClInclude Include="GL\StreamBuffer.h">
<Filter>Header Files\GL</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="Source Files">
@ -290,6 +308,12 @@
<Filter Include="Header Files">
<UniqueIdentifier>{eef579af-e6a8-4d3b-a88e-c0e4cad9e5d8}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\GL">
<UniqueIdentifier>{92d1bb2d-d172-4356-b9f4-a8fcc8e7fcce}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\GL">
<UniqueIdentifier>{5e76b340-cb0e-4946-83ec-7d72e397cac8}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<None Include="EventSource.inl">
@ -301,4 +325,4 @@
<Filter>Source Files</Filter>
</MASM>
</ItemGroup>
</Project>
</Project>