Skip to content

Commit ff4cc28

Browse files
authored
Port of latest changes in api sample hello_triangle to hpp_hello_triangle (#1369)
1 parent 390567e commit ff4cc28

File tree

3 files changed

+184
-67
lines changed

3 files changed

+184
-67
lines changed

samples/api/hpp_hello_triangle/CMakeLists.txt

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2021-2023, NVIDIA CORPORATION. All rights reserved.
1+
# Copyright (c) 2021-2025, NVIDIA CORPORATION. All rights reserved.
22
#
33
# SPDX-License-Identifier: Apache-2.0
44
#
@@ -26,8 +26,14 @@ add_sample(
2626
NAME "HPP Hello Triangle"
2727
DESCRIPTION "An introduction into Vulkan and its respective objects using vulkan.hpp."
2828
SHADER_FILES_GLSL
29-
"triangle.vert"
30-
"triangle.frag")
29+
"hello_triangle/glsl/triangle.vert"
30+
"hello_triangle/glsl/triangle.frag"
31+
SHADER_FILES_HLSL
32+
"hello_triangle/hlsl/triangle.vert.hlsl"
33+
"hello_triangle/hlsl/triangle.frag.hlsl"
34+
SHADER_FILES_SLANG
35+
"hello_triangle/slang/triangle.vert.slang"
36+
"hello_triangle/slang/triangle.frag.slang")
3137

3238
if(${VKB_${FOLDER_NAME}})
3339
target_compile_definitions(${FOLDER_NAME} PUBLIC VULKAN_HPP_DISPATCH_LOADER_DYNAMIC=1)

samples/api/hpp_hello_triangle/hpp_hello_triangle.cpp

Lines changed: 136 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,16 @@ HPPHelloTriangle::~HPPHelloTriangle()
122122
instance.destroySurfaceKHR(surface);
123123
}
124124

125+
if (vertex_buffer_allocation != VK_NULL_HANDLE)
126+
{
127+
vmaDestroyBuffer(vma_allocator, vertex_buffer, vertex_buffer_allocation);
128+
}
129+
130+
if (vma_allocator != VK_NULL_HANDLE)
131+
{
132+
vmaDestroyAllocator(vma_allocator);
133+
}
134+
125135
if (device)
126136
{
127137
device.destroy();
@@ -160,6 +170,9 @@ bool HPPHelloTriangle::prepare(const vkb::ApplicationOptions &options)
160170
// get the (graphics) queue
161171
queue = device.getQueue(graphics_queue_index, 0);
162172

173+
vma_allocator = create_vma_allocator();
174+
std::tie(vertex_buffer, vertex_buffer_allocation) = create_vertex_buffer();
175+
163176
init_swapchain();
164177

165178
// Create the necessary objects for rendering.
@@ -338,11 +351,40 @@ vk::Device HPPHelloTriangle::create_device(const std::vector<const char *> &requ
338351
vk::Pipeline HPPHelloTriangle::create_graphics_pipeline()
339352
{
340353
// Load our SPIR-V shaders.
354+
355+
// Samples support different shading languages, all of which are offline compiled to SPIR-V, the shader format that Vulkan uses.
356+
// The shading language to load for can be selected via command line
357+
std::string shader_folder{""};
358+
switch (get_shading_language())
359+
{
360+
case vkb::ShadingLanguage::HLSL:
361+
shader_folder = "hlsl";
362+
break;
363+
case vkb::ShadingLanguage::SLANG:
364+
shader_folder = "slang";
365+
break;
366+
default:
367+
shader_folder = "glsl";
368+
}
369+
341370
std::vector<vk::PipelineShaderStageCreateInfo> shader_stages{
342-
{.stage = vk::ShaderStageFlagBits::eVertex, .module = create_shader_module("triangle.vert.spv"), .pName = "main"},
343-
{.stage = vk::ShaderStageFlagBits::eFragment, .module = create_shader_module("triangle.frag.spv"), .pName = "main"}};
371+
{.stage = vk::ShaderStageFlagBits::eVertex, .module = create_shader_module("hello_triangle/" + shader_folder + "/triangle.vert.spv"), .pName = "main"},
372+
{.stage = vk::ShaderStageFlagBits::eFragment, .module = create_shader_module("hello_triangle/" + shader_folder + "/triangle.frag.spv"), .pName = "main"}};
373+
374+
// Define the vertex input binding.
375+
vk::VertexInputBindingDescription binding_description{.binding = 0, .stride = sizeof(Vertex), .inputRate = vk::VertexInputRate::eVertex};
344376

345-
vk::PipelineVertexInputStateCreateInfo vertex_input;
377+
// Define the vertex input attribute.
378+
std::array<vk::VertexInputAttributeDescription, 2> attribute_descriptions{
379+
{{.location = 0, .binding = 0, .format = vk::Format::eR32G32B32Sfloat, .offset = offsetof(Vertex, position)},
380+
{.location = 1, .binding = 0, .format = vk::Format::eR32G32B32Sfloat, .offset = offsetof(Vertex, color)}}};
381+
382+
// Define the pipeline vertex input.
383+
vk::PipelineVertexInputStateCreateInfo vertex_input{
384+
.vertexBindingDescriptionCount = 1,
385+
.pVertexBindingDescriptions = &binding_description,
386+
.vertexAttributeDescriptionCount = static_cast<uint32_t>(attribute_descriptions.size()),
387+
.pVertexAttributeDescriptions = attribute_descriptions.data()};
346388

347389
// Our attachment will write to all color channels, but no blending is enabled.
348390
vk::PipelineColorBlendAttachmentState blend_attachment{.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG |
@@ -378,8 +420,8 @@ vk::ImageView HPPHelloTriangle::create_image_view(vk::Image image)
378420
.image = image,
379421
.viewType = vk::ImageViewType::e2D,
380422
.format = swapchain_data.format,
381-
.components = {vk::ComponentSwizzle::eR, vk::ComponentSwizzle::eG, vk::ComponentSwizzle::eB, vk::ComponentSwizzle::eA},
382-
.subresourceRange = {vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}};
423+
.components = {.r = vk::ComponentSwizzle::eR, .g = vk::ComponentSwizzle::eG, .b = vk::ComponentSwizzle::eB, .a = vk::ComponentSwizzle::eA},
424+
.subresourceRange = {.aspectMask = vk::ImageAspectFlagBits::eColor, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1}};
383425
return device.createImageView(image_view_create_info);
384426
}
385427

