[Vulkan Tutorial] 11-Create Image Views

목차: 01-Overview (Link)
이전 글: 10-Create Swap Chain (Link)
다음 글: 12-Introduction of Graphics Pipeline (Link)


앞 장에서 Swap Chain을 생성하는 방법을 정리하였다. Swap Chain을 생성하면 Image (VkImage)가 생성된다. 해당 Image (VkImage)를 사용하기 위해서 Image View (VkImageView)를 생성해야 한다. 출처 1에서 Image View (VkImageView)에 대한 설명이 다소 어려워서 다른 설명을 찾아보았다. 개인적으로 출처 2에서 제공하는 Image View (VkImageView)에 대한 설명이 조금 더 이해하기 쉬운 것 같다.

Graphics Pipeline이 Rendering을 하면 결과를 Image에 작성해야 한다. 하지만, Graphics Pipeline은 Image (VkImage)의 데이터 값에 직접 접근(Read/Write)이 불가능하다고 한다. 대신 Image View(VkImageView)를 사용해야 한다. Image View는 Image 특정 부분의 연속된 데이터(Contiguous Ranges of the Image Subresource)와 추가적인 Metadata로 구성되어 있다고 한다.

MK: 정확하게 이해를 하지 못하여서 설명이 다소 난해한 것 같다. 조금 더 설명이 잘 되어 있는 소스를 찾게 되면 수정을 하겠다. 현재까지 이해한 바에 의하면 Image는 단순히 우리가 보는 RGBA로 구성된 데이터를 의미한다. Image View는 해당 Image (RGBA로 구성된 데이터)와 Image를 표현하기 위한 추가적인 Metadata 값을 의미하는 것 같다. 아마도 Graphics Pipeline은 이러한 Metadata 값을 사용해서 Image의 특정 데이터 영역에 접근하는 것으로 판단된다.

아래 코드 1은 Image View 생성을 위한 코드이다. 전체 코드가 너무 길어져서 중간에 몇 함수 코드를 … 으로 변경하였다. 전체 코드는 아래 출처 4에서 확인할 수 있다.

코드 1: Image View 생성을 위한 코드 추가

#include <iostream>
#include <stdexcept>
#include <functional>
#include <cstdlib>
#include <util_init.hpp>
#include <set>

#define APP_SHORT_NAME "mkVulkanExample"

#define MK_GET_INSTANCE_PROC_ADDR(inst, entrypoint)												\
{																								\
	fp##entrypoint = (PFN_vk##entrypoint)vkGetInstanceProcAddr(instance, "vk" #entrypoint);		\
	if (fp##entrypoint == NULL) {																\
		std::cout << "vkGetDeviceProcAddr failed to find vk" #entrypoint;						\
		exit(-1);																				\
	}																							\
}


class mkTriangle{
	public:
		void run(){
			LOGI("MK: mkTriangle-->run Function");
			initVulkan();
			mainLoop();
			cleanup();
		}

	private:
		void initVulkan(){
			createInstance();
			createSurface();
			pickPhysicalDevice();
			searchQueue();
			createLogicalDevice();
			createSwapChain();

			//MK: (코드 1-3) createImageViews() 함수 호출
			createImageViews();
		}

		void mainLoop(){
		}

		void cleanup(){
			LOGI("MK: cleanup Function");

			//MK: (코드 1-6) Destroy Image Views
			for(int i = 0; i < swapChainImageViews.size(); i++){
				vkDestroyImageView(device, swapChainImageViews[i], NULL);
			}

			vkDestroySwapchainKHR(device, swapChain, NULL);
			vkDestroyDevice(device, NULL);
			vkDestroySurfaceKHR(instance, surface, NULL);
			vkDestroyInstance(instance, NULL);
		}

		void createInstance(){

			...

		}

		void createSurface(){
			
			...

		}

		void pickPhysicalDevice(){

			...

		}

		void printPhysicalDeviceInfo(VkPhysicalDevice device){

			...

		}

		void searchQueue(){

			...

		}

		void createLogicalDevice(){

			...

		}

		void createSwapChain(){

			...

		}

