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++
최신 댓글