@@ -498,19 +540,20 @@ vk::Instance HPPHelloTriangle::create_instance(std::vector<const char *> const &
498540

499541
vk::RenderPass HPPHelloTriangle::create_render_pass()
500542
{
501-
vk::AttachmentDescription attachment{{},
502-
swapchain_data.format, // Backbuffer format
503-
vk::SampleCountFlagBits::e1, // Not multisampled
504-
vk::AttachmentLoadOp::eClear, // When starting the frame, we want tiles to be cleared
505-
vk::AttachmentStoreOp::eStore, // When ending the frame, we want tiles to be written out
506-
vk::AttachmentLoadOp::eDontCare, // Don't care about stencil since we're not using it
507-
vk::AttachmentStoreOp::eDontCare,
508-
vk::ImageLayout::eUndefined, // The image layout will be undefined when the render pass begins
509-
vk::ImageLayout::ePresentSrcKHR}; // After the render pass is complete, we will transition to ePresentSrcKHR layout
543+
vk::AttachmentDescription attachment{
544+
.format = swapchain_data.format, // Backbuffer format.
545+
.samples = vk::SampleCountFlagBits::e1, // Not multisampled.
546+
.loadOp = vk::AttachmentLoadOp::eClear, // When starting the frame, we want tiles to be cleared.
547+
.storeOp = vk::AttachmentStoreOp::eStore, // When ending the frame, we want tiles to be written out.
548+
.stencilLoadOp = vk::AttachmentLoadOp::eDontCare, // Don't care about stencil since we're not using it.
549+
.stencilStoreOp = vk::AttachmentStoreOp::eDontCare, // Don't care about stencil since we're not using it.
550+
.initialLayout = vk::ImageLayout::eUndefined, // The image layout will be undefined when the render pass begins.
551+
.finalLayout = vk::ImageLayout::ePresentSrcKHR // After the render pass is complete, we will transition to PRESENT_SRC_KHR layout.
552+
};
510553

511554
// We have one subpass. This subpass has one color attachment.
512555
// While executing this subpass, the attachment will be in attachment optimal layout.
513-
vk::AttachmentReference color_ref{0, vk::ImageLayout::eColorAttachmentOptimal};
556+
vk::AttachmentReference color_ref{.attachment = 0, .layout = vk::ImageLayout::eColorAttachmentOptimal};
514557

515558
// We will end up with two transitions.
516559
// The first one happens right before we start subpass #0, where
@@ -538,20 +581,17 @@ vk::RenderPass HPPHelloTriangle::create_render_pass()
538581
}
539582

540583
/**
541-
* @brief Helper function to load a shader module.
584+
* @brief Helper function to load a shader module from an offline-compiled SPIR-V file.
542585
* @param path The path for the shader (relative to the assets directory).
543586
* @returns A vk::ShaderModule handle. Aborts execution if shader creation fails.
544587
*/
545-
vk::ShaderModule HPPHelloTriangle::create_shader_module(const char *path)
588+
vk::ShaderModule HPPHelloTriangle::create_shader_module(std::string const &path)
546589
{
547590
auto spirv = vkb::fs::read_shader_binary_u32(path);
548591

549-
VkShaderModuleCreateInfo module_info{
550-
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
551-
.codeSize = spirv.size() * sizeof(uint32_t),
552-
.pCode = spirv.data()};
592+
vk::ShaderModuleCreateInfo shader_module_create_info{.codeSize = spirv.size() * sizeof(uint32_t), .pCode = spirv.data()};
553593

554-
return device.createShaderModule({.codeSize = static_cast<uint32_t>(spirv.size() * sizeof(uint32_t)), .pCode = spirv.data()});
594+
return device.createShaderModule(shader_module_create_info);
555595
}
556596

557597
vk::SwapchainKHR
@@ -595,25 +635,80 @@ vk::SwapchainKHR
595635
// FIFO must be supported by all implementations.
596636
vk::PresentModeKHR swapchain_present_mode = vk::PresentModeKHR::eFifo;
597637

598-
vk::SwapchainCreateInfoKHR swapchain_create_info;
599-
swapchain_create_info.surface = surface;
600-
swapchain_create_info.minImageCount = desired_swapchain_images;
601-
swapchain_create_info.imageFormat = surface_format.format;
602-
swapchain_create_info.imageColorSpace = surface_format.colorSpace;
603-
swapchain_create_info.imageExtent.width = swapchain_extent.width;
604-
swapchain_create_info.imageExtent.height = swapchain_extent.height;
605-
swapchain_create_info.imageArrayLayers = 1;
606-
swapchain_create_info.imageUsage = vk::ImageUsageFlagBits::eColorAttachment;
607-
swapchain_create_info.imageSharingMode = vk::SharingMode::eExclusive;
608-
swapchain_create_info.preTransform = pre_transform;
609-
swapchain_create_info.compositeAlpha = composite;
610-
swapchain_create_info.presentMode = swapchain_present_mode;
611-
swapchain_create_info.clipped = true;
612-
swapchain_create_info.oldSwapchain = old_swapchain;
638+
vk::SwapchainCreateInfoKHR swapchain_create_info{
639+
.surface = surface,
640+
.minImageCount = desired_swapchain_images,
641+
.imageFormat = surface_format.format,
642+
.imageColorSpace = surface_format.colorSpace,
643+
.imageExtent = swapchain_extent,
644+
.imageArrayLayers = 1,
645+
.imageUsage = vk::ImageUsageFlagBits::eColorAttachment,
646+
.imageSharingMode = vk::SharingMode::eExclusive,
647+
.preTransform = pre_transform,
648+
.compositeAlpha = composite,
649+
.presentMode = swapchain_present_mode,
650+
.clipped = true,
651+
.oldSwapchain = old_swapchain};
613652

614653
return device.createSwapchainKHR(swapchain_create_info);
615654
}
616655