		//MK: (코드 1-2) createImageViews() 함수 추가
		void createImageViews(){
			LOGI("MK: (Begin) createImageViews Function");

			//MK: (코드 1-4) Image View 변수 개수 파악
			swapChainImageViews.resize(swapChainImages.size());

			//MK: (코드 1-5) Image View 생성 코드
			for(int i = 0; i < swapChainImageViews.size(); i++){
				VkImageViewCreateInfo createInfo = {};
				createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
				createInfo.pNext = NULL;
				createInfo.flags = 0;
				createInfo.image = swapChainImages[i];
				createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
				createInfo.format = selectedSurfaceFormat.format;
				createInfo.components.r = VK_COMPONENT_SWIZZLE_R;
				createInfo.components.g = VK_COMPONENT_SWIZZLE_G;
				createInfo.components.b = VK_COMPONENT_SWIZZLE_B;
				createInfo.components.a = VK_COMPONENT_SWIZZLE_A;
				createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT ;
				createInfo.subresourceRange.baseMipLevel = 0;
				createInfo.subresourceRange.levelCount = 1;
				createInfo.subresourceRange.baseArrayLayer = 0;
				createInfo.subresourceRange.layerCount = 1;

				VkResult result = vkCreateImageView(device, &createInfo, NULL, &swapChainImageViews[i]);
				assert(result == VK_SUCCESS);
				LOGI("MK:\t Successfully Create %d Image View (if not, should not see this print)", i);
			}
			
			LOGI("MK: (End) createImageViews Function");
		}

		VkInstance instance;

		VkSurfaceKHR surface;
		PFN_vkCreateAndroidSurfaceKHR fpCreateAndroidSurfaceKHR;

		VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;

		uint32_t graphicQueueFamilyIndex = 0;
		uint32_t presentQueueFamilyIndex = 0;

		VkDevice device; 

		VkQueue graphicsQueue;
		VkQueue presentQueue;

		VkSurfaceCapabilitiesKHR surfaceCapabilities;
		std::vector<VkSurfaceFormatKHR> surfaceFormats;
		std::vector<VkPresentModeKHR> presentModes;

		VkSurfaceFormatKHR selectedSurfaceFormat;
		VkPresentModeKHR selectedPresentMode;
		VkExtent2D selectedExtent;
		uint32_t selectedImageCount;

		VkSwapchainKHR swapChain;

		std::vector<VkImage> swapChainImages;

		//MK: (코드 1-1) Image View를 저장할 변수 추가
		std::vector<VkImageView> swapChainImageViews;
};

int sample_main(int argc, char *argv[]) {
	
	...
	
}

가장 먼저 코드 1-1과 같이 Image View를 저장한 변수를 하나 추가한다. 다음은 코드 1-2와 같이 createImageViews() 함수를 하나 생성한다. 해당 함수는 코드 1-3 initVulkan()에서 호출한다. 

Image View는 Image 개수와 동일하게 생성해야 한다. 그래서 createImageViews() 함수에서 제일 먼저 Image View 개수를 파악해서 Image View 변수 크기를 결정한다. 해당 코드는 1-4에서 확인할 수 있다. 

코드 1-5는 Image View를 생성하는 코드이다. Image View 생성을 위해서 VkImageViewCreateInfo Sturct에 필요한 값을 먼저 저장한다. VkImageViewCreateInfo Struct은 아래와 같이 8개의 변수로 구성되어 있다. 

VkImageViewCreateInfo Strcut (출처 5) 

  • sType (VkStructureType): 생성하고자 하는 Struct의 Type을 나타냄
  • pNext (const void*): Extension Struct Pointer
  • flags (VkDeviceCreateFlags): 미래에 사용하기 위해서 미리 만들어 두었다고 함
  • image (VkImage): 해당 Image View와 연동할 Image 변수 (Swap Chain이 생성한 Image (VkImage) 변수)
  • viewType (VkImageViewType): Image View Type을 나타냄 
  • format (VkFormat): VkImage의 Format을 의미함 (Swap Chain 생성 부분에서 사용한 값을 사용)
  • components (VkComponentMapping): Color 값을 Remapping하기 위해 사용함
  • subresourceRange (VkImageSubresourceRange): Image의 사용 목적과 Image의 어떤 부분에 접근(Access)할 것인지 정의함

