diff --git a/examples/texture/texture.cpp b/examples/texture/texture.cpp index 9c9dfbaa..d8dee7cb 100644 --- a/examples/texture/texture.cpp +++ b/examples/texture/texture.cpp @@ -1,7 +1,9 @@ /* * Vulkan Example - Texture loading (and display) example (including mip maps) +* +* 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-2017 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) */ @@ -23,41 +25,33 @@ public: // 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 struct Texture { - VkSampler sampler; - VkImage image; + VkSampler sampler{ VK_NULL_HANDLE }; + VkImage image{ VK_NULL_HANDLE }; VkImageLayout imageLayout; - VkDeviceMemory deviceMemory; - VkImageView view; - uint32_t width, height; - uint32_t mipLevels; + VkDeviceMemory deviceMemory{ VK_NULL_HANDLE }; + VkImageView view{ VK_NULL_HANDLE }; + uint32_t width{ 0 }; + uint32_t height{ 0 }; + uint32_t mipLevels{ 0 }; } texture; - struct { - VkPipelineVertexInputStateCreateInfo inputState; - std::vector bindingDescriptions; - std::vector attributeDescriptions; - } vertices; - vks::Buffer vertexBuffer; vks::Buffer indexBuffer; - uint32_t indexCount; + uint32_t indexCount{ 0 }; - vks::Buffer uniformBufferVS; - - struct { + struct UniformData { glm::mat4 projection; glm::mat4 modelView; glm::vec4 viewPos; + // This is used to change the bias for the level-of-detail (mips) in the fragment shader float lodBias = 0.0f; - } uboVS; + } uniformData; + vks::Buffer uniformBuffer; - struct { - VkPipeline solid; - } pipelines; - - VkPipelineLayout pipelineLayout; - VkDescriptorSet descriptorSet; - VkDescriptorSetLayout descriptorSetLayout; + VkPipeline pipeline{ VK_NULL_HANDLE }; + VkPipelineLayout pipelineLayout{ VK_NULL_HANDLE }; + VkDescriptorSet descriptorSet{ VK_NULL_HANDLE }; + VkDescriptorSetLayout descriptorSetLayout{ VK_NULL_HANDLE }; VulkanExample() : VulkanExampleBase() { @@ -70,19 +64,15 @@ public: ~VulkanExample() { - // Clean up used Vulkan resources - // Note : Inherited destructor cleans up resources stored in base class - - destroyTextureImage(texture); - - vkDestroyPipeline(device, pipelines.solid, nullptr); - - vkDestroyPipelineLayout(device, pipelineLayout, nullptr); - vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); - - vertexBuffer.destroy(); - indexBuffer.destroy(); - uniformBufferVS.destroy(); + if (device) { + destroyTextureImage(texture); + vkDestroyPipeline(device, pipeline, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); + vertexBuffer.destroy(); + indexBuffer.destroy(); + uniformBuffer.destroy(); + } } // Enable physical device features required for this example @@ -480,8 +470,8 @@ public: VkRect2D scissor = vks::initializers::rect2D(width, height, 0, 0); vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor); - vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, NULL); - vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.solid); + vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr); + vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); VkDeviceSize offsets[1] = { 0 }; vkCmdBindVertexBuffers(drawCmdBuffers[i], 0, 1, &vertexBuffer.buffer, offsets); @@ -497,20 +487,8 @@ public: } } - void draw() - { - 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(); - } - + // Creates a vertex and index buffer for a quad made of two triangles + // This is used to display the texture on void generateQuad() { // Setup vertices for a single uv-mapped quad made from two triangles @@ -526,213 +504,115 @@ public: std::vector indices = { 0,1,2, 2,3,0 }; indexCount = static_cast(indices.size()); - // Create buffers - // For the sake of simplicity we won't stage the vertex data to the gpu memory - // Vertex buffer - VK_CHECK_RESULT(vulkanDevice->createBuffer( - VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, - &vertexBuffer, - vertices.size() * sizeof(Vertex), - vertices.data())); - // Index buffer - VK_CHECK_RESULT(vulkanDevice->createBuffer( - VK_BUFFER_USAGE_INDEX_BUFFER_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, - &indexBuffer, - indices.size() * sizeof(uint32_t), - indices.data())); + // Create buffers and upload data to the GPU + struct StagingBuffers { + vks::Buffer vertices; + vks::Buffer indices; + } stagingBuffers; + + // Host visible source buffers (staging) + 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_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())); + + // Device local destination buffers + 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_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))); + + // 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 setupVertexDescriptions() + void setupDescriptors() { - // Binding description - vertices.bindingDescriptions.resize(1); - 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(vertices.bindingDescriptions.size()); - vertices.inputState.pVertexBindingDescriptions = vertices.bindingDescriptions.data(); - vertices.inputState.vertexAttributeDescriptionCount = static_cast(vertices.attributeDescriptions.size()); - vertices.inputState.pVertexAttributeDescriptions = vertices.attributeDescriptions.data(); - } - - void setupDescriptorPool() - { - // Example uses one ubo and one image sampler - std::vector poolSizes = - { + // Pool + std::vector poolSizes = { 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) }; - - VkDescriptorPoolCreateInfo descriptorPoolInfo = - vks::initializers::descriptorPoolCreateInfo( - static_cast(poolSizes.size()), - poolSizes.data(), - 2); - + VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo(poolSizes, 2); VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool)); - } - void setupDescriptorSetLayout() - { - std::vector setLayoutBindings = - { + // Layout + std::vector setLayoutBindings = { // 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 - 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.data(), - static_cast(setLayoutBindings.size())); - + VkDescriptorSetLayoutCreateInfo descriptorLayout = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings); VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout)); - VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = - vks::initializers::pipelineLayoutCreateInfo( - &descriptorSetLayout, - 1); - - VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pPipelineLayoutCreateInfo, nullptr, &pipelineLayout)); - } - - void setupDescriptorSet() - { - VkDescriptorSetAllocateInfo allocInfo = - vks::initializers::descriptorSetAllocateInfo( - descriptorPool, - &descriptorSetLayout, - 1); - + // Set + VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1); VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet)); // Setup a descriptor image info for the current texture to be used as a combined image sampler VkDescriptorImageInfo textureDescriptor; - textureDescriptor.imageView = texture.view; // 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.imageLayout = texture.imageLayout; // The current layout of the image (Note: Should always fit the actual use, e.g. shader read) + // The image's view (images are never directly accessed by the shader, but rather through views defining subresources) + textureDescriptor.imageView = texture.view; + // 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 writeDescriptorSets = - { + std::vector writeDescriptorSets = { // 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 texture sampler // Fragment shader: layout (binding = 1) uniform sampler2D samplerColor; - vks::initializers::writeDescriptorSet( - descriptorSet, - VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, // The descriptor set will use a combined image sampler (sampler and image could be split) + vks::initializers::writeDescriptorSet(descriptorSet, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, // The descriptor set will use a combined image sampler (as opposed to splitting image and sampler) 1, // Shader binding point 1 &textureDescriptor) // Pointer to the descriptor image for our texture }; - - vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL); + vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr); } void preparePipelines() { - VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = - vks::initializers::pipelineInputAssemblyStateCreateInfo( - VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, - 0, - VK_FALSE); + // Layout + VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1); + VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout)); - VkPipelineRasterizationStateCreateInfo rasterizationState = - 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); - - 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 dynamicStateEnables = { - VK_DYNAMIC_STATE_VIEWPORT, - VK_DYNAMIC_STATE_SCISSOR - }; - VkPipelineDynamicStateCreateInfo dynamicState = - vks::initializers::pipelineDynamicStateCreateInfo( - dynamicStateEnables.data(), - static_cast(dynamicStateEnables.size()), - 0); - - // Load shaders + // Pipeline + 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_NONE, VK_FRONT_FACE_COUNTER_CLOCKWISE, 0); + VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, 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 dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; + VkPipelineDynamicStateCreateInfo dynamicState = vks::initializers::pipelineDynamicStateCreateInfo(dynamicStateEnables); std::array shaderStages; + // Shaders 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); - VkGraphicsPipelineCreateInfo pipelineCreateInfo = - vks::initializers::pipelineCreateInfo( - pipelineLayout, - renderPass, - 0); + // Vertex input state + std::vector vertexInputBindings = { + vks::initializers::vertexInputBindingDescription(0, sizeof(Vertex), VK_VERTEX_INPUT_RATE_VERTEX) + }; + std::vector 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(vertexInputBindings.size()); + vertexInputState.pVertexBindingDescriptions = vertexInputBindings.data(); + vertexInputState.vertexAttributeDescriptionCount = static_cast(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.pRasterizationState = &rasterizationState; pipelineCreateInfo.pColorBlendState = &colorBlendState; @@ -742,31 +622,23 @@ public: pipelineCreateInfo.pDynamicState = &dynamicState; pipelineCreateInfo.stageCount = static_cast(shaderStages.size()); pipelineCreateInfo.pStages = shaderStages.data(); - - VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.solid)); + VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipeline)); } // Prepare and initialize uniform buffer containing shader uniforms void prepareUniformBuffers() { // Vertex shader uniform buffer block - VK_CHECK_RESULT(vulkanDevice->createBuffer( - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, - &uniformBufferVS, - sizeof(uboVS), - &uboVS)); - VK_CHECK_RESULT(uniformBufferVS.map()); - - updateUniformBuffers(); + 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_CHECK_RESULT(uniformBuffer.map()); } void updateUniformBuffers() { - uboVS.projection = camera.matrices.perspective; - uboVS.modelView = camera.matrices.view; - uboVS.viewPos = camera.viewPos; - memcpy(uniformBufferVS.mapped, &uboVS, sizeof(uboVS)); + uniformData.projection = camera.matrices.perspective; + uniformData.modelView = camera.matrices.view; + uniformData.viewPos = camera.viewPos; + memcpy(uniformBuffer.mapped, &uniformData, sizeof(uniformData)); } void prepare() @@ -774,32 +646,34 @@ public: VulkanExampleBase::prepare(); loadTexture(); generateQuad(); - setupVertexDescriptions(); prepareUniformBuffers(); - setupDescriptorSetLayout(); + setupDescriptors(); preparePipelines(); - setupDescriptorPool(); - setupDescriptorSet(); buildCommandBuffers(); 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() { if (!prepared) return; - draw(); - } - - virtual void viewChanged() - { updateUniformBuffers(); + draw(); } virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay) { 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(); } } diff --git a/examples/texture3d/texture3d.cpp b/examples/texture3d/texture3d.cpp index f7de92b3..a7ac3320 100644 --- a/examples/texture3d/texture3d.cpp +++ b/examples/texture3d/texture3d.cpp @@ -134,36 +134,30 @@ public: VkImageView view = VK_NULL_HANDLE; VkDescriptorImageInfo descriptor; VkFormat format; - uint32_t width, height, depth; - uint32_t mipLevels; + uint32_t width{ 0 }; + uint32_t height{ 0 }; + uint32_t depth{ 0 }; + uint32_t mipLevels{ 0 }; } texture; - struct { - VkPipelineVertexInputStateCreateInfo inputState; - std::vector inputBinding; - std::vector inputAttributes; - } vertices; - vks::Buffer vertexBuffer; vks::Buffer indexBuffer; - uint32_t indexCount; + uint32_t indexCount{ 0 }; - vks::Buffer uniformBufferVS; - - struct UboVS { + struct UniformData { glm::mat4 projection; glm::mat4 modelView; glm::vec4 viewPos; + // The current depth level of the texture to display + // This is animated float depth = 0.0f; - } uboVS; + } uniformData; + vks::Buffer uniformBuffer; - struct { - VkPipeline solid; - } pipelines; - - VkPipelineLayout pipelineLayout; - VkDescriptorSet descriptorSet; - VkDescriptorSetLayout descriptorSetLayout; + VkPipeline pipeline{ VK_NULL_HANDLE }; + VkPipelineLayout pipelineLayout{ VK_NULL_HANDLE }; + VkDescriptorSet descriptorSet{ VK_NULL_HANDLE }; + VkDescriptorSetLayout descriptorSetLayout{ VK_NULL_HANDLE }; VulkanExample() : VulkanExampleBase() { @@ -177,19 +171,15 @@ public: ~VulkanExample() { - // Clean up used Vulkan resources - // Note : Inherited destructor cleans up resources stored in base class - - destroyTextureImage(texture); - - vkDestroyPipeline(device, pipelines.solid, nullptr); - - vkDestroyPipelineLayout(device, pipelineLayout, nullptr); - vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); - - vertexBuffer.destroy(); - indexBuffer.destroy(); - uniformBufferVS.destroy(); + if (device) { + destroyTextureImage(texture); + vkDestroyPipeline(device, pipeline, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); + vertexBuffer.destroy(); + indexBuffer.destroy(); + uniformBuffer.destroy(); + } } // Prepare all Vulkan resources for the 3D texture (including descriptors) @@ -312,14 +302,8 @@ public: float nx = (float)x / (float)texture.width; float ny = (float)y / (float)texture.height; float nz = (float)z / (float)texture.depth; -#define FRACTAL -#ifdef FRACTAL 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); - data[x + y * texture.width + z * texture.width * texture.height] = static_cast(floor(n * 255)); } } @@ -457,7 +441,7 @@ public: vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor); 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 }; vkCmdBindVertexBuffers(drawCmdBuffers[i], 0, 1, &vertexBuffer.buffer, offsets); @@ -472,20 +456,8 @@ public: } } - void draw() - { - 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(); - } - + // Creates a vertex and index buffer for a quad made of two triangles + // This is used to display the texture on void generateQuad() { // Setup vertices for a single uv-mapped quad made from two triangles @@ -501,206 +473,108 @@ public: std::vector indices = { 0,1,2, 2,3,0 }; indexCount = static_cast(indices.size()); - // Create buffers - // For the sake of simplicity we won't stage the vertex data to the gpu memory - // Vertex buffer - VK_CHECK_RESULT(vulkanDevice->createBuffer( - VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, - &vertexBuffer, - vertices.size() * sizeof(Vertex), - vertices.data())); - // Index buffer - VK_CHECK_RESULT(vulkanDevice->createBuffer( - VK_BUFFER_USAGE_INDEX_BUFFER_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, - &indexBuffer, - indices.size() * sizeof(uint32_t), - indices.data())); + // Create buffers and upload data to the GPU + struct StagingBuffers { + vks::Buffer vertices; + vks::Buffer indices; + } stagingBuffers; + + // Host visible source buffers (staging) + 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_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())); + + // Device local destination buffers + 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_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))); + + // 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 setupVertexDescriptions() + void setupDescriptors() { - // Binding description - vertices.inputBinding.resize(1); - 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(vertices.inputBinding.size()); - vertices.inputState.pVertexBindingDescriptions = vertices.inputBinding.data(); - vertices.inputState.vertexAttributeDescriptionCount = static_cast(vertices.inputAttributes.size()); - vertices.inputState.pVertexAttributeDescriptions = vertices.inputAttributes.data(); - } - - void setupDescriptorPool() - { - // Example uses one ubo and one image sampler - std::vector poolSizes = - { + // Pool + std::vector poolSizes = { vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1), vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1) }; - - VkDescriptorPoolCreateInfo descriptorPoolInfo = - vks::initializers::descriptorPoolCreateInfo( - static_cast(poolSizes.size()), - poolSizes.data(), - 2); - + VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo(poolSizes, 2); VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool)); - } - void setupDescriptorSetLayout() - { - std::vector setLayoutBindings = - { + // Layout + std::vector setLayoutBindings = { // 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 - 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.data(), - static_cast(setLayoutBindings.size())); - + VkDescriptorSetLayoutCreateInfo descriptorLayout = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings); VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout)); - VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = - vks::initializers::pipelineLayoutCreateInfo( - &descriptorSetLayout, - 1); - - VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pPipelineLayoutCreateInfo, nullptr, &pipelineLayout)); - } - - void setupDescriptorSet() - { - VkDescriptorSetAllocateInfo allocInfo = - vks::initializers::descriptorSetAllocateInfo( - descriptorPool, - &descriptorSetLayout, - 1); - + // Set + VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1); VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet)); - std::vector writeDescriptorSets = - { - // Binding 0 : Vertex shader uniform buffer - vks::initializers::writeDescriptorSet( - descriptorSet, - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - 0, - &uniformBufferVS.descriptor), - // Binding 1 : Fragment shader texture sampler - vks::initializers::writeDescriptorSet( - descriptorSet, - VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - 1, - &texture.descriptor) - }; + // Image descriptor for the 3D texture + VkDescriptorImageInfo textureDescriptor = + vks::initializers::descriptorImageInfo( + texture.sampler, + texture.view, + texture.imageLayout); - vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL); + std::vector 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(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr); } void preparePipelines() { - VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = - vks::initializers::pipelineInputAssemblyStateCreateInfo( - VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, - 0, - VK_FALSE); + // Layout + VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1); + VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout)); - VkPipelineRasterizationStateCreateInfo rasterizationState = - 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); - - 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 dynamicStateEnables = { - VK_DYNAMIC_STATE_VIEWPORT, - VK_DYNAMIC_STATE_SCISSOR - }; - VkPipelineDynamicStateCreateInfo dynamicState = - vks::initializers::pipelineDynamicStateCreateInfo( - dynamicStateEnables.data(), - static_cast(dynamicStateEnables.size()), - 0); - - // Load shaders + // Pipeline + 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_NONE, VK_FRONT_FACE_COUNTER_CLOCKWISE, 0); + VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, 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 dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; + VkPipelineDynamicStateCreateInfo dynamicState = vks::initializers::pipelineDynamicStateCreateInfo(dynamicStateEnables); std::array shaderStages; + // Shaders 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); - VkGraphicsPipelineCreateInfo pipelineCreateInfo = - vks::initializers::pipelineCreateInfo( - pipelineLayout, - renderPass, - 0); + // Vertex input state + std::vector vertexInputBindings = { + vks::initializers::vertexInputBindingDescription(0, sizeof(Vertex), VK_VERTEX_INPUT_RATE_VERTEX) + }; + std::vector 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(vertexInputBindings.size()); + vertexInputState.pVertexBindingDescriptions = vertexInputBindings.data(); + vertexInputState.vertexAttributeDescriptionCount = static_cast(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.pRasterizationState = &rasterizationState; pipelineCreateInfo.pColorBlendState = &colorBlendState; @@ -710,59 +584,59 @@ public: pipelineCreateInfo.pDynamicState = &dynamicState; pipelineCreateInfo.stageCount = static_cast(shaderStages.size()); pipelineCreateInfo.pStages = shaderStages.data(); - - VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.solid)); + VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipeline)); } // Prepare and initialize uniform buffer containing shader uniforms void prepareUniformBuffers() { // Vertex shader uniform buffer block - VK_CHECK_RESULT(vulkanDevice->createBuffer( - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, - &uniformBufferVS, - sizeof(uboVS), - &uboVS)); - VK_CHECK_RESULT(uniformBufferVS.map()); - updateUniformBuffers(); + 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_CHECK_RESULT(uniformBuffer.map()); } void updateUniformBuffers() { - uboVS.projection = camera.matrices.perspective; - uboVS.modelView = camera.matrices.view; - uboVS.viewPos = camera.viewPos; - uboVS.depth += frameTimer * 0.15f; - if (uboVS.depth > 1.0f) { - uboVS.depth = uboVS.depth - 1.0f; + uniformData.projection = camera.matrices.perspective; + uniformData.modelView = camera.matrices.view; + uniformData.viewPos = camera.viewPos; + if (!paused) { + // Animate depth + 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() { VulkanExampleBase::prepare(); generateQuad(); - setupVertexDescriptions(); prepareUniformBuffers(); prepareNoiseTexture(128, 128, 128); - setupDescriptorSetLayout(); + setupDescriptors(); preparePipelines(); - setupDescriptorPool(); - setupDescriptorSet(); buildCommandBuffers(); 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() { if (!prepared) return; + updateUniformBuffers(); draw(); - if (!paused) { - updateUniformBuffers(); - } } virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay) diff --git a/examples/texturearray/texturearray.cpp b/examples/texturearray/texturearray.cpp index 414007da..ca7e2c59 100644 --- a/examples/texturearray/texturearray.cpp +++ b/examples/texturearray/texturearray.cpp @@ -1,5 +1,8 @@ /* * 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 * @@ -23,38 +26,36 @@ class VulkanExample : public VulkanExampleBase public: // Number of array layers in texture array // Also used as instance count - uint32_t layerCount; + uint32_t layerCount{ 0 }; vks::Texture textureArray; vks::Buffer vertexBuffer; vks::Buffer indexBuffer; - uint32_t indexCount; + uint32_t indexCount{ 0 }; - vks::Buffer uniformBufferVS; - - struct UboInstanceData { + // Values passed to the shader per drawn instance + struct alignas(16) PerInstanceData { // Model matrix glm::mat4 model; - // Texture array index - // Vec4 due to padding - glm::vec4 arrayIndex; + // Layer index from which this instance will sample in the fragment shader + float arrayIndex{ 0 }; }; - struct { + struct UniformData { // Global matrices struct { glm::mat4 projection; glm::mat4 view; } matrices; // Separate data for each instance - UboInstanceData *instance; - } uboVS; + PerInstanceData* instance{ nullptr }; + } uniformData; + vks::Buffer uniformBuffer; - - VkPipeline pipeline; - VkPipelineLayout pipelineLayout; - VkDescriptorSet descriptorSet; - VkDescriptorSetLayout descriptorSetLayout; + VkPipeline pipeline{ VK_NULL_HANDLE }; + VkPipelineLayout pipelineLayout{ VK_NULL_HANDLE }; + VkDescriptorSet descriptorSet{ VK_NULL_HANDLE }; + VkDescriptorSetLayout descriptorSetLayout{ VK_NULL_HANDLE }; VulkanExample() : VulkanExampleBase() { @@ -67,25 +68,19 @@ public: ~VulkanExample() { - // Clean up used Vulkan resources - // Note : Inherited destructor cleans up resources stored in base class - - vkDestroyImageView(device, textureArray.view, nullptr); - vkDestroyImage(device, textureArray.image, nullptr); - vkDestroySampler(device, textureArray.sampler, nullptr); - vkFreeMemory(device, textureArray.deviceMemory, nullptr); - - vkDestroyPipeline(device, pipeline, nullptr); - - vkDestroyPipelineLayout(device, pipelineLayout, nullptr); - vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); - - vertexBuffer.destroy(); - indexBuffer.destroy(); - - uniformBufferVS.destroy(); - - delete[] uboVS.instance; + if (device) { + vkDestroyImageView(device, textureArray.view, nullptr); + vkDestroyImage(device, textureArray.image, nullptr); + vkDestroySampler(device, textureArray.sampler, nullptr); + vkFreeMemory(device, textureArray.deviceMemory, nullptr); + vkDestroyPipeline(device, pipeline, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); + vertexBuffer.destroy(); + indexBuffer.destroy(); + uniformBuffer.destroy(); + delete[] uniformData.instance; + } } 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() { std::vector vertices = { @@ -363,49 +360,50 @@ public: indexCount = static_cast(indices.size()); - // Create buffers - // For the sake of simplicity we won't stage the vertex data to the gpu memory - VK_CHECK_RESULT(vulkanDevice->createBuffer( - VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, - &vertexBuffer, - vertices.size() * sizeof(Vertex), - vertices.data())); - VK_CHECK_RESULT(vulkanDevice->createBuffer( - VK_BUFFER_USAGE_INDEX_BUFFER_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, - &indexBuffer, - indices.size() * sizeof(uint32_t), - indices.data())); + // Create buffers and upload data to the GPU + struct StagingBuffers { + vks::Buffer vertices; + vks::Buffer indices; + } stagingBuffers; + + // Host visible source buffers (staging) + 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_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())); + + // Device local destination buffers + 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_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))); + + // 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 poolSizes = { vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1), vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1) }; VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo(poolSizes, 2); VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool)); - } - void setupDescriptorSetLayout() - { + // Layout std::vector setLayoutBindings = { // Binding 0 : Vertex shader uniform buffer 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) }; VkDescriptorSetLayoutCreateInfo descriptorLayout = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings); VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout)); - VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1); - VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout)); - } - - void setupDescriptorSet() - { + // Set VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1); VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet)); @@ -418,8 +416,8 @@ public: std::vector writeDescriptorSets = { // Binding 0 : Vertex shader uniform buffer - vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBufferVS.descriptor), - // Binding 1 : Fragment shader cubemap sampler + 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(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr); @@ -427,6 +425,11 @@ public: 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); 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); @@ -466,64 +469,46 @@ public: pipelineCI.pDynamicState = &dynamicStateCI; pipelineCI.stageCount = static_cast(shaderStages.size()); pipelineCI.pStages = shaderStages.data(); - VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipeline)); } 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 - VK_CHECK_RESULT(vulkanDevice->createBuffer( - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, - &uniformBufferVS, - uboSize)); + 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)); // Array indices and model matrices are fixed float offset = -1.5f; float center = (layerCount*offset) / 2.0f - (offset * 0.5f); for (uint32_t i = 0; i < layerCount; i++) { // Instance model matrix - uboVS.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::translate(glm::mat4(1.0f), glm::vec3(i * offset - center, 0.0f, 0.0f)); + uniformData.instance[i].model = glm::scale(uniformData.instance[i].model, glm::vec3(0.5f)); // Instance texture array index - uboVS.instance[i].arrayIndex.x = (float)i; + uniformData.instance[i].arrayIndex = (float)i; } // Update instanced part of the uniform buffer uint8_t *pData; - uint32_t dataOffset = sizeof(uboVS.matrices); - uint32_t dataSize = layerCount * sizeof(UboInstanceData); - VK_CHECK_RESULT(vkMapMemory(device, uniformBufferVS.memory, dataOffset, dataSize, 0, (void **)&pData)); - memcpy(pData, uboVS.instance, dataSize); - vkUnmapMemory(device, uniformBufferVS.memory); + uint32_t dataOffset = sizeof(uniformData.matrices); + uint32_t dataSize = layerCount * sizeof(PerInstanceData); + VK_CHECK_RESULT(vkMapMemory(device, uniformBuffer.memory, dataOffset, dataSize, 0, (void **)&pData)); + memcpy(pData, uniformData.instance, dataSize); + vkUnmapMemory(device, uniformBuffer.memory); // Map persistent - VK_CHECK_RESULT(uniformBufferVS.map()); - - updateUniformBuffersCamera(); + VK_CHECK_RESULT(uniformBuffer.map()); } void updateUniformBuffersCamera() { - uboVS.matrices.projection = camera.matrices.perspective; - uboVS.matrices.view = camera.matrices.view; - memcpy(uniformBufferVS.mapped, &uboVS.matrices, sizeof(uboVS.matrices)); - } - - void draw() - { - VulkanExampleBase::prepareFrame(); - - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer]; - VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE)); - - VulkanExampleBase::submitFrame(); + uniformData.matrices.projection = camera.matrices.perspective; + uniformData.matrices.view = camera.matrices.view; + memcpy(uniformBuffer.mapped, &uniformData.matrices, sizeof(uniformData.matrices)); } void prepare() @@ -532,26 +517,27 @@ public: loadAssets(); generateCube(); prepareUniformBuffers(); - setupDescriptorSetLayout(); + setupDescriptors(); preparePipelines(); - setupDescriptorPool(); - setupDescriptorSet(); buildCommandBuffers(); 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() { if (!prepared) return; - draw(); - if (camera.updated) - updateUniformBuffersCamera(); - } - - virtual void viewChanged() - { updateUniformBuffersCamera(); + draw(); } }; diff --git a/examples/texturecubemap/texturecubemap.cpp b/examples/texturecubemap/texturecubemap.cpp index a398153c..c426916d 100644 --- a/examples/texturecubemap/texturecubemap.cpp +++ b/examples/texturecubemap/texturecubemap.cpp @@ -1,6 +1,9 @@ /* * 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 * * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) @@ -18,36 +21,29 @@ public: vks::Texture cubeMap; - struct Meshes { + struct Models { vkglTF::Model skybox; + // The sample lets you select different models to apply the cubemap to std::vector objects; int32_t objectIndex = 0; } models; - struct { - vks::Buffer object; - vks::Buffer skybox; - } uniformBuffers; - struct UBOVS { glm::mat4 projection; glm::mat4 modelView; glm::mat4 inverseModelview; float lodBias = 0.0f; } uboVS; + vks::Buffer uniformBuffer; struct { - VkPipeline skybox; - VkPipeline reflect; + VkPipeline skybox{ VK_NULL_HANDLE }; + VkPipeline reflect{ VK_NULL_HANDLE }; } pipelines; - struct { - VkDescriptorSet object; - VkDescriptorSet skybox; - } descriptorSets; - - VkPipelineLayout pipelineLayout; - VkDescriptorSetLayout descriptorSetLayout; + VkPipelineLayout pipelineLayout{ VK_NULL_HANDLE }; + VkDescriptorSet descriptorSet{ VK_NULL_HANDLE }; + VkDescriptorSetLayout descriptorSetLayout{ VK_NULL_HANDLE }; std::vector objectNames; @@ -63,23 +59,17 @@ public: ~VulkanExample() { - // Clean up used Vulkan resources - // Note : Inherited destructor cleans up resources stored in base class - - // Clean up texture resources - vkDestroyImageView(device, cubeMap.view, nullptr); - vkDestroyImage(device, cubeMap.image, nullptr); - vkDestroySampler(device, cubeMap.sampler, nullptr); - vkFreeMemory(device, cubeMap.deviceMemory, nullptr); - - vkDestroyPipeline(device, pipelines.skybox, nullptr); - vkDestroyPipeline(device, pipelines.reflect, nullptr); - - vkDestroyPipelineLayout(device, pipelineLayout, nullptr); - vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); - - uniformBuffers.object.destroy(); - uniformBuffers.skybox.destroy(); + if (device) { + vkDestroyImageView(device, cubeMap.view, nullptr); + vkDestroyImage(device, cubeMap.image, nullptr); + vkDestroySampler(device, cubeMap.sampler, nullptr); + vkFreeMemory(device, cubeMap.deviceMemory, nullptr); + vkDestroyPipeline(device, pipelines.skybox, nullptr); + vkDestroyPipeline(device, pipelines.reflect, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); + uniformBuffer.destroy(); + } } // 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; ktxTexture* ktxTexture; @@ -315,16 +306,16 @@ public: VkRect2D scissor = vks::initializers::rect2D(width, height, 0, 0); vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor); + vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr); + // Skybox 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); models.skybox.draw(drawCmdBuffers[i]); } // 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); models.objects[models.objectIndex].draw(drawCmdBuffers[i]); @@ -349,111 +340,53 @@ public: models.objects[i].loadFromFile(getAssetPath() + "models/" + filenames[i], vulkanDevice, queue, glTFLoadingFlags); } // Cubemap texture - const bool forceLinearTiling = false; - loadCubemap(getAssetPath() + "textures/cubemap_yokohama_rgba.ktx", VK_FORMAT_R8G8B8A8_UNORM, forceLinearTiling); + loadCubemap(getAssetPath() + "textures/cubemap_yokohama_rgba.ktx", VK_FORMAT_R8G8B8A8_UNORM); } - void setupDescriptorPool() + void setupDescriptors() { - std::vector poolSizes = - { - vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2), - vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2) + // Pool + std::vector poolSizes = { + vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 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)); - } - void setupDescriptorSetLayout() - { - std::vector setLayoutBindings = - { - // Binding 0 : Uniform buffer - vks::initializers::descriptorSetLayoutBinding( - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, - 0), + // Layout + std::vector 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 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)); - VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = - vks::initializers::pipelineLayoutCreateInfo( - &descriptorSetLayout, - 1); + // Set + VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1); + VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet)); - VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pPipelineLayoutCreateInfo, nullptr, &pipelineLayout)); - } - - void setupDescriptorSets() - { // Image descriptor for the cube map texture - VkDescriptorImageInfo textureDescriptor = - 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)); + VkDescriptorImageInfo textureDescriptor = vks::initializers::descriptorImageInfo(cubeMap.sampler, cubeMap.view, cubeMap.imageLayout); std::vector writeDescriptorSets = { // 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 - vks::initializers::writeDescriptorSet( - descriptorSets.object, - VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - 1, - &textureDescriptor) - }; - vkUpdateDescriptorSets(device, static_cast(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) + vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &textureDescriptor) }; vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr); } 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); 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); @@ -496,39 +429,28 @@ public: // Prepare and initialize uniform buffer containing shader uniforms 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, - &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(); + 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(uniformBuffer.map()); } void updateUniformBuffers() { - // 3D object 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.inverseModelview = glm::inverse(camera.matrices.view); - memcpy(uniformBuffers.object.mapped, &uboVS, sizeof(uboVS)); - // Skybox - uboVS.modelView = camera.matrices.view; - // Cancel out translation - uboVS.modelView[3] = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); - memcpy(uniformBuffers.skybox.mapped, &uboVS, sizeof(uboVS)); + memcpy(uniformBuffer.mapped, &uboVS, sizeof(uboVS)); + } + + void prepare() + { + VulkanExampleBase::prepare(); + loadAssets(); + prepareUniformBuffers(); + setupDescriptors(); + preparePipelines(); + buildCommandBuffers(); + prepared = true; } void draw() @@ -539,30 +461,12 @@ public: VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE)); VulkanExampleBase::submitFrame(); } - - void prepare() - { - VulkanExampleBase::prepare(); - loadAssets(); - prepareUniformBuffers(); - setupDescriptorSetLayout(); - preparePipelines(); - setupDescriptorPool(); - setupDescriptorSets(); - buildCommandBuffers(); - prepared = true; - } - virtual void render() { if (!prepared) return; - draw(); - } - - virtual void viewChanged() - { updateUniformBuffers(); + draw(); } virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay) diff --git a/examples/texturecubemaparray/texturecubemaparray.cpp b/examples/texturecubemaparray/texturecubemaparray.cpp index 74a6cf3c..f5b62885 100644 --- a/examples/texturecubemaparray/texturecubemaparray.cpp +++ b/examples/texturecubemaparray/texturecubemaparray.cpp @@ -1,7 +1,10 @@ /* * 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) */ @@ -24,31 +27,24 @@ public: int32_t objectIndex = 0; } models; - struct { - vks::Buffer object; - vks::Buffer skybox; - } uniformBuffers; - - struct ShaderData { + struct UniformData { glm::mat4 projection; glm::mat4 modelView; glm::mat4 inverseModelview; float lodBias = 0.0f; + // Used by the fragment shader to select the cubemap from the array cubemap int cubeMapIndex = 1; - } shaderData; + } uniformData; + vks::Buffer uniformBuffer; struct { - VkPipeline skybox; - VkPipeline reflect; + VkPipeline skybox{ VK_NULL_HANDLE }; + VkPipeline reflect{ VK_NULL_HANDLE }; } pipelines; - struct { - VkDescriptorSet object; - VkDescriptorSet skybox; - } descriptorSets; - - VkPipelineLayout pipelineLayout; - VkDescriptorSetLayout descriptorSetLayout; + VkPipelineLayout pipelineLayout{ VK_NULL_HANDLE }; + VkDescriptorSet descriptorSet{ VK_NULL_HANDLE }; + VkDescriptorSetLayout descriptorSetLayout{ VK_NULL_HANDLE }; std::vector objectNames; @@ -63,25 +59,23 @@ public: ~VulkanExample() { - // Clean up texture resources - vkDestroyImageView(device, cubeMapArray.view, nullptr); - vkDestroyImage(device, cubeMapArray.image, nullptr); - vkDestroySampler(device, cubeMapArray.sampler, nullptr); - vkFreeMemory(device, cubeMapArray.deviceMemory, nullptr); - - vkDestroyPipeline(device, pipelines.skybox, nullptr); - vkDestroyPipeline(device, pipelines.reflect, nullptr); - - vkDestroyPipelineLayout(device, pipelineLayout, nullptr); - vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); - - uniformBuffers.object.destroy(); - uniformBuffers.skybox.destroy(); + if (device) { + vkDestroyImageView(device, cubeMapArray.view, nullptr); + vkDestroyImage(device, cubeMapArray.image, nullptr); + vkDestroySampler(device, cubeMapArray.sampler, nullptr); + vkFreeMemory(device, cubeMapArray.deviceMemory, nullptr); + vkDestroyPipeline(device, pipelines.skybox, nullptr); + vkDestroyPipeline(device, pipelines.reflect, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); + uniformBuffer.destroy(); + } } // Enable physical device features required for this example virtual void getEnabledFeatures() { + // This sample requires support for cube map arrays if (deviceFeatures.imageCubeArray) { enabledFeatures.imageCubeArray = VK_TRUE; } 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; ktxTexture* ktxTexture; @@ -320,16 +315,16 @@ public: VkRect2D scissor = vks::initializers::rect2D(width, height, 0, 0); vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor); + vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr); + // Skybox 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); models.skybox.draw(drawCmdBuffers[i]); } // 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); models.objects[models.objectIndex].draw(drawCmdBuffers[i]); @@ -354,66 +349,53 @@ public: models.objects[i].loadFromFile(getAssetPath() + "models/" + filenames[i], vulkanDevice, queue, glTFLoadingFlags); } // 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 poolSizes = { - vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2), - vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2) + // Pool + std::vector poolSizes = { + 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)); - } - void setupDescriptorSetLayout() - { - const std::vector setLayoutBindings = { - // Binding 0 : Uniform buffer + // Layout + std::vector 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 1 : Fragment shader image sampler vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 1) }; - - const VkDescriptorSetLayoutCreateInfo descriptorLayout = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings); + VkDescriptorSetLayoutCreateInfo descriptorLayout = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings); VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout)); - const VkPipelineLayoutCreateInfo pipelineLayoutCI = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1); - 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); + // Set 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 writeDescriptorSets = { // 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 - vks::initializers::writeDescriptorSet(descriptorSets.object, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &textureDescriptor) - }; - vkUpdateDescriptorSets(device, static_cast(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) + vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &textureDescriptor) }; vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr); } 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); 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); @@ -457,39 +439,28 @@ public: 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, - &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))); - + 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))); // Map persistent - VK_CHECK_RESULT(uniformBuffers.object.map()); - VK_CHECK_RESULT(uniformBuffers.skybox.map()); - - updateUniformBuffers(); + VK_CHECK_RESULT(uniformBuffer.map()); } void updateUniformBuffers() { - // 3D object - shaderData.projection = camera.matrices.perspective; - shaderData.modelView = camera.matrices.view; - shaderData.inverseModelview = glm::inverse(camera.matrices.view); - memcpy(uniformBuffers.object.mapped, &shaderData, sizeof(ShaderData)); + uniformData.projection = camera.matrices.perspective; + uniformData.modelView = camera.matrices.view; + uniformData.inverseModelview = glm::inverse(camera.matrices.view); + memcpy(uniformBuffer.mapped, &uniformData, sizeof(UniformData)); + } - // Skybox - shaderData.modelView = camera.matrices.view; - // Cancel out translation - shaderData.modelView[3] = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); - memcpy(uniformBuffers.skybox.mapped, &shaderData, sizeof(ShaderData)); + void prepare() + { + VulkanExampleBase::prepare(); + loadAssets(); + prepareUniformBuffers(); + setupDescriptors(); + preparePipelines(); + buildCommandBuffers(); + prepared = true; } void draw() @@ -501,43 +472,19 @@ public: VulkanExampleBase::submitFrame(); } - void prepare() - { - VulkanExampleBase::prepare(); - loadAssets(); - prepareUniformBuffers(); - setupDescriptorSetLayout(); - preparePipelines(); - setupDescriptorPool(); - setupDescriptorSets(); - buildCommandBuffers(); - prepared = true; - } - virtual void render() { if (!prepared) return; - draw(); - if (camera.updated) { - updateUniformBuffers(); - } - } - - virtual void viewChanged() - { updateUniformBuffers(); + draw(); } virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay) { if (overlay->header("Settings")) { - if (overlay->sliderInt("Cube map", &shaderData.cubeMapIndex, 0, cubeMapArray.layerCount - 1)) { - updateUniformBuffers(); - } - if (overlay->sliderFloat("LOD bias", &shaderData.lodBias, 0.0f, (float)cubeMapArray.mipLevels)) { - updateUniformBuffers(); - } + overlay->sliderInt("Cube map", &uniformData.cubeMapIndex, 0, cubeMapArray.layerCount - 1); + overlay->sliderFloat("LOD bias", &uniformData.lodBias, 0.0f, (float)cubeMapArray.mipLevels); if (overlay->comboBox("Object type", &models.objectIndex, objectNames)) { buildCommandBuffers(); } diff --git a/shaders/glsl/texturearray/instancing.vert b/shaders/glsl/texturearray/instancing.vert index 45570505..cbfd27c7 100644 --- a/shaders/glsl/texturearray/instancing.vert +++ b/shaders/glsl/texturearray/instancing.vert @@ -6,7 +6,7 @@ layout (location = 1) in vec2 inUV; struct Instance { mat4 model; - vec4 arrayIndex; + float arrayIndex; }; layout (binding = 0) uniform UBO @@ -20,7 +20,7 @@ layout (location = 0) out vec3 outUV; 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; gl_Position = ubo.projection * modelView * vec4(inPos, 1.0); } diff --git a/shaders/glsl/texturearray/instancing.vert.spv b/shaders/glsl/texturearray/instancing.vert.spv index 363adc9e..63849a67 100644 Binary files a/shaders/glsl/texturearray/instancing.vert.spv and b/shaders/glsl/texturearray/instancing.vert.spv differ diff --git a/shaders/glsl/texturecubemap/skybox.vert b/shaders/glsl/texturecubemap/skybox.vert index d1f8887d..6fd365c1 100644 --- a/shaders/glsl/texturecubemap/skybox.vert +++ b/shaders/glsl/texturecubemap/skybox.vert @@ -15,5 +15,7 @@ void main() outUVW = inPos; // Convert cubemap coordinates into Vulkan coordinate space 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); } diff --git a/shaders/glsl/texturecubemap/skybox.vert.spv b/shaders/glsl/texturecubemap/skybox.vert.spv index fc6b9862..147cdaa2 100644 Binary files a/shaders/glsl/texturecubemap/skybox.vert.spv and b/shaders/glsl/texturecubemap/skybox.vert.spv differ diff --git a/shaders/glsl/texturecubemaparray/skybox.vert b/shaders/glsl/texturecubemaparray/skybox.vert index b4df167b..b6165026 100644 --- a/shaders/glsl/texturecubemaparray/skybox.vert +++ b/shaders/glsl/texturecubemaparray/skybox.vert @@ -17,5 +17,7 @@ void main() { outUVW = inPos; 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); } diff --git a/shaders/hlsl/texturearray/instancing.vert b/shaders/hlsl/texturearray/instancing.vert index d9d77d52..67670258 100644 --- a/shaders/hlsl/texturearray/instancing.vert +++ b/shaders/hlsl/texturearray/instancing.vert @@ -9,7 +9,7 @@ struct VSInput struct Instance { float4x4 model; - float4 arrayIndex; + float arrayIndex; }; struct UBO @@ -30,7 +30,7 @@ struct VSOutput VSOutput main(VSInput input, uint InstanceIndex : SV_InstanceID) { 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); output.Pos = mul(ubo.projection, mul(modelView, float4(input.Pos, 1.0))); return output; diff --git a/shaders/hlsl/texturearray/instancing.vert.spv b/shaders/hlsl/texturearray/instancing.vert.spv index ad916322..eace2863 100644 Binary files a/shaders/hlsl/texturearray/instancing.vert.spv and b/shaders/hlsl/texturearray/instancing.vert.spv differ diff --git a/shaders/hlsl/texturecubemap/skybox.vert b/shaders/hlsl/texturecubemap/skybox.vert index 632f3cea..4395feb4 100644 --- a/shaders/hlsl/texturecubemap/skybox.vert +++ b/shaders/hlsl/texturecubemap/skybox.vert @@ -20,6 +20,11 @@ VSOutput main([[vk::location(0)]] float3 Pos : POSITION0) output.UVW = Pos; // Convert cubemap coordinates into Vulkan coordinate space 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; } diff --git a/shaders/hlsl/texturecubemap/skybox.vert.spv b/shaders/hlsl/texturecubemap/skybox.vert.spv index e95a2708..94b7c48d 100644 Binary files a/shaders/hlsl/texturecubemap/skybox.vert.spv and b/shaders/hlsl/texturecubemap/skybox.vert.spv differ diff --git a/shaders/hlsl/texturecubemaparray/skybox.vert b/shaders/hlsl/texturecubemaparray/skybox.vert index 37f07a08..6076e2f7 100644 --- a/shaders/hlsl/texturecubemaparray/skybox.vert +++ b/shaders/hlsl/texturecubemaparray/skybox.vert @@ -22,6 +22,11 @@ VSOutput main([[vk::location(0)]] float3 Pos : POSITION0) VSOutput output = (VSOutput)0; output.UVW = Pos; 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; } diff --git a/shaders/hlsl/texturecubemaparray/skybox.vert.spv b/shaders/hlsl/texturecubemaparray/skybox.vert.spv index a086e9d6..fb4a1ae9 100644 Binary files a/shaders/hlsl/texturecubemaparray/skybox.vert.spv and b/shaders/hlsl/texturecubemaparray/skybox.vert.spv differ