656+
std::pair<vk::Buffer, VmaAllocation> HPPHelloTriangle::create_vertex_buffer()
657+
{
658+
// Vertex data for a single colored triangle
659+
const std::vector<Vertex> vertices = {
660+
{{0.5f, -0.5f, 0.5f}, {1.0f, 0.0f, 0.0f}},
661+
{{0.5f, 0.5f, 0.5f}, {0.0f, 1.0f, 0.0f}},
662+
{{-0.5f, 0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}}};
663+
664+
const vk::DeviceSize buffer_size = sizeof(vertices[0]) * vertices.size();
665+
666+
// Copy Vertex data to a buffer accessible by the device
667+
668+
vk::BufferCreateInfo buffer_create_info{.size = buffer_size, .usage = vk::BufferUsageFlagBits::eVertexBuffer};
669+
670+
// We use the Vulkan Memory Allocator to find a memory type that can be written and mapped from the host
671+
// On most setups this will return a memory type that resides in VRAM and is accessible from the host
672+
VmaAllocationCreateInfo allocation_create_info{
673+
.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT,
674+
.usage = VMA_MEMORY_USAGE_AUTO,
675+
.requiredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT};
676+
677+
vk::Buffer vertex_buffer;
678+
VmaAllocation vertex_buffer_allocation;
679+
VmaAllocationInfo allocation_info{};
680+
vmaCreateBuffer(vma_allocator, reinterpret_cast<VkBufferCreateInfo *>(&buffer_create_info), &allocation_create_info, reinterpret_cast<VkBuffer *>(&vertex_buffer), &vertex_buffer_allocation, &allocation_info);
681+
if (allocation_info.pMappedData)
682+
{
683+
memcpy(allocation_info.pMappedData, vertices.data(), buffer_size);
684+
}
685+
else
686+
{
687+
throw std::runtime_error("Could not map vertex buffer.");
688+
}
689+
690+
return {vertex_buffer, vertex_buffer_allocation};
691+
}
692+
693+
VmaAllocator HPPHelloTriangle::create_vma_allocator()
694+
{
695+
// This sample uses the Vulkan Memory Alloctor (VMA), which needs to be set up
696+
VmaVulkanFunctions vma_vulkan_functions{
697+
.vkGetInstanceProcAddr = VULKAN_HPP_DEFAULT_DISPATCHER.vkGetInstanceProcAddr,
698+
.vkGetDeviceProcAddr = VULKAN_HPP_DEFAULT_DISPATCHER.vkGetDeviceProcAddr};
699+
700+
VmaAllocatorCreateInfo allocator_info{.physicalDevice = gpu, .device = device, .pVulkanFunctions = &vma_vulkan_functions, .instance = instance};
701+
702+
VmaAllocator allocator;
703+
VkResult result = vmaCreateAllocator(&allocator_info, &allocator);
704+
if (result != VK_SUCCESS)
705+
{
706+
throw std::runtime_error("Could not create allocator for VMA allocator");
707+
}
708+
709+
return allocator;
710+
}
711+
617712
/**
618713
* @brief Initializes the Vulkan framebuffers.
619714
*/
@@ -732,6 +827,10 @@ void HPPHelloTriangle::render_triangle(uint32_t swapchain_index)
732827
// Set scissor dynamically
733828
cmd.setScissor(0, scissor);
734829

