Skip to content

Port of latest changes in api sample hello_triangle to hpp_hello_triangle #1369

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions samples/api/hpp_hello_triangle/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2021-2023, NVIDIA CORPORATION. All rights reserved.
# Copyright (c) 2021-2025, NVIDIA CORPORATION. All rights reserved.
#
# SPDX-License-Identifier: Apache-2.0
#
Expand Down Expand Up @@ -26,8 +26,14 @@ add_sample(
NAME "HPP Hello Triangle"
DESCRIPTION "An introduction into Vulkan and its respective objects using vulkan.hpp."
SHADER_FILES_GLSL
"triangle.vert"
"triangle.frag")
"hello_triangle/glsl/triangle.vert"
"hello_triangle/glsl/triangle.frag"
SHADER_FILES_HLSL
"hello_triangle/hlsl/triangle.vert.hlsl"
"hello_triangle/hlsl/triangle.frag.hlsl"
SHADER_FILES_SLANG
"hello_triangle/slang/triangle.vert.slang"
"hello_triangle/slang/triangle.frag.slang")

if(${VKB_${FOLDER_NAME}})
target_compile_definitions(${FOLDER_NAME} PUBLIC VULKAN_HPP_DISPATCH_LOADER_DYNAMIC=1)
Expand Down
173 changes: 136 additions & 37 deletions samples/api/hpp_hello_triangle/hpp_hello_triangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,16 @@ HPPHelloTriangle::~HPPHelloTriangle()
instance.destroySurfaceKHR(surface);
}

if (vertex_buffer_allocation != VK_NULL_HANDLE)
{
vmaDestroyBuffer(vma_allocator, vertex_buffer, vertex_buffer_allocation);
}

if (vma_allocator != VK_NULL_HANDLE)
{
vmaDestroyAllocator(vma_allocator);
}

