Code cleanup, refactoring and simplification

This commit is contained in:
Sascha Willems 2024-01-14 15:23:58 +01:00
parent 0888d1c9b0
commit 47c3bd16c4
16 changed files with 500 additions and 901 deletions

View file

@ -1,7 +1,9 @@
/* /*
* Vulkan Example - Texture loading (and display) example (including mip maps) * Vulkan Example - Texture loading (and display) example (including mip maps)
* *
* Copyright (C) 2016-2017 by Sascha Willems - www.saschawillems.de * This sample shows how to upload a 2D texture to the device and how to display it. In Vulkan this is done using images, views and samplers.
*
* Copyright (C) 2016-2023 by Sascha Willems - www.saschawillems.de
* *
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
*/ */
@ -23,41 +25,33 @@ public:
// Contains all Vulkan objects that are required to store and use a texture // Contains all Vulkan objects that are required to store and use a texture
// Note that this repository contains a texture class (VulkanTexture.hpp) that encapsulates texture loading functionality in a class that is used in subsequent demos // Note that this repository contains a texture class (VulkanTexture.hpp) that encapsulates texture loading functionality in a class that is used in subsequent demos
struct Texture { struct Texture {
VkSampler sampler; VkSampler sampler{ VK_NULL_HANDLE };
VkImage image; VkImage image{ VK_NULL_HANDLE };
VkImageLayout imageLayout; VkImageLayout imageLayout;
VkDeviceMemory deviceMemory; VkDeviceMemory deviceMemory{ VK_NULL_HANDLE };
VkImageView view; VkImageView view{ VK_NULL_HANDLE };
uint32_t width, height; uint32_t width{ 0 };
uint32_t mipLevels; uint32_t height{ 0 };
uint32_t mipLevels{ 0 };
} texture; } texture;
struct {
VkPipelineVertexInputStateCreateInfo inputState;
std::vector<VkVertexInputBindingDescription> bindingDescriptions;
std::vector<VkVertexInputAttributeDescription> attributeDescriptions;
} vertices;
vks::Buffer vertexBuffer; vks::Buffer vertexBuffer;
vks::Buffer indexBuffer; vks::Buffer indexBuffer;
uint32_t indexCount; uint32_t indexCount{ 0 };
vks::Buffer uniformBufferVS; struct UniformData {
struct {
glm::mat4 projection; glm::mat4 projection;
glm::mat4 modelView; glm::mat4 modelView;
glm::vec4 viewPos; glm::vec4 viewPos;
// This is used to change the bias for the level-of-detail (mips) in the fragment shader
float lodBias = 0.0f; float lodBias = 0.0f;
} uboVS; } uniformData;
vks::Buffer uniformBuffer;
struct { VkPipeline pipeline{ VK_NULL_HANDLE };
VkPipeline solid; VkPipelineLayout pipelineLayout{ VK_NULL_HANDLE };
} pipelines; VkDescriptorSet descriptorSet{ VK_NULL_HANDLE };
VkDescriptorSetLayout descriptorSetLayout{ VK_NULL_HANDLE };
VkPipelineLayout pipelineLayout;
VkDescriptorSet descriptorSet;
VkDescriptorSetLayout descriptorSetLayout;
VulkanExample() : VulkanExampleBase() VulkanExample() : VulkanExampleBase()
{ {
@ -70,19 +64,15 @@ public:
~VulkanExample() ~VulkanExample()
{ {
// Clean up used Vulkan resources if (device) {
// Note : Inherited destructor cleans up resources stored in base class destroyTextureImage(texture);
vkDestroyPipeline(device, pipeline, nullptr);
destroyTextureImage(texture); vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
vkDestroyPipeline(device, pipelines.solid, nullptr); vertexBuffer.destroy();
indexBuffer.destroy();
vkDestroyPipelineLayout(device, pipelineLayout, nullptr); uniformBuffer.destroy();
vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); }
vertexBuffer.destroy();
indexBuffer.destroy();
uniformBufferVS.destroy();
} }
// Enable physical device features required for this example // Enable physical device features required for this example
@ -480,8 +470,8 @@ public:
VkRect2D scissor = vks::initializers::rect2D(width, height, 0, 0); VkRect2D scissor = vks::initializers::rect2D(width, height, 0, 0);
vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor); vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor);
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, NULL); vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr);
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.solid); vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
VkDeviceSize offsets[1] = { 0 }; VkDeviceSize offsets[1] = { 0 };
vkCmdBindVertexBuffers(drawCmdBuffers[i], 0, 1, &vertexBuffer.buffer, offsets); vkCmdBindVertexBuffers(drawCmdBuffers[i], 0, 1, &vertexBuffer.buffer, offsets);
@ -497,20 +487,8 @@ public:
} }
} }
void draw() // Creates a vertex and index buffer for a quad made of two triangles
{ // This is used to display the texture on
VulkanExampleBase::prepareFrame();
// Command buffer to be submitted to the queue
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
// Submit to queue
VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));
VulkanExampleBase::submitFrame();
}
void generateQuad() void generateQuad()
{ {
// Setup vertices for a single uv-mapped quad made from two triangles // Setup vertices for a single uv-mapped quad made from two triangles
@ -526,213 +504,115 @@ public:
std::vector<uint32_t> indices = { 0,1,2, 2,3,0 }; std::vector<uint32_t> indices = { 0,1,2, 2,3,0 };
indexCount = static_cast<uint32_t>(indices.size()); indexCount = static_cast<uint32_t>(indices.size());
// Create buffers // Create buffers and upload data to the GPU
// For the sake of simplicity we won't stage the vertex data to the gpu memory struct StagingBuffers {
// Vertex buffer vks::Buffer vertices;
VK_CHECK_RESULT(vulkanDevice->createBuffer( vks::Buffer indices;
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, } stagingBuffers;
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
&vertexBuffer, // Host visible source buffers (staging)
vertices.size() * sizeof(Vertex), VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &stagingBuffers.vertices, vertices.size() * sizeof(Vertex), vertices.data()));
vertices.data())); VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &stagingBuffers.indices, indices.size() * sizeof(uint32_t), indices.data()));
// Index buffer
VK_CHECK_RESULT(vulkanDevice->createBuffer( // Device local destination buffers
VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &vertexBuffer, vertices.size() * sizeof(Vertex)));
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &indexBuffer, indices.size() * sizeof(uint32_t)));
&indexBuffer,
indices.size() * sizeof(uint32_t), // Copy from host do device
indices.data())); vulkanDevice->copyBuffer(&stagingBuffers.vertices, &vertexBuffer, queue);
vulkanDevice->copyBuffer(&stagingBuffers.indices, &indexBuffer, queue);
// Clean up
stagingBuffers.vertices.destroy();
stagingBuffers.indices.destroy();
} }
void setupVertexDescriptions() void setupDescriptors()
{ {
// Binding description // Pool
vertices.bindingDescriptions.resize(1); std::vector<VkDescriptorPoolSize> poolSizes = {
vertices.bindingDescriptions[0] =
vks::initializers::vertexInputBindingDescription(
0,
sizeof(Vertex),
VK_VERTEX_INPUT_RATE_VERTEX);
// Attribute descriptions
// Describes memory layout and shader positions
vertices.attributeDescriptions.resize(3);
// Location 0 : Position
vertices.attributeDescriptions[0] =
vks::initializers::vertexInputAttributeDescription(
0,
0,
VK_FORMAT_R32G32B32_SFLOAT,
offsetof(Vertex, pos));
// Location 1 : Texture coordinates
vertices.attributeDescriptions[1] =
vks::initializers::vertexInputAttributeDescription(
0,
1,
VK_FORMAT_R32G32_SFLOAT,
offsetof(Vertex, uv));
// Location 1 : Vertex normal
vertices.attributeDescriptions[2] =
vks::initializers::vertexInputAttributeDescription(
0,
2,
VK_FORMAT_R32G32B32_SFLOAT,
offsetof(Vertex, normal));
vertices.inputState = vks::initializers::pipelineVertexInputStateCreateInfo();
vertices.inputState.vertexBindingDescriptionCount = static_cast<uint32_t>(vertices.bindingDescriptions.size());
vertices.inputState.pVertexBindingDescriptions = vertices.bindingDescriptions.data();
vertices.inputState.vertexAttributeDescriptionCount = static_cast<uint32_t>(vertices.attributeDescriptions.size());
vertices.inputState.pVertexAttributeDescriptions = vertices.attributeDescriptions.data();
}
void setupDescriptorPool()
{
// Example uses one ubo and one image sampler
std::vector<VkDescriptorPoolSize> poolSizes =
{
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1), vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1),
// The sample uses a combined image + sampler descriptor to sample the texture in the fragment shader
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1) vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1)
}; };
VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo(poolSizes, 2);
VkDescriptorPoolCreateInfo descriptorPoolInfo =
vks::initializers::descriptorPoolCreateInfo(
static_cast<uint32_t>(poolSizes.size()),
poolSizes.data(),
2);
VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool)); VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool));
}
void setupDescriptorSetLayout() // Layout
{ std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = {
std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings =
{
// Binding 0 : Vertex shader uniform buffer // Binding 0 : Vertex shader uniform buffer
vks::initializers::descriptorSetLayoutBinding( vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 0),
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
VK_SHADER_STAGE_VERTEX_BIT,
0),
// Binding 1 : Fragment shader image sampler // Binding 1 : Fragment shader image sampler
vks::initializers::descriptorSetLayoutBinding( vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 1)
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
VK_SHADER_STAGE_FRAGMENT_BIT,
1)
}; };
VkDescriptorSetLayoutCreateInfo descriptorLayout = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings);
VkDescriptorSetLayoutCreateInfo descriptorLayout =
vks::initializers::descriptorSetLayoutCreateInfo(
setLayoutBindings.data(),
static_cast<uint32_t>(setLayoutBindings.size()));
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout)); VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout));
VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = // Set
vks::initializers::pipelineLayoutCreateInfo( VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1);
&descriptorSetLayout,
1);
VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pPipelineLayoutCreateInfo, nullptr, &pipelineLayout));
}
void setupDescriptorSet()
{
VkDescriptorSetAllocateInfo allocInfo =
vks::initializers::descriptorSetAllocateInfo(
descriptorPool,
&descriptorSetLayout,
1);
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet)); VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet));
// Setup a descriptor image info for the current texture to be used as a combined image sampler // Setup a descriptor image info for the current texture to be used as a combined image sampler
VkDescriptorImageInfo textureDescriptor; VkDescriptorImageInfo textureDescriptor;
textureDescriptor.imageView = texture.view; // The image's view (images are never directly accessed by the shader, but rather through views defining subresources) // The image's view (images are never directly accessed by the shader, but rather through views defining subresources)
textureDescriptor.sampler = texture.sampler; // The sampler (Telling the pipeline how to sample the texture, including repeat, border, etc.) textureDescriptor.imageView = texture.view;
textureDescriptor.imageLayout = texture.imageLayout; // The current layout of the image (Note: Should always fit the actual use, e.g. shader read) // The sampler (Telling the pipeline how to sample the texture, including repeat, border, etc.)
textureDescriptor.sampler = texture.sampler;
// The current layout of the image(Note: Should always fit the actual use, e.g.shader read)
textureDescriptor.imageLayout = texture.imageLayout;
std::vector<VkWriteDescriptorSet> writeDescriptorSets = std::vector<VkWriteDescriptorSet> writeDescriptorSets = {
{
// Binding 0 : Vertex shader uniform buffer // Binding 0 : Vertex shader uniform buffer
vks::initializers::writeDescriptorSet( vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffer.descriptor),
descriptorSet,
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
0,
&uniformBufferVS.descriptor),
// Binding 1 : Fragment shader texture sampler // Binding 1 : Fragment shader texture sampler
// Fragment shader: layout (binding = 1) uniform sampler2D samplerColor; // Fragment shader: layout (binding = 1) uniform sampler2D samplerColor;
vks::initializers::writeDescriptorSet( vks::initializers::writeDescriptorSet(descriptorSet,
descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, // The descriptor set will use a combined image sampler (as opposed to splitting image and sampler)
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, // The descriptor set will use a combined image sampler (sampler and image could be split)
1, // Shader binding point 1 1, // Shader binding point 1
&textureDescriptor) // Pointer to the descriptor image for our texture &textureDescriptor) // Pointer to the descriptor image for our texture
}; };
vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr);
vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL);
} }
void preparePipelines() void preparePipelines()
{ {
VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = // Layout
vks::initializers::pipelineInputAssemblyStateCreateInfo( VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1);
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout));
0,
VK_FALSE);
VkPipelineRasterizationStateCreateInfo rasterizationState = // Pipeline
vks::initializers::pipelineRasterizationStateCreateInfo( VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE);
VK_POLYGON_MODE_FILL, VkPipelineRasterizationStateCreateInfo rasterizationState = vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_NONE, VK_FRONT_FACE_COUNTER_CLOCKWISE, 0);
VK_CULL_MODE_NONE, VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE);
VK_FRONT_FACE_COUNTER_CLOCKWISE, VkPipelineColorBlendStateCreateInfo colorBlendState = vks::initializers::pipelineColorBlendStateCreateInfo(1, &blendAttachmentState);
0); VkPipelineDepthStencilStateCreateInfo depthStencilState = vks::initializers::pipelineDepthStencilStateCreateInfo(VK_TRUE, VK_TRUE, VK_COMPARE_OP_LESS_OR_EQUAL);
VkPipelineViewportStateCreateInfo viewportState = vks::initializers::pipelineViewportStateCreateInfo(1, 1, 0);
VkPipelineColorBlendAttachmentState blendAttachmentState = VkPipelineMultisampleStateCreateInfo multisampleState = vks::initializers::pipelineMultisampleStateCreateInfo(VK_SAMPLE_COUNT_1_BIT, 0);
vks::initializers::pipelineColorBlendAttachmentState( std::vector<VkDynamicState> dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
0xf, VkPipelineDynamicStateCreateInfo dynamicState = vks::initializers::pipelineDynamicStateCreateInfo(dynamicStateEnables);
VK_FALSE);
VkPipelineColorBlendStateCreateInfo colorBlendState =
vks::initializers::pipelineColorBlendStateCreateInfo(
1,
&blendAttachmentState);
VkPipelineDepthStencilStateCreateInfo depthStencilState =
vks::initializers::pipelineDepthStencilStateCreateInfo(
VK_TRUE,
VK_TRUE,
VK_COMPARE_OP_LESS_OR_EQUAL);
VkPipelineViewportStateCreateInfo viewportState =
vks::initializers::pipelineViewportStateCreateInfo(1, 1, 0);
VkPipelineMultisampleStateCreateInfo multisampleState =
vks::initializers::pipelineMultisampleStateCreateInfo(
VK_SAMPLE_COUNT_1_BIT,
0);
std::vector<VkDynamicState> dynamicStateEnables = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR
};
VkPipelineDynamicStateCreateInfo dynamicState =
vks::initializers::pipelineDynamicStateCreateInfo(
dynamicStateEnables.data(),
static_cast<uint32_t>(dynamicStateEnables.size()),
0);
// Load shaders
std::array<VkPipelineShaderStageCreateInfo,2> shaderStages; std::array<VkPipelineShaderStageCreateInfo,2> shaderStages;
// Shaders
shaderStages[0] = loadShader(getShadersPath() + "texture/texture.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); shaderStages[0] = loadShader(getShadersPath() + "texture/texture.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
shaderStages[1] = loadShader(getShadersPath() + "texture/texture.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); shaderStages[1] = loadShader(getShadersPath() + "texture/texture.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
VkGraphicsPipelineCreateInfo pipelineCreateInfo = // Vertex input state
vks::initializers::pipelineCreateInfo( std::vector<VkVertexInputBindingDescription> vertexInputBindings = {
pipelineLayout, vks::initializers::vertexInputBindingDescription(0, sizeof(Vertex), VK_VERTEX_INPUT_RATE_VERTEX)
renderPass, };
0); std::vector<VkVertexInputAttributeDescription> vertexInputAttributes = {
vks::initializers::vertexInputAttributeDescription(0, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, pos)),
vks::initializers::vertexInputAttributeDescription(0, 1, VK_FORMAT_R32G32_SFLOAT, offsetof(Vertex, uv)),
vks::initializers::vertexInputAttributeDescription(0, 2, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, normal)),
};
VkPipelineVertexInputStateCreateInfo vertexInputState = vks::initializers::pipelineVertexInputStateCreateInfo();
vertexInputState.vertexBindingDescriptionCount = static_cast<uint32_t>(vertexInputBindings.size());
vertexInputState.pVertexBindingDescriptions = vertexInputBindings.data();
vertexInputState.vertexAttributeDescriptionCount = static_cast<uint32_t>(vertexInputAttributes.size());
vertexInputState.pVertexAttributeDescriptions = vertexInputAttributes.data();
pipelineCreateInfo.pVertexInputState = &vertices.inputState; VkGraphicsPipelineCreateInfo pipelineCreateInfo = vks::initializers::pipelineCreateInfo(pipelineLayout, renderPass, 0);
pipelineCreateInfo.pVertexInputState = &vertexInputState;
pipelineCreateInfo.pInputAssemblyState = &inputAssemblyState; pipelineCreateInfo.pInputAssemblyState = &inputAssemblyState;
pipelineCreateInfo.pRasterizationState = &rasterizationState; pipelineCreateInfo.pRasterizationState = &rasterizationState;
pipelineCreateInfo.pColorBlendState = &colorBlendState; pipelineCreateInfo.pColorBlendState = &colorBlendState;
@ -742,31 +622,23 @@ public:
pipelineCreateInfo.pDynamicState = &dynamicState; pipelineCreateInfo.pDynamicState = &dynamicState;
pipelineCreateInfo.stageCount = static_cast<uint32_t>(shaderStages.size()); pipelineCreateInfo.stageCount = static_cast<uint32_t>(shaderStages.size());
pipelineCreateInfo.pStages = shaderStages.data(); pipelineCreateInfo.pStages = shaderStages.data();
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipeline));
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.solid));
} }
// Prepare and initialize uniform buffer containing shader uniforms // Prepare and initialize uniform buffer containing shader uniforms
void prepareUniformBuffers() void prepareUniformBuffers()
{ {
// Vertex shader uniform buffer block // Vertex shader uniform buffer block
VK_CHECK_RESULT(vulkanDevice->createBuffer( VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &uniformBuffer, sizeof(uniformData), &uniformData));
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_CHECK_RESULT(uniformBuffer.map());
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
&uniformBufferVS,
sizeof(uboVS),
&uboVS));
VK_CHECK_RESULT(uniformBufferVS.map());
updateUniformBuffers();
} }
void updateUniformBuffers() void updateUniformBuffers()
{ {
uboVS.projection = camera.matrices.perspective; uniformData.projection = camera.matrices.perspective;
uboVS.modelView = camera.matrices.view; uniformData.modelView = camera.matrices.view;
uboVS.viewPos = camera.viewPos; uniformData.viewPos = camera.viewPos;
memcpy(uniformBufferVS.mapped, &uboVS, sizeof(uboVS)); memcpy(uniformBuffer.mapped, &uniformData, sizeof(uniformData));
} }
void prepare() void prepare()
@ -774,32 +646,34 @@ public:
VulkanExampleBase::prepare(); VulkanExampleBase::prepare();
loadTexture(); loadTexture();
generateQuad(); generateQuad();
setupVertexDescriptions();
prepareUniformBuffers(); prepareUniformBuffers();
setupDescriptorSetLayout(); setupDescriptors();
preparePipelines(); preparePipelines();
setupDescriptorPool();
setupDescriptorSet();
buildCommandBuffers(); buildCommandBuffers();
prepared = true; prepared = true;
} }
void draw()
{
VulkanExampleBase::prepareFrame();
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));
VulkanExampleBase::submitFrame();
}
virtual void render() virtual void render()
{ {
if (!prepared) if (!prepared)
return; return;
draw();
}
virtual void viewChanged()
{
updateUniformBuffers(); updateUniformBuffers();
draw();
} }
virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay) virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay)
{ {
if (overlay->header("Settings")) { if (overlay->header("Settings")) {
if (overlay->sliderFloat("LOD bias", &uboVS.lodBias, 0.0f, (float)texture.mipLevels)) { if (overlay->sliderFloat("LOD bias", &uniformData.lodBias, 0.0f, (float)texture.mipLevels)) {
updateUniformBuffers(); updateUniformBuffers();
} }
} }

View file

@ -134,36 +134,30 @@ public:
VkImageView view = VK_NULL_HANDLE; VkImageView view = VK_NULL_HANDLE;
VkDescriptorImageInfo descriptor; VkDescriptorImageInfo descriptor;
VkFormat format; VkFormat format;
uint32_t width, height, depth; uint32_t width{ 0 };
uint32_t mipLevels; uint32_t height{ 0 };
uint32_t depth{ 0 };
uint32_t mipLevels{ 0 };
} texture; } texture;
struct {
VkPipelineVertexInputStateCreateInfo inputState;
std::vector<VkVertexInputBindingDescription> inputBinding;
std::vector<VkVertexInputAttributeDescription> inputAttributes;
} vertices;
vks::Buffer vertexBuffer; vks::Buffer vertexBuffer;
vks::Buffer indexBuffer; vks::Buffer indexBuffer;
uint32_t indexCount; uint32_t indexCount{ 0 };
vks::Buffer uniformBufferVS; struct UniformData {
struct UboVS {
glm::mat4 projection; glm::mat4 projection;
glm::mat4 modelView; glm::mat4 modelView;
glm::vec4 viewPos; glm::vec4 viewPos;
// The current depth level of the texture to display
// This is animated
float depth = 0.0f; float depth = 0.0f;
} uboVS; } uniformData;
vks::Buffer uniformBuffer;
struct { VkPipeline pipeline{ VK_NULL_HANDLE };
VkPipeline solid; VkPipelineLayout pipelineLayout{ VK_NULL_HANDLE };
} pipelines; VkDescriptorSet descriptorSet{ VK_NULL_HANDLE };
VkDescriptorSetLayout descriptorSetLayout{ VK_NULL_HANDLE };
VkPipelineLayout pipelineLayout;
VkDescriptorSet descriptorSet;
VkDescriptorSetLayout descriptorSetLayout;
VulkanExample() : VulkanExampleBase() VulkanExample() : VulkanExampleBase()
{ {
@ -177,19 +171,15 @@ public:
~VulkanExample() ~VulkanExample()
{ {
// Clean up used Vulkan resources if (device) {
// Note : Inherited destructor cleans up resources stored in base class destroyTextureImage(texture);
vkDestroyPipeline(device, pipeline, nullptr);
destroyTextureImage(texture); vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
vkDestroyPipeline(device, pipelines.solid, nullptr); vertexBuffer.destroy();
indexBuffer.destroy();
vkDestroyPipelineLayout(device, pipelineLayout, nullptr); uniformBuffer.destroy();
vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); }
vertexBuffer.destroy();
indexBuffer.destroy();
uniformBufferVS.destroy();
} }
// Prepare all Vulkan resources for the 3D texture (including descriptors) // Prepare all Vulkan resources for the 3D texture (including descriptors)
@ -312,14 +302,8 @@ public:
float nx = (float)x / (float)texture.width; float nx = (float)x / (float)texture.width;
float ny = (float)y / (float)texture.height; float ny = (float)y / (float)texture.height;
float nz = (float)z / (float)texture.depth; float nz = (float)z / (float)texture.depth;
#define FRACTAL
#ifdef FRACTAL
float n = fractalNoise.noise(nx * noiseScale, ny * noiseScale, nz * noiseScale); float n = fractalNoise.noise(nx * noiseScale, ny * noiseScale, nz * noiseScale);
#else
float n = 20.0 * perlinNoise.noise(nx, ny, nz);
#endif
n = n - floor(n); n = n - floor(n);
data[x + y * texture.width + z * texture.width * texture.height] = static_cast<uint8_t>(floor(n * 255)); data[x + y * texture.width + z * texture.width * texture.height] = static_cast<uint8_t>(floor(n * 255));
} }
} }
@ -457,7 +441,7 @@ public:
vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor); vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor);
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, NULL); vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, NULL);
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.solid); vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
VkDeviceSize offsets[1] = { 0 }; VkDeviceSize offsets[1] = { 0 };
vkCmdBindVertexBuffers(drawCmdBuffers[i], 0, 1, &vertexBuffer.buffer, offsets); vkCmdBindVertexBuffers(drawCmdBuffers[i], 0, 1, &vertexBuffer.buffer, offsets);
@ -472,20 +456,8 @@ public:
} }
} }
void draw() // Creates a vertex and index buffer for a quad made of two triangles
{ // This is used to display the texture on
VulkanExampleBase::prepareFrame();
// Command buffer to be submitted to the queue
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
// Submit to queue
VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));
VulkanExampleBase::submitFrame();
}
void generateQuad() void generateQuad()
{ {
// Setup vertices for a single uv-mapped quad made from two triangles // Setup vertices for a single uv-mapped quad made from two triangles
@ -501,206 +473,108 @@ public:
std::vector<uint32_t> indices = { 0,1,2, 2,3,0 }; std::vector<uint32_t> indices = { 0,1,2, 2,3,0 };
indexCount = static_cast<uint32_t>(indices.size()); indexCount = static_cast<uint32_t>(indices.size());
// Create buffers // Create buffers and upload data to the GPU
// For the sake of simplicity we won't stage the vertex data to the gpu memory struct StagingBuffers {
// Vertex buffer vks::Buffer vertices;
VK_CHECK_RESULT(vulkanDevice->createBuffer( vks::Buffer indices;
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, } stagingBuffers;
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
&vertexBuffer, // Host visible source buffers (staging)
vertices.size() * sizeof(Vertex), VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &stagingBuffers.vertices, vertices.size() * sizeof(Vertex), vertices.data()));
vertices.data())); VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &stagingBuffers.indices, indices.size() * sizeof(uint32_t), indices.data()));
// Index buffer
VK_CHECK_RESULT(vulkanDevice->createBuffer( // Device local destination buffers
VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &vertexBuffer, vertices.size() * sizeof(Vertex)));
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &indexBuffer, indices.size() * sizeof(uint32_t)));
&indexBuffer,
indices.size() * sizeof(uint32_t), // Copy from host do device
indices.data())); vulkanDevice->copyBuffer(&stagingBuffers.vertices, &vertexBuffer, queue);
vulkanDevice->copyBuffer(&stagingBuffers.indices, &indexBuffer, queue);
// Clean up
stagingBuffers.vertices.destroy();
stagingBuffers.indices.destroy();
} }
void setupVertexDescriptions() void setupDescriptors()
{ {
// Binding description // Pool
vertices.inputBinding.resize(1); std::vector<VkDescriptorPoolSize> poolSizes = {
vertices.inputBinding[0] =
vks::initializers::vertexInputBindingDescription(
0,
sizeof(Vertex),
VK_VERTEX_INPUT_RATE_VERTEX);
// Attribute descriptions
// Describes memory layout and shader positions
vertices.inputAttributes.resize(3);
// Location 0 : Position
vertices.inputAttributes[0] =
vks::initializers::vertexInputAttributeDescription(
0,
0,
VK_FORMAT_R32G32B32_SFLOAT,
offsetof(Vertex, pos));
// Location 1 : Texture coordinates
vertices.inputAttributes[1] =
vks::initializers::vertexInputAttributeDescription(
0,
1,
VK_FORMAT_R32G32_SFLOAT,
offsetof(Vertex, uv));
// Location 1 : Vertex normal
vertices.inputAttributes[2] =
vks::initializers::vertexInputAttributeDescription(
0,
2,
VK_FORMAT_R32G32B32_SFLOAT,
offsetof(Vertex, normal));
vertices.inputState = vks::initializers::pipelineVertexInputStateCreateInfo();
vertices.inputState.vertexBindingDescriptionCount = static_cast<uint32_t>(vertices.inputBinding.size());
vertices.inputState.pVertexBindingDescriptions = vertices.inputBinding.data();
vertices.inputState.vertexAttributeDescriptionCount = static_cast<uint32_t>(vertices.inputAttributes.size());
vertices.inputState.pVertexAttributeDescriptions = vertices.inputAttributes.data();
}
void setupDescriptorPool()
{
// Example uses one ubo and one image sampler
std::vector<VkDescriptorPoolSize> poolSizes =
{
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1), vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1),
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1) vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1)
}; };
VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo(poolSizes, 2);
VkDescriptorPoolCreateInfo descriptorPoolInfo =
vks::initializers::descriptorPoolCreateInfo(
static_cast<uint32_t>(poolSizes.size()),
poolSizes.data(),
2);
VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool)); VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool));
}
void setupDescriptorSetLayout() // Layout
{ std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = {
std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings =
{
// Binding 0 : Vertex shader uniform buffer // Binding 0 : Vertex shader uniform buffer
vks::initializers::descriptorSetLayoutBinding( vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 0),
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
VK_SHADER_STAGE_VERTEX_BIT,
0),
// Binding 1 : Fragment shader image sampler // Binding 1 : Fragment shader image sampler
vks::initializers::descriptorSetLayoutBinding( vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 1)
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
VK_SHADER_STAGE_FRAGMENT_BIT,
1)
}; };
VkDescriptorSetLayoutCreateInfo descriptorLayout = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings);
VkDescriptorSetLayoutCreateInfo descriptorLayout =
vks::initializers::descriptorSetLayoutCreateInfo(
setLayoutBindings.data(),
static_cast<uint32_t>(setLayoutBindings.size()));
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout)); VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout));
VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = // Set
vks::initializers::pipelineLayoutCreateInfo( VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1);
&descriptorSetLayout,
1);
VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pPipelineLayoutCreateInfo, nullptr, &pipelineLayout));
}
void setupDescriptorSet()
{
VkDescriptorSetAllocateInfo allocInfo =
vks::initializers::descriptorSetAllocateInfo(
descriptorPool,
&descriptorSetLayout,
1);
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet)); VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet));
std::vector<VkWriteDescriptorSet> writeDescriptorSets = // Image descriptor for the 3D texture
{ VkDescriptorImageInfo textureDescriptor =
// Binding 0 : Vertex shader uniform buffer vks::initializers::descriptorImageInfo(
vks::initializers::writeDescriptorSet( texture.sampler,
descriptorSet, texture.view,
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, texture.imageLayout);
0,
&uniformBufferVS.descriptor),
// Binding 1 : Fragment shader texture sampler
vks::initializers::writeDescriptorSet(
descriptorSet,
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
1,
&texture.descriptor)
};
vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL); std::vector<VkWriteDescriptorSet> writeDescriptorSets = {
// Binding 0 : Vertex shader uniform buffer
vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffer.descriptor),
// Binding 1 : Fragment shader texture sampler
vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &textureDescriptor)
};
vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr);
} }
void preparePipelines() void preparePipelines()
{ {
VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = // Layout
vks::initializers::pipelineInputAssemblyStateCreateInfo( VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1);
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout));
0,
VK_FALSE);
VkPipelineRasterizationStateCreateInfo rasterizationState = // Pipeline
vks::initializers::pipelineRasterizationStateCreateInfo( VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE);
VK_POLYGON_MODE_FILL, VkPipelineRasterizationStateCreateInfo rasterizationState = vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_NONE, VK_FRONT_FACE_COUNTER_CLOCKWISE, 0);
VK_CULL_MODE_NONE, VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE);
VK_FRONT_FACE_COUNTER_CLOCKWISE, VkPipelineColorBlendStateCreateInfo colorBlendState = vks::initializers::pipelineColorBlendStateCreateInfo(1, &blendAttachmentState);
0); VkPipelineDepthStencilStateCreateInfo depthStencilState = vks::initializers::pipelineDepthStencilStateCreateInfo(VK_TRUE, VK_TRUE, VK_COMPARE_OP_LESS_OR_EQUAL);
VkPipelineViewportStateCreateInfo viewportState = vks::initializers::pipelineViewportStateCreateInfo(1, 1, 0);
VkPipelineColorBlendAttachmentState blendAttachmentState = VkPipelineMultisampleStateCreateInfo multisampleState = vks::initializers::pipelineMultisampleStateCreateInfo(VK_SAMPLE_COUNT_1_BIT, 0);
vks::initializers::pipelineColorBlendAttachmentState( std::vector<VkDynamicState> dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
0xf, VkPipelineDynamicStateCreateInfo dynamicState = vks::initializers::pipelineDynamicStateCreateInfo(dynamicStateEnables);
VK_FALSE);
VkPipelineColorBlendStateCreateInfo colorBlendState =
vks::initializers::pipelineColorBlendStateCreateInfo(
1,
&blendAttachmentState);
VkPipelineDepthStencilStateCreateInfo depthStencilState =
vks::initializers::pipelineDepthStencilStateCreateInfo(
VK_TRUE,
VK_TRUE,
VK_COMPARE_OP_LESS_OR_EQUAL);
VkPipelineViewportStateCreateInfo viewportState =
vks::initializers::pipelineViewportStateCreateInfo(1, 1, 0);
VkPipelineMultisampleStateCreateInfo multisampleState =
vks::initializers::pipelineMultisampleStateCreateInfo(
VK_SAMPLE_COUNT_1_BIT,
0);
std::vector<VkDynamicState> dynamicStateEnables = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR
};
VkPipelineDynamicStateCreateInfo dynamicState =
vks::initializers::pipelineDynamicStateCreateInfo(
dynamicStateEnables.data(),
static_cast<uint32_t>(dynamicStateEnables.size()),
0);
// Load shaders
std::array<VkPipelineShaderStageCreateInfo,2> shaderStages; std::array<VkPipelineShaderStageCreateInfo,2> shaderStages;
// Shaders
shaderStages[0] = loadShader(getShadersPath() + "texture3d/texture3d.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); shaderStages[0] = loadShader(getShadersPath() + "texture3d/texture3d.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
shaderStages[1] = loadShader(getShadersPath() + "texture3d/texture3d.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); shaderStages[1] = loadShader(getShadersPath() + "texture3d/texture3d.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
VkGraphicsPipelineCreateInfo pipelineCreateInfo = // Vertex input state
vks::initializers::pipelineCreateInfo( std::vector<VkVertexInputBindingDescription> vertexInputBindings = {
pipelineLayout, vks::initializers::vertexInputBindingDescription(0, sizeof(Vertex), VK_VERTEX_INPUT_RATE_VERTEX)
renderPass, };
0); std::vector<VkVertexInputAttributeDescription> vertexInputAttributes = {
vks::initializers::vertexInputAttributeDescription(0, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, pos)),
vks::initializers::vertexInputAttributeDescription(0, 1, VK_FORMAT_R32G32_SFLOAT, offsetof(Vertex, uv)),
vks::initializers::vertexInputAttributeDescription(0, 2, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, normal)),
};
VkPipelineVertexInputStateCreateInfo vertexInputState = vks::initializers::pipelineVertexInputStateCreateInfo();
vertexInputState.vertexBindingDescriptionCount = static_cast<uint32_t>(vertexInputBindings.size());
vertexInputState.pVertexBindingDescriptions = vertexInputBindings.data();
vertexInputState.vertexAttributeDescriptionCount = static_cast<uint32_t>(vertexInputAttributes.size());
vertexInputState.pVertexAttributeDescriptions = vertexInputAttributes.data();
pipelineCreateInfo.pVertexInputState = &vertices.inputState; VkGraphicsPipelineCreateInfo pipelineCreateInfo = vks::initializers::pipelineCreateInfo(pipelineLayout, renderPass, 0);
pipelineCreateInfo.pVertexInputState = &vertexInputState;
pipelineCreateInfo.pInputAssemblyState = &inputAssemblyState; pipelineCreateInfo.pInputAssemblyState = &inputAssemblyState;
pipelineCreateInfo.pRasterizationState = &rasterizationState; pipelineCreateInfo.pRasterizationState = &rasterizationState;
pipelineCreateInfo.pColorBlendState = &colorBlendState; pipelineCreateInfo.pColorBlendState = &colorBlendState;
@ -710,59 +584,59 @@ public:
pipelineCreateInfo.pDynamicState = &dynamicState; pipelineCreateInfo.pDynamicState = &dynamicState;
pipelineCreateInfo.stageCount = static_cast<uint32_t>(shaderStages.size()); pipelineCreateInfo.stageCount = static_cast<uint32_t>(shaderStages.size());
pipelineCreateInfo.pStages = shaderStages.data(); pipelineCreateInfo.pStages = shaderStages.data();
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipeline));
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.solid));
} }
// Prepare and initialize uniform buffer containing shader uniforms // Prepare and initialize uniform buffer containing shader uniforms
void prepareUniformBuffers() void prepareUniformBuffers()
{ {
// Vertex shader uniform buffer block // Vertex shader uniform buffer block
VK_CHECK_RESULT(vulkanDevice->createBuffer( VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &uniformBuffer, sizeof(UniformData), &uniformData));
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_CHECK_RESULT(uniformBuffer.map());
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
&uniformBufferVS,
sizeof(uboVS),
&uboVS));
VK_CHECK_RESULT(uniformBufferVS.map());
updateUniformBuffers();
} }
void updateUniformBuffers() void updateUniformBuffers()
{ {
uboVS.projection = camera.matrices.perspective; uniformData.projection = camera.matrices.perspective;
uboVS.modelView = camera.matrices.view; uniformData.modelView = camera.matrices.view;
uboVS.viewPos = camera.viewPos; uniformData.viewPos = camera.viewPos;
uboVS.depth += frameTimer * 0.15f; if (!paused) {
if (uboVS.depth > 1.0f) { // Animate depth
uboVS.depth = uboVS.depth - 1.0f; uniformData.depth += frameTimer * 0.15f;
if (uniformData.depth > 1.0f) {
uniformData.depth = uniformData.depth - 1.0f;
}
} }
memcpy(uniformBufferVS.mapped, &uboVS, sizeof(uboVS)); memcpy(uniformBuffer.mapped, &uniformData, sizeof(UniformData));
} }
void prepare() void prepare()
{ {
VulkanExampleBase::prepare(); VulkanExampleBase::prepare();
generateQuad(); generateQuad();
setupVertexDescriptions();
prepareUniformBuffers(); prepareUniformBuffers();
prepareNoiseTexture(128, 128, 128); prepareNoiseTexture(128, 128, 128);
setupDescriptorSetLayout(); setupDescriptors();
preparePipelines(); preparePipelines();
setupDescriptorPool();
setupDescriptorSet();
buildCommandBuffers(); buildCommandBuffers();
prepared = true; prepared = true;
} }
void draw()
{
VulkanExampleBase::prepareFrame();
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));
VulkanExampleBase::submitFrame();
}
virtual void render() virtual void render()
{ {
if (!prepared) if (!prepared)
return; return;
updateUniformBuffers();
draw(); draw();
if (!paused) {
updateUniformBuffers();
}
} }
virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay) virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay)

