openlang

제출한 뒤 Vulkan 버퍼는 누가 소유하나요?

Vulkan 버퍼를 감싼 C++ RAII 래퍼가 그 버퍼를 쓰는 작업을 기록해 제출하고 스코프를 벗어난다. 소멸자는 그 자리에서 vkDestroyBuffer를 호출한다. 버퍼를 참조하는 제출된 명령은 아직 실행 중이다.

이 호출은 잘못이다. Vulkan은 버퍼를 파괴하기 전에 그 버퍼를 참조하는 제출된 명령이 모두 실행을 마쳤을 것을 요구한다. 스코프를 벗어났다는 건 호스트가 래퍼를 다 썼다는 사실뿐이다. GPU에 대해서는 아무것도 보여 주지 않는다.

처음 떠오른 방법은 제출 시점에 래퍼를 프레임 회수 목록으로 옮기고, 그 제출의 완료가 확인된 뒤에만 파괴하는 것이다. 계속 마음이 오가는 건 옮기는 위치다. 제출 호출 안인가, 프레임을 소유한 쪽인가? 프레임 목록이라고 부른다고 해서 항목을 언제 풀어 줘도 되는지가 정해지지도 않는다.

이어지는 대화 3

서로의 근거를 따라
  1. 고유 링크

    해제 책임자는 하나여야 하고, 이름이 있어야 합니다. 완료가 확인되면 누군가 항목을 제거하고, 호스트 작업이 일찍 끝나면 누군가 그 항목을 보관해야 합니다. 둘이 같은 구성 요소일 수는 있지만 어느 쪽인지 설계에 적혀 있어야 합니다. 래퍼의 지역 포인터를 널로 바꾸면 그 변수만 바뀔 뿐입니다. 아무것도 해제하지 않고, 제출된 명령이 끝났는지에 대해서도 아무것도 알려 주지 않습니다.

  2. 고유 링크

    회수 목록 자체가 어디에 있는지부터 본다. 버퍼가 아직 GPU를 기다리는데 항목이 프레임 끝에 비워지는 임시 저장 공간에 있다면, 항목은 할 일을 하기도 전에 사라진다. Odin에서는 제출 시점에 컨텍스트가 가진 할당자가 아니라 동적 배열이 기록해 둔 할당자를 확인해야 한다.

  3. 고유 링크

    넘겨주는 방식이 폐기 대기 목록으로의 이동이라면, 래퍼의 이동 생성자는 원본이 아무것도 소유하지 않게 만들어서 그 소멸자가 아무 일도 하지 않게 해야 한다. 보통은 이동된 객체에 VK_NULL_HANDLE을 써 넣고 소멸자에서 그걸 확인한다. 이걸 빠뜨리면 지역 변수는 여전히 스코프를 벗어날 때 버퍼를 파괴하고, 이제 목록 안에 두 번째 소유자까지 생긴다.

    이동된 객체도 소멸자는 불린다. 표준의 "유효하지만 명시되지 않은 상태"는 라이브러리 타입에만 해당한다. 직접 만든 래퍼에서 이동된 상태란 이동 생성자가 써 넣은 그것이고, 그 이상은 없다.

같은 질문의 다른 갈래

  1. 시그널은 프레임 끝이 아니라 지금 바로 실행돼요

    이거 Godot에서 저도 당했고, 다들 언젠가 한 번은 당하는 것 같아요. emit_signal()이나 signal.emit()은 연결된 함수를 그 자리에서 바로 불러요. 보내는 쪽 호출 안에서, 보내는 쪽의 다음 줄이 돌기…