파이프라인 배리어란 ?
파이프 라인인 내부 장벽을 설치하여 장벽 전의 명령을 완료하는 동기화하는 메커니즘이다.
함수 이름이 vkCmdPipelineBarrier 를 사용하여 일반적으로 파이프 라인 배리어 라고 불리고,
이미지 레이아웃을 진행할때, 메모리를 동기화 하는데 사용을 해야 하기 때문에
메모리 장벽이라고도 불린다.
메모리 장벽 (MemoryBarrier) 이란 ?
메모리 장벽은 데이터와 읽기와 쓰기를 동기화 하는데 도움이 된다.
메모리 장벽은 메모리 장벽 전 후 지정한 작업이 동기화 되도록 보장한다.
즉 메모리 장벽을 삽입하면 메모리 지시를 실행하기 앞서 메모리 장벽이 실행되기 전
메모리 작동이 완료 됐는지를 확인한다.
메모리 장벽 종류
1) 전역 메모리 장벽
- 이 유형의 메모리 장벽은 모든 종류의 실행 메모리 개체에 적용할 수 있으며 VkMemoryBarrier 구조체 인스턴스
2) 버퍼 메모리 장벽
- 이 유형의 메모리 장벽은 지정된 버퍼 개체의 특정 범위에 적용할 수 있으며 VkBufferMemoryBarrier 구조체 인스턴스
3) 이미지 메모리 방벽 ( ImageMemoryBarrier )
- 이미지 메모리 방벽은 VkImageMemoryBarrier 인스턴스로 표현하며, 지정된 이미지의 개체의 특정 이미지 하위 리소스 범위를 통해 다른 메모리 액세스 유형에 적용할 수 있다.
이미지 레이아웃이란 ?
여기서 언급된 메모리는 -> GPU 메모리.
벌칸 이미지를 효율적으로 처리하기 위해 데이터를 메모리에 배치하는 방식을 의미 한다.
큰 이미지의 사이즈를 최적화 알고리즘을 사용하여 더 작게 메모리로 유지할 수 있음.
레이아웃은 VkImageLayout 이름으로 열거되어 있음.
이미지 메모리 장벽(barrier)를 통해 벌칸 이미지 레이아웃을 변경 할 수 있다.
이미지 메모리 배리어 구조체
// Provided by VK_VERSION_1_0
typedef struct VkImageMemoryBarrier {
VkStructureType sType;
const void* pNext;
VkAccessFlags srcAccessMask;
VkAccessFlags dstAccessMask;
VkImageLayout oldLayout;
VkImageLayout newLayout;
uint32_t srcQueueFamilyIndex;
uint32_t dstQueueFamilyIndex;
VkImage image;
VkImageSubresourceRange subresourceRange;
} VkImageMemoryBarrier;
- sType : VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER
- pNext : 확장 기능의 구조체 포인터
- srcAccessMask : 현재 Access mask
- dstAccessMask : 새로운 Access mask
- oldLayout : 현재의 이미지 layout
- newLayout : 새로운 이미지 layout
- srcQueueFamilyIndex : Transfer ownership 의 소스 큐 패밀리
- dstQueueFamilyIndex : Transfer ownership 의 타겟 큐 패밀리
- image : 배리어에 영향받는 이미지 핸들
- subresourceRange : 베리어에 영향받는 이미지 내부 subresource range;
이미지에 각 영역이 다른 액세스 마스크와 레이아웃이 가질수 있다.
VkAccessFlagBits ( VkAccessFlags ) 사용 방법
리소스의 대한 메모리 접근 유형 나타내며,
파이프라인 베리어 어떤 작업이 메모리 읽기 쓰기 등 메모리 접근순서를 정의할수 있음.
ex)
버퍼가 vertex attribute 용도로 사용되는 경우, VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT
클리어 작업이 TRANSFER 작업에 수행되며, clear 값이 이미지에 쓰여지기 때문에 VK_ACCESS_TRANSFER_WRITE_BIT 설정.
액세스 플래그 비트
리소스의 대한 메모리 접근 유형을 나타냅니다.
파이프 라인 베리어를 설정할때 전 / 후로 메모리 접근 유형을 설정한다.
예를 들어
1) 버퍼가 버텍스 버퍼로 사용될 경우, 버퍼의 내용이 버텍스 셰이더 어트리뷰로 전달되야 하기 때문에
이때는 " VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT "을 사용한다.
2) 클리어 작업은 Transfer 스테이지에서 실행되고, 클리어 값이 이미지에 쓰여지기 때문에,
" VK_ACCESS_TRANSFER_WRITE_BIT " 을 사용한다.
typedef enum VkAccessFlagBits {
VK_ACCESS_INDIRECT_COMMAND_READ_BIT = 0x00000001,
VK_ACCESS_INDEX_READ_BIT = 0x00000002,
VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT = 0x00000004,
VK_ACCESS_UNIFORM_READ_BIT = 0x00000008,
VK_ACCESS_INPUT_ATTACHMENT_READ_BIT = 0x00000010,
VK_ACCESS_SHADER_READ_BIT = 0x00000020,
VK_ACCESS_SHADER_WRITE_BIT = 0x00000040,
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT = 0x00000080,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT = 0x00000100,
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT = 0x00000200,
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT = 0x00000400,
VK_ACCESS_TRANSFER_READ_BIT = 0x00000800,
VK_ACCESS_TRANSFER_WRITE_BIT = 0x00001000,
VK_ACCESS_HOST_READ_BIT = 0x00002000,
VK_ACCESS_HOST_WRITE_BIT = 0x00004000,
VK_ACCESS_MEMORY_READ_BIT = 0x00008000,
VK_ACCESS_MEMORY_WRITE_BIT = 0x00010000,
....
} VkAccessFlagBits;
typedef VkFlags VkAccessFlags;
이미지 레이아웃 열거형
이미지 레이아웃 사용 용도의 대한 정의
typedef enum VkImageLayout {
VK_IMAGE_LAYOUT_UNDEFINED = 0,
VK_IMAGE_LAYOUT_GENERAL = 1,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL = 2,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL = 3,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL = 4,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL = 5,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL = 6,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL = 7,
VK_IMAGE_LAYOUT_PREINITIALIZED = 8,
...
} VkImageLayout;
- VK_IMAGE_LAYOUT_UNDEFINED : 이 레이아웃의 이미지 내용과 부분 영역은 상당 부분 정의되지 않은 상태이며, 생성된 바로 직후에는 이 상태라고 가정, 데이터를 유지할 필요가 없을 때 사용
- VK_IMAGE_LAYOUT_GENERAL : 이 레이아웃은 이미지나 이미지의 부분 영역에 대해 모든 연산을 할 수 있게 한다. 그 외에는 용도 플래그를 통해 연산이 지정된다. 최적화 되지 않음.
- VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : 이 레이아웃의 이미지는 프레임 버퍼 색상 첨부에만 사용한다. 프레임 버퍼의 색상 읽기와 드로잉 명령을 사용한 쓰기 접근을 할 수 있다.
- VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL : 이 레이아웃의 이미지는 프레임 버퍼의 깊이/스텐실 첨부에만 사용한다.
- VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : 이 레이아웃은 이미지를 읽기 전용 셰이더 리소스로 사용한.다. 셰이더가 읽는 용도로만 접근 가능하다.
- VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL : 이 레이아웃은 이미지를 데이터 전송 소스로 사용할때 사용.
- VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL : 이 레이아웃은 이미지를 데이터 전송 타겟으로 사용할때 사용.
메모리 장벽을 삽입하는 코드이다.
// Provided by VK_VERSION_1_0
void vkCmdPipelineBarrier(
VkCommandBuffer commandBuffer,
VkPipelineStageFlags srcStageMask,
VkPipelineStageFlags dstStageMask,
VkDependencyFlags dependencyFlags,
uint32_t memoryBarrierCount,
const VkMemoryBarrier* pMemoryBarriers,
uint32_t bufferMemoryBarrierCount,
const VkBufferMemoryBarrier* pBufferMemoryBarriers,
uint32_t imageMemoryBarrierCount,
const VkImageMemoryBarrier* pImageMemoryBarriers);
- commandBuffer : 메모리 장벽이 정의된 커맨드 버퍼
- srcStageMask : 이 비트 마스크 필드는 장벽 구현 전에 수행이 완료돼야 하는 파이프라인 스테이지를 정의 (VkPipelineStageFlagBits의 조합)
- dstStageMask : 이 비트 마스크 필드는 장벽 이전의 명령어가 모두 수행되기 전까지는 시작하면 안 되는 파이프라인의 스테이지를 정의 (VkPipelineStageFlagBits의 조합)
- dependencyFlags : 이 필드는 VkDependencyFlagBits 값을 참조한다. 이 값은 장벽이 스크린 공간 지역성이 있는 지를 알려준다.
- pMemoryBarriers : 이필드는 VkMemoryBarrier 배열
- memoryBarrierCount : 개수
- pBufferMemoryBarriers : 이 필드 VkBufferMemoryBarrier 개체의 배열 포인터
- bufferMemoryBarrierCount : 개수
- pImageMemoryBarriers : VkImageMemoryBarrier 개체의 배열 포인터
- imageMemoryBarrierCount : 개수
VkPipelineStageFlagBits 정의된 항목은 파이프라인 Stage를 나타낸다.
정확한 시점을 GPU에 알려주어 효율적인 처리가 가능하도록 한다.
typedef enum VkPipelineStageFlagBits {
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT = 0x00000001,
VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT = 0x00000002,
VK_PIPELINE_STAGE_VERTEX_INPUT_BIT = 0x00000004,
VK_PIPELINE_STAGE_VERTEX_SHADER_BIT = 0x00000008,
VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT = 0x00000010,
VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT = 0x00000020,
VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT = 0x00000040,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT = 0x00000080,
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT = 0x00000100,
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT = 0x00000200,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT = 0x00000400,
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT = 0x00000800,
VK_PIPELINE_STAGE_TRANSFER_BIT = 0x00001000,
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT = 0x00002000,
VK_PIPELINE_STAGE_HOST_BIT = 0x00004000,
VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT = 0x00008000,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT = 0x00010000,
VK_PIPELINE_STAGE_NONE = 0,
....
} VkPipelineStageFlagBits;
TOP_OF_PIPE_BIT : 파이프라인 시작 단계
BOTTOM_OF_PIPE_BIT : 파이프라인 끝난 단계
FRAGMENT_SHADER_BIT : Fragment Shader Staging
COMPUTE_SHADER_BIT : Compute Sahder Staging
ALL_GRAPHICS_BIT : 좌측 파이프라인 Staging
ALL_COMMANDS_BIT : 모든 파이프라인 Staging
...
파이프라인 스테이트
1) 클리어 컬러 작업을 위한 이미지 레이아웃 변경
액세스 마스크 " VK_ACCESS_NONE " --> " VK_ACCESS_TRASNFER_WRITE_BIT "
레이아웃은 " VK_IMAGE_LAYOUT_UNDEFINED " --> " VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL "
2) 클리어 컬러 작업 수행
3) 출력을 위해 이미지 레이아웃 재변경
액세스 마스크 : " VK_ACCESS_TRANSFER_WRITE_BIT " --> 0
레이아웃 : " VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL " --> " VK_IMAGE_LAYOUT_PRESENT_SRC_KHR "
Usage >>
// 클리어 컬러를 위한 이미지 레이아웃 변경
VkImageMemoryBarrier imageMemoryBarrierForClearColorImage
{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_NONE,
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = swapchainImage.image,
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1
}
};
vkCmdPipelineBarrier(
mCommandBuffer,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
0,
0,
nullptr,
0,
nullptr,
1,
&imageMemoryBarrierForClearColorImage);
// 클리어 컬러
VkClearColorValue clearColorValue {
.float32 = {0.64, 0.7, 0.2, 1.0}
};
VkImageSubresourceRange imageSubresourceRange {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1
};
vkCmdClearColorImage(mCommandBuffer,
swapchainImage.image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
&clearColorValue,
1,
&imageSubresourceRange);
// 화면 출력을 위한 이미지 레이아웃 변경
VkImageMemoryBarrier imageMemoryBarrierForPresentSwapchainImage{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = 0,
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = swapchainImage.image,
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1
}
};
vkCmdPipelineBarrier(
mCommandBuffer,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
0,
0,
nullptr,
0,
nullptr,
1,
&imageMemoryBarrierForPresentSwapchainImage);
}
Reference
https://docs.vulkan.org/spec/latest/chapters/synchronization.html#VkPipelineStageFlagBits
'Vulkan' 카테고리의 다른 글
6. RenderPass (0) | 2024.07.01 |
---|---|
5. Presentation And Synchronize ( Fence, Semaphore, Event ) (0) | 2024.06.27 |
3. Command buffer (0) | 2024.06.21 |
2. Surface And SwapChain (1) | 2024.06.11 |
1. Vulkan Instance And device (0) | 2024.06.09 |