Skip to content

窗口Resize解决方案

GLFW

先简单说下什么是 GLFW —— 它用于

  • 窗口管理
  • 处理用户输入

特点:轻量、跨平台

Vulkan

一个跨平台的渲染API而已,它并不知道Win下的HWND,或者是Linux的XCB等窗口句柄,通过Surface这个抽象来做全部的适配。

也就是说,GLFW 屏蔽了所有 OS 细节(比如在Win下申请HWND等,),直接根据平台创建好Surface,然后递给Vulkan一个干净的 VkSurfaceKHR。对 Vulkan 来说,它并不需要理财这是什么系统的窗口,只需要知道这是一个可以把图像塞进去的合法目标即可

同时,Swapchain 的尺寸必须的和 Surface 的尺寸一致,这样才可以做到多缓冲的呈现图片

问题

假设 GLFW 进行了窗口的 Resize 操作,但是不对原先 Vulkan 的渲染逻辑进行任何更改 ——

  1. 系统通知 GLFW 的窗口被 Resize 到了新的宽度和高度
  2. Surface 发生改变,但是 Swapchain 上依旧是旧的大小
  3. Vulkan 依旧向 Swapchain 索要画布,同时在旧的大小上绘制好所有内容
  4. Vulkan 绘制好后把尺寸不匹配的图片强行塞进新的 Surface 中
  5. 显示驱动对比尺寸后发现二者不对应,为了防止越界访问显存,驱动直接抛出 VK_ERROR_OUT_OF_DATE_KHR,程序崩溃

解决方案

所以在遇到 Window Resize 的时候需要重建 Swapchain,具体操作 ——

  1. 通过vkDeviceWaitIdle等待所有队列里的任务执行完毕,这样使得所有资源没有被占用,可以随意销毁创建等
  2. 把旧的 Swapchain 销毁,同时把基于旧 Image 创建的 Framebuffer 和 ImageView 也全部销毁
  3. 利用新的尺寸重新创建一个新的 Swapchain,重新申请新的显存图片
  4. 继续进行渲染等操作

View Resize 方案

和 Window Resize 相比简单许多,因为只是 Image 发生了大小变化。

这次引擎使用了 Imgui 来制作一个简单的 ViewPortPanel。

先简单说下投放流程 ——

  • Renderer 离屏渲染出一个纹理,放在一个 Framebuffer 中
  • 通过 Imgui 提供的方法ImGui_ImplVulkan_AddTexture把离屏渲染的图像注册到 Imgui 中(返回一个指向 Vulkan Descriptor Set 的指针实际上
  • 最后在Imgui中通过ImGui::Image()绘制

那么在ViewPort Resize的时候,只需要通知 Renderer 动态重建离屏渲染目标即可(按照目标大小来Resize,不会出现画面的拉伸等),同时更新相机的 AspectRatio

踩坑点 —— 如果在 GPU 还在处理旧的图片的时候直接进行离屏目标的销毁和新建,会爆。所以需要等待 GPU 空闲的时候再进行离屏的 Framebuffer 的销毁与重建

Released under the MIT License.