mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-12 06:08:30 +09:00
[Fix] (MG_Impl): handle Retina drawable resize on macOS
This commit is contained in:
@@ -211,7 +211,8 @@ namespace MobileGL::MG_Backend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (m_eglSurfaceInitialized && m_eglSurfaceKind == SurfaceKind::Window &&
|
if (m_eglSurfaceInitialized && m_eglSurfaceKind == SurfaceKind::Window &&
|
||||||
m_windowHandle.Backend == handle.Backend && m_windowHandle.Handle == handle.Handle) {
|
m_windowHandle.Backend == handle.Backend && m_windowHandle.Handle == handle.Handle &&
|
||||||
|
m_windowHandle.Width == handle.Width && m_windowHandle.Height == handle.Height) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -232,6 +232,8 @@ namespace MobileGL {
|
|||||||
struct WindowHandle {
|
struct WindowHandle {
|
||||||
WindowBackend Backend = WindowBackend::Unknown;
|
WindowBackend Backend = WindowBackend::Unknown;
|
||||||
void* Handle = nullptr;
|
void* Handle = nullptr;
|
||||||
|
Uint32 Width = 0;
|
||||||
|
Uint32 Height = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class BackendObject {
|
class BackendObject {
|
||||||
|
|||||||
@@ -420,7 +420,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Bool sameHandle = m_eglSurfaceInitialized && m_eglSurfaceKind == SurfaceKind::Window &&
|
const Bool sameHandle = m_eglSurfaceInitialized && m_eglSurfaceKind == SurfaceKind::Window &&
|
||||||
m_windowHandle.Backend == handle.Backend && m_windowHandle.Handle == handle.Handle;
|
m_windowHandle.Backend == handle.Backend && m_windowHandle.Handle == handle.Handle &&
|
||||||
|
m_windowHandle.Width == handle.Width && m_windowHandle.Height == handle.Height;
|
||||||
if (sameHandle) {
|
if (sameHandle) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -232,6 +232,30 @@ namespace MobileGL::MG_Impl::CGLImpl {
|
|||||||
}
|
}
|
||||||
return kCGLNoError;
|
return kCGLNoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CGLError RecreateSurfaceLocked(CGLContextObj ctx, ContextObject& object) {
|
||||||
|
if (!object.MetalLayer) {
|
||||||
|
return kCGLBadDrawable;
|
||||||
|
}
|
||||||
|
if (object.Surface != EGL_NO_SURFACE) {
|
||||||
|
EGLImpl::DestroySurface(object.Display, object.Surface);
|
||||||
|
object.Surface = EGL_NO_SURFACE;
|
||||||
|
}
|
||||||
|
const EGLAttrib attribs[] = {
|
||||||
|
EGL_WIDTH, std::max<GLint>(object.SurfaceBackingSize[0], 1),
|
||||||
|
EGL_HEIGHT, std::max<GLint>(object.SurfaceBackingSize[1], 1),
|
||||||
|
EGL_NONE,
|
||||||
|
};
|
||||||
|
EGLSurface surface = EGLImpl::CreatePlatformWindowSurface(object.Display, object.Config,
|
||||||
|
object.MetalLayer, attribs);
|
||||||
|
if (surface == EGL_NO_SURFACE) {
|
||||||
|
object.HasDrawable = false;
|
||||||
|
return kCGLBadDrawable;
|
||||||
|
}
|
||||||
|
object.Surface = surface;
|
||||||
|
object.HasDrawable = true;
|
||||||
|
return GetCurrentContext() == ctx ? MakeCurrentLocked(ctx, object) : kCGLNoError;
|
||||||
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
CGLError ChoosePixelFormat(const CGLPixelFormatAttribute* attribs, CGLPixelFormatObj* pix, GLint* npix) {
|
CGLError ChoosePixelFormat(const CGLPixelFormatAttribute* attribs, CGLPixelFormatObj* pix, GLint* npix) {
|
||||||
@@ -460,9 +484,19 @@ namespace MobileGL::MG_Impl::CGLImpl {
|
|||||||
EGLImpl::SwapInterval(object->Display, object->SwapInterval);
|
EGLImpl::SwapInterval(object->Display, object->SwapInterval);
|
||||||
return kCGLNoError;
|
return kCGLNoError;
|
||||||
case kCGLCPSurfaceBackingSize:
|
case kCGLCPSurfaceBackingSize:
|
||||||
object->SurfaceBackingSize[0] = params[0];
|
{
|
||||||
object->SurfaceBackingSize[1] = params[1];
|
const GLint width = std::max<GLint>(params[0], 1);
|
||||||
|
const GLint height = std::max<GLint>(params[1], 1);
|
||||||
|
if (object->SurfaceBackingSize[0] == width && object->SurfaceBackingSize[1] == height) {
|
||||||
|
return kCGLNoError;
|
||||||
|
}
|
||||||
|
object->SurfaceBackingSize[0] = width;
|
||||||
|
object->SurfaceBackingSize[1] = height;
|
||||||
|
if (object->MetalLayer) {
|
||||||
|
return RecreateSurfaceLocked(ctx, *object);
|
||||||
|
}
|
||||||
return kCGLNoError;
|
return kCGLNoError;
|
||||||
|
}
|
||||||
case kCGLCPSurfaceOpacity:
|
case kCGLCPSurfaceOpacity:
|
||||||
case kCGLCPSurfaceOrder:
|
case kCGLCPSurfaceOrder:
|
||||||
case kCGLCPMPSwapsInFlight:
|
case kCGLCPMPSwapsInFlight:
|
||||||
@@ -600,7 +634,7 @@ namespace MobileGL::MG_Impl::CGLImpl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CGLError AttachDrawable(CGLContextObj ctx, void* nsView, void* metalLayer) {
|
CGLError AttachDrawable(CGLContextObj ctx, void* nsView, void* metalLayer, GLint width, GLint height) {
|
||||||
const std::lock_guard<std::recursive_mutex> lock(RegistryMutex());
|
const std::lock_guard<std::recursive_mutex> lock(RegistryMutex());
|
||||||
auto* object = TryGetContext(ctx);
|
auto* object = TryGetContext(ctx);
|
||||||
if (!object) {
|
if (!object) {
|
||||||
@@ -609,7 +643,12 @@ namespace MobileGL::MG_Impl::CGLImpl {
|
|||||||
if (!metalLayer) {
|
if (!metalLayer) {
|
||||||
return kCGLBadDrawable;
|
return kCGLBadDrawable;
|
||||||
}
|
}
|
||||||
if (object->Surface != EGL_NO_SURFACE && object->MetalLayer == metalLayer) {
|
width = std::max<GLint>(width, 1);
|
||||||
|
height = std::max<GLint>(height, 1);
|
||||||
|
const Bool sameSize = object->SurfaceBackingSize[0] == width && object->SurfaceBackingSize[1] == height;
|
||||||
|
object->SurfaceBackingSize[0] = width;
|
||||||
|
object->SurfaceBackingSize[1] = height;
|
||||||
|
if (object->Surface != EGL_NO_SURFACE && object->MetalLayer == metalLayer && sameSize) {
|
||||||
object->View = nsView;
|
object->View = nsView;
|
||||||
object->HasDrawable = true;
|
object->HasDrawable = true;
|
||||||
return kCGLNoError;
|
return kCGLNoError;
|
||||||
@@ -618,16 +657,11 @@ namespace MobileGL::MG_Impl::CGLImpl {
|
|||||||
EGLImpl::DestroySurface(object->Display, object->Surface);
|
EGLImpl::DestroySurface(object->Display, object->Surface);
|
||||||
object->Surface = EGL_NO_SURFACE;
|
object->Surface = EGL_NO_SURFACE;
|
||||||
}
|
}
|
||||||
EGLSurface surface = EGLImpl::CreatePlatformWindowSurface(object->Display, object->Config, metalLayer, nullptr);
|
|
||||||
if (surface == EGL_NO_SURFACE) {
|
|
||||||
return kCGLBadDrawable;
|
|
||||||
}
|
|
||||||
object->Surface = surface;
|
|
||||||
object->View = nsView;
|
object->View = nsView;
|
||||||
object->MetalLayer = metalLayer;
|
object->MetalLayer = metalLayer;
|
||||||
object->HasDrawable = true;
|
const auto recreateError = RecreateSurfaceLocked(ctx, *object);
|
||||||
if (GetCurrentContext() == ctx) {
|
if (recreateError != kCGLNoError) {
|
||||||
return MakeCurrentLocked(ctx, *object);
|
return recreateError;
|
||||||
}
|
}
|
||||||
return kCGLNoError;
|
return kCGLNoError;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ namespace MobileGL::MG_Impl::CGLImpl {
|
|||||||
void GetVersion(GLint* majorvers, GLint* minorvers);
|
void GetVersion(GLint* majorvers, GLint* minorvers);
|
||||||
const char* ErrorString(CGLError error);
|
const char* ErrorString(CGLError error);
|
||||||
|
|
||||||
CGLError AttachDrawable(CGLContextObj ctx, void* nsView, void* metalLayer);
|
CGLError AttachDrawable(CGLContextObj ctx, void* nsView, void* metalLayer, GLint width, GLint height);
|
||||||
void* GetContextNSObject(CGLContextObj ctx);
|
void* GetContextNSObject(CGLContextObj ctx);
|
||||||
void SetContextNSObject(CGLContextObj ctx, void* nsObject);
|
void SetContextNSObject(CGLContextObj ctx, void* nsObject);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,18 @@ namespace MobileGL::MG_Impl::EGLImpl {
|
|||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EGLint GetAttribValue(const EGLAttrib* attribList, EGLint attrib, EGLint defaultValue) {
|
||||||
|
if (!attribList) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
for (SizeT i = 0; attribList[i] != EGL_NONE; i += 2) {
|
||||||
|
if (attribList[i] == attrib) {
|
||||||
|
return static_cast<EGLint>(attribList[i + 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename NativeType>
|
template <typename NativeType>
|
||||||
Bool IsNullNativeHandle(NativeType nativeHandle) {
|
Bool IsNullNativeHandle(NativeType nativeHandle) {
|
||||||
if constexpr (std::is_pointer_v<NativeType>) {
|
if constexpr (std::is_pointer_v<NativeType>) {
|
||||||
@@ -102,6 +114,8 @@ namespace MobileGL::MG_Impl::EGLImpl {
|
|||||||
const MG_Backend::WindowHandle windowHandle = {
|
const MG_Backend::WindowHandle windowHandle = {
|
||||||
.Backend = DetectWindowBackend(),
|
.Backend = DetectWindowBackend(),
|
||||||
.Handle = ToVoidHandle(window),
|
.Handle = ToVoidHandle(window),
|
||||||
|
.Width = static_cast<Uint32>(std::max<EGLint>(GetAttribValue(attrib_list, EGL_WIDTH, 0), 0)),
|
||||||
|
.Height = static_cast<Uint32>(std::max<EGLint>(GetAttribValue(attrib_list, EGL_HEIGHT, 0), 0)),
|
||||||
};
|
};
|
||||||
if (!backendObject->CreateEGLWindowSurface(windowHandle)) {
|
if (!backendObject->CreateEGLWindowSurface(windowHandle)) {
|
||||||
state->SetError(EGL_BAD_NATIVE_WINDOW);
|
state->SetError(EGL_BAD_NATIVE_WINDOW);
|
||||||
@@ -598,6 +612,8 @@ namespace MobileGL::MG_Impl::EGLImpl {
|
|||||||
const MG_Backend::WindowHandle windowHandle = {
|
const MG_Backend::WindowHandle windowHandle = {
|
||||||
.Backend = DetectWindowBackend(),
|
.Backend = DetectWindowBackend(),
|
||||||
.Handle = native_window,
|
.Handle = native_window,
|
||||||
|
.Width = static_cast<Uint32>(std::max<EGLint>(GetAttribValue(attrib_list, EGL_WIDTH, 0), 0)),
|
||||||
|
.Height = static_cast<Uint32>(std::max<EGLint>(GetAttribValue(attrib_list, EGL_HEIGHT, 0), 0)),
|
||||||
};
|
};
|
||||||
if (!backendObject->CreateEGLWindowSurface(windowHandle)) {
|
if (!backendObject->CreateEGLWindowSurface(windowHandle)) {
|
||||||
state->SetError(EGL_BAD_NATIVE_WINDOW);
|
state->SetError(EGL_BAD_NATIVE_WINDOW);
|
||||||
|
|||||||
@@ -77,6 +77,12 @@ namespace MobileGL::MG_Impl::NSOpenGLImpl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SendVoidCGFloat(id receiver, const char* selector, CGFloat value) {
|
||||||
|
if (receiver) {
|
||||||
|
ObjcMsgSend<void (*)(id, SEL, CGFloat)>()(receiver, sel_registerName(selector), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool SendBool(id receiver, const char* selector) {
|
bool SendBool(id receiver, const char* selector) {
|
||||||
return receiver ? ObjcMsgSend<bool (*)(id, SEL)>()(receiver, sel_registerName(selector)) : false;
|
return receiver ? ObjcMsgSend<bool (*)(id, SEL)>()(receiver, sel_registerName(selector)) : false;
|
||||||
}
|
}
|
||||||
@@ -88,6 +94,13 @@ namespace MobileGL::MG_Impl::NSOpenGLImpl {
|
|||||||
return ObjcMsgSend<CGRect (*)(id, SEL)>()(receiver, sel_registerName(selector));
|
return ObjcMsgSend<CGRect (*)(id, SEL)>()(receiver, sel_registerName(selector));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CGRect SendCGRectCGRect(id receiver, const char* selector, CGRect value) {
|
||||||
|
if (!receiver) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return ObjcMsgSend<CGRect (*)(id, SEL, CGRect)>()(receiver, sel_registerName(selector), value);
|
||||||
|
}
|
||||||
|
|
||||||
CGFloat SendCGFloat(id receiver, const char* selector) {
|
CGFloat SendCGFloat(id receiver, const char* selector) {
|
||||||
return receiver ? ObjcMsgSend<CGFloat (*)(id, SEL)>()(receiver, sel_registerName(selector)) : 1.0;
|
return receiver ? ObjcMsgSend<CGFloat (*)(id, SEL)>()(receiver, sel_registerName(selector)) : 1.0;
|
||||||
}
|
}
|
||||||
@@ -200,7 +213,48 @@ namespace MobileGL::MG_Impl::NSOpenGLImpl {
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
id CreateMetalLayerForView(id view) {
|
struct DrawableGeometry {
|
||||||
|
CGRect Bounds = {};
|
||||||
|
CGSize DrawableSize = {1.0, 1.0};
|
||||||
|
CGFloat Scale = 1.0;
|
||||||
|
};
|
||||||
|
|
||||||
|
DrawableGeometry GetDrawableGeometry(id view) {
|
||||||
|
DrawableGeometry geometry;
|
||||||
|
geometry.Bounds = SendCGRect(view, "bounds");
|
||||||
|
if (geometry.Bounds.size.width <= 0.0 || geometry.Bounds.size.height <= 0.0) {
|
||||||
|
geometry.Bounds.size.width = 1.0;
|
||||||
|
geometry.Bounds.size.height = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
id window = SendId(view, "window");
|
||||||
|
if (window) {
|
||||||
|
geometry.Scale = SendCGFloat(window, "backingScaleFactor");
|
||||||
|
}
|
||||||
|
if (geometry.Scale <= 0.0) {
|
||||||
|
geometry.Scale = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
CGRect backingBounds = SendCGRectCGRect(view, "convertRectToBacking:", geometry.Bounds);
|
||||||
|
if (backingBounds.size.width > 0.0 && backingBounds.size.height > 0.0) {
|
||||||
|
geometry.DrawableSize = backingBounds.size;
|
||||||
|
} else {
|
||||||
|
geometry.DrawableSize = {geometry.Bounds.size.width * geometry.Scale,
|
||||||
|
geometry.Bounds.size.height * geometry.Scale};
|
||||||
|
}
|
||||||
|
geometry.DrawableSize.width = std::max<CGFloat>(std::round(geometry.DrawableSize.width), 1.0);
|
||||||
|
geometry.DrawableSize.height = std::max<CGFloat>(std::round(geometry.DrawableSize.height), 1.0);
|
||||||
|
return geometry;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigureMetalLayerForView(id layer, id view, const DrawableGeometry& geometry) {
|
||||||
|
SendVoidCGRect(layer, "setFrame:", geometry.Bounds);
|
||||||
|
SendVoidCGFloat(layer, "setContentsScale:", geometry.Scale);
|
||||||
|
SendVoidCGSize(layer, "setDrawableSize:", geometry.DrawableSize);
|
||||||
|
SendVoidBool(layer, "setNeedsDisplayOnBoundsChange:", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
id CreateMetalLayerForView(id view, DrawableGeometry* outGeometry) {
|
||||||
if (!view) {
|
if (!view) {
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
@@ -216,23 +270,12 @@ namespace MobileGL::MG_Impl::NSOpenGLImpl {
|
|||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
CGRect bounds = SendCGRect(view, "bounds");
|
const auto geometry = GetDrawableGeometry(view);
|
||||||
if (bounds.size.width <= 0.0 || bounds.size.height <= 0.0) {
|
ConfigureMetalLayerForView(layer, view, geometry);
|
||||||
bounds.size.width = 1.0;
|
|
||||||
bounds.size.height = 1.0;
|
|
||||||
}
|
|
||||||
CGFloat scale = 1.0;
|
|
||||||
id window = SendId(view, "window");
|
|
||||||
if (window) {
|
|
||||||
scale = SendCGFloat(window, "backingScaleFactor");
|
|
||||||
}
|
|
||||||
if (scale <= 0.0) {
|
|
||||||
scale = 1.0;
|
|
||||||
}
|
|
||||||
CGSize drawableSize = {bounds.size.width * scale, bounds.size.height * scale};
|
|
||||||
SendVoidCGRect(layer, "setFrame:", bounds);
|
|
||||||
SendVoidCGSize(layer, "setDrawableSize:", drawableSize);
|
|
||||||
SendVoidId(view, "setLayer:", layer);
|
SendVoidId(view, "setLayer:", layer);
|
||||||
|
if (outGeometry) {
|
||||||
|
*outGeometry = geometry;
|
||||||
|
}
|
||||||
return layer;
|
return layer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,13 +290,16 @@ namespace MobileGL::MG_Impl::NSOpenGLImpl {
|
|||||||
SetAssoc(self, &kContextLayerKey, nil, kAssociationRetain);
|
SetAssoc(self, &kContextLayerKey, nil, kAssociationRetain);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
id layer = CreateMetalLayerForView(view);
|
DrawableGeometry geometry;
|
||||||
|
id layer = CreateMetalLayerForView(view, &geometry);
|
||||||
if (!layer) {
|
if (!layer) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SetAssoc(self, &kContextViewKey, view, kAssociationAssign);
|
SetAssoc(self, &kContextViewKey, view, kAssociationAssign);
|
||||||
SetAssoc(self, &kContextLayerKey, layer, kAssociationRetain);
|
SetAssoc(self, &kContextLayerKey, layer, kAssociationRetain);
|
||||||
const auto error = CGLImpl::AttachDrawable(context, view, layer);
|
const auto error = CGLImpl::AttachDrawable(context, view, layer,
|
||||||
|
static_cast<GLint>(geometry.DrawableSize.width),
|
||||||
|
static_cast<GLint>(geometry.DrawableSize.height));
|
||||||
if (error != kCGLNoError) {
|
if (error != kCGLNoError) {
|
||||||
MGLOG_E("NSOpenGLImpl: failed to attach drawable: %s", CGLImpl::ErrorString(error));
|
MGLOG_E("NSOpenGLImpl: failed to attach drawable: %s", CGLImpl::ErrorString(error));
|
||||||
}
|
}
|
||||||
@@ -316,16 +362,15 @@ namespace MobileGL::MG_Impl::NSOpenGLImpl {
|
|||||||
ContextSetView(self, nullptr, view);
|
ContextSetView(self, nullptr, view);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
CGRect bounds = SendCGRect(view, "bounds");
|
const auto geometry = GetDrawableGeometry(view);
|
||||||
CGFloat scale = 1.0;
|
ConfigureMetalLayerForView(layer, view, geometry);
|
||||||
if (id window = SendId(view, "window")) {
|
const auto error = CGLImpl::AttachDrawable(context, view, layer,
|
||||||
scale = SendCGFloat(window, "backingScaleFactor");
|
static_cast<GLint>(geometry.DrawableSize.width),
|
||||||
|
static_cast<GLint>(geometry.DrawableSize.height));
|
||||||
|
if (error != kCGLNoError) {
|
||||||
|
MGLOG_E("NSOpenGLImpl: update failed to attach drawable: %s", CGLImpl::ErrorString(error));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (scale <= 0.0) {
|
|
||||||
scale = 1.0;
|
|
||||||
}
|
|
||||||
SendVoidCGRect(layer, "setFrame:", bounds);
|
|
||||||
SendVoidCGSize(layer, "setDrawableSize:", {bounds.size.width * scale, bounds.size.height * scale});
|
|
||||||
CGLImpl::UpdateContext(context);
|
CGLImpl::UpdateContext(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user