diff --git a/data/shaders/glsl/descriptorbuffer/cube.frag b/data/shaders/glsl/descriptorbuffer/cube.frag new file mode 100644 index 00000000..cd331d4a --- /dev/null +++ b/data/shaders/glsl/descriptorbuffer/cube.frag @@ -0,0 +1,14 @@ +#version 450 + +layout (set = 2, binding = 0) uniform sampler2D samplerColorMap; + +layout (location = 0) in vec3 inNormal; +layout (location = 1) in vec3 inColor; +layout (location = 2) in vec2 inUV; + +layout (location = 0) out vec4 outFragColor; + +void main() +{ + outFragColor = texture(samplerColorMap, inUV) * vec4(inColor, 1.0); +} \ No newline at end of file diff --git a/data/shaders/glsl/descriptorbuffer/cube.frag.spv b/data/shaders/glsl/descriptorbuffer/cube.frag.spv new file mode 100644 index 00000000..cff0bd93 Binary files /dev/null and b/data/shaders/glsl/descriptorbuffer/cube.frag.spv differ diff --git a/data/shaders/glsl/descriptorbuffer/cube.vert b/data/shaders/glsl/descriptorbuffer/cube.vert new file mode 100644 index 00000000..28c2b156 --- /dev/null +++ b/data/shaders/glsl/descriptorbuffer/cube.vert @@ -0,0 +1,31 @@ +#version 450 + +layout (location = 0) in vec3 inPos; +layout (location = 1) in vec3 inNormal; +layout (location = 2) in vec2 inUV; +layout (location = 3) in vec3 inColor; + +layout (set = 0, binding = 0) uniform UBOCamera { + mat4 projection; + mat4 view; +} camera; + +layout (set = 1, binding = 0) uniform UBOModel { + mat4 matrix; +} model; + +layout (location = 0) out vec3 outNormal; +layout (location = 1) out vec3 outColor; +layout (location = 2) out vec2 outUV; + +out gl_PerVertex { + vec4 gl_Position; +}; + +void main() +{ + outNormal = inNormal; + outColor = inColor; + outUV = inUV; + gl_Position = camera.projection * camera.view * model.matrix * vec4(inPos.xyz, 1.0); +} \ No newline at end of file diff --git a/data/shaders/glsl/descriptorbuffer/cube.vert.spv b/data/shaders/glsl/descriptorbuffer/cube.vert.spv new file mode 100644 index 00000000..a94bb0f2 Binary files /dev/null and b/data/shaders/glsl/descriptorbuffer/cube.vert.spv differ diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 0e8a1271..5a24fe7a 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -92,6 +92,7 @@ set(EXAMPLES deferred deferredmultisampling deferredshadows + descriptorbuffer descriptorindexing descriptorsets displacement diff --git a/examples/descriptorbuffer/descriptorbuffer.cpp b/examples/descriptorbuffer/descriptorbuffer.cpp new file mode 100644 index 00000000..5056509e --- /dev/null +++ b/examples/descriptorbuffer/descriptorbuffer.cpp @@ -0,0 +1,449 @@ +/* + * Vulkan Example - Using descriptor buffers via VK_EXT_descriptor_buffer + * + * Code is work-in-progress + * + * Copyright (C) 2022 by Sascha Willems - www.saschawillems.de + * + * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + */ + +#include "vulkanexamplebase.h" +#include "VulkanglTFModel.h" + +#define ENABLE_VALIDATION false + +class VulkanExample : public VulkanExampleBase +{ +public: + bool animate = true; + + struct Cube { + glm::mat4 matrix; + vks::Texture2D texture; + vks::Buffer uniformBuffer; + glm::vec3 rotation; + }; + std::array cubes; + + vks::Buffer uniformBufferCamera; + + vkglTF::Model model; + + VkPipeline pipeline; + VkPipelineLayout pipelineLayout; + + VkDescriptorSetLayout descriptorSetLayoutBuffers; + VkDescriptorSetLayout descriptorSetLayoutImages; + + vks::Buffer resourceDescriptorBuffer; + vks::Buffer imageDescriptorBuffer; + uint64_t resourceDescriptorBufferDeviceAddress; + VkDeviceOrHostAddressConstKHR imageDescriptorBufferDeviceAddress; + + PFN_vkGetBufferDeviceAddressKHR vkGetBufferDeviceAddressKHR; + + VkPhysicalDeviceDescriptorBufferFeaturesEXT enabledDeviceDescriptorBufferFeaturesEXT{}; + VkPhysicalDeviceBufferDeviceAddressFeatures enabledBufferDeviceAddresFeatures{}; + VkPhysicalDeviceDescriptorBufferPropertiesEXT descriptorBufferProperties{}; + + PFN_vkGetDescriptorSetLayoutSizeEXT vkGetDescriptorSetLayoutSizeEXT; + PFN_vkGetDescriptorSetLayoutBindingOffsetEXT vkGetDescriptorSetLayoutBindingOffsetEXT; + PFN_vkCmdBindDescriptorBuffersEXT vkCmdBindDescriptorBuffersEXT; + PFN_vkCmdSetDescriptorBufferOffsetsEXT vkCmdSetDescriptorBufferOffsetsEXT; + PFN_vkGetDescriptorEXT vkGetDescriptorEXT; + PFN_vkCmdBindDescriptorBufferEmbeddedSamplersEXT vkCmdBindDescriptorBufferEmbeddedSamplersEXT; + + uint64_t getBufferDeviceAddress(VkBuffer buffer) + { + VkBufferDeviceAddressInfoKHR bufferDeviceAI{}; + bufferDeviceAI.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO; + bufferDeviceAI.buffer = buffer; + return vkGetBufferDeviceAddressKHR(vulkanDevice->logicalDevice, &bufferDeviceAI); + } + + VulkanExample() : VulkanExampleBase(ENABLE_VALIDATION) + { + title = "Descriptor buffers (VK_EXT_descriptor_buffer)"; + camera.type = Camera::CameraType::lookat; + camera.setPerspective(60.0f, (float)width / (float)height, 0.1f, 512.0f); + camera.setRotation(glm::vec3(0.0f, 0.0f, 0.0f)); + camera.setTranslation(glm::vec3(0.0f, 0.0f, -5.0f)); + + apiVersion = VK_API_VERSION_1_1; + + enabledInstanceExtensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); + + enabledDeviceExtensions.push_back(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME); + enabledDeviceExtensions.push_back(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME); + enabledDeviceExtensions.push_back(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME); + enabledDeviceExtensions.push_back(VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME); + enabledDeviceExtensions.push_back(VK_KHR_MAINTENANCE3_EXTENSION_NAME); + + enabledDeviceExtensions.push_back(VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME); + + enabledBufferDeviceAddresFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES; + enabledBufferDeviceAddresFeatures.bufferDeviceAddress = VK_TRUE; + + enabledDeviceDescriptorBufferFeaturesEXT.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_FEATURES_EXT; + enabledDeviceDescriptorBufferFeaturesEXT.descriptorBuffer = VK_TRUE; + enabledDeviceDescriptorBufferFeaturesEXT.pNext = &enabledBufferDeviceAddresFeatures; + + deviceCreatepNextChain = &enabledDeviceDescriptorBufferFeaturesEXT; + } + + ~VulkanExample() + { + vkDestroyPipeline(device, pipeline, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + for (auto cube : cubes) { + cube.uniformBuffer.destroy(); + cube.texture.destroy(); + } + } + + virtual void getEnabledFeatures() + { + if (deviceFeatures.samplerAnisotropy) { + enabledFeatures.samplerAnisotropy = VK_TRUE; + }; + } + + void setupDescriptors() + { + VkDescriptorSetLayoutCreateInfo descriptorLayoutCI{}; + descriptorLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + descriptorLayoutCI.bindingCount = 1; + descriptorLayoutCI.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_DESCRIPTOR_BUFFER_BIT_EXT; + + VkDescriptorSetLayoutBinding setLayoutBinding = {}; + + setLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + setLayoutBinding.binding = 0; + setLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + setLayoutBinding.descriptorCount = 1; + + descriptorLayoutCI.pBindings = &setLayoutBinding; + VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayoutCI, nullptr, &descriptorSetLayoutBuffers)); + + setLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + setLayoutBinding.binding = 0; + setLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + setLayoutBinding.descriptorCount = 1; + + descriptorLayoutCI.pBindings = &setLayoutBinding; + VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayoutCI, nullptr, &descriptorSetLayoutImages)); + } + + void preparePipelines() + { + // Set 0 = Camera UBO + // Set 1 = Model UBO + // Set 2 = Model image + const std::array setLayouts = { descriptorSetLayoutBuffers, descriptorSetLayoutBuffers, descriptorSetLayoutImages }; + + VkPipelineLayoutCreateInfo pipelineLayoutCI{}; + pipelineLayoutCI.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + // The pipeline layout is based on the descriptor set layout we created above + pipelineLayoutCI.setLayoutCount = static_cast(setLayouts.size()); + pipelineLayoutCI.pSetLayouts = setLayouts.data(); + VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCI, nullptr, &pipelineLayout)); + + const std::vector dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; + + 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); + VkPipelineColorBlendStateCreateInfo colorBlendStateCI = vks::initializers::pipelineColorBlendStateCreateInfo(1, &blendAttachmentState); + VkPipelineDepthStencilStateCreateInfo depthStencilStateCI = vks::initializers::pipelineDepthStencilStateCreateInfo(VK_TRUE, VK_TRUE, VK_COMPARE_OP_LESS_OR_EQUAL); + VkPipelineViewportStateCreateInfo viewportStateCI = vks::initializers::pipelineViewportStateCreateInfo(1, 1, 0); + VkPipelineMultisampleStateCreateInfo multisampleStateCI = vks::initializers::pipelineMultisampleStateCreateInfo(VK_SAMPLE_COUNT_1_BIT, 0); + VkPipelineDynamicStateCreateInfo dynamicStateCI = vks::initializers::pipelineDynamicStateCreateInfo(dynamicStateEnables.data(), static_cast(dynamicStateEnables.size()), 0); + std::array shaderStages; + + VkGraphicsPipelineCreateInfo pipelineCI = vks::initializers::pipelineCreateInfo(pipelineLayout, renderPass, 0); + pipelineCI.pInputAssemblyState = &inputAssemblyStateCI; + pipelineCI.pRasterizationState = &rasterizationStateCI; + pipelineCI.pColorBlendState = &colorBlendStateCI; + pipelineCI.pMultisampleState = &multisampleStateCI; + pipelineCI.pViewportState = &viewportStateCI; + pipelineCI.pDepthStencilState = &depthStencilStateCI; + pipelineCI.pDynamicState = &dynamicStateCI; + pipelineCI.stageCount = static_cast(shaderStages.size()); + pipelineCI.pStages = shaderStages.data(); + pipelineCI.pVertexInputState = vkglTF::Vertex::getPipelineVertexInputState({ vkglTF::VertexComponent::Position, vkglTF::VertexComponent::Normal, vkglTF::VertexComponent::UV, vkglTF::VertexComponent::Color }); + pipelineCI.flags = VK_PIPELINE_CREATE_DESCRIPTOR_BUFFER_BIT_EXT; + + shaderStages[0] = loadShader(getShadersPath() + "descriptorbuffer/cube.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); + shaderStages[1] = loadShader(getShadersPath() + "descriptorbuffer/cube.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); + VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipeline)); + } + + void prepareDescriptorBuffer() + { + std::array descriptorLayoutSizes{}; + vkGetDescriptorSetLayoutSizeEXT(device, descriptorSetLayoutBuffers, &descriptorLayoutSizes[0]); + vkGetDescriptorSetLayoutSizeEXT(device, descriptorSetLayoutImages, &descriptorLayoutSizes[1]); + + VkDeviceSize offset{ 0 }; + vkGetDescriptorSetLayoutBindingOffsetEXT(device, descriptorSetLayoutBuffers, 0, &offset); + + VkDeviceSize img_offset{ 0 }; + vkGetDescriptorSetLayoutBindingOffsetEXT(device, descriptorSetLayoutImages, 1, &img_offset); + + // @todo: check memory sizes + + VK_CHECK_RESULT(vulkanDevice->createBuffer( + VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &resourceDescriptorBuffer, + 3 * descriptorLayoutSizes[0])); + resourceDescriptorBuffer.map(); + + VK_CHECK_RESULT(vulkanDevice->createBuffer( + VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT | VK_BUFFER_USAGE_SAMPLER_DESCRIPTOR_BUFFER_BIT_EXT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, // Flags 1 & 2 are required for combined images + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &imageDescriptorBuffer, + 2 * descriptorLayoutSizes[1])); + imageDescriptorBuffer.map(); + + resourceDescriptorBufferDeviceAddress = getBufferDeviceAddress(resourceDescriptorBuffer.buffer); + //imageDescriptorBufferDeviceAddress = getBufferDeviceAddress(imageDescriptorBuffer.buffer); + + imageDescriptorBufferDeviceAddress.deviceAddress = getBufferDeviceAddress(imageDescriptorBuffer.buffer); + + // @todo: sizes + + PFN_vkGetPhysicalDeviceProperties2KHR vkGetPhysicalDeviceProperties2KHR = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceProperties2KHR")); + assert(vkGetPhysicalDeviceProperties2KHR); + VkPhysicalDeviceProperties2KHR deviceProps2{}; + descriptorBufferProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_PROPERTIES_EXT; + deviceProps2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR; + deviceProps2.pNext = &descriptorBufferProperties; + vkGetPhysicalDeviceProperties2KHR(physicalDevice, &deviceProps2); + + VkDescriptorGetInfoEXT desc_info = { VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT }; + + // Set descriptors for images + // @todo: get from props + const uint32_t alignment = 64; + + char* buf_ptr = (char*)imageDescriptorBuffer.mapped; + desc_info.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + for (uint32_t i = 0; i < static_cast(cubes.size()); i++) { + desc_info.data.pCombinedImageSampler = &cubes[i].texture.descriptor; + vkGetDescriptorEXT(device, &desc_info, descriptorBufferProperties.combinedImageSamplerDescriptorSize, buf_ptr + i * alignment); + } + + // For uniform buffers we only need buffer device addresses + // Global uniform buffer + buf_ptr = (char*)resourceDescriptorBuffer.mapped; + + VkDescriptorAddressInfoEXT descriptorAddressInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_ADDRESS_INFO_EXT }; + descriptorAddressInfo.address = getBufferDeviceAddress(uniformBufferCamera.buffer); + descriptorAddressInfo.range = uniformBufferCamera.size; + descriptorAddressInfo.format = VK_FORMAT_UNDEFINED; + + desc_info.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + desc_info.data.pCombinedImageSampler = nullptr; + desc_info.data.pUniformBuffer = &descriptorAddressInfo; + vkGetDescriptorEXT(device, &desc_info, descriptorBufferProperties.uniformBufferDescriptorSize, buf_ptr); + + // Per-model uniform buffers + buf_ptr += alignment; + for (uint32_t i = 0; i < static_cast(cubes.size()); i++) { + VkDescriptorAddressInfoEXT addr_info = { VK_STRUCTURE_TYPE_DESCRIPTOR_ADDRESS_INFO_EXT }; + addr_info.address = getBufferDeviceAddress(cubes[i].uniformBuffer.buffer); + addr_info.range = cubes[i].uniformBuffer.size; + addr_info.format = VK_FORMAT_UNDEFINED; + + desc_info.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + desc_info.data.pCombinedImageSampler = nullptr; + desc_info.data.pUniformBuffer = &addr_info; + vkGetDescriptorEXT(device, &desc_info, descriptorBufferProperties.uniformBufferDescriptorSize, buf_ptr); + buf_ptr += alignment; + } + } + + + void buildCommandBuffers() + { + VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo(); + + VkClearValue clearValues[2]; + clearValues[0].color = defaultClearColor; + clearValues[1].depthStencil = { 1.0f, 0 }; + + VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo(); + renderPassBeginInfo.renderPass = renderPass; + renderPassBeginInfo.renderArea.offset.x = 0; + renderPassBeginInfo.renderArea.offset.y = 0; + renderPassBeginInfo.renderArea.extent.width = width; + renderPassBeginInfo.renderArea.extent.height = height; + renderPassBeginInfo.clearValueCount = 2; + renderPassBeginInfo.pClearValues = clearValues; + + for (int32_t i = 0; i < drawCmdBuffers.size(); ++i) { + renderPassBeginInfo.framebuffer = frameBuffers[i]; + + VK_CHECK_RESULT(vkBeginCommandBuffer(drawCmdBuffers[i], &cmdBufInfo)); + + vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); + + vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + + VkViewport viewport = vks::initializers::viewport((float)width, (float)height, 0.0f, 1.0f); + vkCmdSetViewport(drawCmdBuffers[i], 0, 1, &viewport); + + VkRect2D scissor = vks::initializers::rect2D(width, height, 0, 0); + vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor); + + VkDeviceSize offsets[1] = { 0 }; + model.bindBuffers(drawCmdBuffers[i]); + + // Descriptor buffer bindings + // Set 0 = uniform buffer + VkDescriptorBufferBindingInfoEXT bindingInfos[2]{}; + bindingInfos[0].sType = VK_STRUCTURE_TYPE_DESCRIPTOR_BUFFER_BINDING_INFO_EXT; + bindingInfos[0].address = resourceDescriptorBufferDeviceAddress; + bindingInfos[0].usage = VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT;// | VK_BUFFER_USAGE_PUSH_DESCRIPTORS_DESCRIPTOR_BUFFER_BIT_EXT; + // Set 1 = Image + bindingInfos[1].sType = VK_STRUCTURE_TYPE_DESCRIPTOR_BUFFER_BINDING_INFO_EXT; + bindingInfos[1].pNext = nullptr; + bindingInfos[1].address = imageDescriptorBufferDeviceAddress.deviceAddress; + bindingInfos[1].usage = VK_BUFFER_USAGE_SAMPLER_DESCRIPTOR_BUFFER_BIT_EXT | VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT; + vkCmdBindDescriptorBuffersEXT(drawCmdBuffers[i], 2, bindingInfos); + + // @todo: from props + uint32_t bufferIndexUbo = 0; + VkDeviceSize alignment = 64; + VkDeviceSize bufferOffset = 0; + + // Global Matrices (set 0) + bufferOffset = 0; + vkCmdSetDescriptorBufferOffsetsEXT(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &bufferIndexUbo, &bufferOffset); + + // Set and offset into descriptor for each model + for (uint32_t j = 0; j < static_cast(cubes.size()); j++) { + // Uniform buffer (set 1) + // Model ubos start at offset * 1 (slot 0 is global matrices) + bufferOffset = alignment + j * alignment; + vkCmdSetDescriptorBufferOffsetsEXT(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 1, 1, &bufferIndexUbo, &bufferOffset); + // Image (set 2) + uint32_t bufferIndexImage = 1; + bufferOffset = j * alignment; + vkCmdSetDescriptorBufferOffsetsEXT(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 2, 1, &bufferIndexImage, &bufferOffset); + model.draw(drawCmdBuffers[i]); + } + + // @todo: UI still uses descriptors, mix and match problematic + //drawUI(drawCmdBuffers[i]); + + vkCmdEndRenderPass(drawCmdBuffers[i]); + + VK_CHECK_RESULT(vkEndCommandBuffer(drawCmdBuffers[i])); + } + } + + void loadAssets() + { + const uint32_t glTFLoadingFlags = vkglTF::FileLoadingFlags::PreTransformVertices | vkglTF::FileLoadingFlags::PreMultiplyVertexColors | vkglTF::FileLoadingFlags::FlipY; + model.loadFromFile(getAssetPath() + "models/cube.gltf", vulkanDevice, queue, glTFLoadingFlags); + cubes[0].texture.loadFromFile(getAssetPath() + "textures/crate01_color_height_rgba.ktx", VK_FORMAT_R8G8B8A8_UNORM, vulkanDevice, queue); + cubes[1].texture.loadFromFile(getAssetPath() + "textures/crate02_color_height_rgba.ktx", VK_FORMAT_R8G8B8A8_UNORM, vulkanDevice, queue); + } + + void prepareUniformBuffers() + { + // UBO for camera matrices + VK_CHECK_RESULT(vulkanDevice->createBuffer( + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &uniformBufferCamera, + sizeof(glm::mat4) * 2)); + VK_CHECK_RESULT(uniformBufferCamera.map()); + + // UBOs for model matrices + for (uint32_t i = 0; i < static_cast(cubes.size()); i++) { + VK_CHECK_RESULT(vulkanDevice->createBuffer( + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &cubes[i].uniformBuffer, + sizeof(glm::mat4))); + VK_CHECK_RESULT(cubes[i].uniformBuffer.map()); + } + updateUniformBuffers(); + } + + void updateUniformBuffers() + { + memcpy(uniformBufferCamera.mapped, &camera.matrices.perspective, sizeof(glm::mat4)); + memcpy((char*)uniformBufferCamera.mapped + sizeof(glm::mat4), &camera.matrices.view, sizeof(glm::mat4)); + + cubes[0].matrix = glm::translate(glm::mat4(1.0f), glm::vec3(-2.0f, 0.0f, 0.0f)); + cubes[1].matrix = glm::translate(glm::mat4(1.0f), glm::vec3( 1.5f, 0.5f, 0.0f)); + + for (uint32_t i = 0; i < static_cast(cubes.size()); i++) { + cubes[i].matrix = glm::scale(cubes[i].matrix, glm::vec3(0.25f)); + memcpy(cubes[i].uniformBuffer.mapped, &cubes[i].matrix, sizeof(glm::mat4)); + } + } + + 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() + { + VulkanExampleBase::prepare(); + + vkGetBufferDeviceAddressKHR = reinterpret_cast(vkGetDeviceProcAddr(device, "vkGetBufferDeviceAddressKHR")); + + vkGetDescriptorSetLayoutSizeEXT = reinterpret_cast(vkGetDeviceProcAddr(device, "vkGetDescriptorSetLayoutSizeEXT")); + vkGetDescriptorSetLayoutBindingOffsetEXT = reinterpret_cast(vkGetDeviceProcAddr(device, "vkGetDescriptorSetLayoutBindingOffsetEXT")); + vkCmdBindDescriptorBuffersEXT = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCmdBindDescriptorBuffersEXT")); + vkGetDescriptorEXT = reinterpret_cast(vkGetDeviceProcAddr(device, "vkGetDescriptorEXT")); + vkCmdBindDescriptorBufferEmbeddedSamplersEXT = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCmdBindDescriptorBufferEmbeddedSamplersEXT")); + vkCmdSetDescriptorBufferOffsetsEXT = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCmdSetDescriptorBufferOffsetsEXT")); + + loadAssets(); + prepareUniformBuffers(); + setupDescriptors(); + prepareDescriptorBuffer(); + preparePipelines(); + buildCommandBuffers(); + prepared = true; + } + + virtual void render() + { + if (!prepared) + return; + draw(); + if (animate && !paused) { + cubes[0].rotation.x += 2.5f * frameTimer; + if (cubes[0].rotation.x > 360.0f) + cubes[0].rotation.x -= 360.0f; + cubes[1].rotation.y += 2.0f * frameTimer; + if (cubes[1].rotation.x > 360.0f) + cubes[1].rotation.x -= 360.0f; + } + if ((camera.updated) || (animate && !paused)) { + updateUniformBuffers(); + } + } + + virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay) + { + if (overlay->header("Settings")) { + overlay->checkBox("Animate", &animate); + } + } +}; + +VULKAN_EXAMPLE_MAIN()