Vulkan HPP – 삼각형 그리기

Vulkan SDK의 vulkan.hpp 헤더에서 Vulkan API의 C++ wrapper를 정의하고 있습니다. 이를 이용하면 vulkan.h를 사용하는 기존 C 스타일 보다 코드를 간소화해서 작성할 수 있습니다. 쉐이더는 glsl로 작성했고 쉐이더 코드를 미리 컴파일 해서 spir-v 바이트 코드로 변환했습니다.

main.cpp
#include <vulkan/vulkan.hpp>
#include <GLFW/glfw3.h>

#define WIDTH  500
#define HEIGHT 500

using namespace std;

/*
#version 450 core
out gl_PerVertex { vec4 gl_Position; };
layout(location = 0) out struct { vec4 color; } Out;

const vec2 vertex[3] = { vec2(-1, 1), vec2(0, -1), vec2(1, 1) };
const vec3 colors[3] = { vec3(1, 0, 0), vec3(0, 1, 0), vec3(0, 0, 1) };

void main()
{
	Out.color   = vec4(colors[gl_VertexIndex], 1);
	gl_Position = vec4(vertex[gl_VertexIndex], 0, 1);
}
*/
uint32_t glsl_vert_spv[] = {
	0x07230203,0x00010000,0x0008000b,0x00000039,0x00000000,0x00020011,0x00000001,0x0006000b,
	0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e,0x00000000,0x00000001,
	0x0008000f,0x00000000,0x00000004,0x6e69616d,0x00000000,0x0000000a,0x00000018,0x00000027,
	0x00030003,0x00000002,0x000001c2,0x00040005,0x00000004,0x6e69616d,0x00000000,0x00030005,
	0x00000008,0x00000000,0x00050006,0x00000008,0x00000000,0x6f6c6f63,0x00000072,0x00030005,
	0x0000000a,0x0074754f,0x00060005,0x00000018,0x565f6c67,0x65747265,0x646e4978,0x00007865,
	0x00050005,0x0000001b,0x65646e69,0x6c626178,0x00000065,0x00060005,0x00000025,0x505f6c67,
	0x65567265,0x78657472,0x00000000,0x00060006,0x00000025,0x00000000,0x505f6c67,0x7469736f,
	0x006e6f69,0x00030005,0x00000027,0x00000000,0x00050005,0x00000031,0x65646e69,0x6c626178,
	0x00000065,0x00040047,0x0000000a,0x0000001e,0x00000000,0x00040047,0x00000018,0x0000000b,
	0x0000002a,0x00050048,0x00000025,0x00000000,0x0000000b,0x00000000,0x00030047,0x00000025,
	0x00000002,0x00020013,0x00000002,0x00030021,0x00000003,0x00000002,0x00030016,0x00000006,
	0x00000020,0x00040017,0x00000007,0x00000006,0x00000004,0x0003001e,0x00000008,0x00000007,
	0x00040020,0x00000009,0x00000003,0x00000008,0x0004003b,0x00000009,0x0000000a,0x00000003,
	0x00040015,0x0000000b,0x00000020,0x00000001,0x0004002b,0x0000000b,0x0000000c,0x00000000,
	0x00040017,0x0000000d,0x00000006,0x00000003,0x00040015,0x0000000e,0x00000020,0x00000000,
	0x0004002b,0x0000000e,0x0000000f,0x00000003,0x0004001c,0x00000010,0x0000000d,0x0000000f,
	0x0004002b,0x00000006,0x00000011,0x3f800000,0x0004002b,0x00000006,0x00000012,0x00000000,
	0x0006002c,0x0000000d,0x00000013,0x00000011,0x00000012,0x00000012,0x0006002c,0x0000000d,
	0x00000014,0x00000012,0x00000011,0x00000012,0x0006002c,0x0000000d,0x00000015,0x00000012,
	0x00000012,0x00000011,0x0006002c,0x00000010,0x00000016,0x00000013,0x00000014,0x00000015,
	0x00040020,0x00000017,0x00000001,0x0000000b,0x0004003b,0x00000017,0x00000018,0x00000001,
	0x00040020,0x0000001a,0x00000007,0x00000010,0x00040020,0x0000001c,0x00000007,0x0000000d,
	0x00040020,0x00000023,0x00000003,0x00000007,0x0003001e,0x00000025,0x00000007,0x00040020,
	0x00000026,0x00000003,0x00000025,0x0004003b,0x00000026,0x00000027,0x00000003,0x00040017,
	0x00000028,0x00000006,0x00000002,0x0004001c,0x00000029,0x00000028,0x0000000f,0x0004002b,
	0x00000006,0x0000002a,0xbf800000,0x0005002c,0x00000028,0x0000002b,0x0000002a,0x00000011,
	0x0005002c,0x00000028,0x0000002c,0x00000012,0x0000002a,0x0005002c,0x00000028,0x0000002d,
	0x00000011,0x00000011,0x0006002c,0x00000029,0x0000002e,0x0000002b,0x0000002c,0x0000002d,
	0x00040020,0x00000030,0x00000007,0x00000029,0x00040020,0x00000032,0x00000007,0x00000028,
	0x00050036,0x00000002,0x00000004,0x00000000,0x00000003,0x000200f8,0x00000005,0x0004003b,
	0x0000001a,0x0000001b,0x00000007,0x0004003b,0x00000030,0x00000031,0x00000007,0x0004003d,
	0x0000000b,0x00000019,0x00000018,0x0003003e,0x0000001b,0x00000016,0x00050041,0x0000001c,
	0x0000001d,0x0000001b,0x00000019,0x0004003d,0x0000000d,0x0000001e,0x0000001d,0x00050051,
	0x00000006,0x0000001f,0x0000001e,0x00000000,0x00050051,0x00000006,0x00000020,0x0000001e,
	0x00000001,0x00050051,0x00000006,0x00000021,0x0000001e,0x00000002,0x00070050,0x00000007,
	0x00000022,0x0000001f,0x00000020,0x00000021,0x00000011,0x00050041,0x00000023,0x00000024,
	0x0000000a,0x0000000c,0x0003003e,0x00000024,0x00000022,0x0004003d,0x0000000b,0x0000002f,
	0x00000018,0x0003003e,0x00000031,0x0000002e,0x00050041,0x00000032,0x00000033,0x00000031,
	0x0000002f,0x0004003d,0x00000028,0x00000034,0x00000033,0x00050051,0x00000006,0x00000035,
	0x00000034,0x00000000,0x00050051,0x00000006,0x00000036,0x00000034,0x00000001,0x00070050,
	0x00000007,0x00000037,0x00000035,0x00000036,0x00000012,0x00000011,0x00050041,0x00000023,
	0x00000038,0x00000027,0x0000000c,0x0003003e,0x00000038,0x00000037,0x000100fd,0x00010038
};