if (device)
{
device.destroy();
Expand Down Expand Up @@ -160,6 +170,9 @@ bool HPPHelloTriangle::prepare(const vkb::ApplicationOptions &options)
// get the (graphics) queue
queue = device.getQueue(graphics_queue_index, 0);

vma_allocator = create_vma_allocator();
std::tie(vertex_buffer, vertex_buffer_allocation) = create_vertex_buffer();

init_swapchain();

// Create the necessary objects for rendering.
Expand Down Expand Up @@ -338,11 +351,40 @@ vk::Device HPPHelloTriangle::create_device(const std::vector<const char *> &requ
vk::Pipeline HPPHelloTriangle::create_graphics_pipeline()
{
// Load our SPIR-V shaders.

// Samples support different shading languages, all of which are offline compiled to SPIR-V, the shader format that Vulkan uses.
// The shading language to load for can be selected via command line
std::string shader_folder{""};
switch (get_shading_language())
{
case vkb::ShadingLanguage::HLSL:
shader_folder = "hlsl";
break;
case vkb::ShadingLanguage::SLANG:
shader_folder = "slang";
break;
default:
shader_folder = "glsl";
}

std::vector<vk::PipelineShaderStageCreateInfo> shader_stages{
{.stage = vk::ShaderStageFlagBits::eVertex, .module = create_shader_module("triangle.vert.spv"), .pName = "main"},
{.stage = vk::ShaderStageFlagBits::eFragment, .module = create_shader_module("triangle.frag.spv"), .pName = "main"}};
{.stage = vk::ShaderStageFlagBits::eVertex, .module = create_shader_module("hello_triangle/" + shader_folder + "/triangle.vert.spv"), .pName = "main"},
{.stage = vk::ShaderStageFlagBits::eFragment, .module = create_shader_module("hello_triangle/" + shader_folder + "/triangle.frag.spv"), .pName = "main"}};

// Define the vertex input binding.
vk::VertexInputBindingDescription binding_description{.binding = 0, .stride = sizeof(Vertex), .inputRate = vk::VertexInputRate::eVertex};

vk::PipelineVertexInputStateCreateInfo vertex_input;
// Define the vertex input attribute.
std::array<vk::VertexInputAttributeDescription, 2> attribute_descriptions{
{{.location = 0, .binding = 0, .format = vk::Format::eR32G32B32Sfloat, .offset = offsetof(Vertex, position)},
{.location = 1, .binding = 0, .format = vk::Format::eR32G32B32Sfloat, .offset = offsetof(Vertex, color)}}};

// Define the pipeline vertex input.
vk::PipelineVertexInputStateCreateInfo vertex_input{
.vertexBindingDescriptionCount = 1,
.pVertexBindingDescriptions = &binding_description,
.vertexAttributeDescriptionCount = static_cast<uint32_t>(attribute_descriptions.size()),
.pVertexAttributeDescriptions = attribute_descriptions.data()};

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

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

vk::RenderPass HPPHelloTriangle::create_render_pass()
{
vk::AttachmentDescription attachment{{},
swapchain_data.format, // Backbuffer format
vk::SampleCountFlagBits::e1, // Not multisampled
vk::AttachmentLoadOp::eClear, // When starting the frame, we want tiles to be cleared
vk::AttachmentStoreOp::eStore, // When ending the frame, we want tiles to be written out
vk::AttachmentLoadOp::eDontCare, // Don't care about stencil since we're not using it
vk::AttachmentStoreOp::eDontCare,
vk::ImageLayout::eUndefined, // The image layout will be undefined when the render pass begins
vk::ImageLayout::ePresentSrcKHR}; // After the render pass is complete, we will transition to ePresentSrcKHR layout
vk::AttachmentDescription attachment{
.format = swapchain_data.format, // Backbuffer format.
.samples = vk::SampleCountFlagBits::e1, // Not multisampled.
.loadOp = vk::AttachmentLoadOp::eClear, // When starting the frame, we want tiles to be cleared.
.storeOp = vk::AttachmentStoreOp::eStore, // When ending the frame, we want tiles to be written out.
.stencilLoadOp = vk::AttachmentLoadOp::eDontCare, // Don't care about stencil since we're not using it.
.stencilStoreOp = vk::AttachmentStoreOp::eDontCare, // Don't care about stencil since we're not using it.
.initialLayout = vk::ImageLayout::eUndefined, // The image layout will be undefined when the render pass begins.
.finalLayout = vk::ImageLayout::ePresentSrcKHR // After the render pass is complete, we will transition to PRESENT_SRC_KHR layout.
};

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

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

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

VkShaderModuleCreateInfo module_info{
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
.codeSize = spirv.size() * sizeof(uint32_t),
.pCode = spirv.data()};
vk::ShaderModuleCreateInfo shader_module_create_info{.codeSize = spirv.size() * sizeof(uint32_t), .pCode = spirv.data()};

return device.createShaderModule({.codeSize = static_cast<uint32_t>(spirv.size() * sizeof(uint32_t)), .pCode = spirv.data()});
return device.createShaderModule(shader_module_create_info);
}

vk::SwapchainKHR
Expand Down Expand Up @@ -595,25 +635,80 @@ vk::SwapchainKHR
// FIFO must be supported by all implementations.
vk::PresentModeKHR swapchain_present_mode = vk::PresentModeKHR::eFifo;

vk::SwapchainCreateInfoKHR swapchain_create_info;
swapchain_create_info.surface = surface;
swapchain_create_info.minImageCount = desired_swapchain_images;
swapchain_create_info.imageFormat = surface_format.format;
swapchain_create_info.imageColorSpace = surface_format.colorSpace;
swapchain_create_info.imageExtent.width = swapchain_extent.width;
swapchain_create_info.imageExtent.height = swapchain_extent.height;
swapchain_create_info.imageArrayLayers = 1;
swapchain_create_info.imageUsage = vk::ImageUsageFlagBits::eColorAttachment;
swapchain_create_info.imageSharingMode = vk::SharingMode::eExclusive;
swapchain_create_info.preTransform = pre_transform;
swapchain_create_info.compositeAlpha = composite;
swapchain_create_info.presentMode = swapchain_present_mode;
swapchain_create_info.clipped = true;
swapchain_create_info.oldSwapchain = old_swapchain;
vk::SwapchainCreateInfoKHR swapchain_create_info{
.surface = surface,
.minImageCount = desired_swapchain_images,
.imageFormat = surface_format.format,
.imageColorSpace = surface_format.colorSpace,
.imageExtent = swapchain_extent,
.imageArrayLayers = 1,
.imageUsage = vk::ImageUsageFlagBits::eColorAttachment,
.imageSharingMode = vk::SharingMode::eExclusive,
.preTransform = pre_transform,
.compositeAlpha = composite,
.presentMode = swapchain_present_mode,
.clipped = true,
.oldSwapchain = old_swapchain};

return device.createSwapchainKHR(swapchain_create_info);
}

std::pair<vk::Buffer, VmaAllocation> HPPHelloTriangle::create_vertex_buffer()
{
// Vertex data for a single colored triangle
const std::vector<Vertex> vertices = {
{{0.5f, -0.5f, 0.5f}, {1.0f, 0.0f, 0.0f}},
{{0.5f, 0.5f, 0.5f}, {0.0f, 1.0f, 0.0f}},
{{-0.5f, 0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}}};

const vk::DeviceSize buffer_size = sizeof(vertices[0]) * vertices.size();

// Copy Vertex data to a buffer accessible by the device

vk::BufferCreateInfo buffer_create_info{.size = buffer_size, .usage = vk::BufferUsageFlagBits::eVertexBuffer};

// We use the Vulkan Memory Allocator to find a memory type that can be written and mapped from the host
// On most setups this will return a memory type that resides in VRAM and is accessible from the host
VmaAllocationCreateInfo allocation_create_info{
.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT,
.usage = VMA_MEMORY_USAGE_AUTO,
.requiredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT};

vk::Buffer vertex_buffer;
VmaAllocation vertex_buffer_allocation;
VmaAllocationInfo allocation_info{};
vmaCreateBuffer(vma_allocator, reinterpret_cast<VkBufferCreateInfo *>(&buffer_create_info), &allocation_create_info, reinterpret_cast<VkBuffer *>(&vertex_buffer), &vertex_buffer_allocation, &allocation_info);
if (allocation_info.pMappedData)
{
memcpy(allocation_info.pMappedData, vertices.data(), buffer_size);
}
else
{
throw std::runtime_error("Could not map vertex buffer.");
}

return {vertex_buffer, vertex_buffer_allocation};
}

VmaAllocator HPPHelloTriangle::create_vma_allocator()
{
// This sample uses the Vulkan Memory Alloctor (VMA), which needs to be set up
VmaVulkanFunctions vma_vulkan_functions{
.vkGetInstanceProcAddr = VULKAN_HPP_DEFAULT_DISPATCHER.vkGetInstanceProcAddr,
.vkGetDeviceProcAddr = VULKAN_HPP_DEFAULT_DISPATCHER.vkGetDeviceProcAddr};

VmaAllocatorCreateInfo allocator_info{.physicalDevice = gpu, .device = device, .pVulkanFunctions = &vma_vulkan_functions, .instance = instance};

VmaAllocator allocator;
VkResult result = vmaCreateAllocator(&allocator_info, &allocator);
if (result != VK_SUCCESS)
{
throw std::runtime_error("Could not create allocator for VMA allocator");
}

return allocator;
}

/**
* @brief Initializes the Vulkan framebuffers.
*/
Expand Down Expand Up @@ -732,6 +827,10 @@ void HPPHelloTriangle::render_triangle(uint32_t swapchain_index)
// Set scissor dynamically
cmd.setScissor(0, scissor);

// Bind the vertex buffer to source the draw calls from.
vk::DeviceSize offset = {0};
cmd.bindVertexBuffers(0, vertex_buffer, offset);

// Draw three vertices with one instance.
cmd.draw(3, 1, 0, 0);

Expand Down
66 changes: 39 additions & 27 deletions samples/api/hpp_hello_triangle/hpp_hello_triangle.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ class HPPHelloTriangle : public vkb::Application
vk::Semaphore swapchain_release_semaphore;
};

