From 250c7df08b655fedeb1f48a322944243cd8cecc4 Mon Sep 17 00:00:00 2001 From: Sascha Willems Date: Sun, 4 Dec 2022 12:51:14 +0100 Subject: [PATCH] Added sample for VK_EXT_descriptor_buffer Work-in-progress --- data/shaders/glsl/descriptorbuffer/cube.frag | 14 + .../glsl/descriptorbuffer/cube.frag.spv | Bin 0 -> 848 bytes data/shaders/glsl/descriptorbuffer/cube.vert | 31 ++ .../glsl/descriptorbuffer/cube.vert.spv | Bin 0 -> 1792 bytes examples/CMakeLists.txt | 1 + .../descriptorbuffer/descriptorbuffer.cpp | 449 ++++++++++++++++++ 6 files changed, 495 insertions(+) create mode 100644 data/shaders/glsl/descriptorbuffer/cube.frag create mode 100644 data/shaders/glsl/descriptorbuffer/cube.frag.spv create mode 100644 data/shaders/glsl/descriptorbuffer/cube.vert create mode 100644 data/shaders/glsl/descriptorbuffer/cube.vert.spv create mode 100644 examples/descriptorbuffer/descriptorbuffer.cpp 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 0000000000000000000000000000000000000000..cff0bd93d229ce94291563b284af2299c594dcbc GIT binary patch literal 848 zcmYk3OG^S_6oyagn3wXBrI}r{mVvaW2%=g9xv?msS`QR4;LONq`!@ZtepQ>G_nAxh z!ST#_&-0zjXFhkZY_?<-E7_hUr)fDUW>qW3el&g@4`=h&;ql24M9uOs2vfJR734AB z^#T%QSyR@Q?aCVB$*YmR@~Uq3t;>$o#e0tWaX0*!-$d^FbvOwlh2n~~DnE1m>Etbn z^;>ru-=~ikJUu5|A@1n~$yG-L{k`BWjC^+jGg3!-pmT_+y)R>~EFS2aLrf3&ET8Sg z{^ZGJ^;)9f2JxoJowmM<()zNFc;11S-TV42$@8M<=V{E0XkATPk%y<>;>qHk$f^=> z)LTg*H0lwvBZvNVdFnAId_$gE=4nQJoUC1$2I8S}h|;*DA|7<3rs=*XDiK@9!7sV4WYZKWD> nWLz)RnCV{CslP4bK0JAL`AU25=$rm9mztjc3GG+goyqgFF?`$ZUR>Fd0{WUa5-?d>x6 zeidxK>tOP|$`4w8b`dA%X*YY_jP8uPDXyHv&NS=LCif=T`OPC9v9l9fU+moau{#pG zRqf79?2g3FEbtZ`I5UrVJ)1A!`4%wVX%<+r1zhQB!G2p=9_I$dyOoIUo(iH&=X zM4Vj2S=(IEt213hJoYYLC{Wp3%rj~P?(Z$Q{u**o!}(joKCB^*8e;e1ygXAx8Mi>6 z&R2Z~ZJ4``%;IkyvGzK6q|)UdSJ<6~eH7i=VHdj#x$t+#`ir%@W9`;+-qy1h>rHa& zE3d!UHxTuv(2Whd*f$aRX3)*&-|;+Az6CjR&vBba&fH@E56De}&vVxib1hZ)0=j1o ze39E+W$yBg$SLNJcj+52zu29}x790M|H$7!x1PPa*T{7P-8eaOiQTQ7*g3k}=+$q& zi=_1ZnZ2x``CazA&MP*~zCH54VSIN_VBOtG#JxSm>VCyxn?QGG+T5jBn>(0BoWC~r zBz{5WI`;zN_d7%V{kBC!K76jA>*E~4=PJ7VPx2UZ4KYqW;%=ckkN(_CVEKr 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()