/*
#version 450 core
layout(location = 0) in struct { vec4 color; } In;
layout(location = 0) out vec4 colorOut;
void main()
{
    colorOut = In.color;
}
*/
uint32_t glsl_frag_spv[] = {
	0x07230203,0x00010000,0x0008000b,0x00000012,0x00000000,0x00020011,0x00000001,0x0006000b,
	0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e,0x00000000,0x00000001,
	0x0007000f,0x00000004,0x00000004,0x6e69616d,0x00000000,0x00000009,0x0000000c,0x00030010,
	0x00000004,0x00000007,0x00030003,0x00000002,0x000001c2,0x00040005,0x00000004,0x6e69616d,
	0x00000000,0x00050005,0x00000009,0x6f6c6f63,0x74754f72,0x00000000,0x00030005,0x0000000a,
	0x00000000,0x00050006,0x0000000a,0x00000000,0x6f6c6f63,0x00000072,0x00030005,0x0000000c,
	0x00006e49,0x00040047,0x00000009,0x0000001e,0x00000000,0x00040047,0x0000000c,0x0000001e,
	0x00000000,0x00020013,0x00000002,0x00030021,0x00000003,0x00000002,0x00030016,0x00000006,
	0x00000020,0x00040017,0x00000007,0x00000006,0x00000004,0x00040020,0x00000008,0x00000003,
	0x00000007,0x0004003b,0x00000008,0x00000009,0x00000003,0x0003001e,0x0000000a,0x00000007,
	0x00040020,0x0000000b,0x00000001,0x0000000a,0x0004003b,0x0000000b,0x0000000c,0x00000001,
	0x00040015,0x0000000d,0x00000020,0x00000001,0x0004002b,0x0000000d,0x0000000e,0x00000000,
	0x00040020,0x0000000f,0x00000001,0x00000007,0x00050036,0x00000002,0x00000004,0x00000000,
	0x00000003,0x000200f8,0x00000005,0x00050041,0x0000000f,0x00000010,0x0000000c,0x0000000e,
	0x0004003d,0x00000007,0x00000011,0x00000010,0x0003003e,0x00000009,0x00000011,0x000100fd,
	0x00010038
};

