This commit is contained in:
xsl
2025-09-04 10:54:47 +08:00
commit 6bc8f61b18
1808 changed files with 208268 additions and 0 deletions
@@ -0,0 +1,33 @@
# Copyright (c) 2022-2024, NVIDIA CORPORATION. All rights reserved.
#
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 the "License";
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
get_filename_component(FOLDER_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
get_filename_component(PARENT_DIR ${CMAKE_CURRENT_LIST_DIR} PATH)
get_filename_component(CATEGORY_NAME ${PARENT_DIR} NAME)
add_sample_with_tags(
ID ${FOLDER_NAME}
CATEGORY ${CATEGORY_NAME}
AUTHOR "Sascha Willems"
NAME "HPP Separate image sampler"
DESCRIPTION "Displays a texture with a separated image and sampler"
SHADER_FILES_GLSL
"separate_image_sampler/glsl/separate_image_sampler.vert"
"separate_image_sampler/glsl/separate_image_sampler.frag"
SHADER_FILES_HLSL
"separate_image_sampler/hlsl/separate_image_sampler.vert.hlsl"
"separate_image_sampler/hlsl/separate_image_sampler.frag.hlsl")
@@ -0,0 +1,143 @@
////
- Copyright (c) 2022-2023, The Khronos Group
-
- SPDX-License-Identifier: Apache-2.0
-
- Licensed under the Apache License, Version 2.0 the "License";
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
////
:doctype: book
:pp: {plus}{plus}
= Separating samplers and images with Vulkan-Hpp
ifdef::site-gen-antora[]
TIP: The source for this sample can be found in the https://github.com/KhronosGroup/Vulkan-Samples/tree/main/samples/api/hpp_separate_image_sampler[Khronos Vulkan samples github repository].
endif::[]
NOTE: A transcoded version of the API sample https://github.com/KhronosGroup/Vulkan-Samples/tree/main/samples/api/separate_image_sampler[Separate image sampler] that illustrates the usage of the C{pp} bindings of vulkan provided by vulkan.hpp.
This tutorial, along with the accompanying example code, shows how to separate samplers and images in a Vulkan application.
Opposite to combined image and samplers, this allows the application to freely mix an arbitrary set of samplers and images in the shader.
In the sample code, a single image and multiple samplers with different options will be created.
The sampler to be used for sampling the image can then be selected at runtime.
As image and sampler objects are separated, this only requires selecting a different descriptor at runtime.
== In the application
From the application's point of view, images and samplers are always created separately.
Access to the image is done via the image's `vk::ImageView`.
Samplers are created using a `vk::Sampler` object, specifying how an image will be sampled.
The difference between separating and combining them starts at the descriptor level, which defines how the shader accesses the samplers and images.
A separate setup uses a descriptor of type `vk::DescriptorType::eSampledImage` for the sampled image, and a `vk::DescriptorType::eSampler` for the sampler, separating the image and sampler object:
// {% raw %}
[,cpp]
----
// Image info only references the image
vk::DescriptorImageInfo image_info({}, texture.image->get_vk_image_view().get_handle(), vk::ImageLayout::eShaderReadOnlyOptimal);
// Sampled image descriptor
vk::WriteDescriptorSet image_write_descriptor_set(base_descriptor_set, 1, 0, vk::DescriptorType::eSampledImage, image_info);
// One set for the sampled image
std::array<vk::WriteDescriptorSet, 2> write_descriptor_sets = {{
{base_descriptor_set, 0, 0, vk::DescriptorType::eUniformBuffer, {}, buffer_descriptor}, // Binding 0 : Vertex shader uniform buffer
image_write_descriptor_set // Binding 1 : Fragment shader sampled image
}};
get_device()->get_handle().updateDescriptorSets(write_descriptor_sets, {});
----
// {% endraw %}
For this sample, we then create two samplers with different filtering options:
[,cpp]
----
// Sets for each of the sampler
descriptor_set_alloc_info.pSetLayouts = &sampler_descriptor_set_layout;
for (size_t i = 0; i < sampler_descriptor_sets.size(); i++)
{
sampler_descriptor_sets[i] = get_device()->get_handle().allocateDescriptorSets(descriptor_set_alloc_info).front();
// Descriptor info only references the sampler
vk::DescriptorImageInfo sampler_info(samplers[i]);
vk::WriteDescriptorSet sampler_write_descriptor_set(sampler_descriptor_sets[i], 0, 0, vk::DescriptorType::eSampler, sampler_info);
get_device()->get_handle().updateDescriptorSets(sampler_write_descriptor_set, {});
}
----
At draw-time, the descriptor containing the sampled image is bound to set 0 and the descriptor for the currently selected sampler is bound to set 1:
[,cpp]
----
// Bind the uniform buffer and sampled image to set 0
draw_cmd_buffers[i].bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline_layout, 0, base_descriptor_set, {});
// Bind the selected sampler to set 1
draw_cmd_buffers[i].bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline_layout, 1, sampler_descriptor_sets[selected_sampler], {});
...
draw_cmd_buffers[i].drawIndexed(index_count, 1, 0, 0, 0);
----
== In the shader
There are no changes in the shader code to get it working with vulkan.hpp.
With the above setup, the shader interface for the fragment shader also separates the sampler and image as two distinct uniforms:
[,glsl]
----
layout (set = 0, binding = 1) uniform texture2D _texture;
layout (set = 1, binding = 0) uniform sampler _sampler;
----
To sample from the image referenced by `_texture`, with the currently set sampler in '_sampler', we create a sampled image in the fragment shader at runtime using the `sampler2D` function.
[,glsl]
----
void main()
{
vec4 color = texture(sampler2D(_texture, _sampler), inUV);
}
----
== Comparison with combined image samplers
For reference, a combined image and sampler setup would differ for both the application and the shader.
The app would use a single descriptor of type `VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER`, and set both image and sampler related values in the descriptor:
[,cpp]
----
// Descriptor info references image and sampler
vk::DescriptorImageInfo image_info(texture.sampler, texture.view, texture.image_layout);
vk::WriteDescriptorSet image_write_descriptor_set(descriptor_set, 1, {}, vk::DescriptorType::eCombinedImageSampler, image_info);
----
The shader interface only uses one uniform for accessing the combined image and sampler and also doesn't construct a `sampler2D` at runtime:
[,glsl]
----
layout (binding = 1) uniform sampler2D _combined_image;
void main()
{
vec4 color = texture(_combined_image, inUV);
}
----
Compared to the separated setup, changing a sampler in this setup would either require creating multiple descriptors with each image/sampler combination or rebuilding the descriptor.
@@ -0,0 +1,393 @@
/* Copyright (c) 2022-2025, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Separate samplers and image to draw a single image with different sampling options, using vulkan.hpp
*/
#include "hpp_separate_image_sampler.h"
HPPSeparateImageSampler::HPPSeparateImageSampler()
{
title = "HPP Separate sampler and image";
zoom = -0.5f;
rotation = {45.0f, 0.0f, 0.0f};
}
HPPSeparateImageSampler::~HPPSeparateImageSampler()
{
if (has_device() && get_device().get_handle())
{
vk::Device device = get_device().get_handle();
// Clean up used Vulkan resources
// Note : Inherited destructor cleans up resources stored in base class
device.destroyPipeline(pipeline);
device.destroyPipelineLayout(pipeline_layout);
device.destroyDescriptorSetLayout(base_descriptor_set_layout);
device.destroyDescriptorSetLayout(sampler_descriptor_set_layout);
for (vk::Sampler sampler : samplers)
{
device.destroySampler(sampler);
}
// Delete the implicitly created sampler for the texture loaded via the framework
device.destroySampler(texture.sampler);
}
}
bool HPPSeparateImageSampler::prepare(const vkb::ApplicationOptions &options)
{
assert(!prepared);
if (HPPApiVulkanSample::prepare(options))
{
load_assets();
generate_quad();
prepare_uniform_buffers();
// Create two samplers with different options, first one with linear filtering, the second one with nearest filtering
samplers = {{create_sampler(vk::Filter::eLinear), create_sampler(vk::Filter::eNearest)}};
descriptor_pool = create_descriptor_pool();
// We separate the descriptor sets for the uniform buffer + image and samplers, so we don't need to duplicate the descriptors for the former
// Descriptors set for the uniform buffer and the image
base_descriptor_set_layout = create_base_descriptor_set_layout();
base_descriptor_set = vkb::common::allocate_descriptor_set(get_device().get_handle(), descriptor_pool, base_descriptor_set_layout);
update_base_descriptor_set();
// Sets for each of the sampler
sampler_descriptor_set_layout = create_sampler_descriptor_set_layout();
for (size_t i = 0; i < sampler_descriptor_sets.size(); i++)
{
sampler_descriptor_sets[i] = vkb::common::allocate_descriptor_set(get_device().get_handle(), descriptor_pool, sampler_descriptor_set_layout);
update_sampler_descriptor_set(i);
}
// Pipeline layout
// Set layout for the base descriptors in set 0 and set layout for the sampler descriptors in set 1
pipeline_layout = create_pipeline_layout({base_descriptor_set_layout, sampler_descriptor_set_layout});
pipeline = create_graphics_pipeline();
build_command_buffers();
prepared = true;
}
return prepared;
}
// Enable physical device features required for this example
void HPPSeparateImageSampler::request_gpu_features(vkb::core::HPPPhysicalDevice &gpu)
{
// Enable anisotropic filtering if supported
if (gpu.get_features().samplerAnisotropy)
{
gpu.get_mutable_requested_features().samplerAnisotropy = true;
}
}
void HPPSeparateImageSampler::build_command_buffers()
{
vk::CommandBufferBeginInfo command_buffer_begin_info;
std::array<vk::ClearValue, 2> clear_values = {{default_clear_color, vk::ClearDepthStencilValue{0.0f, 0}}};
vk::RenderPassBeginInfo render_pass_begin_info{.renderPass = render_pass,
.renderArea = {{0, 0}, extent},
.clearValueCount = static_cast<uint32_t>(clear_values.size()),
.pClearValues = clear_values.data()};
for (int32_t i = 0; i < draw_cmd_buffers.size(); ++i)
{
// Set target frame buffer
render_pass_begin_info.framebuffer = framebuffers[i];
auto command_buffer = draw_cmd_buffers[i];
command_buffer.begin(command_buffer_begin_info);
command_buffer.beginRenderPass(render_pass_begin_info, vk::SubpassContents::eInline);
vk::Viewport viewport{0.0f, 0.0f, static_cast<float>(extent.width), static_cast<float>(extent.height), 0.0f, 1.0f};
command_buffer.setViewport(0, viewport);
vk::Rect2D scissor{{0, 0}, extent};
command_buffer.setScissor(0, scissor);
// Bind the uniform buffer and sampled image to set 0
command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline_layout, 0, base_descriptor_set, {});
// Bind the selected sampler to set 1
command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline_layout, 1, sampler_descriptor_sets[selected_sampler], {});
command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline);
vk::DeviceSize offset = 0;
command_buffer.bindVertexBuffers(0, vertex_buffer->get_handle(), offset);
command_buffer.bindIndexBuffer(index_buffer->get_handle(), 0, vk::IndexType::eUint32);
command_buffer.drawIndexed(index_count, 1, 0, 0, 0);
draw_ui(command_buffer);
command_buffer.endRenderPass();
command_buffer.end();
}
}
void HPPSeparateImageSampler::on_update_ui_overlay(vkb::Drawer &drawer)
{
if (drawer.header("Settings"))
{
const std::vector<std::string> sampler_names = {"Linear filtering", "Nearest filtering"};
if (drawer.combo_box("Sampler", &selected_sampler, sampler_names))
{
update_uniform_buffers();
}
}
}
void HPPSeparateImageSampler::render(float delta_time)
{
if (prepared)
{
draw();
}
}
void HPPSeparateImageSampler::view_changed()
{
update_uniform_buffers();
}
vk::DescriptorSetLayout HPPSeparateImageSampler::create_base_descriptor_set_layout()
{
// Set layout for the uniform buffer and the image
std::array<vk::DescriptorSetLayoutBinding, 2> set_layout_bindings_buffer_and_image = {{
{0, vk::DescriptorType::eUniformBuffer, 1, vk::ShaderStageFlagBits::eVertex}, // Binding 0 : Vertex shader uniform buffer
{1, vk::DescriptorType::eSampledImage, 1, vk::ShaderStageFlagBits::eFragment} // Binding 1 : Fragment shader sampled image
}};
vk::DescriptorSetLayoutCreateInfo descriptor_layout_create_info{.bindingCount = static_cast<uint32_t>(set_layout_bindings_buffer_and_image.size()),
.pBindings = set_layout_bindings_buffer_and_image.data()};
return get_device().get_handle().createDescriptorSetLayout(descriptor_layout_create_info);
}
vk::DescriptorPool HPPSeparateImageSampler::create_descriptor_pool()
{
std::array<vk::DescriptorPoolSize, 3> pool_sizes = {
{{vk::DescriptorType::eUniformBuffer, 1}, {vk::DescriptorType::eSampledImage, 1}, {vk::DescriptorType::eSampler, 2}}};
vk::DescriptorPoolCreateInfo descriptor_pool_create_info{.maxSets = 3,
.poolSizeCount = static_cast<uint32_t>(pool_sizes.size()),
.pPoolSizes = pool_sizes.data()};
return get_device().get_handle().createDescriptorPool(descriptor_pool_create_info);
}
vk::Pipeline HPPSeparateImageSampler::create_graphics_pipeline()
{
// Load shaders
std::vector<vk::PipelineShaderStageCreateInfo> shader_stages = {
load_shader("separate_image_sampler", "separate_image_sampler.vert.spv", vk::ShaderStageFlagBits::eVertex),
load_shader("separate_image_sampler", "separate_image_sampler.frag.spv", vk::ShaderStageFlagBits::eFragment)};
// Vertex bindings and attributes
vk::VertexInputBindingDescription input_binding{0, sizeof(VertexStructure), vk::VertexInputRate::eVertex};
std::array<vk::VertexInputAttributeDescription, 3> input_attributes = {
{{0, 0, vk::Format::eR32G32B32Sfloat, offsetof(VertexStructure, pos)}, // Location 0 : Position
{1, 0, vk::Format::eR32G32Sfloat, offsetof(VertexStructure, uv)}, // Location 1 : Texture Coordinates
{2, 0, vk::Format::eR32G32B32Sfloat, offsetof(VertexStructure, normal)}}}; // Location 2 : Normal
vk::PipelineVertexInputStateCreateInfo input_state{.vertexBindingDescriptionCount = 1,
.pVertexBindingDescriptions = &input_binding,
.vertexAttributeDescriptionCount = static_cast<uint32_t>(input_attributes.size()),
.pVertexAttributeDescriptions = input_attributes.data()};
vk::PipelineColorBlendAttachmentState blend_attachment_state{.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG |
vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA};
// Note: Using reversed depth-buffer for increased precision, so Greater depth values are kept
vk::PipelineDepthStencilStateCreateInfo depth_stencil_state;
depth_stencil_state.depthTestEnable = true;
depth_stencil_state.depthWriteEnable = true;
depth_stencil_state.depthCompareOp = vk::CompareOp::eGreater;
depth_stencil_state.back.compareOp = vk::CompareOp::eGreater;
return vkb::common::create_graphics_pipeline(get_device().get_handle(),
pipeline_cache,
shader_stages,
input_state,
vk::PrimitiveTopology::eTriangleList,
0,
vk::PolygonMode::eFill,
vk::CullModeFlagBits::eNone,
vk::FrontFace::eCounterClockwise,
{blend_attachment_state},
depth_stencil_state,
pipeline_layout,
render_pass);
}
vk::PipelineLayout HPPSeparateImageSampler::create_pipeline_layout(std::vector<vk::DescriptorSetLayout> const &descriptor_set_layouts)
{
vk::PipelineLayoutCreateInfo pipeline_layout_create_info{.setLayoutCount = static_cast<uint32_t>(descriptor_set_layouts.size()),
.pSetLayouts = descriptor_set_layouts.data()};
return get_device().get_handle().createPipelineLayout(pipeline_layout_create_info);
}
vk::Sampler HPPSeparateImageSampler::create_sampler(vk::Filter filter)
{
return vkb::common::create_sampler(
get_device().get_gpu().get_handle(),
get_device().get_handle(),
texture.image->get_format(),
filter,
vk::SamplerAddressMode::eRepeat,
get_device().get_gpu().get_features().samplerAnisotropy ? (get_device().get_gpu().get_properties().limits.maxSamplerAnisotropy) : 1.0f,
static_cast<float>(texture.image->get_mipmaps().size()));
}
vk::DescriptorSetLayout HPPSeparateImageSampler::create_sampler_descriptor_set_layout()
{
// Set layout for the samplers
vk::DescriptorSetLayoutBinding set_layout_binding_sampler{0, vk::DescriptorType::eSampler, 1, vk::ShaderStageFlagBits::eFragment};
vk::DescriptorSetLayoutCreateInfo descriptor_layout_create_info{.bindingCount = 1, .pBindings = &set_layout_binding_sampler};
return get_device().get_handle().createDescriptorSetLayout(descriptor_layout_create_info);
}
void HPPSeparateImageSampler::draw()
{
HPPApiVulkanSample::prepare_frame();
// Command buffer to be submitted to the queue
submit_info.setCommandBuffers(draw_cmd_buffers[current_buffer]);
// Submit to queue
queue.submit(submit_info);
HPPApiVulkanSample::submit_frame();
}
void HPPSeparateImageSampler::generate_quad()
{
// Setup vertices for a single uv-mapped quad made from two triangles
std::vector<VertexStructure> vertices =
{
{{1.0f, 1.0f, 0.0f}, {1.0f, 1.0f}, {0.0f, 0.0f, 1.0f}},
{{-1.0f, 1.0f, 0.0f}, {0.0f, 1.0f}, {0.0f, 0.0f, 1.0f}},
{{-1.0f, -1.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f, 1.0f}},
{{1.0f, -1.0f, 0.0f}, {1.0f, 0.0f}, {0.0f, 0.0f, 1.0f}}};
// Setup indices
std::vector<uint32_t> indices = {0, 1, 2, 2, 3, 0};
index_count = static_cast<uint32_t>(indices.size());
auto vertex_buffer_size = vkb::to_u32(vertices.size() * sizeof(VertexStructure));
auto index_buffer_size = vkb::to_u32(indices.size() * sizeof(uint32_t));
// Create buffers
// For the sake of simplicity we won't stage the vertex data to the gpu memory
// Vertex buffer
vertex_buffer = std::make_unique<vkb::core::BufferCpp>(get_device(),
vertex_buffer_size,
vk::BufferUsageFlagBits::eTransferDst | vk::BufferUsageFlagBits::eVertexBuffer,
VMA_MEMORY_USAGE_CPU_TO_GPU);
vertex_buffer->update(vertices.data(), vertex_buffer_size);
index_buffer = std::make_unique<vkb::core::BufferCpp>(get_device(),
index_buffer_size,
vk::BufferUsageFlagBits::eTransferDst | vk::BufferUsageFlagBits::eIndexBuffer,
VMA_MEMORY_USAGE_CPU_TO_GPU);
index_buffer->update(indices.data(), index_buffer_size);
}
void HPPSeparateImageSampler::load_assets()
{
texture = load_texture("textures/metalplate01_rgba.ktx", vkb::scene_graph::components::HPPImage::Color);
}
// Prepare and initialize uniform buffer containing shader uniforms
void HPPSeparateImageSampler::prepare_uniform_buffers()
{
// Vertex shader uniform buffer block
uniform_buffer_vs =
std::make_unique<vkb::core::BufferCpp>(get_device(), sizeof(ubo_vs), vk::BufferUsageFlagBits::eUniformBuffer, VMA_MEMORY_USAGE_CPU_TO_GPU);
update_uniform_buffers();
}
void HPPSeparateImageSampler::update_base_descriptor_set()
{
vk::DescriptorBufferInfo buffer_descriptor{uniform_buffer_vs->get_handle(), 0, vk::WholeSize};
// Image info only references the image
vk::DescriptorImageInfo image_info{{}, texture.image->get_vk_image_view().get_handle(), vk::ImageLayout::eShaderReadOnlyOptimal};
// Sampled image descriptor
std::array<vk::WriteDescriptorSet, 2> write_descriptor_sets = {
{
{.dstSet = base_descriptor_set,
.dstBinding = 0,
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eUniformBuffer,
.pBufferInfo = &buffer_descriptor}, // Binding 0 : Vertex shader uniform buffer
{.dstSet = base_descriptor_set,
.dstBinding = 1,
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eSampledImage,
.pImageInfo = &image_info} // Binding 1 : Fragment shader sampled image
}};
get_device().get_handle().updateDescriptorSets(write_descriptor_sets, {});
}
void HPPSeparateImageSampler::update_sampler_descriptor_set(size_t index)
{
assert((index < samplers.size()) && (index < sampler_descriptor_sets.size()));
// Descriptor info only references the sampler
vk::DescriptorImageInfo sampler_info{samplers[index]};
vk::WriteDescriptorSet sampler_write_descriptor_set{
.dstSet = sampler_descriptor_sets[index], .dstBinding = 0, .descriptorCount = 1, .descriptorType = vk::DescriptorType::eSampler, .pImageInfo = &sampler_info};
get_device().get_handle().updateDescriptorSets(sampler_write_descriptor_set, {});
}
void HPPSeparateImageSampler::update_uniform_buffers()
{
// Vertex shader
ubo_vs.projection = glm::perspective(glm::radians(60.0f), static_cast<float>(extent.width) / static_cast<float>(extent.height), 0.001f, 256.0f);
glm::mat4 view_matrix = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, zoom));
ubo_vs.model = view_matrix * glm::translate(glm::mat4(1.0f), camera_pos);
ubo_vs.model = glm::rotate(ubo_vs.model, glm::radians(rotation.x), glm::vec3(1.0f, 0.0f, 0.0f));
ubo_vs.model = glm::rotate(ubo_vs.model, glm::radians(rotation.y), glm::vec3(0.0f, 1.0f, 0.0f));
ubo_vs.model = glm::rotate(ubo_vs.model, glm::radians(rotation.z), glm::vec3(0.0f, 0.0f, 1.0f));
ubo_vs.view_pos = glm::vec4(0.0f, 0.0f, -zoom, 0.0f);
uniform_buffer_vs->convert_and_update(ubo_vs);
}
std::unique_ptr<vkb::Application> create_hpp_separate_image_sampler()
{
return std::make_unique<HPPSeparateImageSampler>();
}
@@ -0,0 +1,92 @@
/* Copyright (c) 2023-2024, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Separate samplers and image to draw a single image with different sampling options, using vulkan.hpp
*/
#pragma once
#include <hpp_api_vulkan_sample.h>
class HPPSeparateImageSampler : public HPPApiVulkanSample
{
public:
HPPSeparateImageSampler();
~HPPSeparateImageSampler() override;
private:
// Vertex layout for this example
struct VertexStructure
{
float pos[3];
float uv[2];
float normal[3];
};
struct UBO
{
glm::mat4 projection;
glm::mat4 model;
glm::vec4 view_pos;
};
private:
// from vkb::Application
bool prepare(const vkb::ApplicationOptions &options) override;
// from vkb::VulkanSample
void request_gpu_features(vkb::core::HPPPhysicalDevice &gpu) override;
// from HPPApiVulkanSample
void build_command_buffers() override;
void on_update_ui_overlay(vkb::Drawer &drawer) override;
void render(float delta_time) override;
void view_changed() override;
vk::DescriptorSetLayout create_base_descriptor_set_layout();
vk::DescriptorPool create_descriptor_pool();
vk::Pipeline create_graphics_pipeline();
vk::PipelineLayout create_pipeline_layout(std::vector<vk::DescriptorSetLayout> const &descriptor_set_layouts);
vk::Sampler create_sampler(vk::Filter filter);
vk::DescriptorSetLayout create_sampler_descriptor_set_layout();
void draw();
void generate_quad();
void load_assets();
void prepare_uniform_buffers();
void update_base_descriptor_set();
void update_sampler_descriptor_set(size_t index);
void update_uniform_buffers();
private:
vk::DescriptorSet base_descriptor_set;
vk::DescriptorSetLayout base_descriptor_set_layout;
std::unique_ptr<vkb::core::BufferCpp> index_buffer;
uint32_t index_count = 0;
vk::Pipeline pipeline;
vk::PipelineLayout pipeline_layout;
vk::DescriptorSetLayout sampler_descriptor_set_layout;
std::array<vk::DescriptorSet, 2> sampler_descriptor_sets;
std::array<vk::Sampler, 2> samplers;
int32_t selected_sampler = 0;
HPPTexture texture;
UBO ubo_vs;
std::unique_ptr<vkb::core::BufferCpp> uniform_buffer_vs;
std::unique_ptr<vkb::core::BufferCpp> vertex_buffer;
};
std::unique_ptr<vkb::Application> create_hpp_separate_image_sampler();