FileSystem
파일시스템을 래핑하는 클래스를 구현했다.




원래 이런식으로 사용하고 있었는데
내가 함수 용도를 알아보기 쉽게 이름을 붙이고 싶었고, 필요하다고 std::filesystem::func(param)을 쓰는 함수들도 그냥 Path에서 일괄적으로 호출하면 편할것 같아서 래퍼 클래스를 구현했다.

Renderer는 static 함수만 지원하고 실제로 실행되는 함수들은 전부 하위 클래스에 구현되어 있는 것을 그냥 가져다 쓰기만 한다.
여기서 Renderer가 파라미터로 shared_ptr을 받도록 예전에 구현했던 것들이 다시 보니까 이상하다는 생각이 들었는데
굳이 shared_ptr로 파라미터를 넘겨줘야 할 이유가 보이지 않았다.
Renderer는 잠시 자원을 빌려서 그리는데 사용하는 것이지 소유권이 필요한게 아니라고 생각했다.
아무래도 과거의 나는 이 함수에 shared로 전달하는 것으로 수명을 붙잡아둬야 겠다는 생각을 했었던 것 같다.
현재의 엔진은 에셋 매니저와 리소스 캐시가 확실하게 소유권을 가지고 있기 때문에 렌더링에 필요한 객체들을 원시 포인터로 전달한다고 해서 소멸할 원인이 없다고 판단했고
성능 면에서도 쓸데없는 참조카운트 증가로 인한 오버헤드로 성능에 영향을 미칠 수 있다고 생각했기 때문에
Renderer가 전부 원시포인터를 기반으로 동작하게 변경했다.


전부 바꿔주었고 일부는 편의성을 위해 shared_ptr도 인자로 받을 수 있게 했다.
그리고 역할을 확실하게 나눠 주었는데
Renderer에서 받는 인자는 Texture, Buffer 클래스와 같은 래퍼 클래스를 받고
Renderer함수에서 호출되는 renderContext의 함수들은 Texture와 Buffer클래스 내부의 GPUTexture, GPUBuffer를 인자로 받게 구분했다.

Asset도 shared_ptr 대신 raw ptr을 리턴하게 했다.
아직은 Asset의 소유권이 다른 곳에서 공유될 일이 없다는 확신이 없기 때문에 나중에 필요하다면 shared_ptr을 리턴하는 함수를 다시 추가할 수도 있지만 일단은 이렇게 바꿨다.
BulitInResource

그리고 이런 리소스들에 대해서 원래는 문자열로 GetResource함수를 호출했었는데
오타가 나서 nullptr을 가져오는 경우나
인텔리센스의 도움을 받지 못하는 게 좀 불편해서 이런식으로 빌트인 리소스를 따로 만들어줬다.

이런식으로 인텔리센스의 도움도 받을 수 있고 편하다고 생각해서 도입했다.
ResourcePool
그리고 파라미터들을 원시포인터로 바꾸면서 문제가 생겼는데
Buffer의 Create함수에서 데이터 복사를 위해 생성한 uploadBuffer가 더이상 shared_ptr로 람다 캡쳐를 하지 않기 때문에 이전에 Renderer 클래스에 구현했던 CaptureResource처럼 Shared를 잡아둬서 수명을 늘린다거나 할 수 없게 되었다.
그래서 Create 함수 스코프를 벗어나면 uploadBuffer가 파괴되고 나중에 엔진이 Submit 함수에서 렌더링 명령을 실행하려고 보면 이미 파괴된 버퍼에 접근하려고 한다는 오류가 발생해서 다른 방법을 찾았다.

이 문제를 처리하기 위해 풀 방식을 도입했다.

currentLoop - 현재가 몇번째 루프인지
removeLimit - 현재 루프와 얼마나 차이나는 개체를 풀에서 삭제할것인지
maxFramesInFlight - pendingQueue에서도 사용이이 끝났음을 보장하는 루프횟수
pendingQueue - 풀에 반납되었더라도 렌더링 명령이 끝나는 시점까지 다른 객체가 사용하지 못하도록 잡아두는 큐
freeQueue - 사용가능한 리소스가 대기중인 큐
이 Base class를 상속해서 풀을 구현했다.
그리고 요구하는 UploadBuffer의 사이즈가 다를 수 있기 때문에 Key로 구분해주었다.
어차피 uploadBuffer라는게 한 번 쓰고 버려질 것 같긴한데 일단 리소스가 소멸되지 않도록 소유하는 객체가 필요하다고 생각했다.


필요한 풀들을 렌더러에 추가해 주었다.
현재 렌더러가 너무 많은 일을 떠맡고 있어서 클래스를 분리해야 되나 싶은데 일단은 렌더러가 다 하는걸로 계속간다.

UploadBufferPool은 이런식으로 리소스를 풀에서 꺼내주는 함수만 구현해주면 된다.
근데 보면 UploadBufferPoolHandle이라는 객체를 반환하는데


이 객체는 UploadBuffer외에도 key와 ownerPool을 가지고 있어서
사용자가 할당받은 리소스를 풀에 반납하지 않아도 소멸자에서 ownerPool에 알아서 리소스를 돌려놓게 설계했다.
D3D12MemoryAllocator
Vulkan Memory Allocator처럼 DX12도 memory Allocator 오픈소스가 있었다.
이걸 도입하게 된 이유는 DX12에서 리소스 생성할 때 CreateCommittedResource를 썼었는데
AI가 이게 성능이 되게 비싸다고 해서 대안을 물어봤더니 DX12도 vma같은 오픈소스가 있다고 해서 쓰게 되었다.

Device생성이후 Memory Allocator를 생성해준다.

당연한 듯이 ComPtr 인터페이스도 지원해서 좋았다.

CreateCommittedResource와 인자도 거의 같은데 다른점은 HeapDesc대신 allocationDesc를 쓴다는 점 정도다.
코드를 타고 들여다 봤는데 내부적으로 CreatePlacedResource라는 함수를 사용하는 걸 볼 수 있었다.
저런 함수를 지원하는줄 몰랐는데 아무튼 편하게 구현해놨으니까 내가 굳이 지금 공부할 필요는 없을 것 같아서 넘어가자
이번 포스팅은 여기까지고 다음에는 RenderGraph와 관련된 작업을 포스팅할 것이다.
'개인 공부 및 프로젝트 > Daydream Engine' 카테고리의 다른 글
| 자체 게임엔진 DevLog - ImGui Vulkan이 ImageView를 사용하게 하기 (0) | 2026.05.27 |
|---|---|
| 자체 게임엔진 DevLog - Texture Class 리팩토링 (0) | 2026.05.20 |
| 자체 게임엔진 DevLog - 디버깅 자체가 뻗어버리는 문제 (0) | 2026.05.05 |
| 자체 게임엔진 DevLog - RenderGraph 작성 (0) | 2026.04.29 |
| 자체 게임엔진 DevLog - 버퍼 클래스 구조 수정 (0) | 2026.04.29 |