우리 프로그램이 실행되는 동안 창 크기를 바꿔본 적이 있나요?
하지 마세요. 무슨 일이 일어나는지 제가 알려드리죠:
thread 'main' panicked at 'queue presentation: ERROR_OUT_OF_DATE_KHR', src/main.rs:216:17
이 에러는 다음 코드를 가리킵니다:
unsafe {
aetna
.swapchain
.swapchain_loader
.queue_present(aetna.queues.graphics_queue, &present_info)
.expect("queue presentation");
};
문제는 스왑체인과 서피스가 더 이상 일치하지 않는다는 것입니다. expect는 여기서 무언가 잘못될 수 있다는 것을 알려줬을 겁니다. 조금 더 나은 방식으로 처리해 봅시다:
unsafe {
match aetna
.swapchain
.swapchain_loader
.queue_present(aetna.queues.graphics_queue, &present_info)
{
Ok(..) => {}
Err(ash::vk::Result::ERROR_OUT_OF_DATE_KHR) => {
aetna.recreate_swapchain().expect("swapchain recreation");
}
_ => {
panic!("unhandled queue presentation error");
}
}
};
이는 recreate_swapchain 함수 또한 필요하다는 것을 의미합니다.
다음과 같은 함수가 될 겁니다:
pub fn recreate_swapchain(&mut self) -> Result<(), Box<dyn std::error::Error>> {
unsafe {
self.swapchain.cleanup(&self.device, &self.allocator);
}
self.swapchain = SwapchainDongXi::init(
&self.instance,
self.physical_device,
&self.device,
&self.surfaces,
&self.queue_families,
&self.allocator,
)?;
Ok(())
}
(이 기회에 _physical_device를 physical_device로 이름 바꿨습니다.)
이제 어떻게 될까요? 음…
[Debug][error][validation] "Cannot call vkDestroyImageView on VkImageView 0xc000000000c[] that is currently in use by a command buffer. The Vulkan spec states: All submitted comma
nds that refer to imageView must have completed execution (https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-vkDestroyImageView-imageView-01026)"
[Debug][error][validation] "Cannot call vkDestroyImage on VkImage 0xa000000000a[] that is currently in use by a command buffer. The Vulkan spec states: All submitted commands that
refer to image, either directly or via a VkImageView, must have completed execution (https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-vkDestroyI
mage-image-01000)"
[Debug][error][validation] "VkFence 0xf000000000f[] is in use. The Vulkan spec states: All queue submission commands that refer to fence must have completed execution (https://www
.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-vkDestroyFence-fence-01120)"
[Debug][error][validation] "VkFence 0x120000000012[] is in use. The Vulkan spec states: All queue submission commands that refer to fence must have completed execution (https://ww
w.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-vkDestroyFence-fence-01120)"
[Debug][error][validation] "VkFence 0x150000000015[] is in use. The Vulkan spec states: All queue submission commands that refer to fence must have completed execution (https://ww
w.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-vkDestroyFence-fence-01120)"
[Debug][error][validation] "Cannot call vkDestroySemaphore on VkSemaphore 0xe000000000e[] that is currently in use by a command buffer. The Vulkan spec states: All submitted batch
es that refer to semaphore must have completed execution (https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-vkDestroySemaphore-semaphore-01137)"
[Debug][error][validation] "Cannot call vkDestroySemaphore on VkSemaphore 0x110000000011[] that is currently in use by a command buffer. The Vulkan spec states: All submitted batc
hes that refer to semaphore must have completed execution (https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-vkDestroySemaphore-semaphore-01137)"
[Debug][error][validation] "Cannot call vkDestroySemaphore on VkSemaphore 0x140000000014[] that is currently in use by a command buffer. The Vulkan spec states: All submitted batc
hes that refer to semaphore must have completed execution (https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-vkDestroySemaphore-semaphore-01137)"
[Debug][error][validation] "Cannot call vkDestroyFramebuffer on VkFramebuffer 0x170000000017[] that is currently in use by a command buffer. The Vulkan spec states: All submitted
commands that refer to framebuffer must have completed execution (https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-vkDestroyFramebuffer-framebuff
er-00892)"
[Debug][error][validation] "Cannot call vkDestroyFramebuffer on VkFramebuffer 0x180000000018[] that is currently in use by a command buffer. The Vulkan spec states: All submitted
commands that refer to framebuffer must have completed execution (https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-vkDestroyFramebuffer-framebuff
er-00892)"
[Debug][error][validation] "Cannot call vkDestroyFramebuffer on VkFramebuffer 0x190000000019[] that is currently in use by a command buffer. The Vulkan spec states: All submitted
commands that refer to framebuffer must have completed execution (https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-vkDestroyFramebuffer-framebuff
er-00892)"
[Debug][error][validation] "Cannot call vkDestroyImageView on VkImageView 0x70000000007[] that is currently in use by a command buffer. The Vulkan spec states: All submitted comma
nds that refer to imageView must have completed execution (https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-vkDestroyImageView-imageView-01026)"
[Debug][error][validation] "Cannot call vkDestroyImageView on VkImageView 0x80000000008[] that is currently in use by a command buffer. The Vulkan spec states: All submitted comma
nds that refer to imageView must have completed execution (https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-vkDestroyImageView-imageView-01026)"
[Debug][error][validation] "Cannot call vkDestroyImageView on VkImageView 0x90000000009[] that is currently in use by a command buffer. The Vulkan spec states: All submitted comma
nds that refer to imageView must have completed execution (https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-vkDestroyImageView-imageView-01026)"
[Debug][error][validation] "vkCreateSwapchainKHR() called with imageExtent = (862,600), which is outside the bounds returned by vkGetPhysicalDeviceSurfaceCapabilitiesKHR(): curren
tExtent = (872,600), minImageExtent = (872,600), maxImageExtent = (872,600). The Vulkan spec states: imageExtent must be between minImageExtent and maxImageExtent, inclusive, wher
e minImageExtent and maxImageExtent are members of the VkSurfaceCapabilitiesKHR structure returned by vkGetPhysicalDeviceSurfaceCapabilitiesKHR for the surface (https://www.khrono
s.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-VkSwapchainCreateInfoKHR-imageExtent-01274)"
[Debug][error][validation] "Calling vkBeginCommandBuffer() on active VkCommandBuffer 0x55c181cda8d0[] before it has completed. You must check command buffer fence before this call
. The Vulkan spec states: commandBuffer must not be in the recording or pending state. (https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-vkBeginC
ommandBuffer-commandBuffer-00049)"
thread 'main' panicked at 'index out of bounds: the len is 0 but the index is 0', /rustc/4fb7144ed159f94491249e86d5bbd033b5d60550/src/libcore/slice/mod.rs:2842:10
우리는 파괴하려는 것들이 사용 중이 아닌지 확실히 해야 합니다. 좋습니다, 먼저 기다려 봅시다:
unsafe {
self.device
.device_wait_idle()
.expect("something wrong while waiting");
}
에러가 줄었습니다:
thread 'main' panicked at 'index out of bounds: the len is 0 but the index is 0',
(앞부분의 유효성 검사 에러 없이) — 참고로, 이 에러는 update_commandbuffer 함수 안의 한 지점을 가리킵니다:
let renderpass_begininfo = vk::RenderPassBeginInfo::builder()
.render_pass(self.renderpass)
.framebuffer(self.swapchain.framebuffers[index])
.render_area(vk::Rect2D {
offset: vk::Offset2D { x: 0, y: 0 },
extent: self.swapchain.extent,
})
.clear_values(&clearvalues);
더 정확히는 self.swapchain.framebuffers[index] 부분입니다. 그리고 실제로, 우리 구현에서 swapchain은 create_framebuffers를 별도로 호출해야 합니다:
self.swapchain
.create_framebuffers(&self.device, self.renderpass)?;
이것으로 에러는 해결됩니다.
하지만, 우리는 보이는 이미지가 창에 맞춰 크기가 조절되기를 기대하지만 (x=-1은 항상 왼쪽, x=+1은 항상 오른쪽), 그렇지 않습니다. 또한, 창을 키운 후에 일부 영역에서는 구체가 있어야 할 곳임에도 표시되지 않습니다.
이것은 그래픽스 파이프라인의 일부인 뷰포트(viewport) 및/또는 씨저(scissors) 문제인 것 같습니다. recreate_swapchain 함수에 두 줄을 더 추가합니다:
self.pipeline.cleanup(&self.device);
self.pipeline = Pipeline::init(&self.device, &self.swapchain, &self.renderpass)?;
좋습니다. 더 이상 에러는 없습니다.
하지만 이미지가 늘어나 보입니다 (창이 양방향으로 균일하게 조절되지 않았다면 말이죠). 따라서 카메라도 업데이트해야 합니다. 여기서 바꾸고 싶은 것은 카메라의 종횡비(aspect ratio)입니다.
Camera에 메서드를 하나 추가합시다:
pub fn set_aspect(&mut self, aspect: f32) {
self.aspect = aspect;
self.update_projectionmatrix();
}
(프로젝션 행렬(projection matrix) 또한 최신 상태로 유지하기 위해, 그냥 public 필드로 두는 대신 온전한 함수로 만들었습니다.)
그리고 나서 이 함수를 호출합니다. 버퍼를 업데이트하는 것도 잊지 말아야 합니다:
Err(ash::vk::Result::ERROR_OUT_OF_DATE_KHR) => {
aetna.recreate_swapchain().expect("swapchain recreation");
camera.set_aspect(
aetna.swapchain.extent.width as f32
/ aetna.swapchain.extent.height as f32,
);
camera
.update_buffer(&aetna.allocator, &mut aetna.uniformbuffer)
.expect("camera buffer update");
}