sType, pNext, flags, image, format은 코드를 보면 대략 어떤 의미인지 파악이 가능하다고 판단된다. viewType의 경우 크게 1D Texture, 2D Texture, 3D Texture, 또는 Cube Maps을 선택할 수 있다. 우리의 경우 2D Texture로 사용할 예정이라 VK_IMAGE_VIEW_TYPE_2D를 선택한다.

다음은 components 필드이다. components 필드는 r, g, b, a로 구성되어 있다. r, g, b, a에 VK_COMPONENT_SWIZZLE_R/G/B/A 값으로 설정하였다. VK_COMPONENT_SWIZZLE_R/G/B/A (VK_COMPONENT_SWIZZLE_IDENTITY)은 아마 Rendering 된 결과 값을 Image에 Write(작성) 한다는 의미로 파악된다. r, g, b, a에 0, 1과 같은 Constant 값도 사용이 가능하다고 한다. 반대로 r, g, b, a 에 모두 r(VK_COMPONENT_SWIZZLE_R을 사용) 값을 사용하여서 Monochrome Texture로도 사용 가능하다고 한다.

이제 마지막인 subresourceRange 값을 설정할 차례이다. subresourceRange (VkImageSubresourceRange)는 5개의 추가적인 변수로 구성되어 있다.

VkImageSubresourceRange Struct (출처 6)

  • aspectMask (VkImageAspectFlags): Image의 어떤 부분을 접근 (Access)할 것인지 선택함 (Color, Depth, Stencil 중 선택함) (예제에서는 VK_IMAGE_ASPECT_COLOR_BIT (Color)를 설정함)
  • baseMipLevel (uint32_t): 처음 접근 (Access)하는 Mipmap Level (the first mipmap level accessible to the view) (예제에서는 0으로 설정함)
  • levelCount (uint32_t): Mipmap Level (예제에서는 1로 설정하여 Mipmap을 생성하지 않음)
  • baseArrayLayer (uint32_t): 처음 접근 (Access)하는 Layer 번호 (the first array layer accessible to the view) (예제에서는 0으로 설정함)
  • layerCount (uint32_t): Array Layer 개수 (예제에서는 1로 설정하여 1개만 생성함. 아마 3D (좌/우 2개로 나뉘어 지는 화면) Application이 아닌 관계로 1로 작성한 것으로 판단됨)

필요한 값을 모두 작성하면 vkCreateImageView(…) 함수를 사용해서 Image View를 생성할 차례이다. vkCreateImageView(…) 함수는 아래와 같이 4개의 Parameter를 가진다. 

vkCreateImageView(…) (출처 7)

  • device (VkDevice): Logical Device 변수 (Create Logical Device에서 생성)
  • pCreateInfo (const VkImageViewCreateInfo*): 앞에서 작성한  VkImageViewCreateInfo Struct 변수
  • pAllocator (const VkAllocationCallbacks*): Pointer to custom allocator callbacks
  • pView (VkImageView*): Image View를 저장할 변수

Image View는 Image 개수만큼 생성이 필요하다. For Loop을 사용해서 Image View를 여러 번 생성한다. Image View는 Application 종료 전에 Destory를 해야 한다. 코드 1-6은 vkDestroyImageView(…) 함수를 사용해서 Image View를 제거하는 코드이다. 역시 For Loop을 사용해서 여러 개의 Image View를 제거한다.

그림 1: Image View 생성 실행 후 Logcat 결과

위 코드를 빌드해서 실행하면 여전히 검은색 화면만 나타난다. 그림 1은 해당 코드를 빌드하고 실행하면 출력되는 Logcat 결과이다.


출처

  1. https://vulkan-tutorial.com/
  2. https://vulkan.lunarg.com/doc/view/1.0.26.0/linux/vkspec.chunked/ch11s05.html
  3. https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkImageViewCreateInfo.html
  4. https://github.com/mkblog-cokr/androidVulkanTutorial
  5. https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkImageViewCreateInfo.html
  6. https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkImageSubresourceRange.html
  7. https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkCreateImageView.html
  8. https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkDestroyImageView.html


Leave a Comment