렌더스레드를 작성했는데
Vulkan API에서 Present를 할때마다 오류가 생겼다. 레이아웃이 올바르지 않다고 한다.
근데 RenderPass를 거친 Framebuffer는 레이아웃을 알아서 final layout인 Present 레이아웃으로
바꿔주기 때문에 문제가 없어야 한다.
실제로 메인 스레드에서 렌더링을 할 때는 문제가 없다가 렌더 스레드를 작성한 뒤부터 문제가 생겨서
렌더스레드에서만 안되는 문제일 것이라고 추측했다.
AI와 원인을 찾아봤다.
원인은 호출 시점의 문제였다.
렌더 스레드로 명령을 모아서 보내고 한번에 실행하는 구조와
메인 스레드에서 호출하면 바로 코드가 실행되는 차이를 놓쳐서 발생한 에러였다.
Renderer::BeginFrame(mainWindow->GetSwapchain());
Renderer::FlushSingleTimeCommands(); // 실행할게 있을때만
for (Layer* layer : layerStack)
{
layer->OnUpdate(deltaTime);
}
Renderer::BeginRenderPass(mainWindow->GetSwapchain()->GetRenderPass(), mainWindow->GetSwapchain()->GetCurrentFramebuffer());
imGuiLayer->BeginImGui();
{
for (Layer* layer : layerStack)
layer->OnImGuiRender();
}
imGuiLayer->EndImGui();
Renderer::EndRenderPass(mainWindow->GetSwapchain()->GetRenderPass());
Renderer::EndFrame(mainWindow->GetSwapchain());
Renderer::Submit();
inline virtual Shared<Framebuffer> GetCurrentFramebuffer() const { return framebuffers[imageIndex]; };
코드를 보면
디버깅 했을 때 첫 루프는 잘 통과했다. 어차피 첫 프레임은 0번 백버퍼를 그릴것이기 때문이다.
0번 백버퍼를 Present하고 다시 엔진루프의 처음으로 오면 이제 1번 백버퍼에 이미지를 그릴 차례이다.
그리고 현재 imageIndex는 아직 0이다.
BeginFrame() 함수에서 acquireNextImageKHR 를 통해 다음 이미지를 그릴 imageIndex를 가져온다.(1을 가져와야 함)
그런데 렌더스레드를 사용할시 이 시점에서의 imageIndex는 acquireNextImageKHR가 들어있는
BeginFrame함수를 RenderCommandQueue에 push만 해둔 상태이고 호출이 되지 않았다.(매우 중요)
그래서 imageIndex는 이 시점에서 아직 0이다!!
Renderer::Submit에서 RenderThread에 실행할 함수들을 제출하고 RenderThread가 큐에서 꺼내서 함수들을 호출해야 그때서야 acquireNextImageKHR이 호출되는 것이다.
엔진 루프의 Renderer::BeginRenderPass를 보자
이 함수도 큐에 특정 프레임버퍼를 바인딩하라는 명령을 RenderCommandQueue에 push하게 된다.
이 때 BeginRenderPass의 매개변수로 framebuffers[1]이 전달되어야 하는데
imageIndex가 업데이트 되지 않은 상태이기 때문에 GetCurrentFramebuffer 함수는 framebuffers[imageIndex = 0]을 매개변수로 전달하게되고 이걸 큐에 push한다.
그러면 렌더스레드에서 이제 명령큐를 다받아서 한번에 처리를 하는데 이때가 되어서야 imageIndex가 1이 되고
BeginRenderPass는 매개변수로 이미 framebuffers[0]을 받은상태라서 framebuffers[0]의 layout을 변경하기 때문에 framebuffers[1]은 렌더타겟으로 설정도 되지 않고 이미지 레이아웃도 변경되지 않은 상태로 present 함수에 들어가게 되면서 문제가 발생한 것이었다.
메인스레드에서는 즉각적으로 코드가 실행되니까 Renderer::BeginFrame()의 acquireNextImageKHR이 바로 실행되어서 imageIndex가 업데이트 된 채로 GetCurrentFrameBuffer가 호출되어서 이런일이 생기지도 않았고 내가 놓친 부분이었다.
렌더스레드는 명령을 큐에 모아서 한번에 제출했기 때문에 이런 문제가 발생한것이다.
해결 방법은 Swapchain의 백버퍼를 바인드하는 함수를 따로 만들어주는 방식으로 문제를 해결했다.
void Renderer::BeginSwapchainRenderPass(Swapchain* _swapchain)
{
Enqueue([_swapchain]()
{
renderContext->BeginRenderPass(_swapchain->GetRenderPass(), _swapchain->GetCurrentFramebuffer());
});
}
이렇게 해주면 GetCurrentFramebuffer가 호출되는 시점이 RenderThread에서 AcquireNextImage를 하고나서 GetCurrentFramebuffer를 호출하는 것이기 때문에 메인스레드의 코드 흐름처럼 가게된다.
이 문제를 해결하니까 Framebuffer, Swapchain Resize와 관련되서 터지던 에러들도 같이 사라져서 기부니가 좋았음
'개인 공부 및 프로젝트 > Daydream Engine' 카테고리의 다른 글
| 자체 게임엔진 DevLog - OpenGL 렌더링 스레드 (0) | 2026.04.21 |
|---|---|
| 자체 게임엔진 DevLog - 수학 라이브러리 (0) | 2026.04.20 |
| 자체 게임엔진 Devlog - 14 (0) | 2026.04.08 |
| 자체 게임엔진 Devlog - 13 (0) | 2025.11.20 |
| 자체 게임엔진 Devlog - 12 (0) | 2025.10.27 |