830+
// Bind the vertex buffer to source the draw calls from.
831+
vk::DeviceSize offset = {0};
832+
cmd.bindVertexBuffers(0, vertex_buffer, offset);
833+
735834
// Draw three vertices with one instance.
736835
cmd.draw(3, 1, 0, 0);
737836

samples/api/hpp_hello_triangle/hpp_hello_triangle.h

Lines changed: 39 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ class HPPHelloTriangle : public vkb::Application
5151
vk::Semaphore swapchain_release_semaphore;
5252
};
5353

54+
/// Properties of the vertices used in this sample.
55+
struct Vertex
56+
{
57+
glm::vec3 position;
58+
glm::vec3 color;
59+
};
60+
5461
public:
5562
HPPHelloTriangle();
5663
virtual ~HPPHelloTriangle();
@@ -61,35 +68,40 @@ class HPPHelloTriangle : public vkb::Application
6168
virtual bool resize(const uint32_t width, const uint32_t height) override;
6269
virtual void update(float delta_time) override;
6370

64-
std::pair<vk::Result, uint32_t> acquire_next_image();
65-
vk::Device create_device(const std::vector<const char *> &required_device_extensions);
66-
vk::Pipeline create_graphics_pipeline();
67-
vk::ImageView create_image_view(vk::Image image);
68-
vk::Instance create_instance(std::vector<const char *> const &required_instance_extensions, std::vector<const char *> const &required_validation_layers);
69-
vk::RenderPass create_render_pass();
70-
vk::ShaderModule create_shader_module(const char *path);
71-
vk::SwapchainKHR create_swapchain(vk::Extent2D const &swapchain_extent, vk::SurfaceFormatKHR surface_format, vk::SwapchainKHR old_swapchain);
72-
void init_framebuffers();
73-
void init_swapchain();
74-
void render_triangle(uint32_t swapchain_index);
75-
void select_physical_device_and_surface();
76-
void teardown_framebuffers();
77-
void teardown_per_frame(FrameData &per_frame_data);
71+
std::pair<vk::Result, uint32_t> acquire_next_image();
72+
vk::Device create_device(const std::vector<const char *> &required_device_extensions);
73+
vk::Pipeline create_graphics_pipeline();
74+
vk::ImageView create_image_view(vk::Image image);
75+
vk::Instance create_instance(std::vector<const char *> const &required_instance_extensions, std::vector<const char *> const &required_validation_layers);
76+
vk::RenderPass create_render_pass();
77+
vk::ShaderModule create_shader_module(std::string const &path);
78+
vk::SwapchainKHR create_swapchain(vk::Extent2D const &swapchain_extent, vk::SurfaceFormatKHR surface_format, vk::SwapchainKHR old_swapchain);
79+
std::pair<vk::Buffer, VmaAllocation> create_vertex_buffer();
80+
VmaAllocator create_vma_allocator();
81+
void init_framebuffers();
82+
void init_swapchain();
83+
void render_triangle(uint32_t swapchain_index);
84+
void select_physical_device_and_surface();
85+
void teardown_framebuffers();
86+
void teardown_per_frame(FrameData &per_frame_data);
7887