int main() {
	GLFWwindow*        window;
	vk::Instance       instance;
	vk::PhysicalDevice physicalDevice;
	vk::Device         device;
	uint32_t           queueFamilyIdx;
	vk::Queue          queue;
	vk::SurfaceKHR     surface;
	vk::RenderPass     renderPass;
	vk::SwapchainKHR   swapchain;
	vk::Framebuffer    frameBuffer;
	vk::ImageView      backBuffer;
	vk::ShaderModule   vertexShader;
	vk::ShaderModule   fragmentShader;
	vk::PipelineLayout pipelineLayout;
	vk::Pipeline       pipeline;
	vk::Semaphore      imageAcquired;
	vk::Semaphore      renderComplete;
	vk::CommandPool    cmdPool;
	vk::CommandBuffer  cmdBuffer;

	glfwInit();

	{ // create instance
		uint32_t count;
		auto exts = glfwGetRequiredInstanceExtensions(&count);

		instance = vk::createInstance({ {}, {}, 0, {}, count, exts});
	}
	{ // create device and get queue
		physicalDevice = instance.enumeratePhysicalDevices().front();

		queueFamilyIdx = 0;
		for (const auto& prop : physicalDevice.getQueueFamilyProperties()) {
			if (prop.queueFlags & vk::QueueFlagBits::eGraphics) break;
			queueFamilyIdx++;
		}

		array<const char*, 1> device_exts = {{ "VK_KHR_swapchain" }};
		const float queue_priority = 1.0f;

		vk::DeviceQueueCreateInfo queue_info = { {}, queueFamilyIdx, 1, &queue_priority };

		device = physicalDevice.createDevice({ {}, queue_info, {}, device_exts });
		queue  = device.getQueue(queueFamilyIdx, 0);
	}
	{ // create window and surface
		assert(glfwVulkanSupported());

		glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
		window = glfwCreateWindow(WIDTH, HEIGHT, "vk triangle", nullptr, nullptr);
		glfwCreateWindowSurface(instance, window, nullptr, (VkSurfaceKHR*)&surface);

		assert(physicalDevice.getSurfaceSupportKHR(queueFamilyIdx, surface));
	}
	{ // create renderpass
		vk::AttachmentDescription attachment = {
			{},
			vk::Format::eR8G8B8A8Unorm,
			vk::SampleCountFlagBits::e1,
			vk::AttachmentLoadOp::eDontCare,
			vk::AttachmentStoreOp::eStore,
			vk::AttachmentLoadOp::eDontCare,
			vk::AttachmentStoreOp::eDontCare,
			vk::ImageLayout::eUndefined,
			vk::ImageLayout::ePresentSrcKHR
		};

		vk::AttachmentReference color_attachment = { 0, vk::ImageLayout::eColorAttachmentOptimal };

		vk::SubpassDescription subpass = { {}, vk::PipelineBindPoint::eGraphics, {}, color_attachment };

		vk::SubpassDependency dependency = {
			VK_SUBPASS_EXTERNAL,
			0,
			vk::PipelineStageFlagBits::eColorAttachmentOutput,
			vk::PipelineStageFlagBits::eColorAttachmentOutput,
			{},
			vk::AccessFlagBits::eColorAttachmentWrite
		};

		renderPass = device.createRenderPass({ {}, attachment, subpass, dependency });
	}
	{ // create swapchain and frame buffer
		swapchain = device.createSwapchainKHR({
			{},
			surface,
			2,
			vk::Format::eR8G8B8A8Unorm,
			vk::ColorSpaceKHR::eSrgbNonlinear,
			{ WIDTH, HEIGHT },
			1,
			vk::ImageUsageFlagBits::eColorAttachment,
			vk::SharingMode::eExclusive,
			{},
			vk::SurfaceTransformFlagBitsKHR::eIdentity,
			vk::CompositeAlphaFlagBitsKHR::eOpaque,
			vk::PresentModeKHR::eFifo,
			true,
			nullptr
		});

		backBuffer = device.createImageView({
			{},
			device.getSwapchainImagesKHR(swapchain).front(),
			vk::ImageViewType::e2D,
			vk::Format::eR8G8B8A8Unorm,
			vk::ComponentMapping {
				vk::ComponentSwizzle::eR,
				vk::ComponentSwizzle::eG,
				vk::ComponentSwizzle::eB,
				vk::ComponentSwizzle::eA,
			},
			vk::ImageSubresourceRange {
				vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1
			}
		});

		frameBuffer = device.createFramebuffer({
			{}, renderPass, backBuffer, WIDTH, HEIGHT, 1
		});
	}
	{ // create shader modules
		vertexShader   = device.createShaderModule({ {}, sizeof(glsl_vert_spv), glsl_vert_spv });
		fragmentShader = device.createShaderModule({ {}, sizeof(glsl_frag_spv), glsl_frag_spv });
	}
	{ // create pipeline
		pipelineLayout = device.createPipelineLayout({});

		vk::PipelineVertexInputStateCreateInfo vertex_info = {};

		vk::PipelineInputAssemblyStateCreateInfo ia_info = { {}, vk::PrimitiveTopology::eTriangleList, false };

		vk::Viewport viewport = { 0, 0, WIDTH, HEIGHT, 0.f, 1.f };
		vk::Rect2D   scissor  = { { 0, 0 }, { WIDTH, HEIGHT } };

		vk::PipelineViewportStateCreateInfo viewport_info = { {}, viewport, scissor };

		vk::PipelineRasterizationStateCreateInfo raster_info = {
			{},
			false,
			false,
			vk::PolygonMode::eFill,
			vk::CullModeFlagBits::eNone,
			vk::FrontFace::eCounterClockwise,
			false,
			0.f,
			0.f,
			0.f,
			1.f
		};

		vk::PipelineMultisampleStateCreateInfo ms_info = { {}, vk::SampleCountFlagBits::e1 };

		vk::PipelineColorBlendAttachmentState color_attachment = {
			true,
			vk::BlendFactor::eSrcAlpha,
			vk::BlendFactor::eOneMinusSrcAlpha,
			vk::BlendOp::eAdd,
			vk::BlendFactor::eOne,
			vk::BlendFactor::eOneMinusSrcAlpha,
			vk::BlendOp::eAdd,
			vk::ColorComponentFlags{
				vk::ColorComponentFlagBits::eR |
				vk::ColorComponentFlagBits::eG |
				vk::ColorComponentFlagBits::eB |
				vk::ColorComponentFlagBits::eA
			}
		};

		vk::PipelineDepthStencilStateCreateInfo depth_info = {};

		vk::PipelineColorBlendStateCreateInfo blend_info = { {}, false, {}, color_attachment };

		vk::PipelineDynamicStateCreateInfo ds_info = {};

		array<vk::PipelineShaderStageCreateInfo, 2> shader_info = {{
			{ {}, vk::ShaderStageFlagBits::eVertex, vertexShader, "main" },
			{ {}, vk::ShaderStageFlagBits::eFragment, fragmentShader, "main" }
		}};

		pipeline = device.createGraphicsPipeline(nullptr, {
			{},
			shader_info,
			&vertex_info,
			&ia_info,
			nullptr,
			&viewport_info,
			&raster_info,
			&ms_info,
			&depth_info,
			&blend_info,
			&ds_info,
			pipelineLayout,
			renderPass,
			0
		}).value;
	}
	{ // allocate and record command buffer
		cmdPool   = device.createCommandPool({ vk::CommandPoolCreateFlagBits::eResetCommandBuffer, queueFamilyIdx });
		cmdBuffer = device.allocateCommandBuffers({ cmdPool, vk::CommandBufferLevel::ePrimary, 1 }).front();

		vk::ClearValue clearValue = vk::ClearColorValue({ 0.f, 0.f, 0.f, 1.f });
		vk::Rect2D     area       = { { 0, 0 }, { WIDTH, HEIGHT } };

		cmdBuffer.begin({ vk::CommandBufferUsageFlagBits::eOneTimeSubmit });
		cmdBuffer.beginRenderPass({ renderPass, frameBuffer, area, clearValue }, vk::SubpassContents::eInline);
		cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline);
		cmdBuffer.draw(3, 1, 0, 0);
		cmdBuffer.endRenderPass();
		cmdBuffer.end();
	}
	{ // create semaphores
		imageAcquired  = device.createSemaphore({});
		renderComplete = device.createSemaphore({});
	}
	{ // submit
		vk::PipelineStageFlags wait_stage = vk::PipelineStageFlagBits::eColorAttachmentOutput;
		vk::SubmitInfo         submit     = { imageAcquired, wait_stage, cmdBuffer, renderComplete };

		device.acquireNextImageKHR(swapchain, UINT64_MAX, imageAcquired);
		queue.submit(submit);
	}
	{ // present
		uint32_t imageIdx = 0;
		
		queue.presentKHR({ renderComplete, swapchain, imageIdx });
		device.waitIdle();
	}

	while (!glfwWindowShouldClose(window)) glfwPollEvents();
	
	{ // destroy objects
		device.freeCommandBuffers(cmdPool, cmdBuffer);
		device.destroy(cmdPool);
		device.destroy(imageAcquired);
		device.destroy(renderComplete);
		device.destroy(pipeline);
		device.destroy(pipelineLayout);
		device.destroy(vertexShader);
		device.destroy(fragmentShader);
		device.destroy(backBuffer);
		device.destroy(frameBuffer);
		device.destroy(swapchain);
		device.destroy(renderPass);
		device.destroy();
		instance.destroy(surface);
		instance.destroy();
	}

	glfwDestroyWindow(window);
	glfwTerminate();

	return 0;
}
C++

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다