duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

opengl_context_agl.mm (6224B)


      1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
      2 // SPDX-License-Identifier: (GPL-3.0 OR PolyForm-Strict-1.0.0)
      3 
      4 #include "opengl_context_agl.h"
      5 
      6 #include "common/assert.h"
      7 #include "common/error.h"
      8 #include "common/log.h"
      9 
     10 #include <dlfcn.h>
     11 
     12 Log_SetChannel(OpenGLContext);
     13 
     14 OpenGLContextAGL::OpenGLContextAGL(const WindowInfo& wi) : OpenGLContext(wi)
     15 {
     16   m_opengl_module_handle = dlopen("/System/Library/Frameworks/OpenGL.framework/Versions/Current/OpenGL", RTLD_NOW);
     17   if (!m_opengl_module_handle)
     18     ERROR_LOG("Could not open OpenGL.framework, function lookups will probably fail");
     19 }
     20 
     21 OpenGLContextAGL::~OpenGLContextAGL()
     22 {
     23   if ([NSOpenGLContext currentContext] == m_context)
     24     [NSOpenGLContext clearCurrentContext];
     25 
     26   if (m_context)
     27     [m_context release];
     28 
     29   if (m_pixel_format)
     30     [m_pixel_format release];
     31 
     32   if (m_opengl_module_handle)
     33     dlclose(m_opengl_module_handle);
     34 }
     35 
     36 std::unique_ptr<OpenGLContext> OpenGLContextAGL::Create(const WindowInfo& wi, std::span<const Version> versions_to_try, Error* error)
     37 {
     38   std::unique_ptr<OpenGLContextAGL> context = std::make_unique<OpenGLContextAGL>(wi);
     39   if (!context->Initialize(versions_to_try, error))
     40     return nullptr;
     41 
     42   return context;
     43 }
     44 
     45 bool OpenGLContextAGL::Initialize(const std::span<const Version> versions_to_try, Error* error)
     46 {
     47   for (const Version& cv : versions_to_try)
     48   {
     49     if (cv.profile == Profile::NoProfile && CreateContext(nullptr, NSOpenGLProfileVersionLegacy, true, error))
     50     {
     51       // we already have the dummy context, so just use that
     52       m_version = cv;
     53       return true;
     54     }
     55     else if (cv.profile == Profile::Core)
     56     {
     57       if (cv.major_version > 4 || cv.minor_version > 1)
     58         continue;
     59 
     60       const NSOpenGLPixelFormatAttribute profile =
     61         (cv.major_version > 3 || cv.minor_version > 2) ? NSOpenGLProfileVersion4_1Core : NSOpenGLProfileVersion3_2Core;
     62       if (CreateContext(nullptr, static_cast<int>(profile), true, error))
     63       {
     64         m_version = cv;
     65         return true;
     66       }
     67     }
     68   }
     69 
     70   Error::SetStringView(error, "Failed to create any context versions.");
     71   return false;
     72 }
     73 
     74 void* OpenGLContextAGL::GetProcAddress(const char* name)
     75 {
     76   void* addr = m_opengl_module_handle ? dlsym(m_opengl_module_handle, name) : nullptr;
     77   if (addr)
     78     return addr;
     79 
     80   return dlsym(RTLD_NEXT, name);
     81 }
     82 
     83 bool OpenGLContextAGL::ChangeSurface(const WindowInfo& new_wi)
     84 {
     85   m_wi = new_wi;
     86   BindContextToView();
     87   return true;
     88 }
     89 
     90 void OpenGLContextAGL::ResizeSurface(u32 new_surface_width /*= 0*/, u32 new_surface_height /*= 0*/)
     91 {
     92   UpdateDimensions();
     93 }
     94 
     95 bool OpenGLContextAGL::UpdateDimensions()
     96 {
     97   const NSSize window_size = [GetView() frame].size;
     98   const CGFloat window_scale = [[GetView() window] backingScaleFactor];
     99   const u32 new_width = static_cast<u32>(static_cast<CGFloat>(window_size.width) * window_scale);
    100   const u32 new_height = static_cast<u32>(static_cast<CGFloat>(window_size.height) * window_scale);
    101 
    102   if (m_wi.surface_width == new_width && m_wi.surface_height == new_height)
    103     return false;
    104 
    105   m_wi.surface_width = new_width;
    106   m_wi.surface_height = new_height;
    107 
    108   dispatch_block_t block = ^{
    109     [m_context update];
    110   };
    111 
    112   if ([NSThread isMainThread])
    113     block();
    114   else
    115     dispatch_sync(dispatch_get_main_queue(), block);
    116 
    117   return true;
    118 }
    119 
    120 bool OpenGLContextAGL::SwapBuffers()
    121 {
    122   [m_context flushBuffer];
    123   return true;
    124 }
    125 
    126 bool OpenGLContextAGL::IsCurrent() const
    127 {
    128   return (m_context != nil && [NSOpenGLContext currentContext] == m_context);
    129 }
    130 
    131 bool OpenGLContextAGL::MakeCurrent()
    132 {
    133   [m_context makeCurrentContext];
    134   return true;
    135 }
    136 
    137 bool OpenGLContextAGL::DoneCurrent()
    138 {
    139   [NSOpenGLContext clearCurrentContext];
    140   return true;
    141 }
    142 
    143 bool OpenGLContextAGL::SupportsNegativeSwapInterval() const
    144 {
    145   return false;
    146 }
    147 
    148 bool OpenGLContextAGL::SetSwapInterval(s32 interval)
    149 {
    150   GLint gl_interval = static_cast<GLint>(interval);
    151   [m_context setValues:&gl_interval forParameter:NSOpenGLCPSwapInterval];
    152   return true;
    153 }
    154 
    155 std::unique_ptr<OpenGLContext> OpenGLContextAGL::CreateSharedContext(const WindowInfo& wi, Error* error)
    156 {
    157   std::unique_ptr<OpenGLContextAGL> context = std::make_unique<OpenGLContextAGL>(wi);
    158 
    159   context->m_context = [[NSOpenGLContext alloc] initWithFormat:m_pixel_format shareContext:m_context];
    160   if (context->m_context == nil)
    161   {
    162     Error::SetStringView(error, "NSOpenGLContext initWithFormat failed");
    163     return nullptr;
    164   }
    165 
    166   context->m_version = m_version;
    167   context->m_pixel_format = m_pixel_format;
    168   [context->m_pixel_format retain];
    169 
    170   if (wi.type == WindowInfo::Type::MacOS)
    171     context->BindContextToView();
    172 
    173   return context;
    174 }
    175 
    176 bool OpenGLContextAGL::CreateContext(NSOpenGLContext* share_context, int profile, bool make_current, Error* error)
    177 {
    178   if (m_context)
    179   {
    180     [m_context release];
    181     m_context = nullptr;
    182   }
    183 
    184   if (m_pixel_format)
    185     [m_pixel_format release];
    186 
    187   const std::array<NSOpenGLPixelFormatAttribute, 5> attribs = {{NSOpenGLPFADoubleBuffer, NSOpenGLPFAOpenGLProfile,
    188                                                                 static_cast<NSOpenGLPixelFormatAttribute>(profile),
    189                                                                 NSOpenGLPFAAccelerated, 0}};
    190   m_pixel_format = [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs.data()];
    191   if (m_pixel_format == nil)
    192   {
    193     Error::SetStringView(error, "Failed to initialize pixel format");
    194     return false;
    195   }
    196 
    197   m_context = [[NSOpenGLContext alloc] initWithFormat:m_pixel_format shareContext:nil];
    198   if (m_context == nil)
    199   {
    200     Error::SetStringView(error, "NSOpenGLContext initWithFormat failed");
    201     return false;
    202   }
    203 
    204   if (m_wi.type == WindowInfo::Type::MacOS)
    205     BindContextToView();
    206 
    207   if (make_current)
    208     [m_context makeCurrentContext];
    209 
    210   return true;
    211 }
    212 
    213 void OpenGLContextAGL::BindContextToView()
    214 {
    215   NSView* const view = GetView();
    216   NSWindow* const window = [view window];
    217   [view setWantsBestResolutionOpenGLSurface:YES];
    218 
    219   UpdateDimensions();
    220 
    221   dispatch_block_t block = ^{
    222     [window makeFirstResponder:view];
    223     [m_context setView:view];
    224     [window makeKeyAndOrderFront:nil];
    225   };
    226 
    227   if ([NSThread isMainThread])
    228     block();
    229   else
    230     dispatch_sync(dispatch_get_main_queue(), block);
    231 }