7988
private:
80-
vk::Instance instance; // The Vulkan instance.
81-
vk::PhysicalDevice gpu; // The Vulkan physical device.
82-
vk::Device device; // The Vulkan device.
83-
vk::Queue queue; // The Vulkan device queue.
84-
SwapchainData swapchain_data; // The swapchain state.
85-
vk::SurfaceKHR surface; // The surface we will render to.
86-
uint32_t graphics_queue_index; // The queue family index where graphics work will be submitted.
87-
vk::RenderPass render_pass; // The renderpass description.
88-
vk::PipelineLayout pipeline_layout; // The pipeline layout for resources.
89-
vk::Pipeline pipeline; // The graphics pipeline.
90-
vk::DebugUtilsMessengerEXT debug_utils_messenger; // The debug utils messenger.
91-
std::vector<vk::Semaphore> recycled_semaphores; // A set of semaphores that can be reused.
92-
std::vector<FrameData> per_frame_data; // A set of per-frame data.
89+
vk::Instance instance; // The Vulkan instance.
90+
vk::PhysicalDevice gpu; // The Vulkan physical device.
91+
vk::Device device; // The Vulkan device.
92+
vk::Queue queue; // The Vulkan device queue.
93+
SwapchainData swapchain_data; // The swapchain state.
94+
vk::SurfaceKHR surface; // The surface we will render to.
95+
uint32_t graphics_queue_index; // The queue family index where graphics work will be submitted.
96+
vk::RenderPass render_pass; // The renderpass description.
97+
vk::PipelineLayout pipeline_layout; // The pipeline layout for resources.
98+
vk::Pipeline pipeline; // The graphics pipeline.
99+
vk::DebugUtilsMessengerEXT debug_utils_messenger; // The debug utils messenger.
100+
std::vector<vk::Semaphore> recycled_semaphores; // A set of semaphores that can be reused.
101+
std::vector<FrameData> per_frame_data; // A set of per-frame data.
102+
vk::Buffer vertex_buffer; // The Vulkan buffer object that holds the vertex data for the triangle.
103+
VmaAllocation vertex_buffer_allocation = VK_NULL_HANDLE; // Vulkan Memory Allocator (VMA) allocation info for the vertex buffer.
104+
VmaAllocator vma_allocator; // The VMA allocator for memory management.
93105

94106
#if defined(VKB_DEBUG) || defined(VKB_VALIDATION_LAYERS)
95107
vk::DebugUtilsMessengerCreateInfoEXT debug_utils_create_info;

0 commit comments

Comments
 (0)