macOS下GLFW无装饰窗口全屏切换后出现黑色边框问题
GLFW全屏切回窗口模式出现黑边的原因及修复方案
问题复现参考
初始创建的无边框透明窗口显示正常:
经过一次全屏->窗口模式切换后,窗口边缘出现明显黑色边框:
核心成因
- 高DPI缩放参数不匹配:创建窗口时开启了
GLFW_SCALE_TO_MONITOR属性,glfwGetWindowSize返回的是窗口逻辑像素尺寸,而全屏切换时GLFW会将窗口适配到显示器的物理原生分辨率,切回窗口时如果直接传入存储的逻辑尺寸、没有同步内容缩放比,窗口合成器会自动对窗口做缩放适配,未被窗口内容覆盖的区域就会填充为黑色。 - 刷新率参数错误:切回窗口模式时传入了全屏状态下获取的显示器刷新率,窗口化模式下该刷新率参数和当前窗口模式不匹配,会触发窗口管理器的非原生兼容模式,自动给窗口添加填充边距。
- 无边框透明窗口的属性残留:同时开启了
GLFW_DECORATED(无边框)和GLFW_TRANSPARENT_FRAMEBUFFER(透明帧缓冲)属性时,部分平台的窗口合成器会在全屏切换后保留全屏状态下的帧缓冲边距标记,切回窗口后这部分保留的边距没有被清除,就会渲染为黑色边框。 - 提前调用
glfwHideWindow的副作用:切模式前隐藏窗口会导致GLFW无法在模式切换过程中同步更新窗口的帧缓冲适配参数,切换完成后显示窗口时就会出现尺寸错位。
修复方案
按照以下步骤修改代码即可彻底消除黑边:
- 调整切回窗口模式的参数逻辑,刷新率参数传入
GLFW_DONT_CARE,交由系统自动匹配窗口模式下的最优刷新率,不要强制使用全屏模式的刷新率。 - 移除切换前的
glfwHideWindow调用,模式切换完成后再调用显示相关接口,保证GLFW能正常同步窗口状态。 - 切换完成后重新设置窗口无边框属性,强制触发窗口合成器清除全屏状态残留的边距标记,再显式调用一次窗口尺寸设置,同步逻辑尺寸和实际帧缓冲尺寸。
修改后的模式切换代码如下:
void set_fullscreen_mode() { // 仅在窗口处于非全屏状态时存储恢复用的位置、尺寸参数,避免取到全屏状态下的错误值 if(!full_screen) { glfwGetWindowSize(main_window, &last_window_size.x, &last_window_size.y); glfwGetWindowPos(main_window, &last_window_position.x, &last_window_position.y); } full_screen = true; current_monitor = get_monitor_by_cpos(get_global_mouse_position(main_window)); const GLFWvidmode* monitor_video_mode = glfwGetVideoMode(current_monitor); glfwSetWindowMonitor(main_window, current_monitor, 0, 0, monitor_video_mode->width, monitor_video_mode->height, monitor_video_mode->refreshRate); } void set_windowed_mode() { full_screen = false; current_monitor = get_monitor_by_cpos(get_global_mouse_position(main_window)); // 切回窗口模式,刷新率传GLFW_DONT_CARE由系统自动适配 glfwSetWindowMonitor( main_window, NULL, last_window_position.x, last_window_position.y, last_window_size.x, last_window_size.y, GLFW_DONT_CARE ); // 重设无边框属性,清除全屏残留的边距标记 glfwSetWindowAttrib(main_window, GLFW_DECORATED, GLFW_FALSE); // 强制同步窗口尺寸,修正高DPI下逻辑/物理像素错位 glfwSetWindowSize(main_window, last_window_size.x, last_window_size.y); glfwShowWindow(main_window); }
平台特殊适配
如果是Windows平台下仍存在1px宽度的细黑边,是DWM(桌面窗口管理器)对无边框透明窗口的已知兼容问题,可以在切回窗口模式后,将窗口尺寸比存储的原尺寸宽高各多设2个像素,同时将窗口位置往左上角各偏移1个像素,把黑边区域挤出屏幕可视范围即可。
内容的提问来源于stack exchange,提问作者THE_CHOODICK
相关产品推荐
相关产品推荐

