539 lines
27 KiB
C++
539 lines
27 KiB
C++
/* 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.
|
|
*/
|
|
|
|
/*
|
|
* Instanced mesh rendering, uses a separate vertex buffer for instanced data, using vulkan.hpp
|
|
*/
|
|
|
|
#include "hpp_instancing.h"
|
|
|
|
#include <benchmark_mode/benchmark_mode.h>
|
|
#include <random>
|
|
|
|
HPPInstancing::HPPInstancing()
|
|
{
|
|
title = "HPP instanced mesh rendering";
|
|
}
|
|
|
|
HPPInstancing::~HPPInstancing()
|
|
{
|
|
if (has_device() && get_device().get_handle())
|
|
{
|
|
vk::Device device = get_device().get_handle();
|
|
|
|
planet.destroy(device);
|
|
rocks.destroy(device);
|
|
device.destroyPipeline(starfield_pipeline);
|
|
device.destroyPipelineLayout(pipeline_layout);
|
|
device.destroyDescriptorSetLayout(descriptor_set_layout);
|
|
}
|
|
}
|
|
|
|
bool HPPInstancing::prepare(const vkb::ApplicationOptions &options)
|
|
{
|
|
assert(!prepared);
|
|
|
|
if (HPPApiVulkanSample::prepare(options))
|
|
{
|
|
initialize_camera();
|
|
load_assets();
|
|
prepare_instance_data();
|
|
prepare_uniform_buffers();
|
|
vk::Device device = get_device().get_handle();
|
|
descriptor_set_layout = create_descriptor_set_layout();
|
|
pipeline_layout = device.createPipelineLayout({.setLayoutCount = 1, .pSetLayouts = &descriptor_set_layout});
|
|
descriptor_pool = create_descriptor_pool();
|
|
|
|
// setup planet
|
|
planet.pipeline = create_planet_pipeline();
|
|
planet.descriptor_set = vkb::common::allocate_descriptor_set(device, descriptor_pool, descriptor_set_layout);
|
|
update_planet_descriptor_set();
|
|
|
|
// setup rocks
|
|
rocks.pipeline = create_rocks_pipeline();
|
|
rocks.descriptor_set = vkb::common::allocate_descriptor_set(device, descriptor_pool, descriptor_set_layout);
|
|
update_rocks_descriptor_set();
|
|
|
|
// setup starfield
|
|
starfield_pipeline = create_starfield_pipeline();
|
|
|
|
build_command_buffers();
|
|
|
|
prepared = true;
|
|
}
|
|
|
|
return prepared;
|
|
}
|
|
|
|
bool HPPInstancing::resize(const uint32_t width, const uint32_t height)
|
|
{
|
|
HPPApiVulkanSample::resize(width, height);
|
|
rebuild_command_buffers();
|
|
return true;
|
|
}
|
|
|
|
void HPPInstancing::request_gpu_features(vkb::core::HPPPhysicalDevice &gpu)
|
|
{
|
|
auto &requested_features = gpu.get_mutable_requested_features();
|
|
auto const &features = gpu.get_features();
|
|
|
|
// Enable anisotropic filtering if supported
|
|
if (features.samplerAnisotropy)
|
|
{
|
|
requested_features.samplerAnisotropy = true;
|
|
}
|
|
// Enable texture compression
|
|
if (features.textureCompressionBC)
|
|
{
|
|
requested_features.textureCompressionBC = true;
|
|
}
|
|
else if (features.textureCompressionASTC_LDR)
|
|
{
|
|
requested_features.textureCompressionASTC_LDR = true;
|
|
}
|
|
else if (features.textureCompressionETC2)
|
|
{
|
|
requested_features.textureCompressionETC2 = true;
|
|
}
|
|
};
|
|
|
|
void HPPInstancing::build_command_buffers()
|
|
{
|
|
vk::CommandBufferBeginInfo command_buffer_begin_info;
|
|
|
|
std::array<vk::ClearValue, 2> clear_values =
|
|
{{vk::ClearColorValue(std::array<float, 4>({{0.0f, 0.0f, 0.033f, 0.0f}})),
|
|
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);
|
|
|
|
vk::DeviceSize offset = 0;
|
|
|
|
// Star field
|
|
// the star field uses the same descriptor_set as planet !
|
|
command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline_layout, 0, planet.descriptor_set, {});
|
|
command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, starfield_pipeline);
|
|
command_buffer.draw(4, 1, 0, 0);
|
|
|
|
// Planet
|
|
command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline_layout, 0, planet.descriptor_set, {});
|
|
command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, planet.pipeline);
|
|
command_buffer.bindVertexBuffers(0, planet.mesh->get_vertex_buffer("vertex_buffer").get_handle(), offset);
|
|
command_buffer.bindIndexBuffer(planet.mesh->get_index_buffer().get_handle(), 0, vk::IndexType::eUint32);
|
|
command_buffer.drawIndexed(planet.mesh->vertex_indices, 1, 0, 0, 0);
|
|
|
|
// Instanced rocks
|
|
command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline_layout, 0, rocks.descriptor_set, {});
|
|
command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, rocks.pipeline);
|
|
// Binding point 0 : Mesh vertex buffer
|
|
command_buffer.bindVertexBuffers(0, rocks.mesh->get_vertex_buffer("vertex_buffer").get_handle(), offset);
|
|
// Binding point 1 : Instance data buffer
|
|
command_buffer.bindVertexBuffers(1, instance_buffer.buffer->get_handle(), offset);
|
|
command_buffer.bindIndexBuffer(rocks.mesh->get_index_buffer().get_handle(), 0, vk::IndexType::eUint32);
|
|
// Render instances
|
|
command_buffer.drawIndexed(rocks.mesh->vertex_indices, INSTANCE_COUNT, 0, 0, 0);
|
|
|
|
draw_ui(command_buffer);
|
|
|
|
command_buffer.endRenderPass();
|
|
|
|
command_buffer.end();
|
|
}
|
|
}
|
|
|
|
void HPPInstancing::on_update_ui_overlay(vkb::Drawer &drawer)
|
|
{
|
|
if (drawer.header("Statistics"))
|
|
{
|
|
drawer.text("Instances: %d", INSTANCE_COUNT);
|
|
}
|
|
}
|
|
|
|
void HPPInstancing::render(float delta_time)
|
|
{
|
|
if (prepared)
|
|
{
|
|
draw();
|
|
if (!paused || camera.updated)
|
|
{
|
|
update_uniform_buffer(delta_time);
|
|
}
|
|
}
|
|
}
|
|
|
|
vk::DescriptorPool HPPInstancing::create_descriptor_pool()
|
|
{
|
|
// Example uses one ubo
|
|
std::array<vk::DescriptorPoolSize, 2> pool_sizes = {{{vk::DescriptorType::eUniformBuffer, 2}, {vk::DescriptorType::eCombinedImageSampler, 2}}};
|
|
vk::DescriptorPoolCreateInfo descriptor_pool_create_info{.maxSets = 2,
|
|
.poolSizeCount = static_cast<uint32_t>(pool_sizes.size()),
|
|
.pPoolSizes = pool_sizes.data()};
|
|
|
|
return get_device().get_handle().createDescriptorPool(descriptor_pool_create_info);
|
|
}
|
|
|
|
vk::DescriptorSetLayout HPPInstancing::create_descriptor_set_layout()
|
|
{
|
|
std::array<vk::DescriptorSetLayoutBinding, 2> set_layout_bindings = {
|
|
{{0, vk::DescriptorType::eUniformBuffer, 1, vk::ShaderStageFlagBits::eVertex}, // Binding 0 : Vertex shader uniform buffer
|
|
{1, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment}}}; // Binding 1 : Fragment shader combined sampler
|
|
|
|
return get_device().get_handle().createDescriptorSetLayout(
|
|
{.bindingCount = static_cast<uint32_t>(set_layout_bindings.size()), .pBindings = set_layout_bindings.data()});
|
|
}
|
|
|
|
vk::Pipeline HPPInstancing::create_planet_pipeline()
|
|
{
|
|
// Planet rendering pipeline
|
|
std::vector<vk::PipelineShaderStageCreateInfo> shader_stages = {load_shader("instancing", "planet.vert.spv", vk::ShaderStageFlagBits::eVertex),
|
|
load_shader("instancing", "planet.frag.spv", vk::ShaderStageFlagBits::eFragment)};
|
|
|
|
// Vertex input bindings
|
|
vk::VertexInputBindingDescription binding_description{0, sizeof(HPPVertex), vk::VertexInputRate::eVertex};
|
|
|
|
// Vertex attribute bindings
|
|
std::array<vk::VertexInputAttributeDescription, 3> attribute_descriptions = {
|
|
{ // Per-vertex attributes
|
|
// These are advanced for each vertex fetched by the vertex shader
|
|
{0, 0, vk::Format::eR32G32B32Sfloat, 0}, // Location 0: Position
|
|
{1, 0, vk::Format::eR32G32B32Sfloat, 3 * sizeof(float)}, // Location 1: Normal
|
|
{2, 0, vk::Format::eR32G32Sfloat, 6 * sizeof(float)}}}; // Location 2: Texture coordinates
|
|
|
|
// Use all input bindings and attribute descriptions
|
|
vk::PipelineVertexInputStateCreateInfo input_state{.vertexBindingDescriptionCount = 1,
|
|
.pVertexBindingDescriptions = &binding_description,
|
|
.vertexAttributeDescriptionCount = static_cast<uint32_t>(attribute_descriptions.size()),
|
|
.pVertexAttributeDescriptions = attribute_descriptions.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.depthCompareOp = vk::CompareOp::eGreater;
|
|
depth_stencil_state.depthTestEnable = true;
|
|
depth_stencil_state.depthWriteEnable = true;
|
|
depth_stencil_state.back.compareOp = vk::CompareOp::eAlways;
|
|
depth_stencil_state.front = depth_stencil_state.back;
|
|
|
|
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::eBack,
|
|
vk::FrontFace::eClockwise,
|
|
{blend_attachment_state},
|
|
depth_stencil_state,
|
|
pipeline_layout,
|
|
render_pass);
|
|
}
|
|
|
|
vk::Pipeline HPPInstancing::create_rocks_pipeline()
|
|
{
|
|
std::vector<vk::PipelineShaderStageCreateInfo> shader_stages{load_shader("instancing", "instancing.vert.spv", vk::ShaderStageFlagBits::eVertex),
|
|
load_shader("instancing", "instancing.frag.spv", vk::ShaderStageFlagBits::eFragment)};
|
|
|
|
// Vertex input bindings
|
|
// The instancing pipeline uses a vertex input state with two bindings
|
|
std::array<vk::VertexInputBindingDescription, 2> binding_descriptions = {
|
|
{{0, sizeof(HPPVertex), vk::VertexInputRate::eVertex}, // Binding point 0: Mesh vertex layout description at per-vertex rate
|
|
{1, sizeof(InstanceData), vk::VertexInputRate::eInstance}}}; // Binding point 1: Instanced data at per-instance rate
|
|
|
|
// Vertex attribute bindings
|
|
// Note that the shader declaration for per-vertex and per-instance attributes is the same, the different input rates are only stored in the bindings:
|
|
// instanced.vert:
|
|
// layout (location = 0) in vec3 inPos; Per-Vertex
|
|
// ...
|
|
// layout (location = 4) in vec3 instancePos; Per-Instance
|
|
std::array<vk::VertexInputAttributeDescription, 7> attribute_descriptions = {
|
|
{ // Per-vertex attributes
|
|
// These are advanced for each vertex fetched by the vertex shader
|
|
{0, 0, vk::Format::eR32G32B32Sfloat, 0}, // Location 0: Position
|
|
{1, 0, vk::Format::eR32G32B32Sfloat, 3 * sizeof(float)}, // Location 1: Normal
|
|
{2, 0, vk::Format::eR32G32Sfloat, 6 * sizeof(float)}, // Location 2: Texture coordinates
|
|
// Per-Instance attributes
|
|
// These are fetched for each instance rendered
|
|
{3, 1, vk::Format::eR32G32B32Sfloat, 0}, // Location 3: Position
|
|
{4, 1, vk::Format::eR32G32B32Sfloat, 3 * sizeof(float)}, // Location 4: Rotation
|
|
{5, 1, vk::Format::eR32Sfloat, 6 * sizeof(float)}, // Location 5: Scale
|
|
{6, 1, vk::Format::eR32Sint, 7 * sizeof(float)}}}; // Location 6: Texture array layer index
|
|
|
|
// Use all input bindings and attribute descriptions
|
|
vk::PipelineVertexInputStateCreateInfo input_state{.vertexBindingDescriptionCount = static_cast<uint32_t>(binding_descriptions.size()),
|
|
.pVertexBindingDescriptions = binding_descriptions.data(),
|
|
.vertexAttributeDescriptionCount = static_cast<uint32_t>(attribute_descriptions.size()),
|
|
.pVertexAttributeDescriptions = attribute_descriptions.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.depthCompareOp = vk::CompareOp::eGreater;
|
|
depth_stencil_state.depthTestEnable = true;
|
|
depth_stencil_state.depthWriteEnable = true;
|
|
depth_stencil_state.back.compareOp = vk::CompareOp::eAlways;
|
|
depth_stencil_state.front = depth_stencil_state.back;
|
|
|
|
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::eBack,
|
|
vk::FrontFace::eClockwise,
|
|
{blend_attachment_state},
|
|
depth_stencil_state,
|
|
pipeline_layout,
|
|
render_pass);
|
|
}
|
|
|
|
vk::Pipeline HPPInstancing::create_starfield_pipeline()
|
|
{
|
|
// Starfield rendering pipeline
|
|
std::vector<vk::PipelineShaderStageCreateInfo> shader_stages = {load_shader("instancing", "starfield.vert.spv", vk::ShaderStageFlagBits::eVertex),
|
|
load_shader("instancing", "starfield.frag.spv", vk::ShaderStageFlagBits::eFragment)};
|
|
|
|
// Vertex input bindings
|
|
vk::VertexInputBindingDescription binding_description{0, sizeof(HPPVertex), vk::VertexInputRate::eVertex};
|
|
|
|
// Vertex attribute bindings
|
|
std::array<vk::VertexInputAttributeDescription, 3> attribute_descriptions = {
|
|
{ // Per-vertex attributes
|
|
// These are advanced for each vertex fetched by the vertex shader
|
|
{0, 0, vk::Format::eR32G32B32Sfloat, 0}, // Location 0: Position
|
|
{1, 0, vk::Format::eR32G32B32Sfloat, 3 * sizeof(float)}, // Location 1: Normal
|
|
{2, 0, vk::Format::eR32G32Sfloat, 6 * sizeof(float)}}}; // Location 2: Texture coordinates
|
|
|
|
// Use all input bindings and attribute descriptions
|
|
vk::PipelineVertexInputStateCreateInfo input_state{.vertexBindingDescriptionCount = 1,
|
|
.pVertexBindingDescriptions = &binding_description,
|
|
.vertexAttributeDescriptionCount = static_cast<uint32_t>(attribute_descriptions.size()),
|
|
.pVertexAttributeDescriptions = attribute_descriptions.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.depthCompareOp = vk::CompareOp::eGreater;
|
|
depth_stencil_state.depthTestEnable = false;
|
|
depth_stencil_state.depthWriteEnable = false;
|
|
depth_stencil_state.back.compareOp = vk::CompareOp::eAlways;
|
|
depth_stencil_state.front = depth_stencil_state.back;
|
|
|
|
return vkb::common::create_graphics_pipeline(get_device().get_handle(),
|
|
pipeline_cache,
|
|
shader_stages,
|
|
{}, // Vertices are generated in the vertex shader
|
|
vk::PrimitiveTopology::eTriangleList,
|
|
0,
|
|
vk::PolygonMode::eFill,
|
|
vk::CullModeFlagBits::eNone,
|
|
vk::FrontFace::eClockwise,
|
|
{blend_attachment_state},
|
|
depth_stencil_state,
|
|
pipeline_layout,
|
|
render_pass);
|
|
}
|
|
|
|
void HPPInstancing::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 HPPInstancing::load_assets()
|
|
{
|
|
rocks.mesh = load_model("scenes/rock.gltf");
|
|
planet.mesh = load_model("scenes/planet.gltf");
|
|
|
|
rocks.texture = load_texture_array("textures/texturearray_rocks_color_rgba.ktx", vkb::scene_graph::components::HPPImage::Color);
|
|
planet.texture = load_texture("textures/lavaplanet_color_rgba.ktx", vkb::scene_graph::components::HPPImage::Color);
|
|
}
|
|
|
|
void HPPInstancing::initialize_camera()
|
|
{
|
|
camera.type = vkb::CameraType::LookAt;
|
|
camera.set_rotation(glm::vec3(-17.2f, -4.7f, 0.0f));
|
|
camera.set_translation(glm::vec3(5.5f, -1.85f, -18.5f));
|
|
|
|
// Note: Using reversed depth-buffer for increased precision, so Znear and Zfar are flipped
|
|
camera.set_perspective(60.0f, static_cast<float>(extent.width) / static_cast<float>(extent.height), 256.0f, 0.1f);
|
|
}
|
|
|
|
void HPPInstancing::prepare_instance_data()
|
|
{
|
|
std::vector<InstanceData> instance_data;
|
|
instance_data.resize(INSTANCE_COUNT);
|
|
|
|
std::default_random_engine rnd_generator(lock_simulation_speed ? 0 : static_cast<unsigned>(time(nullptr)));
|
|
std::uniform_real_distribution<float> uniform_dist(0.0, 1.0);
|
|
std::uniform_int_distribution<uint32_t> rnd_texture_index(0, rocks.texture.image->get_vk_image().get_array_layer_count());
|
|
|
|
// Distribute rocks randomly on two different rings
|
|
glm::vec2 ring0{7.0f, 11.0f};
|
|
glm::vec2 ring1{14.0f, 18.0f};
|
|
for (auto i = 0, j = INSTANCE_COUNT / 2; i < INSTANCE_COUNT / 2; i++, j++)
|
|
{
|
|
float rho, theta;
|
|
|
|
// Inner ring
|
|
rho = sqrt((pow(ring0[1], 2.0f) - pow(ring0[0], 2.0f)) * uniform_dist(rnd_generator) + pow(ring0[0], 2.0f));
|
|
theta = 2.0f * glm::pi<float>() * uniform_dist(rnd_generator);
|
|
instance_data[i].pos = glm::vec3(rho * cos(theta), uniform_dist(rnd_generator) * 0.5f - 0.25f, rho * sin(theta));
|
|
instance_data[i].rot = glm::vec3(glm::pi<float>() * uniform_dist(rnd_generator), glm::pi<float>() * uniform_dist(rnd_generator), glm::pi<float>() * uniform_dist(rnd_generator));
|
|
instance_data[i].scale = 1.5f + uniform_dist(rnd_generator) - uniform_dist(rnd_generator);
|
|
instance_data[i].texIndex = rnd_texture_index(rnd_generator);
|
|
instance_data[i].scale *= 0.75f;
|
|
|
|
// Outer ring
|
|
rho = sqrt((pow(ring1[1], 2.0f) - pow(ring1[0], 2.0f)) * uniform_dist(rnd_generator) + pow(ring1[0], 2.0f));
|
|
theta = 2.0f * glm::pi<float>() * uniform_dist(rnd_generator);
|
|
instance_data[j].pos = glm::vec3(rho * cos(theta), uniform_dist(rnd_generator) * 0.5f - 0.25f, rho * sin(theta));
|
|
instance_data[j].rot = glm::vec3(glm::pi<float>() * uniform_dist(rnd_generator), glm::pi<float>() * uniform_dist(rnd_generator), glm::pi<float>() * uniform_dist(rnd_generator));
|
|
instance_data[j].scale = 1.5f + uniform_dist(rnd_generator) - uniform_dist(rnd_generator);
|
|
instance_data[j].texIndex = rnd_texture_index(rnd_generator);
|
|
instance_data[j].scale *= 0.75f;
|
|
}
|
|
|
|
instance_buffer.size = instance_data.size() * sizeof(InstanceData);
|
|
|
|
// Staging
|
|
// Instanced data is static, copy to device local memory
|
|
// On devices with separate memory types for host visible and device local memory this will result in better performance
|
|
// On devices with unified memory types (DEVICE_LOCAL_BIT and HOST_VISIBLE_BIT supported at once) this isn't necessary and you could skip the staging
|
|
|
|
auto const &device = get_device();
|
|
|
|
vkb::core::BufferCpp staging_buffer(get_device(), instance_buffer.size, vk::BufferUsageFlagBits::eTransferSrc, VMA_MEMORY_USAGE_CPU_TO_GPU);
|
|
staging_buffer.update(instance_data.data(), instance_buffer.size);
|
|
|
|
instance_buffer.buffer = std::make_unique<vkb::core::BufferCpp>(
|
|
get_device(), instance_buffer.size, vk::BufferUsageFlagBits::eVertexBuffer | vk::BufferUsageFlagBits::eTransferDst, VMA_MEMORY_USAGE_GPU_ONLY);
|
|
|
|
// Copy to staging buffer
|
|
vk::CommandBuffer copy_command = get_device().create_command_buffer(vk::CommandBufferLevel::ePrimary, true);
|
|
|
|
vk::BufferCopy copy_region{.size = instance_buffer.size};
|
|
copy_command.copyBuffer(staging_buffer.get_handle(), instance_buffer.buffer->get_handle(), copy_region);
|
|
|
|
get_device().flush_command_buffer(copy_command, queue, true);
|
|
|
|
instance_buffer.descriptor.range = instance_buffer.size;
|
|
instance_buffer.descriptor.buffer = instance_buffer.buffer->get_handle();
|
|
instance_buffer.descriptor.offset = 0;
|
|
}
|
|
|
|
void HPPInstancing::prepare_uniform_buffers()
|
|
{
|
|
uniform_buffers.scene =
|
|
std::make_unique<vkb::core::BufferCpp>(get_device(), sizeof(ubo_vs), vk::BufferUsageFlagBits::eUniformBuffer, VMA_MEMORY_USAGE_CPU_TO_GPU);
|
|
|
|
update_uniform_buffer(0.0f);
|
|
}
|
|
|
|
void HPPInstancing::update_uniform_buffer(float delta_time)
|
|
{
|
|
ubo_vs.projection = camera.matrices.perspective;
|
|
ubo_vs.view = camera.matrices.view;
|
|
|
|
if (!paused)
|
|
{
|
|
ubo_vs.loc_speed += delta_time * 0.35f;
|
|
ubo_vs.glob_speed += delta_time * 0.01f;
|
|
}
|
|
|
|
uniform_buffers.scene->convert_and_update(ubo_vs);
|
|
}
|
|
|
|
void HPPInstancing::update_planet_descriptor_set()
|
|
{
|
|
vk::DescriptorBufferInfo buffer_descriptor{uniform_buffers.scene->get_handle(), 0, vk::WholeSize};
|
|
vk::DescriptorImageInfo image_descriptor{planet.texture.sampler,
|
|
planet.texture.image->get_vk_image_view().get_handle(),
|
|
descriptor_type_to_image_layout(vk::DescriptorType::eCombinedImageSampler,
|
|
planet.texture.image->get_vk_image_view().get_format())};
|
|
std::array<vk::WriteDescriptorSet, 2> write_descriptor_sets = {{{.dstSet = planet.descriptor_set,
|
|
.dstBinding = 0,
|
|
.descriptorCount = 1,
|
|
.descriptorType = vk::DescriptorType::eUniformBuffer,
|
|
.pBufferInfo = &buffer_descriptor}, // Binding 0 : Vertex shader uniform buffer
|
|
{.dstSet = planet.descriptor_set,
|
|
.dstBinding = 1,
|
|
.descriptorCount = 1,
|
|
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
|
|
.pImageInfo = &image_descriptor}}}; // Binding 1 : Color map
|
|
get_device().get_handle().updateDescriptorSets(write_descriptor_sets, {});
|
|
}
|
|
|
|
void HPPInstancing::update_rocks_descriptor_set()
|
|
{
|
|
vk::DescriptorBufferInfo buffer_descriptor{uniform_buffers.scene->get_handle(), 0, vk::WholeSize};
|
|
vk::DescriptorImageInfo image_descriptor{rocks.texture.sampler,
|
|
rocks.texture.image->get_vk_image_view().get_handle(),
|
|
descriptor_type_to_image_layout(vk::DescriptorType::eCombinedImageSampler,
|
|
rocks.texture.image->get_vk_image_view().get_format())};
|
|
std::array<vk::WriteDescriptorSet, 2> write_descriptor_sets = {{{.dstSet = rocks.descriptor_set,
|
|
.dstBinding = 0,
|
|
.descriptorCount = 1,
|
|
.descriptorType = vk::DescriptorType::eUniformBuffer,
|
|
.pBufferInfo = &buffer_descriptor}, // Binding 0 : Vertex shader uniform buffer
|
|
{.dstSet = rocks.descriptor_set,
|
|
.dstBinding = 1,
|
|
.descriptorCount = 1,
|
|
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
|
|
.pImageInfo = &image_descriptor}}}; // Binding 1 : Color map
|
|
get_device().get_handle().updateDescriptorSets(write_descriptor_sets, {});
|
|
}
|
|
|
|
std::unique_ptr<vkb::Application> create_hpp_instancing()
|
|
{
|
|
return std::make_unique<HPPInstancing>();
|
|
}
|