/// Properties of the vertices used in this sample.
struct Vertex
{
glm::vec3 position;
glm::vec3 color;
};

public:
HPPHelloTriangle();
virtual ~HPPHelloTriangle();
Expand All @@ -61,35 +68,40 @@ class HPPHelloTriangle : public vkb::Application
virtual bool resize(const uint32_t width, const uint32_t height) override;
virtual void update(float delta_time) override;

std::pair<vk::Result, uint32_t> acquire_next_image();
vk::Device create_device(const std::vector<const char *> &required_device_extensions);
vk::Pipeline create_graphics_pipeline();
vk::ImageView create_image_view(vk::Image image);
vk::Instance create_instance(std::vector<const char *> const &required_instance_extensions, std::vector<const char *> const &required_validation_layers);
vk::RenderPass create_render_pass();
vk::ShaderModule create_shader_module(const char *path);
vk::SwapchainKHR create_swapchain(vk::Extent2D const &swapchain_extent, vk::SurfaceFormatKHR surface_format, vk::SwapchainKHR old_swapchain);
void init_framebuffers();
void init_swapchain();
void render_triangle(uint32_t swapchain_index);
void select_physical_device_and_surface();
void teardown_framebuffers();
void teardown_per_frame(FrameData &per_frame_data);
std::pair<vk::Result, uint32_t> acquire_next_image();
vk::Device create_device(const std::vector<const char *> &required_device_extensions);
vk::Pipeline create_graphics_pipeline();
vk::ImageView create_image_view(vk::Image image);
vk::Instance create_instance(std::vector<const char *> const &required_instance_extensions, std::vector<const char *> const &required_validation_layers);
vk::RenderPass create_render_pass();
vk::ShaderModule create_shader_module(std::string const &path);
vk::SwapchainKHR create_swapchain(vk::Extent2D const &swapchain_extent, vk::SurfaceFormatKHR surface_format, vk::SwapchainKHR old_swapchain);
std::pair<vk::Buffer, VmaAllocation> create_vertex_buffer();
VmaAllocator create_vma_allocator();
void init_framebuffers();
void init_swapchain();
void render_triangle(uint32_t swapchain_index);
void select_physical_device_and_surface();
void teardown_framebuffers();
void teardown_per_frame(FrameData &per_frame_data);

