ashen-aetna-ko

Ashen Aetna

— 재의 에트나

— 잿더미 화산 위에서 녹슬게 비틀거리기
(3D 그래픽스, Rust, Vulkan, ash를 다루는 튜토리얼)

크기 조절 (Resizing)

우리 프로그램이 실행되는 동안 창 크기를 바꿔본 적이 있나요?

하지 마세요. 무슨 일이 일어나는지 제가 알려드리죠:

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_devicephysical_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] 부분입니다. 그리고 실제로, 우리 구현에서 swapchaincreate_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");
                    }

계속