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 }