View file

@ -1,6 +1,9 @@
/* /*
* Vulkan Example - Texture arrays and instanced rendering * Vulkan Example - Texture arrays and instanced rendering
* *
* This sample shows how to load and render a texture array. This is a single layered texture where each layer contains different image data.
* The different layers are displayed on cubes using instancing, where each instance selects a different layer from the texture
*
* Copyright (C) 2016-2023 Sascha Willems - www.saschawillems.de * Copyright (C) 2016-2023 Sascha Willems - www.saschawillems.de
* *
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
@ -23,38 +26,36 @@ class VulkanExample : public VulkanExampleBase
public: public:
// Number of array layers in texture array // Number of array layers in texture array
// Also used as instance count // Also used as instance count
uint32_t layerCount; uint32_t layerCount{ 0 };
vks::Texture textureArray; vks::Texture textureArray;
vks::Buffer vertexBuffer; vks::Buffer vertexBuffer;
vks::Buffer indexBuffer; vks::Buffer indexBuffer;
uint32_t indexCount; uint32_t indexCount{ 0 };
vks::Buffer uniformBufferVS; // Values passed to the shader per drawn instance
struct alignas(16) PerInstanceData {
struct UboInstanceData {
// Model matrix // Model matrix
glm::mat4 model; glm::mat4 model;
// Texture array index // Layer index from which this instance will sample in the fragment shader
// Vec4 due to padding float arrayIndex{ 0 };
glm::vec4 arrayIndex;
}; };
struct { struct UniformData {
// Global matrices // Global matrices
struct { struct {
glm::mat4 projection; glm::mat4 projection;
glm::mat4 view; glm::mat4 view;
} matrices; } matrices;
// Separate data for each instance // Separate data for each instance
UboInstanceData *instance; PerInstanceData* instance{ nullptr };
} uboVS; } uniformData;
vks::Buffer uniformBuffer;
VkPipeline pipeline{ VK_NULL_HANDLE };
VkPipeline pipeline; VkPipelineLayout pipelineLayout{ VK_NULL_HANDLE };
VkPipelineLayout pipelineLayout; VkDescriptorSet descriptorSet{ VK_NULL_HANDLE };
VkDescriptorSet descriptorSet; VkDescriptorSetLayout descriptorSetLayout{ VK_NULL_HANDLE };
VkDescriptorSetLayout descriptorSetLayout;
VulkanExample() : VulkanExampleBase() VulkanExample() : VulkanExampleBase()
{ {
@ -67,25 +68,19 @@ public:
~VulkanExample() ~VulkanExample()
{ {
// Clean up used Vulkan resources if (device) {
// Note : Inherited destructor cleans up resources stored in base class vkDestroyImageView(device, textureArray.view, nullptr);
vkDestroyImage(device, textureArray.image, nullptr);
vkDestroyImageView(device, textureArray.view, nullptr); vkDestroySampler(device, textureArray.sampler, nullptr);
vkDestroyImage(device, textureArray.image, nullptr); vkFreeMemory(device, textureArray.deviceMemory, nullptr);
vkDestroySampler(device, textureArray.sampler, nullptr); vkDestroyPipeline(device, pipeline, nullptr);
vkFreeMemory(device, textureArray.deviceMemory, nullptr); vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
vkDestroyPipeline(device, pipeline, nullptr); vertexBuffer.destroy();
indexBuffer.destroy();
vkDestroyPipelineLayout(device, pipelineLayout, nullptr); uniformBuffer.destroy();
vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); delete[] uniformData.instance;
}
vertexBuffer.destroy();
indexBuffer.destroy();
uniformBufferVS.destroy();
delete[] uboVS.instance;
} }
void loadTextureArray(std::string filename, VkFormat format) void loadTextureArray(std::string filename, VkFormat format)
@ -324,6 +319,8 @@ public:
} }
} }
// Creates a vertex and index buffer for a cube
// This is used to display the texture on
void generateCube() void generateCube()
{ {
std::vector<Vertex> vertices = { std::vector<Vertex> vertices = {
@ -363,49 +360,50 @@ public:
indexCount = static_cast<uint32_t>(indices.size()); indexCount = static_cast<uint32_t>(indices.size());
// Create buffers // Create buffers and upload data to the GPU
// For the sake of simplicity we won't stage the vertex data to the gpu memory struct StagingBuffers {
VK_CHECK_RESULT(vulkanDevice->createBuffer( vks::Buffer vertices;
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, vks::Buffer indices;
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, } stagingBuffers;
&vertexBuffer,
vertices.size() * sizeof(Vertex), // Host visible source buffers (staging)
vertices.data())); VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &stagingBuffers.vertices, vertices.size() * sizeof(Vertex), vertices.data()));
VK_CHECK_RESULT(vulkanDevice->createBuffer( VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &stagingBuffers.indices, indices.size() * sizeof(uint32_t), indices.data()));
VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, // Device local destination buffers
&indexBuffer, VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &vertexBuffer, vertices.size() * sizeof(Vertex)));
indices.size() * sizeof(uint32_t), VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &indexBuffer, indices.size() * sizeof(uint32_t)));
indices.data()));
// Copy from host do device
vulkanDevice->copyBuffer(&stagingBuffers.vertices, &vertexBuffer, queue);
vulkanDevice->copyBuffer(&stagingBuffers.indices, &indexBuffer, queue);
// Clean up
stagingBuffers.vertices.destroy();
stagingBuffers.indices.destroy();
} }
void setupDescriptorPool() void setupDescriptors()
{ {
// Pool
std::vector<VkDescriptorPoolSize> poolSizes = { std::vector<VkDescriptorPoolSize> poolSizes = {
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1), vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1),
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1) vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1)
}; };
VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo(poolSizes, 2); VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo(poolSizes, 2);
VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool)); VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool));
}
void setupDescriptorSetLayout() // Layout
{
std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = { std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = {
// Binding 0 : Vertex shader uniform buffer // Binding 0 : Vertex shader uniform buffer
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 0), vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 0),
// Binding 1 : Fragment shader image sampler (texture array) // Binding 1 : Fragment shader image sampler
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 1) vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 1)
}; };
VkDescriptorSetLayoutCreateInfo descriptorLayout = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings); VkDescriptorSetLayoutCreateInfo descriptorLayout = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings);
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout)); VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout));
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1); // Set
VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout));
}
void setupDescriptorSet()
{
VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1); VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1);
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet)); VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet));
@ -418,8 +416,8 @@ public:
std::vector<VkWriteDescriptorSet> writeDescriptorSets = { std::vector<VkWriteDescriptorSet> writeDescriptorSets = {
// Binding 0 : Vertex shader uniform buffer // Binding 0 : Vertex shader uniform buffer
vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBufferVS.descriptor), vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffer.descriptor),
// Binding 1 : Fragment shader cubemap sampler // Binding 1 : Fragment shader texture sampler
vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &textureDescriptor) vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &textureDescriptor)
}; };
vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr); vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr);
@ -427,6 +425,11 @@ public:
void preparePipelines() void preparePipelines()
{ {
// Layout
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1);
VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout));
// Pipeline
VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCI = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE); VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCI = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE);
VkPipelineRasterizationStateCreateInfo rasterizationStateCI = vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_NONE, VK_FRONT_FACE_COUNTER_CLOCKWISE, 0); VkPipelineRasterizationStateCreateInfo rasterizationStateCI = vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_NONE, VK_FRONT_FACE_COUNTER_CLOCKWISE, 0);
VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE); VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE);
@ -466,64 +469,46 @@ public:
pipelineCI.pDynamicState = &dynamicStateCI; pipelineCI.pDynamicState = &dynamicStateCI;
pipelineCI.stageCount = static_cast<uint32_t>(shaderStages.size()); pipelineCI.stageCount = static_cast<uint32_t>(shaderStages.size());
pipelineCI.pStages = shaderStages.data(); pipelineCI.pStages = shaderStages.data();
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipeline)); VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipeline));
} }
void prepareUniformBuffers() void prepareUniformBuffers()
{ {
uboVS.instance = new UboInstanceData[layerCount]; uniformData.instance = new PerInstanceData[layerCount];
uint32_t uboSize = sizeof(uboVS.matrices) + (MAX_LAYERS * sizeof(UboInstanceData)); uint32_t uboSize = sizeof(uniformData.matrices) + (MAX_LAYERS * sizeof(PerInstanceData));
// Vertex shader uniform buffer block // Vertex shader uniform buffer block
VK_CHECK_RESULT(vulkanDevice->createBuffer( VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &uniformBuffer, uboSize));
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
&uniformBufferVS,
uboSize));
// Array indices and model matrices are fixed // Array indices and model matrices are fixed
float offset = -1.5f; float offset = -1.5f;
float center = (layerCount*offset) / 2.0f - (offset * 0.5f); float center = (layerCount*offset) / 2.0f - (offset * 0.5f);
for (uint32_t i = 0; i < layerCount; i++) { for (uint32_t i = 0; i < layerCount; i++) {
// Instance model matrix // Instance model matrix
uboVS.instance[i].model = glm::translate(glm::mat4(1.0f), glm::vec3(i * offset - center, 0.0f, 0.0f)); uniformData.instance[i].model = glm::translate(glm::mat4(1.0f), glm::vec3(i * offset - center, 0.0f, 0.0f));
uboVS.instance[i].model = glm::scale(uboVS.instance[i].model, glm::vec3(0.5f)); uniformData.instance[i].model = glm::scale(uniformData.instance[i].model, glm::vec3(0.5f));
// Instance texture array index // Instance texture array index
uboVS.instance[i].arrayIndex.x = (float)i; uniformData.instance[i].arrayIndex = (float)i;
} }
// Update instanced part of the uniform buffer // Update instanced part of the uniform buffer
uint8_t *pData; uint8_t *pData;
uint32_t dataOffset = sizeof(uboVS.matrices); uint32_t dataOffset = sizeof(uniformData.matrices);
uint32_t dataSize = layerCount * sizeof(UboInstanceData); uint32_t dataSize = layerCount * sizeof(PerInstanceData);
VK_CHECK_RESULT(vkMapMemory(device, uniformBufferVS.memory, dataOffset, dataSize, 0, (void **)&pData)); VK_CHECK_RESULT(vkMapMemory(device, uniformBuffer.memory, dataOffset, dataSize, 0, (void **)&pData));
memcpy(pData, uboVS.instance, dataSize); memcpy(pData, uniformData.instance, dataSize);
vkUnmapMemory(device, uniformBufferVS.memory); vkUnmapMemory(device, uniformBuffer.memory);
// Map persistent // Map persistent
VK_CHECK_RESULT(uniformBufferVS.map()); VK_CHECK_RESULT(uniformBuffer.map());
updateUniformBuffersCamera();
} }
void updateUniformBuffersCamera() void updateUniformBuffersCamera()
{ {
uboVS.matrices.projection = camera.matrices.perspective; uniformData.matrices.projection = camera.matrices.perspective;
uboVS.matrices.view = camera.matrices.view; uniformData.matrices.view = camera.matrices.view;
memcpy(uniformBufferVS.mapped, &uboVS.matrices, sizeof(uboVS.matrices)); memcpy(uniformBuffer.mapped, &uniformData.matrices, sizeof(uniformData.matrices));
}
void draw()
{
VulkanExampleBase::prepareFrame();
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));
VulkanExampleBase::submitFrame();
} }
void prepare() void prepare()
@ -532,26 +517,27 @@ public:
loadAssets(); loadAssets();
generateCube(); generateCube();
prepareUniformBuffers(); prepareUniformBuffers();
setupDescriptorSetLayout(); setupDescriptors();
preparePipelines(); preparePipelines();
setupDescriptorPool();
setupDescriptorSet();
buildCommandBuffers(); buildCommandBuffers();
prepared = true; prepared = true;
} }
void draw()
{
VulkanExampleBase::prepareFrame();
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));
VulkanExampleBase::submitFrame();
}
virtual void render() virtual void render()
{ {
if (!prepared) if (!prepared)
return; return;
draw();
if (camera.updated)
updateUniformBuffersCamera();
}
virtual void viewChanged()
{
updateUniformBuffersCamera(); updateUniformBuffersCamera();
draw();
} }
}; };

View file

@ -1,6 +1,9 @@
/* /*
* Vulkan Example - Cube map texture loading and displaying * Vulkan Example - Cube map texture loading and displaying
* *
* This sample shows how to load and render a cubemap. A cubemap is a textures that contains 6 images, one per cube face.
* The sample displays the cubemap as a skybox (background) and as a reflection on a selectable object
*
* Copyright (C) 2016-2023 by Sascha Willems - www.saschawillems.de * Copyright (C) 2016-2023 by Sascha Willems - www.saschawillems.de
* *
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
@ -18,36 +21,29 @@ public:
vks::Texture cubeMap; vks::Texture cubeMap;
struct Meshes { struct Models {
vkglTF::Model skybox; vkglTF::Model skybox;
// The sample lets you select different models to apply the cubemap to
std::vector<vkglTF::Model> objects; std::vector<vkglTF::Model> objects;
int32_t objectIndex = 0; int32_t objectIndex = 0;
} models; } models;
struct {
vks::Buffer object;
vks::Buffer skybox;
} uniformBuffers;
struct UBOVS { struct UBOVS {
glm::mat4 projection; glm::mat4 projection;
glm::mat4 modelView; glm::mat4 modelView;
glm::mat4 inverseModelview; glm::mat4 inverseModelview;
float lodBias = 0.0f; float lodBias = 0.0f;
} uboVS; } uboVS;
vks::Buffer uniformBuffer;
struct { struct {
VkPipeline skybox; VkPipeline skybox{ VK_NULL_HANDLE };
VkPipeline reflect; VkPipeline reflect{ VK_NULL_HANDLE };
} pipelines; } pipelines;
struct { VkPipelineLayout pipelineLayout{ VK_NULL_HANDLE };
VkDescriptorSet object; VkDescriptorSet descriptorSet{ VK_NULL_HANDLE };
VkDescriptorSet skybox; VkDescriptorSetLayout descriptorSetLayout{ VK_NULL_HANDLE };
} descriptorSets;
VkPipelineLayout pipelineLayout;
VkDescriptorSetLayout descriptorSetLayout;
std::vector<std::string> objectNames; std::vector<std::string> objectNames;
@ -63,23 +59,17 @@ public:
~VulkanExample() ~VulkanExample()
{ {
// Clean up used Vulkan resources if (device) {
// Note : Inherited destructor cleans up resources stored in base class vkDestroyImageView(device, cubeMap.view, nullptr);
vkDestroyImage(device, cubeMap.image, nullptr);
// Clean up texture resources vkDestroySampler(device, cubeMap.sampler, nullptr);
vkDestroyImageView(device, cubeMap.view, nullptr); vkFreeMemory(device, cubeMap.deviceMemory, nullptr);
vkDestroyImage(device, cubeMap.image, nullptr); vkDestroyPipeline(device, pipelines.skybox, nullptr);
vkDestroySampler(device, cubeMap.sampler, nullptr); vkDestroyPipeline(device, pipelines.reflect, nullptr);
vkFreeMemory(device, cubeMap.deviceMemory, nullptr); vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
vkDestroyPipeline(device, pipelines.skybox, nullptr); uniformBuffer.destroy();
vkDestroyPipeline(device, pipelines.reflect, nullptr); }
vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
uniformBuffers.object.destroy();
uniformBuffers.skybox.destroy();
} }
// Enable physical device features required for this example // Enable physical device features required for this example
@ -90,7 +80,8 @@ public:
} }
} }
void loadCubemap(std::string filename, VkFormat format, bool forceLinearTiling) // Loads a cubemap from a file, uploads it to the device and create all Vulkan resources required to display it
void loadCubemap(std::string filename, VkFormat format)
{ {
ktxResult result; ktxResult result;
ktxTexture* ktxTexture; ktxTexture* ktxTexture;
@ -315,16 +306,16 @@ public:
VkRect2D scissor = vks::initializers::rect2D(width, height, 0, 0); VkRect2D scissor = vks::initializers::rect2D(width, height, 0, 0);
vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor); vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor);
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr);
// Skybox // Skybox
if (displaySkybox) if (displaySkybox)
{ {
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets.skybox, 0, NULL);
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.skybox); vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.skybox);
models.skybox.draw(drawCmdBuffers[i]); models.skybox.draw(drawCmdBuffers[i]);
} }
// 3D object // 3D object
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets.object, 0, NULL);
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.reflect); vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.reflect);
models.objects[models.objectIndex].draw(drawCmdBuffers[i]); models.objects[models.objectIndex].draw(drawCmdBuffers[i]);
@ -349,111 +340,53 @@ public:
models.objects[i].loadFromFile(getAssetPath() + "models/" + filenames[i], vulkanDevice, queue, glTFLoadingFlags); models.objects[i].loadFromFile(getAssetPath() + "models/" + filenames[i], vulkanDevice, queue, glTFLoadingFlags);
} }
// Cubemap texture // Cubemap texture
const bool forceLinearTiling = false; loadCubemap(getAssetPath() + "textures/cubemap_yokohama_rgba.ktx", VK_FORMAT_R8G8B8A8_UNORM);
loadCubemap(getAssetPath() + "textures/cubemap_yokohama_rgba.ktx", VK_FORMAT_R8G8B8A8_UNORM, forceLinearTiling);
} }
void setupDescriptorPool() void setupDescriptors()
{ {
std::vector<VkDescriptorPoolSize> poolSizes = // Pool
{ std::vector<VkDescriptorPoolSize> poolSizes = {
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2), vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1),
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2) vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1)
}; };
VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo(poolSizes, 2);
VkDescriptorPoolCreateInfo descriptorPoolInfo =
vks::initializers::descriptorPoolCreateInfo(poolSizes, 2);
VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool)); VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool));
}
void setupDescriptorSetLayout() // Layout
{ std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = {
std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = // Binding 0 : Vertex shader uniform buffer
{ vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0),
// Binding 0 : Uniform buffer
vks::initializers::descriptorSetLayoutBinding(
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
0),
// Binding 1 : Fragment shader image sampler // Binding 1 : Fragment shader image sampler
vks::initializers::descriptorSetLayoutBinding( vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 1)
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
VK_SHADER_STAGE_FRAGMENT_BIT,
1)
}; };
VkDescriptorSetLayoutCreateInfo descriptorLayout = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings);
VkDescriptorSetLayoutCreateInfo descriptorLayout =
vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings);
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout)); VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout));
VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = // Set
vks::initializers::pipelineLayoutCreateInfo( VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1);
&descriptorSetLayout, VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet));
1);
VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pPipelineLayoutCreateInfo, nullptr, &pipelineLayout));
}
void setupDescriptorSets()
{
// Image descriptor for the cube map texture // Image descriptor for the cube map texture
VkDescriptorImageInfo textureDescriptor = VkDescriptorImageInfo textureDescriptor = vks::initializers::descriptorImageInfo(cubeMap.sampler, cubeMap.view, cubeMap.imageLayout);
vks::initializers::descriptorImageInfo(
cubeMap.sampler,
cubeMap.view,
cubeMap.imageLayout);
VkDescriptorSetAllocateInfo allocInfo =
vks::initializers::descriptorSetAllocateInfo(
descriptorPool,
&descriptorSetLayout,
1);
// 3D object descriptor set
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSets.object));
std::vector<VkWriteDescriptorSet> writeDescriptorSets = std::vector<VkWriteDescriptorSet> writeDescriptorSets =
{ {
// Binding 0 : Vertex shader uniform buffer // Binding 0 : Vertex shader uniform buffer
vks::initializers::writeDescriptorSet( vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffer.descriptor),
descriptorSets.object,
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
0,
&uniformBuffers.object.descriptor),
// Binding 1 : Fragment shader cubemap sampler // Binding 1 : Fragment shader cubemap sampler
vks::initializers::writeDescriptorSet( vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &textureDescriptor)
descriptorSets.object,
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
1,
&textureDescriptor)
};
vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr);
// Sky box descriptor set
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSets.skybox));
writeDescriptorSets =
{
// Binding 0 : Vertex shader uniform buffer
vks::initializers::writeDescriptorSet(
descriptorSets.skybox,
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
0,
&uniformBuffers.skybox.descriptor),
// Binding 1 : Fragment shader cubemap sampler
vks::initializers::writeDescriptorSet(
descriptorSets.skybox,
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
1,
&textureDescriptor)
}; };
vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr); vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr);
} }
void preparePipelines() void preparePipelines()
{ {
// Layout
const VkPipelineLayoutCreateInfo pipelineLayoutCI = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1);
VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCI, nullptr, &pipelineLayout));
// Pipeline
VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE); VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE);
VkPipelineRasterizationStateCreateInfo rasterizationState = vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_BACK_BIT, VK_FRONT_FACE_COUNTER_CLOCKWISE, 0); VkPipelineRasterizationStateCreateInfo rasterizationState = vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_BACK_BIT, VK_FRONT_FACE_COUNTER_CLOCKWISE, 0);
VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE); VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE);
@ -496,39 +429,28 @@ public:
// Prepare and initialize uniform buffer containing shader uniforms // Prepare and initialize uniform buffer containing shader uniforms
void prepareUniformBuffers() void prepareUniformBuffers()
{ {
// Object vertex shader uniform buffer VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &uniformBuffer, sizeof(uboVS)));
VK_CHECK_RESULT(vulkanDevice->createBuffer( VK_CHECK_RESULT(uniformBuffer.map());
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
&uniformBuffers.object,
sizeof(uboVS)));
// Skybox vertex shader uniform buffer
VK_CHECK_RESULT(vulkanDevice->createBuffer(
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
&uniformBuffers.skybox,
sizeof(uboVS)));
// Map persistent
VK_CHECK_RESULT(uniformBuffers.object.map());
VK_CHECK_RESULT(uniformBuffers.skybox.map());
updateUniformBuffers();
} }
void updateUniformBuffers() void updateUniformBuffers()
{ {
// 3D object
uboVS.projection = camera.matrices.perspective; uboVS.projection = camera.matrices.perspective;
// Note: Both the object and skybox use the same uniform data, the translation part of the skybox is removed in the shader (see skybox.vert)
uboVS.modelView = camera.matrices.view; uboVS.modelView = camera.matrices.view;
uboVS.inverseModelview = glm::inverse(camera.matrices.view); uboVS.inverseModelview = glm::inverse(camera.matrices.view);
memcpy(uniformBuffers.object.mapped, &uboVS, sizeof(uboVS)); memcpy(uniformBuffer.mapped, &uboVS, sizeof(uboVS));
// Skybox }
uboVS.modelView = camera.matrices.view;
// Cancel out translation void prepare()
uboVS.modelView[3] = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); {
memcpy(uniformBuffers.skybox.mapped, &uboVS, sizeof(uboVS)); VulkanExampleBase::prepare();
loadAssets();
prepareUniformBuffers();
setupDescriptors();
preparePipelines();
buildCommandBuffers();
prepared = true;
} }
void draw() void draw()
@ -539,30 +461,12 @@ public:
VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE)); VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));
VulkanExampleBase::submitFrame(); VulkanExampleBase::submitFrame();
} }
void prepare()
{
VulkanExampleBase::prepare();
loadAssets();
prepareUniformBuffers();
setupDescriptorSetLayout();
preparePipelines();
setupDescriptorPool();
setupDescriptorSets();
buildCommandBuffers();
prepared = true;
}
virtual void render() virtual void render()
{ {
if (!prepared) if (!prepared)
return; return;
draw();
}
virtual void viewChanged()
{
updateUniformBuffers(); updateUniformBuffers();
draw();
} }
virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay) virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay)

View file

@ -1,7 +1,10 @@
/* /*
* Vulkan Example - Cube map array texture loading and displaying * Vulkan Example - Cube map array texture loading and displaying
* *
* Copyright (C) 2020 by Sascha Willems - www.saschawillems.de * This sample shows how load and render an cubemap array texture. A single image contains multiple cube maps.
* The cubemap to be displayed is selected in the fragment shader
*
* Copyright (C) 2020-2023 by Sascha Willems - www.saschawillems.de
* *
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
*/ */
@ -24,31 +27,24 @@ public:
int32_t objectIndex = 0; int32_t objectIndex = 0;
} models; } models;
struct { struct UniformData {
vks::Buffer object;
vks::Buffer skybox;
} uniformBuffers;
struct ShaderData {
glm::mat4 projection; glm::mat4 projection;
glm::mat4 modelView; glm::mat4 modelView;
glm::mat4 inverseModelview; glm::mat4 inverseModelview;
float lodBias = 0.0f; float lodBias = 0.0f;
// Used by the fragment shader to select the cubemap from the array cubemap
int cubeMapIndex = 1; int cubeMapIndex = 1;
} shaderData; } uniformData;
vks::Buffer uniformBuffer;
struct { struct {
VkPipeline skybox; VkPipeline skybox{ VK_NULL_HANDLE };
VkPipeline reflect; VkPipeline reflect{ VK_NULL_HANDLE };
} pipelines; } pipelines;
struct { VkPipelineLayout pipelineLayout{ VK_NULL_HANDLE };
VkDescriptorSet object; VkDescriptorSet descriptorSet{ VK_NULL_HANDLE };
VkDescriptorSet skybox; VkDescriptorSetLayout descriptorSetLayout{ VK_NULL_HANDLE };
} descriptorSets;
VkPipelineLayout pipelineLayout;
VkDescriptorSetLayout descriptorSetLayout;
std::vector<std::string> objectNames; std::vector<std::string> objectNames;
@ -63,25 +59,23 @@ public:
~VulkanExample() ~VulkanExample()
{ {
// Clean up texture resources if (device) {
vkDestroyImageView(device, cubeMapArray.view, nullptr); vkDestroyImageView(device, cubeMapArray.view, nullptr);
vkDestroyImage(device, cubeMapArray.image, nullptr); vkDestroyImage(device, cubeMapArray.image, nullptr);
vkDestroySampler(device, cubeMapArray.sampler, nullptr); vkDestroySampler(device, cubeMapArray.sampler, nullptr);
vkFreeMemory(device, cubeMapArray.deviceMemory, nullptr); vkFreeMemory(device, cubeMapArray.deviceMemory, nullptr);
vkDestroyPipeline(device, pipelines.skybox, nullptr);
vkDestroyPipeline(device, pipelines.skybox, nullptr); vkDestroyPipeline(device, pipelines.reflect, nullptr);
vkDestroyPipeline(device, pipelines.reflect, nullptr); vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
vkDestroyPipelineLayout(device, pipelineLayout, nullptr); uniformBuffer.destroy();
vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); }
uniformBuffers.object.destroy();
uniformBuffers.skybox.destroy();
} }
// Enable physical device features required for this example // Enable physical device features required for this example
virtual void getEnabledFeatures() virtual void getEnabledFeatures()
{ {
// This sample requires support for cube map arrays
if (deviceFeatures.imageCubeArray) { if (deviceFeatures.imageCubeArray) {
enabledFeatures.imageCubeArray = VK_TRUE; enabledFeatures.imageCubeArray = VK_TRUE;
} else { } else {
@ -93,7 +87,8 @@ public:
} }
}; };
void loadCubemapArray(std::string filename, VkFormat format, bool forceLinearTiling) // Loads a cubemap array from a file, uploads it to the device and create all Vulkan resources required to display it
void loadCubemapArray(std::string filename, VkFormat format)
{ {
ktxResult result; ktxResult result;
ktxTexture* ktxTexture; ktxTexture* ktxTexture;
@ -320,16 +315,16 @@ public:
VkRect2D scissor = vks::initializers::rect2D(width, height, 0, 0); VkRect2D scissor = vks::initializers::rect2D(width, height, 0, 0);
vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor); vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor);
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr);
// Skybox // Skybox
if (displaySkybox) if (displaySkybox)
{ {
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets.skybox, 0, NULL);
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.skybox); vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.skybox);
models.skybox.draw(drawCmdBuffers[i]); models.skybox.draw(drawCmdBuffers[i]);
} }
// 3D object // 3D object
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets.object, 0, NULL);
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.reflect); vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.reflect);
models.objects[models.objectIndex].draw(drawCmdBuffers[i]); models.objects[models.objectIndex].draw(drawCmdBuffers[i]);
@ -354,66 +349,53 @@ public:
models.objects[i].loadFromFile(getAssetPath() + "models/" + filenames[i], vulkanDevice, queue, glTFLoadingFlags); models.objects[i].loadFromFile(getAssetPath() + "models/" + filenames[i], vulkanDevice, queue, glTFLoadingFlags);
} }
// Load the cube map array from a ktx texture file // Load the cube map array from a ktx texture file
loadCubemapArray(getAssetPath() + "textures/cubemap_array.ktx", VK_FORMAT_R8G8B8A8_UNORM, false); loadCubemapArray(getAssetPath() + "textures/cubemap_array.ktx", VK_FORMAT_R8G8B8A8_UNORM);
} }
void setupDescriptorPool() void setupDescriptors()
{ {
const std::vector<VkDescriptorPoolSize> poolSizes = { // Pool
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2), std::vector<VkDescriptorPoolSize> poolSizes = {
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2) vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1),
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1)
}; };
const VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo(poolSizes, 2); VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo(poolSizes, 2);
VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool)); VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool));
}
void setupDescriptorSetLayout() // Layout
{ std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = {
const std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = { // Binding 0 : Vertex shader uniform buffer
// Binding 0 : Uniform buffer
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0), vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0),
// Binding 1 : Fragment shader image sampler // Binding 1 : Fragment shader image sampler
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 1) vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 1)
}; };
VkDescriptorSetLayoutCreateInfo descriptorLayout = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings);
const VkDescriptorSetLayoutCreateInfo descriptorLayout = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings);
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout)); VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout));
const VkPipelineLayoutCreateInfo pipelineLayoutCI = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1); // Set
VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCI, nullptr, &pipelineLayout));
}
void setupDescriptorSets()
{
// Image descriptor for the cube map texture
VkDescriptorImageInfo textureDescriptor = vks::initializers::descriptorImageInfo(cubeMapArray.sampler, cubeMapArray.view, cubeMapArray.imageLayout);
VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1); VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1);
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet));
// Image descriptor for the cube map array texture
VkDescriptorImageInfo textureDescriptor = vks::initializers::descriptorImageInfo(cubeMapArray.sampler, cubeMapArray.view, cubeMapArray.imageLayout);
// 3D object descriptor set
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSets.object));
std::vector<VkWriteDescriptorSet> writeDescriptorSets = std::vector<VkWriteDescriptorSet> writeDescriptorSets =
{ {
// Binding 0 : Vertex shader uniform buffer // Binding 0 : Vertex shader uniform buffer
vks::initializers::writeDescriptorSet(descriptorSets.object, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffers.object.descriptor), vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffer.descriptor),
// Binding 1 : Fragment shader cubemap sampler // Binding 1 : Fragment shader cubemap sampler
vks::initializers::writeDescriptorSet(descriptorSets.object, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &textureDescriptor) vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &textureDescriptor)
};
vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr);
// Sky box descriptor set
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSets.skybox));
writeDescriptorSets =
{
// Binding 0 : Vertex shader uniform buffer
vks::initializers::writeDescriptorSet(descriptorSets.skybox, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffers.skybox.descriptor),
// Binding 1 : Fragment shader cubemap sampler
vks::initializers::writeDescriptorSet(descriptorSets.skybox, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &textureDescriptor)
}; };
vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr); vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr);
} }
void preparePipelines() void preparePipelines()
{ {
// Layout
const VkPipelineLayoutCreateInfo pipelineLayoutCI = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1);
VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCI, nullptr, &pipelineLayout));
// Pipelines
VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE); VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE);
VkPipelineRasterizationStateCreateInfo rasterizationState = vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_BACK_BIT, VK_FRONT_FACE_COUNTER_CLOCKWISE, 0); VkPipelineRasterizationStateCreateInfo rasterizationState = vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_BACK_BIT, VK_FRONT_FACE_COUNTER_CLOCKWISE, 0);
VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE); VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE);
@ -457,39 +439,28 @@ public:
void prepareUniformBuffers() void prepareUniformBuffers()
{ {
// Object vertex shader uniform buffer // Object vertex shader uniform buffer
VK_CHECK_RESULT(vulkanDevice->createBuffer( VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &uniformBuffer, sizeof(UniformData)));
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
&uniformBuffers.object,
sizeof(ShaderData)));
// Skybox vertex shader uniform buffer
VK_CHECK_RESULT(vulkanDevice->createBuffer(
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
&uniformBuffers.skybox,
sizeof(ShaderData)));
// Map persistent // Map persistent
VK_CHECK_RESULT(uniformBuffers.object.map()); VK_CHECK_RESULT(uniformBuffer.map());
VK_CHECK_RESULT(uniformBuffers.skybox.map());
updateUniformBuffers();
} }
void updateUniformBuffers() void updateUniformBuffers()
{ {
// 3D object uniformData.projection = camera.matrices.perspective;
shaderData.projection = camera.matrices.perspective; uniformData.modelView = camera.matrices.view;
shaderData.modelView = camera.matrices.view; uniformData.inverseModelview = glm::inverse(camera.matrices.view);
shaderData.inverseModelview = glm::inverse(camera.matrices.view); memcpy(uniformBuffer.mapped, &uniformData, sizeof(UniformData));
memcpy(uniformBuffers.object.mapped, &shaderData, sizeof(ShaderData)); }
// Skybox void prepare()
shaderData.modelView = camera.matrices.view; {
// Cancel out translation VulkanExampleBase::prepare();
shaderData.modelView[3] = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); loadAssets();
memcpy(uniformBuffers.skybox.mapped, &shaderData, sizeof(ShaderData)); prepareUniformBuffers();
setupDescriptors();
preparePipelines();
buildCommandBuffers();
prepared = true;
} }
void draw() void draw()
@ -501,43 +472,19 @@ public:
VulkanExampleBase::submitFrame(); VulkanExampleBase::submitFrame();
} }
void prepare()
{
VulkanExampleBase::prepare();
loadAssets();
prepareUniformBuffers();
setupDescriptorSetLayout();
preparePipelines();
setupDescriptorPool();
setupDescriptorSets();
buildCommandBuffers();
prepared = true;
}
virtual void render() virtual void render()
{ {
if (!prepared) if (!prepared)
return; return;
draw();
if (camera.updated) {
updateUniformBuffers();
}
}
virtual void viewChanged()
{
updateUniformBuffers(); updateUniformBuffers();
draw();
} }
virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay) virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay)
{ {
if (overlay->header("Settings")) { if (overlay->header("Settings")) {
if (overlay->sliderInt("Cube map", &shaderData.cubeMapIndex, 0, cubeMapArray.layerCount - 1)) { overlay->sliderInt("Cube map", &uniformData.cubeMapIndex, 0, cubeMapArray.layerCount - 1);
updateUniformBuffers(); overlay->sliderFloat("LOD bias", &uniformData.lodBias, 0.0f, (float)cubeMapArray.mipLevels);
}
if (overlay->sliderFloat("LOD bias", &shaderData.lodBias, 0.0f, (float)cubeMapArray.mipLevels)) {
updateUniformBuffers();
}
if (overlay->comboBox("Object type", &models.objectIndex, objectNames)) { if (overlay->comboBox("Object type", &models.objectIndex, objectNames)) {
buildCommandBuffers(); buildCommandBuffers();
} }

View file

@ -6,7 +6,7 @@ layout (location = 1) in vec2 inUV;
struct Instance struct Instance
{ {
mat4 model; mat4 model;
vec4 arrayIndex; float arrayIndex;
}; };
layout (binding = 0) uniform UBO layout (binding = 0) uniform UBO
@ -20,7 +20,7 @@ layout (location = 0) out vec3 outUV;
void main() void main()
{ {
outUV = vec3(inUV, ubo.instance[gl_InstanceIndex].arrayIndex.x); outUV = vec3(inUV, ubo.instance[gl_InstanceIndex].arrayIndex);
mat4 modelView = ubo.view * ubo.instance[gl_InstanceIndex].model; mat4 modelView = ubo.view * ubo.instance[gl_InstanceIndex].model;
gl_Position = ubo.projection * modelView * vec4(inPos, 1.0); gl_Position = ubo.projection * modelView * vec4(inPos, 1.0);
} }

View file

@ -15,5 +15,7 @@ void main()
outUVW = inPos; outUVW = inPos;
// Convert cubemap coordinates into Vulkan coordinate space // Convert cubemap coordinates into Vulkan coordinate space
outUVW.xy *= -1.0; outUVW.xy *= -1.0;
gl_Position = ubo.projection * ubo.model * vec4(inPos.xyz, 1.0); // Remove translation from view matrix
mat4 viewMat = mat4(mat3(ubo.model));
gl_Position = ubo.projection * viewMat * vec4(inPos.xyz, 1.0);
} }

View file

@ -17,5 +17,7 @@ void main()
{ {
outUVW = inPos; outUVW = inPos;
outUVW.yz *= -1.0f; outUVW.yz *= -1.0f;
gl_Position = ubo.projection * ubo.model * vec4(inPos.xyz, 1.0); // Remove translation from view matrix
mat4 viewMat = mat4(mat3(ubo.model));
gl_Position = ubo.projection * viewMat * vec4(inPos.xyz, 1.0);
} }

View file

@ -9,7 +9,7 @@ struct VSInput
struct Instance struct Instance
{ {
float4x4 model; float4x4 model;
float4 arrayIndex; float arrayIndex;
}; };
struct UBO struct UBO
@ -30,7 +30,7 @@ struct VSOutput
VSOutput main(VSInput input, uint InstanceIndex : SV_InstanceID) VSOutput main(VSInput input, uint InstanceIndex : SV_InstanceID)
{ {
VSOutput output = (VSOutput)0; VSOutput output = (VSOutput)0;
output.UV = float3(input.UV, ubo.instance[InstanceIndex].arrayIndex.x); output.UV = float3(input.UV, ubo.instance[InstanceIndex].arrayIndex);
float4x4 modelView = mul(ubo.view, ubo.instance[InstanceIndex].model); float4x4 modelView = mul(ubo.view, ubo.instance[InstanceIndex].model);
output.Pos = mul(ubo.projection, mul(modelView, float4(input.Pos, 1.0))); output.Pos = mul(ubo.projection, mul(modelView, float4(input.Pos, 1.0)));
return output; return output;

View file

@ -20,6 +20,11 @@ VSOutput main([[vk::location(0)]] float3 Pos : POSITION0)
output.UVW = Pos; output.UVW = Pos;
// Convert cubemap coordinates into Vulkan coordinate space // Convert cubemap coordinates into Vulkan coordinate space
output.UVW.xy *= -1.0; output.UVW.xy *= -1.0;
output.Pos = mul(ubo.projection, mul(ubo.model, float4(Pos.xyz, 1.0))); // Remove translation from view matrix
float4x4 viewMat = ubo.model;
viewMat[0][3] = 0.0;
viewMat[1][3] = 0.0;
viewMat[2][3] = 0.0;
output.Pos = mul(ubo.projection, mul(viewMat, float4(Pos.xyz, 1.0)));
return output; return output;
} }

View file

@ -22,6 +22,11 @@ VSOutput main([[vk::location(0)]] float3 Pos : POSITION0)
VSOutput output = (VSOutput)0; VSOutput output = (VSOutput)0;
output.UVW = Pos; output.UVW = Pos;
output.UVW.yz *= -1.0; output.UVW.yz *= -1.0;
output.Pos = mul(ubo.projection, mul(ubo.model, float4(Pos.xyz, 1.0))); // Remove translation from view matrix
float4x4 viewMat = ubo.model;
viewMat[0][3] = 0.0;
viewMat[1][3] = 0.0;
viewMat[2][3] = 0.0;
output.Pos = mul(ubo.projection, mul(viewMat, float4(Pos.xyz, 1.0)));
return output; return output;
} }