private:
vk::Instance instance; // The Vulkan instance.
vk::PhysicalDevice gpu; // The Vulkan physical device.
vk::Device device; // The Vulkan device.
vk::Queue queue; // The Vulkan device queue.
SwapchainData swapchain_data; // The swapchain state.
vk::SurfaceKHR surface; // The surface we will render to.
uint32_t graphics_queue_index; // The queue family index where graphics work will be submitted.
vk::RenderPass render_pass; // The renderpass description.
vk::PipelineLayout pipeline_layout; // The pipeline layout for resources.
vk::Pipeline pipeline; // The graphics pipeline.
vk::DebugUtilsMessengerEXT debug_utils_messenger; // The debug utils messenger.
std::vector<vk::Semaphore> recycled_semaphores; // A set of semaphores that can be reused.
std::vector<FrameData> per_frame_data; // A set of per-frame data.
vk::Instance instance; // The Vulkan instance.
vk::PhysicalDevice gpu; // The Vulkan physical device.
vk::Device device; // The Vulkan device.
vk::Queue queue; // The Vulkan device queue.
SwapchainData swapchain_data; // The swapchain state.
vk::SurfaceKHR surface; // The surface we will render to.
uint32_t graphics_queue_index; // The queue family index where graphics work will be submitted.
vk::RenderPass render_pass; // The renderpass description.
vk::PipelineLayout pipeline_layout; // The pipeline layout for resources.
vk::Pipeline pipeline; // The graphics pipeline.
vk::DebugUtilsMessengerEXT debug_utils_messenger; // The debug utils messenger.
std::vector<vk::Semaphore> recycled_semaphores; // A set of semaphores that can be reused.
std::vector<FrameData> per_frame_data; // A set of per-frame data.
vk::Buffer vertex_buffer; // The Vulkan buffer object that holds the vertex data for the triangle.
VmaAllocation vertex_buffer_allocation = VK_NULL_HANDLE; // Vulkan Memory Allocator (VMA) allocation info for the vertex buffer.
VmaAllocator vma_allocator; // The VMA allocator for memory management.

#if defined(VKB_DEBUG) || defined(VKB_VALIDATION_LAYERS)
vk::DebugUtilsMessengerCreateInfoEXT debug_utils_create_info;
Expand Down
Loading