https://vulkan-tutorial.com/
위의 vulkan 튜토리얼이나
프랭크 루나의 DirectX 12 책 보면,
스왑 체인 만들면서 프레임 버퍼는 여러개 쓰는데, depth/stencil 버퍼는 한개만 씀.
근데 이러면 depth/stencil 테스트 하는 과정에서 병목현상 생기지 않나?
Stack overflow 뒤져보니까 어떤 사람 답변이
보통 CPU에서 만드는 데이터는 각 프레임 렌더링 하는 과정에서 복사하는 동안 데이터가 유지 되어야하니까,
각 프레임 버퍼 별로 따로따로 데이터를 만들지만,
그렇지 않고 gpu 내부에서만 읽고 쓰는 depth/stencil 같은 경우는 그냥 한개만 쓴다고 함.
아래 링크가 그 답변 페이지.
https://stackoverflow.com/questions/71461930/why-is-only-1-depth-stencil-buffer-needed-even-when-the-swap-chain-is-tripple-b
근데 다른 답변 뒤져보니까 퍼포먼스를 위해서 Gbuffer 같은 경우는 각 프레임 별로 여러개 쓰고, depth/stencil 버퍼는 대개 1개 쓰지만 퍼포먼스를 위해서 depth/stencil도 여러개 쓸 수도 있겠지 라는 답변도 있음.
https://stackoverflow.com/questions/68040979/why-gbuffers-need-to-be-created-for-each-frame-in-d3d12
그래서 질문이,
depth/stencil 한개만 써서 생기는 병목현상으로 인한 퍼포먼스 저하가 대개는 depth/stencil 버퍼 여러개 쓰기에는 아까울 정도로 적은편임?
그렇다면 depth/stencil을 여러개 쓰는 예시 상황 같은것은 없음?
예를 들어 cpu쪽에서 멀티 쓰레드 써서 여러 프레임의 커맨드를 동시에 날린다던가?
Vulkan나 DX12 같은 모던 API들은 throughput을 최대화 시키기위해서 단순히 한 개의 프레임을 그리라고 GPU에 명령해두고 그게 끝날때까지 기다리는게 아니라, 한 프레임이 끝나면 다음 프레임도 미리 그리도록(동시에 여러 프레임이 그려지도록) 할 수 있게 하는데. - dc App
Swapchain에 있는 frame buffer의 경우엔 frame buffer에 쓰기가 끝난 다음에, 모니터에 값을 표시하기위해 GPU->CPU로 readback 되어야함. 그런데 동일한 frame buffer를 동시에 렌더링에도 그대로 사용할 수 있는데 이 경우 data race가 발생하게 됨. 그래서 각 프레임 마다 독립적인 - dc App
D/S Buffer의 경우엔 프레임이 단조 증가하는 형태, 즉 이를 사용하는 명령어나 작업들이 GPU에 순차적으로 제출되고, 이들이 GPU 상에서 순차적으로 처리되기 때문에(그리고 CPU에서 GPU와 비 동기적으로 메모리 접근 하지 않기에) 기본적으로 하나만 있어도 data race가 일어나지 않음 - dc App
그러나 Vulkan이나 DX12의 특징을 살펴보았을때 단순히 CPU에서 메모리를 읽거나 쓸때만 이런일이 일어나는것은 아니란걸 알 수 있는데 - dc App
GPU안에서도 여러개의 독립적인 Queue들이 있고, 이 Queue들은 서로에 독립적이고 비동기적으로(명시적으로 동기화 해주지 않는 이상) 명령어를 실행할 수 있다는 것인데 - dc App
만약 이전 프레임에서 D/S Buffer에 쓴결과를 Graphics Queue(렌더링에 사용할 Queue)가 아닌 이와 비동기적으로 명령을 실행할 Compute Queue에서 read를 한다고 가정하고, 같은 D/S Buffer를 별도의 동기화 없이 그대로 다음 프레임을 렌더링하는데 사용한다고 하면 - dc App
read-after-write 하게 될 가능성이 매우 높아지게 될거고, 이런 경우엔 D/S Buffer도 동시에 렌더링 가능한 프레임의 수 만큼 만들어주는게 맞겠지 - dc App
Vulkan 튜토리얼 읽고, D/S 버퍼에는 data race 생길 수 있지 않은지 그게 정말 헷갈렷음. 튜토리얼 코드에서 사용되고 있는 Fence나 Semaphore같은 sychronization 오브젝트들이 전혀 D/S 버퍼에 data race가 일어나는 것을 전혀 막지 않음. 그래서 더 찾아보니까, Vulkan의 render pass에서 subpass dependency를 지정하면 이것도 베리어를 생성해줘서 DS 버퍼의 data race를 막아준다는 것을 알게됨. 그래서 data race는 안생긴다는 것은 알겠는데, 그 뒤에 생긴 의문점이, 어 data race가 해결된건 알겠는데, 이러면 D/S 버퍼에서 병목현상 생기지 않나? 였음.
data race 막기 위한게 아니라 더 높은 퍼포먼스를 위해서 쓸 수 있지는 않나요?
data race 막기 위한게 아니라 더 높은 퍼포먼스를 위해서 D/S 버퍼 여러개를 쓸 수 있지는 않나요?
아니면 전 프레임이 d/s 버퍼를 사용중인것은 전 프레임이 아직 작업중인것이고, 그렇다면 다음 프레임은 d/s 사용하지 말고 대기해서 gpu가 전 프레임을 렌더링하는데 집중하도록 하는게 더 효율적인가?
즉, 퍼포먼스 높인답시고 d/s 여러개 써서 병렬적으로 실행될 수 있는건 최대한 병렬적으로 실행하면, gpu가 adhd 걸린 사람 마냥 프레임 1 렌더링 하다가 아직 완성도 안됬는데 프레임 3 렌더링하고, 또 프레임 2 렌더링 하는 식으로 왔다갔다 하는게 심할 수 있으니까, 그걸 어느정도 막을려고 하는건가?
일단 마지막 링크에서 말한 일반적인 상황 한 개의 큐에 선형적으로 제출된 여러 프레임의 렌더링 작업들의 경우엔. 이 작업들이 순차적으로 진행되고 파이프라이닝으로 인해 일어나는 data hazard의 원인이 될 수 있는 read/write들은 resource barrier들에 의해서 모두 동기화가 이루어지기에 D/S Buffer는 한개만 - dc App
이게 그림 그려가면서 설명해줄 수 있으면 좋을 것같은데 처지가 처지인지라 어렵네 - dc App
음. 댓글 읽고 다시 생각해봤는데, 독립적인 gpu 내부의 queue 때문에 race condition이 생길 수있다면 여러개 쓰는게 맞지만, 그게 아니면 어차피 큐 안에서 순차적으로 실행되니까 D/S 여러개 써봤자 의미가 없다. 라는 말 같군요. 설사 gpu 내부의 독립적인 queue 때문에 race condition이 생길 수 있다해도, D/S 버퍼 여러개 쓰는거나, 한개 쓰고 barrier로 싱크 맞춰주는거나 퍼포먼스 차이는 별로 없다고 생각해도 될까요?
오 내!!! - dc App
performance 보다는 correctness에 초점을 맞추는게 맞다고 생각해요 - dc App
독립적인 queue 때문에 생기는 data race는 barrier가 아닌 semaphore로 synchronization 해야하긴 하겠지만.. - dc App
설양// 답변 감사합니다. 많은 도움이 되었습니다.
덧붙여 말하자면,
https://stackoverflow.com/questions/62371266/why-is-a-single-depth-buffer-sufficient-for-this-vulkan-swapchain-render-loop
스택
오버플로우의 이 포스트를 보면 위의 vulkan 튜토리얼도 이걸 잘못 설정해서 D/S 버퍼의 race condition을 막지 않았었는데 (지금은 코드 수정되었음.),
실행해도 별 문제는 없었다고 하는걸 보니까 베리어 써서 싱크 안맞춰도 대개는 그냥 한 큐에서 순차적으로 실행되는듯.
진짜 심플하게만 생각하면 2개 쓸 이유가 없긴함, 지연이 좀 생긴다고 해도 메모리 더쓰는거에 비하면 괜찮은 수준이지 지연이라고 해봐야 엄청 적은 수준이라 보통은 뎁스랑 스텐실가지고 뭘 하는 경우가 없으니 있더라도 한번에 한 곳에서만 쓰니까 근데 이렇게 결론내리기도 뭐한게 요즘 큰 엔진들보면 코드가 너무 난해해서, 우리가 생각하는 구조랑 딴판임 이건 그냥 1개 쓴다고만 생각하고 나중에 언리얼 같은 엔진 코드 열어보고 판단해도 문제는 없을 듯