diff --git a/.clang-format b/.clang-format index 2b4ffc1..f5c0cb6 100644 --- a/.clang-format +++ b/.clang-format @@ -3,6 +3,7 @@ AccessModifierOffset: -4 AlignConsecutiveMacros: true AlignTrailingComments: true AllowShortFunctionsOnASingleLine: Inline -AllowShortIfStatementsOnASingleLine: false +AllowShortIfStatementsOnASingleLine: true +AllowAllParametersOfDeclarationOnNextLine: true BreakBeforeBraces: Allman ColumnLimit: 0 \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..3991930 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,16 @@ +{ + // 使用 IntelliSense 了解相关属性。 + // 悬停以查看现有属性的描述。 + // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch", + "type": "lldb", + "request": "launch", + "program": "${workspaceRoot}/", + "args": [], + "cwd": "${workspaceRoot}" + } + ] +} \ No newline at end of file diff --git a/VulkanTexture.hpp b/VulkanTexture.hpp new file mode 100644 index 0000000..a29c306 --- /dev/null +++ b/VulkanTexture.hpp @@ -0,0 +1,756 @@ +/* + +* vulkan 贴图基础封装类 +* 普通贴图使用stbi加载 +* ktx格式cubeMap贴图使用ktx库加载 + +*/ + +#ifndef VULKANTEXTURE_HPP +#define VULKANTEXTURE_HPP + +#include +#include +#include +#include +#include +#include +#include + +#include "VulkanDevice.hpp" +#include "VulkanTools.h" +#include "ktx.h" +#include "stb_image.h" + +#define GLM_ENABLE_EXPERIMENTAL + +namespace vks +{ +class Texture +{ +public: + vks::VulkanDevice *device; + VkImage image = VK_NULL_HANDLE; + VkImageLayout imageLayout; + VkDeviceMemory deviceMemory; + VkImageView view; + uint32_t width, height; + uint32_t mipLevels; + uint32_t layerCount; + VkDescriptorImageInfo descriptor; + VkSampler sampler; + + void updateDescriptor() + { + descriptor.sampler = sampler; + descriptor.imageView = view; + descriptor.imageLayout = imageLayout; + } + + void destroy() + { + vkDestroyImageView(device->logicalDevice, view, nullptr); + vkDestroyImage(device->logicalDevice, image, nullptr); + if (sampler) + { + vkDestroySampler(device->logicalDevice, sampler, nullptr); + } + vkFreeMemory(device->logicalDevice, deviceMemory, nullptr); + } + +private: +}; + +class Texture2D : public Texture +{ +public: + void loadFromFile( + std::string filename, + VkFormat format, + vks::VulkanDevice *device, + VkQueue copyQueue, + VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT, + VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) + { + int texWidth, texHeight, texChannels; + unsigned char *texImageData = stbi_load(filename.c_str(), &texWidth, &texHeight, &texChannels, 0); + + assert(texImageData == nullptr); + + this->device = device; + width = static_cast(texWidth); + height = static_cast(texHeight); + // stb image 不自动生成mipmap层级,使用公式计算 + mipLevels = static_cast(std::floor(std::log2(std::max(this->width, this->height)))) + 1; + size_t texImageSize = texWidth * texHeight * texChannels; + + // Get device properites for the requested texture format + VkFormatProperties formatProperties; + vkGetPhysicalDeviceFormatProperties(device->physicalDevice, format, &formatProperties); + + VkMemoryAllocateInfo memAllocInfo{}; + memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + VkMemoryRequirements memReqs; + + // Use a separate command buffer for texture loading + VkCommandBuffer copyCmd = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); + + // Create a host-visible staging buffer that contains the raw image data + VkBuffer stagingBuffer; + VkDeviceMemory stagingMemory; + + VkBufferCreateInfo bufferCreateInfo{}; + bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferCreateInfo.size = texImageSize; + // This buffer is used as a transfer source for the buffer copy + bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + VK_CHECK_RESULT(vkCreateBuffer(device->logicalDevice, &bufferCreateInfo, nullptr, &stagingBuffer)); + + // Get memory requirements for the staging buffer (alignment, memory type bits) + vkGetBufferMemoryRequirements(device->logicalDevice, stagingBuffer, &memReqs); + + memAllocInfo.allocationSize = memReqs.size; + // Get memory type index for a host visible buffer + memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + + VK_CHECK_RESULT(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &stagingMemory)); + VK_CHECK_RESULT(vkBindBufferMemory(device->logicalDevice, stagingBuffer, stagingMemory, 0)); + + // Copy texture data into staging buffer + uint8_t *data; + VK_CHECK_RESULT(vkMapMemory(device->logicalDevice, stagingMemory, 0, memReqs.size, 0, (void **)&data)); + memcpy(data, texImageData, texImageSize); + vkUnmapMemory(device->logicalDevice, stagingMemory); + + // clean up image data + stbi_image_free(texImageData); + + // Create optimal tiled target image + VkImageCreateInfo imageCreateInfo{}; + imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; + imageCreateInfo.format = format; + imageCreateInfo.mipLevels = 1; + imageCreateInfo.arrayLayers = 1; + imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageCreateInfo.extent = {width, height, 1}; + imageCreateInfo.usage = imageUsageFlags; + // Ensure that the TRANSFER_DST bit is set for staging + if (!(imageCreateInfo.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT)) + { + imageCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + } + VK_CHECK_RESULT(vkCreateImage(device->logicalDevice, &imageCreateInfo, nullptr, &image)); + + vkGetImageMemoryRequirements(device->logicalDevice, image, &memReqs); + + memAllocInfo.allocationSize = memReqs.size; + + memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + VK_CHECK_RESULT(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &deviceMemory)); + VK_CHECK_RESULT(vkBindImageMemory(device->logicalDevice, image, deviceMemory, 0)); + + VkImageSubresourceRange subresourceRange = {}; + subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + subresourceRange.baseMipLevel = 0; + subresourceRange.levelCount = 1; // 目前只处理第一级 + subresourceRange.layerCount = 1; + + // Image barrier for optimal image (target) + // Optimal image will be used as destination for the copy + { + VkImageMemoryBarrier imageMemoryBarrier{}; + imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + imageMemoryBarrier.srcAccessMask = 0; + imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + imageMemoryBarrier.image = image; + imageMemoryBarrier.subresourceRange = subresourceRange; + vkCmdPipelineBarrier(copyCmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); + } + + VkBufferImageCopy bufferCopyRegion = {}; + bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + bufferCopyRegion.imageSubresource.mipLevel = 0; + bufferCopyRegion.imageSubresource.baseArrayLayer = 0; + bufferCopyRegion.imageSubresource.layerCount = 1; + bufferCopyRegion.imageExtent.width = width; + bufferCopyRegion.imageExtent.height = height; + bufferCopyRegion.imageExtent.depth = 1; + bufferCopyRegion.bufferOffset = 0; + + // Copy mip levels from staging buffer + vkCmdCopyBufferToImage( + copyCmd, + stagingBuffer, + image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + static_cast(1), + &bufferCopyRegion); + + generateMipmaps(copyCmd, format); + + // Change texture image layout to shader read after all mip levels have been copied + this->imageLayout = imageLayout; + { + VkImageMemoryBarrier imageMemoryBarrier{}; + imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + imageMemoryBarrier.newLayout = imageLayout; + imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + imageMemoryBarrier.image = image; + imageMemoryBarrier.subresourceRange = subresourceRange; + vkCmdPipelineBarrier(copyCmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); + } + + device->flushCommandBuffer(copyCmd, copyQueue); + + // Clean up staging resources + vkFreeMemory(device->logicalDevice, stagingMemory, nullptr); + vkDestroyBuffer(device->logicalDevice, stagingBuffer, nullptr); + + VkSamplerCreateInfo samplerCreateInfo{}; + samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + samplerCreateInfo.magFilter = VK_FILTER_LINEAR; + samplerCreateInfo.minFilter = VK_FILTER_LINEAR; + samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerCreateInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerCreateInfo.mipLodBias = 0.0f; + samplerCreateInfo.compareOp = VK_COMPARE_OP_NEVER; + samplerCreateInfo.minLod = 0.0f; + samplerCreateInfo.maxLod = (float)mipLevels; + samplerCreateInfo.maxAnisotropy = device->enabledFeatures.samplerAnisotropy ? device->properties.limits.maxSamplerAnisotropy : 1.0f; + samplerCreateInfo.anisotropyEnable = device->enabledFeatures.samplerAnisotropy; + samplerCreateInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; + VK_CHECK_RESULT(vkCreateSampler(device->logicalDevice, &samplerCreateInfo, nullptr, &sampler)); + + VkImageViewCreateInfo viewCreateInfo{}; + viewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + viewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + viewCreateInfo.format = format; + viewCreateInfo.components = {VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A}; + viewCreateInfo.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; + viewCreateInfo.subresourceRange.levelCount = mipLevels; + viewCreateInfo.image = image; + VK_CHECK_RESULT(vkCreateImageView(device->logicalDevice, &viewCreateInfo, nullptr, &view)); + + updateDescriptor(); + } + + void loadFromBuffer( + void *buffer, + VkDeviceSize bufferSize, + VkFormat format, + uint32_t width, + uint32_t height, + vks::VulkanDevice *device, + VkQueue copyQueue, + VkFilter filter = VK_FILTER_LINEAR, + VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT, + VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) + { + assert(buffer); + + this->device = device; + width = width; + height = height; + mipLevels = 1; + + VkMemoryAllocateInfo memAllocInfo{}; + memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + VkMemoryRequirements memReqs; + // Use a separate command buffer for texture loading + VkCommandBuffer copyCmd = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); + + // Create a host-visible staging buffer that contains the raw image data + VkBuffer stagingBuffer; + VkDeviceMemory stagingMemory; + + VkBufferCreateInfo bufferCreateInfo{}; + bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferCreateInfo.size = bufferSize; + // This buffer is used as a transfer source for the buffer copy + bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + VK_CHECK_RESULT(vkCreateBuffer(device->logicalDevice, &bufferCreateInfo, nullptr, &stagingBuffer)); + + // Get memory requirements for the staging buffer (alignment, memory type bits) + vkGetBufferMemoryRequirements(device->logicalDevice, stagingBuffer, &memReqs); + + memAllocInfo.allocationSize = memReqs.size; + // Get memory type index for a host visible buffer + memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + + VK_CHECK_RESULT(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &stagingMemory)); + VK_CHECK_RESULT(vkBindBufferMemory(device->logicalDevice, stagingBuffer, stagingMemory, 0)); + + // Copy texture data into staging buffer + uint8_t *data; + VK_CHECK_RESULT(vkMapMemory(device->logicalDevice, stagingMemory, 0, memReqs.size, 0, (void **)&data)); + memcpy(data, buffer, bufferSize); + vkUnmapMemory(device->logicalDevice, stagingMemory); + + VkBufferImageCopy bufferCopyRegion = {}; + bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + bufferCopyRegion.imageSubresource.mipLevel = 0; + bufferCopyRegion.imageSubresource.baseArrayLayer = 0; + bufferCopyRegion.imageSubresource.layerCount = 1; + bufferCopyRegion.imageExtent.width = width; + bufferCopyRegion.imageExtent.height = height; + bufferCopyRegion.imageExtent.depth = 1; + bufferCopyRegion.bufferOffset = 0; + + // Create optimal tiled target image + VkImageCreateInfo imageCreateInfo{}; + imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; + imageCreateInfo.format = format; + imageCreateInfo.mipLevels = mipLevels; + imageCreateInfo.arrayLayers = 1; + imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageCreateInfo.extent = {width, height, 1}; + imageCreateInfo.usage = imageUsageFlags; + // Ensure that the TRANSFER_DST bit is set for staging + if (!(imageCreateInfo.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT)) + { + imageCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; + } + VK_CHECK_RESULT(vkCreateImage(device->logicalDevice, &imageCreateInfo, nullptr, &image)); + + vkGetImageMemoryRequirements(device->logicalDevice, image, &memReqs); + + memAllocInfo.allocationSize = memReqs.size; + + memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + VK_CHECK_RESULT(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &deviceMemory)); + VK_CHECK_RESULT(vkBindImageMemory(device->logicalDevice, image, deviceMemory, 0)); + + VkImageSubresourceRange subresourceRange = {}; + subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + subresourceRange.baseMipLevel = 0; + subresourceRange.levelCount = mipLevels; + subresourceRange.layerCount = 1; + + { + VkImageMemoryBarrier imageMemoryBarrier{}; + imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + imageMemoryBarrier.srcAccessMask = 0; + imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + imageMemoryBarrier.image = image; + imageMemoryBarrier.subresourceRange = subresourceRange; + vkCmdPipelineBarrier(copyCmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); + } + + vkCmdCopyBufferToImage( + copyCmd, + stagingBuffer, + image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, + &bufferCopyRegion); + + this->imageLayout = imageLayout; + { + VkImageMemoryBarrier imageMemoryBarrier{}; + imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + imageMemoryBarrier.newLayout = imageLayout; + imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + imageMemoryBarrier.image = image; + imageMemoryBarrier.subresourceRange = subresourceRange; + vkCmdPipelineBarrier(copyCmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); + } + + device->flushCommandBuffer(copyCmd, copyQueue); + + // Clean up staging resources + vkFreeMemory(device->logicalDevice, stagingMemory, nullptr); + vkDestroyBuffer(device->logicalDevice, stagingBuffer, nullptr); + + // Create sampler + VkSamplerCreateInfo samplerCreateInfo = {}; + samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + samplerCreateInfo.magFilter = filter; + samplerCreateInfo.minFilter = filter; + samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerCreateInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerCreateInfo.mipLodBias = 0.0f; + samplerCreateInfo.compareOp = VK_COMPARE_OP_NEVER; + samplerCreateInfo.minLod = 0.0f; + samplerCreateInfo.maxLod = 0.0f; + samplerCreateInfo.maxAnisotropy = 1.0f; + VK_CHECK_RESULT(vkCreateSampler(device->logicalDevice, &samplerCreateInfo, nullptr, &sampler)); + + // Create image view + VkImageViewCreateInfo viewCreateInfo = {}; + viewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + viewCreateInfo.pNext = NULL; + viewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + viewCreateInfo.format = format; + viewCreateInfo.components = {VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A}; + viewCreateInfo.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; + viewCreateInfo.subresourceRange.levelCount = 1; + viewCreateInfo.image = image; + VK_CHECK_RESULT(vkCreateImageView(device->logicalDevice, &viewCreateInfo, nullptr, &view)); + + // Update descriptor image info member that can be used for setting up descriptor sets + updateDescriptor(); + } + +private: + void generateMipmaps(VkCommandBuffer commandBuffer, VkFormat format) + { + VkFormatProperties formatProperties; + vkGetPhysicalDeviceFormatProperties(device->physicalDevice, format, &formatProperties); + + if (!(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)) + { + throw std::runtime_error("Texture image format does not support linear blitting!"); + } + + VkImageMemoryBarrier imageMemoryBarrier{}; + imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + imageMemoryBarrier.image = image; + imageMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + imageMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + imageMemoryBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imageMemoryBarrier.subresourceRange.layerCount = 1; + imageMemoryBarrier.subresourceRange.levelCount = 1; + + uint32_t mipWidth = width; + uint32_t mipHeight = height; + + for (uint32_t i = 1; i < mipLevels; i++) + { + imageMemoryBarrier.subresourceRange.baseMipLevel = i - 1; + imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + + vkCmdPipelineBarrier(commandBuffer, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, + 0, + nullptr, + 0, + nullptr, + 1, + &imageMemoryBarrier); + + VkImageBlit blit{}; + blit.srcOffsets[0] = {0, 0, 0}; + blit.srcOffsets[1] = {static_cast(mipWidth), static_cast(mipHeight), 1}; + blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + blit.srcSubresource.mipLevel = i - 1; + blit.srcSubresource.baseArrayLayer = 0; + blit.srcSubresource.layerCount = 1; + blit.dstOffsets[0] = {0, 0, 0}; + int32_t dstOffsetX = 1; + int32_t dstOffsetY = 1; + if (mipWidth > 1) + { + dstOffsetX = static_cast(mipWidth / 2); + } + if (mipHeight > 1) + { + dstOffsetY = static_cast(mipHeight / 2); + } + blit.dstOffsets[1] = {dstOffsetX, dstOffsetY, 1}; + blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + blit.dstSubresource.mipLevel = i; + blit.dstSubresource.baseArrayLayer = 0; + blit.dstSubresource.layerCount = 1; + + vkCmdBlitImage(commandBuffer, + image, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, + &blit, VK_FILTER_LINEAR); + + imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + imageMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + vkCmdPipelineBarrier(commandBuffer, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + 0, + 0, + nullptr, + 0, + nullptr, + 1, + &imageMemoryBarrier); + + if (mipWidth > 1) + { + mipWidth = mipWidth / 2; + } + if (mipHeight > 1) + { + mipHeight = mipHeight / 2; + } + } + imageMemoryBarrier.subresourceRange.baseMipLevel = 0; + imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + imageMemoryBarrier.newLayout = imageLayout; + imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + imageMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + imageMemoryBarrier.subresourceRange.levelCount = mipLevels; + + vkCmdPipelineBarrier(commandBuffer, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + 0, + 0, + nullptr, + 0, + nullptr, + 1, + &imageMemoryBarrier); + } +}; + +class TextureCubeMap : public Texture +{ +public: + void loadFromFile( + std::string filename, + VkFormat format, + vks::VulkanDevice *device, + VkQueue copyQueue, + VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT, + VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) + { + ktxTexture *cubeMapTexture = nullptr; + KTX_error_code ktxResult = ktxTexture_CreateFromNamedFile(filename.c_str(), KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT, &cubeMapTexture); + if (ktxResult != KTX_SUCCESS) + { + throw std::runtime_error("Failed to load KTX texture"); + } + + this->device = device; + width = static_cast(cubeMapTexture->baseWidth); + height = static_cast(cubeMapTexture->baseHeight); + mipLevels = cubeMapTexture->numLevels; + layerCount = cubeMapTexture->numLayers; + + VkMemoryAllocateInfo memAllocInfo{}; + memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + VkMemoryRequirements memReqs; + + // Create a host-visible staging buffer that contains the raw image data + VkBuffer stagingBuffer; + VkDeviceMemory stagingMemory; + + VkBufferCreateInfo bufferCreateInfo{}; + bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferCreateInfo.size = cubeMapTexture->dataSize; + + // This buffer is used as a transfer source for the buffer copy + bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + VK_CHECK_RESULT(vkCreateBuffer(device->logicalDevice, &bufferCreateInfo, nullptr, &stagingBuffer)); + + // Get memory requirements for the staging buffer (alignment, memory type bits) + vkGetBufferMemoryRequirements(device->logicalDevice, stagingBuffer, &memReqs); + + memAllocInfo.allocationSize = memReqs.size; + // Get memory type index for a host visible buffer + memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + + VK_CHECK_RESULT(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &stagingMemory)); + VK_CHECK_RESULT(vkBindBufferMemory(device->logicalDevice, stagingBuffer, stagingMemory, 0)); + + // Copy texture data into staging buffer + uint8_t *data; + VK_CHECK_RESULT(vkMapMemory(device->logicalDevice, stagingMemory, 0, memReqs.size, 0, (void **)&data)); + memcpy(data, ktxTexture_GetData(cubeMapTexture), cubeMapTexture->dataSize); + vkUnmapMemory(device->logicalDevice, stagingMemory); + + // Setup buffer copy regions for each face including all of it's miplevels + std::vector bufferCopyRegions; + size_t offset = 0; + + for (uint32_t face = 0; face < 6; face++) + { + for (uint32_t level = 0; level < mipLevels; level++) + { + ktx_size_t offset; + KTX_error_code result = ktxTexture_GetImageOffset( + cubeMapTexture, + level, // mip level + 0, // array layer + face, // face + &offset); + + if (result != KTX_SUCCESS) + { + ktxTexture_Destroy(cubeMapTexture); + throw std::runtime_error("Failed to get image offset from KTX texture"); + } + + VkBufferImageCopy bufferCopyRegion = {}; + bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + bufferCopyRegion.imageSubresource.mipLevel = level; + bufferCopyRegion.imageSubresource.baseArrayLayer = face; + bufferCopyRegion.imageSubresource.layerCount = 1; + + uint32_t mipWidth = std::max(1u, width >> level); + uint32_t mipHeight = std::max(1u, height >> level); + + bufferCopyRegion.imageExtent.width = mipWidth; + bufferCopyRegion.imageExtent.height = mipHeight; + bufferCopyRegion.imageExtent.depth = 1; + bufferCopyRegion.bufferOffset = offset; + + bufferCopyRegions.push_back(bufferCopyRegion); + } + } + + // Create optimal tiled target image + VkImageCreateInfo imageCreateInfo{}; + imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; + imageCreateInfo.format = format; + imageCreateInfo.mipLevels = mipLevels; + imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageCreateInfo.extent = {width, height, 1}; + imageCreateInfo.usage = imageUsageFlags; + // Ensure that the TRANSFER_DST bit is set for staging + if (!(imageCreateInfo.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT)) + { + imageCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; + } + // Cube faces count as array layers in Vulkan + imageCreateInfo.arrayLayers = 6; + // This flag is required for cube map images + imageCreateInfo.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; + + VK_CHECK_RESULT(vkCreateImage(device->logicalDevice, &imageCreateInfo, nullptr, &image)); + + vkGetImageMemoryRequirements(device->logicalDevice, image, &memReqs); + + memAllocInfo.allocationSize = memReqs.size; + memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + + VK_CHECK_RESULT(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &deviceMemory)); + VK_CHECK_RESULT(vkBindImageMemory(device->logicalDevice, image, deviceMemory, 0)); + + // Use a separate command buffer for texture loading + VkCommandBuffer copyCmd = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); + + // Image barrier for optimal image (target) + // Set initial layout for all array layers (faces) of the optimal (target) tiled texture + VkImageSubresourceRange subresourceRange = {}; + subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + subresourceRange.baseMipLevel = 0; + subresourceRange.levelCount = mipLevels; + subresourceRange.layerCount = 6; + + { + VkImageMemoryBarrier imageMemoryBarrier{}; + imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + imageMemoryBarrier.srcAccessMask = 0; + imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + imageMemoryBarrier.image = image; + imageMemoryBarrier.subresourceRange = subresourceRange; + vkCmdPipelineBarrier(copyCmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); + } + + // Copy the cube map faces from the staging buffer to the optimal tiled image + vkCmdCopyBufferToImage( + copyCmd, + stagingBuffer, + image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + static_cast(bufferCopyRegions.size()), + bufferCopyRegions.data()); + + // Change texture image layout to shader read after all faces have been copied + this->imageLayout = imageLayout; + { + VkImageMemoryBarrier imageMemoryBarrier{}; + imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + imageMemoryBarrier.newLayout = imageLayout; + imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + imageMemoryBarrier.image = image; + imageMemoryBarrier.subresourceRange = subresourceRange; + vkCmdPipelineBarrier(copyCmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); + } + + device->flushCommandBuffer(copyCmd, copyQueue); + + // Create sampler + VkSamplerCreateInfo samplerCreateInfo{}; + samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + samplerCreateInfo.magFilter = VK_FILTER_LINEAR; + samplerCreateInfo.minFilter = VK_FILTER_LINEAR; + samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerCreateInfo.addressModeV = samplerCreateInfo.addressModeU; + samplerCreateInfo.addressModeW = samplerCreateInfo.addressModeU; + samplerCreateInfo.mipLodBias = 0.0f; + samplerCreateInfo.maxAnisotropy = device->enabledFeatures.samplerAnisotropy ? device->properties.limits.maxSamplerAnisotropy : 1.0f; + samplerCreateInfo.anisotropyEnable = device->enabledFeatures.samplerAnisotropy; + samplerCreateInfo.compareOp = VK_COMPARE_OP_NEVER; + samplerCreateInfo.minLod = 0.0f; + samplerCreateInfo.maxLod = (float)mipLevels; + samplerCreateInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; + VK_CHECK_RESULT(vkCreateSampler(device->logicalDevice, &samplerCreateInfo, nullptr, &sampler)); + + // Create image view + VkImageViewCreateInfo viewCreateInfo{}; + viewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + viewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_CUBE; + viewCreateInfo.format = format; + viewCreateInfo.components = {VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A}; + viewCreateInfo.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; + viewCreateInfo.subresourceRange.layerCount = 6; + viewCreateInfo.subresourceRange.levelCount = mipLevels; + viewCreateInfo.image = image; + VK_CHECK_RESULT(vkCreateImageView(device->logicalDevice, &viewCreateInfo, nullptr, &view)); + + // Clean up staging resources + vkFreeMemory(device->logicalDevice, stagingMemory, nullptr); + vkDestroyBuffer(device->logicalDevice, stagingBuffer, nullptr); + + // Update descriptor image info member that can be used for setting up descriptor sets + updateDescriptor(); + } +}; + +} // namespace vks + +#endif \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5110bca..521fab3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -15,8 +15,6 @@ set(GLTF_MODEL_LOADER "gltf/glTFBoundingBox.cpp" "gltf/glTFTexture.h" "gltf/glTFTexture.cpp" - #"gltf/glTFModel.h" - #"gltf/glTFModel.cpp" "gltf/glTFMaterial.h" "gltf/glTFMaterial.cpp" "gltf/glTFPrimitive.h" @@ -35,12 +33,12 @@ set(GLTF_MODEL_LOADER "gltf/glTFAnimation.cpp" "gltf/glTFMainModel.h" "gltf/glTFMainModel.cpp" - ) aux_source_directory(${PROJECT_SOURCE_DIR}/src/render VULKAN_BASE) +aux_source_directory(${PROJECT_SOURCE_DIR}/src/pbr PBR) #include_directories(${3rdParty_gli_path}) include_directories(${3rdParty_glm_path}) @@ -65,6 +63,7 @@ if(WIN32) ${MAIN_FILE} ${GLTF_MODEL_LOADER} ${VULKAN_BASE} + ${PBR} "render/renderFoundation.h" "render/renderFoundation.cpp" diff --git a/src/base/CMakeLists.txt b/src/base/CMakeLists.txt index 04dbb81..90acd90 100644 --- a/src/base/CMakeLists.txt +++ b/src/base/CMakeLists.txt @@ -4,12 +4,10 @@ file(GLOB BASE_SRC "*.cpp" "*.h" "${3rdParty_imgui_path}/*.cpp" ) include_directories(${3rdParty_ktx_path}) include_directories(${3rdParty_ktx_otherInclude_path}) include_directories(${3rdParty_glm_path}) -include_directories(${3rdParty_gli_path}) +include_directories(${3rdParty_stb_path}) include_directories(${3rdParty_vulkan_path}) include_directories(${3rdParty_imgui_path}) -message("======================debug=====================") -message(${BASE_SRC}) add_library(base STATIC ${BASE_SRC} ${KTX_SOURCES}) if(WIN32) diff --git a/src/render/VulkanBase_Common.h b/src/base/VulkanBase_Common.h similarity index 100% rename from src/render/VulkanBase_Common.h rename to src/base/VulkanBase_Common.h diff --git a/src/render/VulkanBase_Marco.h b/src/base/VulkanBase_Marco.h similarity index 100% rename from src/render/VulkanBase_Marco.h rename to src/base/VulkanBase_Marco.h diff --git a/src/base/VulkanBuffer.cpp b/src/base/VulkanBuffer.cpp index 7d4bf24..20e15ef 100644 --- a/src/base/VulkanBuffer.cpp +++ b/src/base/VulkanBuffer.cpp @@ -1,135 +1,137 @@ -/* -* Vulkan buffer class -* -* Encapsulates a Vulkan buffer -* -* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de -* -* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) -*/ - #include "VulkanBuffer.h" +#include "VulkanTools.h" -namespace vks -{ - /** - * Map a memory range of this buffer. If successful, mapped points to the specified buffer range. - * - * @param size (Optional) Size of the memory range to map. Pass VK_WHOLE_SIZE to map the complete buffer range. - * @param offset (Optional) Byte offset from beginning - * - * @return VkResult of the buffer mapping call - */ - VkResult Buffer::map(VkDeviceSize size, VkDeviceSize offset) - { - return vkMapMemory(device, memory, offset, size, 0, &mapped); - } +VULKANBASE_NAMESPACE_BEGIN +/** + * Map a memory range of this buffer. If successful, mapped points to the specified buffer range. + * + * @param size (Optional) Size of the memory range to map. Pass VK_WHOLE_SIZE to map the complete buffer range. + * @param offset (Optional) Byte offset from beginning + * + * @return VkResult of the buffer mapping call + */ +VkResult Buffer::map(VkDeviceSize size, VkDeviceSize offset) +{ + return vkMapMemory(device, memory, offset, size, 0, &mapped); +} - /** - * Unmap a mapped memory range - * - * @note Does not return a result as vkUnmapMemory can't fail - */ - void Buffer::unmap() - { - if (mapped) - { - vkUnmapMemory(device, memory); - mapped = nullptr; - } - } +/** + * Unmap a mapped memory range + * + * @note Does not return a result as vkUnmapMemory can't fail + */ +void Buffer::unmap() +{ + if (mapped) + { + vkUnmapMemory(device, memory); + mapped = nullptr; + } +} - /** - * Attach the allocated memory block to the buffer - * - * @param offset (Optional) Byte offset (from the beginning) for the memory region to bind - * - * @return VkResult of the bindBufferMemory call - */ - VkResult Buffer::bind(VkDeviceSize offset) - { - return vkBindBufferMemory(device, buffer, memory, offset); - } +/** + * Attach the allocated memory block to the buffer + * + * @param offset (Optional) Byte offset (from the beginning) for the memory region to bind + * + * @return VkResult of the bindBufferMemory call + */ +VkResult Buffer::bind(VkDeviceSize offset) +{ + return vkBindBufferMemory(device, buffer, memory, offset); +} - /** - * Setup the default descriptor for this buffer - * - * @param size (Optional) Size of the memory range of the descriptor - * @param offset (Optional) Byte offset from beginning - * - */ - void Buffer::setupDescriptor(VkDeviceSize size, VkDeviceSize offset) - { - descriptor.offset = offset; - descriptor.buffer = buffer; - descriptor.range = size; - } +/** + * Setup the default descriptor for this buffer + * + * @param size (Optional) Size of the memory range of the descriptor + * @param offset (Optional) Byte offset from beginning + * + */ +void Buffer::setupDescriptor(VkDeviceSize size, VkDeviceSize offset) +{ + descriptor.offset = offset; + descriptor.buffer = buffer; + descriptor.range = size; +} - /** - * Copies the specified data to the mapped buffer - * - * @param data Pointer to the data to copy - * @param size Size of the data to copy in machine units - * - */ - void Buffer::copyTo(void* data, VkDeviceSize size) - { - assert(mapped); - memcpy(mapped, data, size); - } +/** + * Copies the specified data to the mapped buffer + * + * @param data Pointer to the data to copy + * @param size Size of the data to copy in machine units + * + */ +void Buffer::copyTo(void *data, VkDeviceSize size) +{ + assert(mapped); + memcpy(mapped, data, size); +} - /** - * Flush a memory range of the buffer to make it visible to the device - * - * @note Only required for non-coherent memory - * - * @param size (Optional) Size of the memory range to flush. Pass VK_WHOLE_SIZE to flush the complete buffer range. - * @param offset (Optional) Byte offset from beginning - * - * @return VkResult of the flush call - */ - VkResult Buffer::flush(VkDeviceSize size, VkDeviceSize offset) - { - VkMappedMemoryRange mappedRange = {}; - mappedRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; - mappedRange.memory = memory; - mappedRange.offset = offset; - mappedRange.size = size; - return vkFlushMappedMemoryRanges(device, 1, &mappedRange); - } +/** + * Flush a memory range of the buffer to make it visible to the device + * + * @note Only required for non-coherent memory + * + * @param size (Optional) Size of the memory range to flush. Pass VK_WHOLE_SIZE to flush the complete buffer range. + * @param offset (Optional) Byte offset from beginning + * + * @return VkResult of the flush call + */ +VkResult Buffer::flush(VkDeviceSize size, VkDeviceSize offset) +{ + VkMappedMemoryRange mappedRange = {}; + mappedRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; + mappedRange.memory = memory; + mappedRange.offset = offset; + mappedRange.size = size; + return vkFlushMappedMemoryRanges(device, 1, &mappedRange); +} - /** - * Invalidate a memory range of the buffer to make it visible to the host - * - * @note Only required for non-coherent memory - * - * @param size (Optional) Size of the memory range to invalidate. Pass VK_WHOLE_SIZE to invalidate the complete buffer range. - * @param offset (Optional) Byte offset from beginning - * - * @return VkResult of the invalidate call - */ - VkResult Buffer::invalidate(VkDeviceSize size, VkDeviceSize offset) - { - VkMappedMemoryRange mappedRange = {}; - mappedRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; - mappedRange.memory = memory; - mappedRange.offset = offset; - mappedRange.size = size; - return vkInvalidateMappedMemoryRanges(device, 1, &mappedRange); - } +/** + * Invalidate a memory range of the buffer to make it visible to the host + * + * @note Only required for non-coherent memory + * + * @param size (Optional) Size of the memory range to invalidate. Pass VK_WHOLE_SIZE to invalidate the complete buffer range. + * @param offset (Optional) Byte offset from beginning + * + * @return VkResult of the invalidate call + */ +VkResult Buffer::invalidate(VkDeviceSize size, VkDeviceSize offset) +{ + VkMappedMemoryRange mappedRange = {}; + mappedRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; + mappedRange.memory = memory; + mappedRange.offset = offset; + mappedRange.size = size; + return vkInvalidateMappedMemoryRanges(device, 1, &mappedRange); +} - /** - * Release all Vulkan resources held by this buffer - */ - void Buffer::destroy() - { - if (buffer) - { - vkDestroyBuffer(device, buffer, nullptr); - } - if (memory) - { - vkFreeMemory(device, memory, nullptr); - } - } -}; +/** + * Release all Vulkan resources held by this buffer + */ +void Buffer::destroy() +{ + if (mapped) + { + unmap(); + } + vkDestroyBuffer(device, buffer, nullptr); + vkFreeMemory(device, memory, nullptr); + buffer = VK_NULL_HANDLE; + memory = VK_NULL_HANDLE; +} + +void Buffer::create(VulkanBase::VulkanDevice *device, VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, VkDeviceSize size, bool map) +{ + VkDevice logicalDevice = device->getLogicalDevice(); + device->createBuffer(usageFlags, memoryPropertyFlags, size, &buffer, &memory); + descriptor = {buffer, 0, size}; + if (map) + { + VK_CHECK_RESULT(vkMapMemory(logicalDevice, memory, 0, size, 0, &mapped)); + } +} + +VULKANBASE_NAMESPACE_END \ No newline at end of file diff --git a/src/base/VulkanBuffer.h b/src/base/VulkanBuffer.h index bd2a421..b258e5b 100644 --- a/src/base/VulkanBuffer.h +++ b/src/base/VulkanBuffer.h @@ -1,46 +1,43 @@ -/* -* Vulkan buffer class -* -* Encapsulates a Vulkan buffer -* -* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de -* -* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) -*/ -#pragma once +#ifndef VULKANBUFFER_H +#define VULKANBUFFER_H #include -#include "vulkan/vulkan.h" -#include "VulkanTools.h" +#include "VulkanBase_Marco.h" +#include "VulkanDevice.h" +#include -namespace vks -{ - /** - * @brief Encapsulates access to a Vulkan buffer backed up by device memory - * @note To be filled by an external source like the VulkanDevice - */ - struct Buffer - { - VkDevice device; - VkBuffer buffer = VK_NULL_HANDLE; - VkDeviceMemory memory = VK_NULL_HANDLE; - VkDescriptorBufferInfo descriptor; - VkDeviceSize size = 0; - VkDeviceSize alignment = 0; - void* mapped = nullptr; - /** @brief Usage flags to be filled by external source at buffer creation (to query at some later point) */ - VkBufferUsageFlags usageFlags; - /** @brief Memory property flags to be filled by external source at buffer creation (to query at some later point) */ - VkMemoryPropertyFlags memoryPropertyFlags; - VkResult map(VkDeviceSize size = VK_WHOLE_SIZE, VkDeviceSize offset = 0); - void unmap(); - VkResult bind(VkDeviceSize offset = 0); - void setupDescriptor(VkDeviceSize size = VK_WHOLE_SIZE, VkDeviceSize offset = 0); - void copyTo(void* data, VkDeviceSize size); - VkResult flush(VkDeviceSize size = VK_WHOLE_SIZE, VkDeviceSize offset = 0); - VkResult invalidate(VkDeviceSize size = VK_WHOLE_SIZE, VkDeviceSize offset = 0); - void destroy(); - }; -} \ No newline at end of file +VULKANBASE_NAMESPACE_BEGIN +/** + * @brief Encapsulates access to a Vulkan buffer backed up by device memory + * @note To be filled by an external source like the VulkanDevice + */ +struct Buffer +{ + VkDevice device; + VkBuffer buffer = VK_NULL_HANDLE; + VkDeviceMemory memory = VK_NULL_HANDLE; + VkDescriptorBufferInfo descriptor; + VkDeviceSize size = 0; + VkDeviceSize alignment = 0; + int32_t count = 0; + void *mapped = nullptr; + /** @brief Usage flags to be filled by external source at buffer creation (to query at some later point) */ + VkBufferUsageFlags usageFlags; + /** @brief Memory property flags to be filled by external source at buffer creation (to query at some later point) */ + VkMemoryPropertyFlags memoryPropertyFlags; + void create(VulkanDevice *device, VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, VkDeviceSize size, bool map = true); + VkResult map(VkDeviceSize size = VK_WHOLE_SIZE, VkDeviceSize offset = 0); + void unmap(); + VkResult bind(VkDeviceSize offset = 0); + void setupDescriptor(VkDeviceSize size = VK_WHOLE_SIZE, VkDeviceSize offset = 0); + void copyTo(void *data, VkDeviceSize size); + VkResult flush(VkDeviceSize size = VK_WHOLE_SIZE, VkDeviceSize offset = 0); + VkResult invalidate(VkDeviceSize size = VK_WHOLE_SIZE, VkDeviceSize offset = 0); + void destroy(); +}; + +VULKANBASE_NAMESPACE_END + +#endif \ No newline at end of file diff --git a/src/base/VulkanDevice.cpp b/src/base/VulkanDevice.cpp new file mode 100644 index 0000000..5a6670d --- /dev/null +++ b/src/base/VulkanDevice.cpp @@ -0,0 +1,254 @@ +#include "VulkanDevice.h" +#include "VulkanTools.h" +#include +#include + +VULKANBASE_NAMESPACE_BEGIN + +VulkanDevice::VulkanDevice() + : m_commandPool(VK_NULL_HANDLE) +{ +} + +VulkanDevice::VulkanDevice(VkPhysicalDevice physicalDevice) +{ + /// 检查物理设备是否存在,后续使用log方式记录 + assert(physicalDevice); + m_physicalDevice = physicalDevice; + + /// 获取设备信息 + vkGetPhysicalDeviceProperties(physicalDevice, &m_properties); + /// 获取设备支持的功能 + vkGetPhysicalDeviceFeatures(physicalDevice, &m_features); + /// 获取设备内存信息,用于创建内存 + vkGetPhysicalDeviceMemoryProperties(physicalDevice, &m_memoryProperties); + /// 队列族信息,用于设备创建时获取设置获取的队列 + uint32_t queueFamilyCount; + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr); + /// 检查设备队列族数量,必须大于0 + assert(queueFamilyCount > 0); + m_queueFamilyProperties.resize(queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, m_queueFamilyProperties.data()); +} + +VulkanDevice::~VulkanDevice() +{ + if (m_commandPool) + { + vkDestroyCommandPool(m_logicalDevice, m_commandPool, nullptr); + } + if (m_logicalDevice) + { + vkDestroyDevice(m_logicalDevice, nullptr); + } +} + +VkDevice VulkanDevice::getLogicalDevice() +{ + return m_logicalDevice; +} + +VkPhysicalDevice VulkanDevice::getPhysicalDevice() +{ + return m_physicalDevice; +} + +VkPhysicalDeviceProperties VulkanDevice::VulkanDevice::getPhysicalDeviceProperties() +{ + return m_properties; +} +VkPhysicalDeviceFeatures VulkanDevice::getPhysicalDeviceFeatures() +{ + return m_features; +} +VkPhysicalDeviceMemoryProperties VulkanDevice::getPhysicalDeviceMemoryProperties() +{ + return m_memoryProperties; +} +std::vector VulkanDevice::getQueueFamilyProperties() +{ + return m_queueFamilyProperties; +} +VkCommandPool VulkanDevice::getCommandPool() +{ + return m_commandPool; +} + +uint32_t VulkanDevice::getMemoryType(uint32_t typeBits, VkMemoryPropertyFlags properties, VkBool32 *memTypeFound) +{ + for (uint32_t i = 0; i < m_memoryProperties.memoryTypeCount; i++) + { + if ((typeBits & 1) == 1) + { + if ((m_memoryProperties.memoryTypes[i].propertyFlags & properties) == properties) + { + if (memTypeFound) + { + *memTypeFound = true; + } + return i; + } + } + typeBits >>= 1; + } + + if (memTypeFound) + { + *memTypeFound = false; + return 0; + } + else + { + throw std::runtime_error("Could not find a matching memory type"); + } +} + +uint32_t VulkanDevice::getQueueFamilyIndex(VkQueueFlagBits queueFlags) +{ + /// 获取支持计算的队列组索引 + if (queueFlags & VK_QUEUE_COMPUTE_BIT) + { + for (uint32_t i = 0; i < static_cast(m_queueFamilyProperties.size()); i++) + { + if ((m_queueFamilyProperties[i].queueFlags & queueFlags) && ((m_queueFamilyProperties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) == 0)) + { + return i; + break; + } + } + } + + /// 对于其他的队列类型,如果当前没有单独的计算队列,返回支持标志类型的第一个队列 + for (uint32_t i = 0; i < static_cast(m_queueFamilyProperties.size()); i++) + { + if (m_queueFamilyProperties[i].queueFlags & queueFlags) + { + return i; + break; + } + } + + throw std::runtime_error("Could not find a matching queue family index"); +} +uint32_t VulkanDevice::getGraphicsQueueFamilyIndex() +{ + return m_queueFamilyIndices.graphics; +} +uint32_t VulkanDevice::getComputeQueueFamilyIndex() +{ + return m_queueFamilyIndices.compute; +} + +VkResult VulkanDevice::createBuffer(VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, VkDeviceSize size, VkBuffer *buffer, VkDeviceMemory *memory, void *data) +{ + // 创建buffer句柄 + VkBufferCreateInfo bufferCreateInfo{}; + bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferCreateInfo.usage = usageFlags; + bufferCreateInfo.size = size; + bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + VK_CHECK_RESULT(vkCreateBuffer(m_logicalDevice, &bufferCreateInfo, nullptr, buffer)); + + // 创建buffer的设备内存分配信息 + VkMemoryRequirements memReqs; + VkMemoryAllocateInfo memAlloc{}; + memAlloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + vkGetBufferMemoryRequirements(m_logicalDevice, *buffer, &memReqs); + memAlloc.allocationSize = memReqs.size; + /// 获取符合buffer的设备内存类型索引 + memAlloc.memoryTypeIndex = getMemoryType(memReqs.memoryTypeBits, memoryPropertyFlags); + VK_CHECK_RESULT(vkAllocateMemory(m_logicalDevice, &memAlloc, nullptr, memory)); + + // 如何buffer指针已经存在,复制或者映射该buffer + if (data != nullptr) + { + void *mapped; + VK_CHECK_RESULT(vkMapMemory(m_logicalDevice, *memory, 0, size, 0, &mapped)); + memcpy(mapped, data, size); + // 如果host coherency 未设置, 对buffer进行flush,让设备侧可见 + if ((memoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) == 0) + { + VkMappedMemoryRange mappedRange{}; + mappedRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; + mappedRange.memory = *memory; + mappedRange.offset = 0; + mappedRange.size = size; + vkFlushMappedMemoryRanges(m_logicalDevice, 1, &mappedRange); + } + vkUnmapMemory(m_logicalDevice, *memory); + } + + // 绑定设备内存到buffer object + VK_CHECK_RESULT(vkBindBufferMemory(m_logicalDevice, *buffer, *memory, 0)); + + return VK_SUCCESS; +} + +VkCommandPool VulkanDevice::createCommandPool(uint32_t queueFamilyIndex, VkCommandPoolCreateFlags createFlags) +{ + /// 创建命令缓冲池 + VkCommandPoolCreateInfo cmdPoolInfo = {}; + cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + cmdPoolInfo.queueFamilyIndex = queueFamilyIndex; + cmdPoolInfo.flags = createFlags; + VkCommandPool cmdPool; + VK_CHECK_RESULT(vkCreateCommandPool(m_logicalDevice, &cmdPoolInfo, nullptr, &cmdPool)); + return cmdPool; +} + +VkCommandBuffer VulkanDevice::createCommandBuffer(VkCommandBufferLevel level, bool begin) +{ + VkCommandBufferAllocateInfo cmdBufAllocateInfo{}; + cmdBufAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + cmdBufAllocateInfo.commandPool = m_commandPool; + cmdBufAllocateInfo.level = level; + cmdBufAllocateInfo.commandBufferCount = 1; + + VkCommandBuffer cmdBuffer; + VK_CHECK_RESULT(vkAllocateCommandBuffers(m_logicalDevice, &cmdBufAllocateInfo, &cmdBuffer)); + + // 开始记录指令buffer + if (begin) + { + beginCommandBuffer(cmdBuffer); + } + + return cmdBuffer; +} + +void VulkanDevice::beginCommandBuffer(VkCommandBuffer commandBuffer) +{ + VkCommandBufferBeginInfo commandBufferBI{}; + commandBufferBI.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + VK_CHECK_RESULT(vkBeginCommandBuffer(commandBuffer, &commandBufferBI)); +} + +void VulkanDevice::flushCommandBuffer(VkCommandBuffer commandBuffer, VkQueue queue, bool free) +{ + VK_CHECK_RESULT(vkEndCommandBuffer(commandBuffer)); + + VkSubmitInfo submitInfo{}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &commandBuffer; + + // 创建同步栅栏,确保命令buffer执行完毕 + VkFenceCreateInfo fenceInfo{}; + fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + VkFence fence; + VK_CHECK_RESULT(vkCreateFence(m_logicalDevice, &fenceInfo, nullptr, &fence)); + + // 提交队列 + VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, fence)); + // 等待栅栏发出信号说明命令buffer执行完毕 + VK_CHECK_RESULT(vkWaitForFences(m_logicalDevice, 1, &fence, VK_TRUE, 100000000000)); + // 同步栅栏使命结束,销毁 + vkDestroyFence(m_logicalDevice, fence, nullptr); + // 需要的时候,销毁命令buffer + if (free) + { + vkFreeCommandBuffers(m_logicalDevice, m_commandPool, 1, &commandBuffer); + } +} + +VULKANBASE_NAMESPACE_END \ No newline at end of file diff --git a/src/base/VulkanDevice.h b/src/base/VulkanDevice.h new file mode 100644 index 0000000..22e66c0 --- /dev/null +++ b/src/base/VulkanDevice.h @@ -0,0 +1,104 @@ +#ifndef VULKANDEVICE_H +#define VULKANDEVICE_H + +#include "VulkanBase_Marco.h" + +#include +#include +#include + +VULKANBASE_NAMESPACE_BEGIN + +/// @brief vulkan的设备类 +class VulkanDevice +{ + +public: + VulkanDevice(); + VulkanDevice(VkPhysicalDevice physicalDevice); + ~VulkanDevice(); + +public: + VkDevice getLogicalDevice(); + VkPhysicalDevice getPhysicalDevice(); + VkPhysicalDeviceProperties getPhysicalDeviceProperties(); + VkPhysicalDeviceFeatures getPhysicalDeviceFeatures(); + VkPhysicalDeviceMemoryProperties getPhysicalDeviceMemoryProperties(); + std::vector getQueueFamilyProperties(); + VkCommandPool getCommandPool(); + uint32_t getGraphicsQueueFamilyIndex(); + uint32_t getComputeQueueFamilyIndex(); + + /// @brief 获取已设置所有请求属性位的设备内存类型的索引 + /// @param typeBits 请求的每种设备内存类型设置的位掩码,通常通过VkMemoryRequirements获取 + /// @param properties 要请求的设备内存类型的属性位掩码 + /// @param memTypeFound 如果符合的设备内存类型存在,则该指针的布尔值为true + /// @return 请求的设备内存类型的索引 + /// @throw 如果 memTypeFound 为空且找不到支持需要的属性的设备内存类型,则抛出异常 + uint32_t getMemoryType(uint32_t typeBits, VkMemoryPropertyFlags properties, VkBool32 *memTypeFound = nullptr); + + /// @brief 获取支持所请求队列标志的队列族的索引 + /// @param queueFlags 用于查找队列族索引的队列标志 + /// @return 与标志匹配的队列族索引 + /// @throw 如果找不到支持所请求标志的队列族索引,则抛出异常 + uint32_t getQueueFamilyIndex(VkQueueFlagBits queueFlags); + + /// @brief 据分配的物理设备创建逻辑设备,同时获取默认队列族索引 + /// @param enabledFeatures 在创建设备时启用某些功能 + /// @param enabledExtensions 在创建设备时启用某些扩展 + /// @param requestedQueueTypes 指定要从设备请求的队列类型 + /// @return 逻辑设备是否成功创建 + VkResult createLogicalDevice(VkPhysicalDeviceFeatures enabledFeatures, std::vector enabledExtensions, VkQueueFlags requestedQueueTypes = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT); + + /// @brief 在设备上创建缓冲区 + /// @param usageFlags 缓冲区的使用标志位掩码(即索引、顶点、统一缓冲区) + /// @param memoryPropertyFlags 此缓冲区的内存属性(即设备本地、主机可见、一致) + /// @param size 缓冲区的大小(以字节为单位) + /// @param buffer 指向函数获取的缓冲区句柄的指针 + /// @param memory 指向函数获取的设备内存句柄的指针 + /// @param data 指向创建后应复制到缓冲区的数据的指针(可选,如果未设置,则不会复制任何数据) + /// @return 如果已创建缓冲区句柄和设备内存并且已复制数据(可选传递),则返回 VK_SUCCESS + VkResult createBuffer(VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, VkDeviceSize size, VkBuffer *buffer, VkDeviceMemory *memory, void *data = nullptr); + + /// @brief 创建命令池以分配命令缓冲区 + /// @param queueFamilyIndex 要为其创建命令池的队列的系列索引 + /// @param createFlags 可选)命令池创建标志(默认为 VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT) + /// @return 创建的命令缓冲区的句柄 + VkCommandPool createCommandPool(uint32_t queueFamilyIndex, VkCommandPoolCreateFlags createFlags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT); + + /// @brief 从命令池中分配命令缓冲区 + /// @param level 新命令缓冲区的级别(主或次) + /// @param begin 为 true时开始在新命令缓冲区上进行记录 + /// @return 分配的命令缓冲区的句柄 + VkCommandBuffer createCommandBuffer(VkCommandBufferLevel level, bool begin = false); + + /// @brief 开始在指定命令缓冲区记录 + /// @param commandBuffer + void beginCommandBuffer(VkCommandBuffer commandBuffer); + + /// @brief 停止指定命令缓冲区记录并将其提交到队列,使用栅栏来确保命令缓冲区已完成执行 + /// @param commandBuffer 要刷新的命令缓冲区 + /// @param queue 要将命令缓冲区提交到的队列 + /// @param free 提交后释放命令缓冲区(默认为 true) + void flushCommandBuffer(VkCommandBuffer commandBuffer, VkQueue queue, bool free = true); + +private: + VkPhysicalDevice m_physicalDevice; + VkDevice m_logicalDevice; + VkPhysicalDeviceProperties m_properties; + VkPhysicalDeviceFeatures m_features; + VkPhysicalDeviceFeatures m_enabledFeatures; + VkPhysicalDeviceMemoryProperties m_memoryProperties; + std::vector m_queueFamilyProperties; + VkCommandPool m_commandPool; + + struct + { + uint32_t graphics; + uint32_t compute; + } m_queueFamilyIndices; +}; + +VULKANBASE_NAMESPACE_END + +#endif // !VULKANDEVICE_H diff --git a/src/base/VulkanDevice.hpp b/src/base/VulkanDevice.hpp deleted file mode 100644 index 3ec171d..0000000 --- a/src/base/VulkanDevice.hpp +++ /dev/null @@ -1,391 +0,0 @@ -/* -* Vulkan device class -* -* Encapsulates a physical Vulkan device and it's logical representation -* -* Copyright (C) 2016-2018 by Sascha Willems - www.saschawillems.de -* -* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) -*/ - -#pragma once -#include -#include -#include -#include -#include -#include -#include "vulkan/vulkan.h" - -#if defined(VK_USE_PLATFORM_MACOS_MVK) && (VK_HEADER_VERSION >= 216) -#include -#endif - -#if defined(VK_USE_PLATFORM_ANDROID_KHR) -#include "VulkanAndroid.h" -#endif - -#include "VulkanTools.h" - - -/// @brief 已单独剥离,等待删除 -namespace vks -{ - struct VulkanDevice - { - VkPhysicalDevice physicalDevice; - VkDevice logicalDevice; - VkPhysicalDeviceProperties properties; - VkPhysicalDeviceFeatures features; - VkPhysicalDeviceFeatures enabledFeatures; - VkPhysicalDeviceMemoryProperties memoryProperties; - std::vector queueFamilyProperties; - VkCommandPool commandPool = VK_NULL_HANDLE; - - struct { - uint32_t graphics; - uint32_t compute; - } queueFamilyIndices; - - operator VkDevice() { return logicalDevice; }; - - /** - * Default constructor - * - * @param physicalDevice Physical device that is to be used - */ - VulkanDevice(VkPhysicalDevice physicalDevice) - { - assert(physicalDevice); - this->physicalDevice = physicalDevice; - - // Store Properties features, limits and properties of the physical device for later use - // Device properties also contain limits and sparse properties - vkGetPhysicalDeviceProperties(physicalDevice, &properties); - // Features should be checked by the examples before using them - vkGetPhysicalDeviceFeatures(physicalDevice, &features); - // Memory properties are used regularly for creating all kinds of buffers - vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memoryProperties); - // Queue family properties, used for setting up requested queues upon device creation - uint32_t queueFamilyCount; - vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr); - assert(queueFamilyCount > 0); - queueFamilyProperties.resize(queueFamilyCount); - vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilyProperties.data()); - } - - /** - * Default destructor - * - * @note Frees the logical device - */ - ~VulkanDevice() - { - if (commandPool) { - vkDestroyCommandPool(logicalDevice, commandPool, nullptr); - } - if (logicalDevice) { - vkDestroyDevice(logicalDevice, nullptr); - } - } - - /** - * Get the index of a memory type that has all the requested property bits set - * - * @param typeBits Bitmask with bits set for each memory type supported by the resource to request for (from VkMemoryRequirements) - * @param properties Bitmask of properties for the memory type to request - * @param (Optional) memTypeFound Pointer to a bool that is set to true if a matching memory type has been found - * - * @return Index of the requested memory type - * - * @throw Throws an exception if memTypeFound is null and no memory type could be found that supports the requested properties - */ - uint32_t getMemoryType(uint32_t typeBits, VkMemoryPropertyFlags properties, VkBool32 *memTypeFound = nullptr) - { - for (uint32_t i = 0; i < memoryProperties.memoryTypeCount; i++) { - if ((typeBits & 1) == 1) { - if ((memoryProperties.memoryTypes[i].propertyFlags & properties) == properties) { - if (memTypeFound) { - *memTypeFound = true; - } - return i; - } - } - typeBits >>= 1; - } - - if (memTypeFound) { - *memTypeFound = false; - return 0; - } else { - throw std::runtime_error("Could not find a matching memory type"); - } - } - - /** - * Get the index of a queue family that supports the requested queue flags - * - * @param queueFlags Queue flags to find a queue family index for - * - * @return Index of the queue family index that matches the flags - * - * @throw Throws an exception if no queue family index could be found that supports the requested flags - */ - uint32_t getQueueFamilyIndex(VkQueueFlagBits queueFlags) - { - // Dedicated queue for compute - // Try to find a queue family index that supports compute but not graphics - if (queueFlags & VK_QUEUE_COMPUTE_BIT) - { - for (uint32_t i = 0; i < static_cast(queueFamilyProperties.size()); i++) { - if ((queueFamilyProperties[i].queueFlags & queueFlags) && ((queueFamilyProperties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) == 0)) { - return i; - break; - } - } - } - - // For other queue types or if no separate compute queue is present, return the first one to support the requested flags - for (uint32_t i = 0; i < static_cast(queueFamilyProperties.size()); i++) { - if (queueFamilyProperties[i].queueFlags & queueFlags) { - return i; - break; - } - } - - throw std::runtime_error("Could not find a matching queue family index"); - } - - /** - * Create the logical device based on the assigned physical device, also gets default queue family indices - * - * @param enabledFeatures Can be used to enable certain features upon device creation - * @param requestedQueueTypes Bit flags specifying the queue types to be requested from the device - * - * @return VkResult of the device creation call - */ - VkResult createLogicalDevice(VkPhysicalDeviceFeatures enabledFeatures, std::vector enabledExtensions, VkQueueFlags requestedQueueTypes = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT) - { - // Desired queues need to be requested upon logical device creation - // Due to differing queue family configurations of Vulkan implementations this can be a bit tricky, especially if the application - // requests different queue types - - std::vector queueCreateInfos{}; - - // Get queue family indices for the requested queue family types - // Note that the indices may overlap depending on the implementation - - const float defaultQueuePriority(0.0f); - - // Graphics queue - if (requestedQueueTypes & VK_QUEUE_GRAPHICS_BIT) { - queueFamilyIndices.graphics = getQueueFamilyIndex(VK_QUEUE_GRAPHICS_BIT); - VkDeviceQueueCreateInfo queueInfo{}; - queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - queueInfo.queueFamilyIndex = queueFamilyIndices.graphics; - queueInfo.queueCount = 1; - queueInfo.pQueuePriorities = &defaultQueuePriority; - queueCreateInfos.push_back(queueInfo); - } else { - queueFamilyIndices.graphics = 0; - } - - // Dedicated compute queue - if (requestedQueueTypes & VK_QUEUE_COMPUTE_BIT) { - queueFamilyIndices.compute = getQueueFamilyIndex(VK_QUEUE_COMPUTE_BIT); - if (queueFamilyIndices.compute != queueFamilyIndices.graphics) { - // If compute family index differs, we need an additional queue create info for the compute queue - VkDeviceQueueCreateInfo queueInfo{}; - queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - queueInfo.queueFamilyIndex = queueFamilyIndices.compute; - queueInfo.queueCount = 1; - queueInfo.pQueuePriorities = &defaultQueuePriority; - queueCreateInfos.push_back(queueInfo); - } - } else { - // Else we use the same queue - queueFamilyIndices.compute = queueFamilyIndices.graphics; - } - - // Create the logical device representation - std::vector deviceExtensions(enabledExtensions); - deviceExtensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); - -#if defined(VK_USE_PLATFORM_MACOS_MVK) && (VK_HEADER_VERSION >= 216) - deviceExtensions.push_back(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME); -#endif - - VkDeviceCreateInfo deviceCreateInfo = {}; - deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; - deviceCreateInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size());; - deviceCreateInfo.pQueueCreateInfos = queueCreateInfos.data(); - deviceCreateInfo.pEnabledFeatures = &enabledFeatures; - - if (deviceExtensions.size() > 0) { - deviceCreateInfo.enabledExtensionCount = (uint32_t)deviceExtensions.size(); - deviceCreateInfo.ppEnabledExtensionNames = deviceExtensions.data(); - } - - VkResult result = vkCreateDevice(physicalDevice, &deviceCreateInfo, nullptr, &logicalDevice); - - if (result == VK_SUCCESS) { - commandPool = createCommandPool(queueFamilyIndices.graphics); - } - - this->enabledFeatures = enabledFeatures; - - return result; - } - - /** - * Create a buffer on the device - * - * @param usageFlags Usage flag bitmask for the buffer (i.e. index, vertex, uniform buffer) - * @param memoryPropertyFlags Memory properties for this buffer (i.e. device local, host visible, coherent) - * @param size Size of the buffer in byes - * @param buffer Pointer to the buffer handle acquired by the function - * @param memory Pointer to the memory handle acquired by the function - * @param data Pointer to the data that should be copied to the buffer after creation (optional, if not set, no data is copied over) - * - * @return VK_SUCCESS if buffer handle and memory have been created and (optionally passed) data has been copied - */ - VkResult createBuffer(VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, VkDeviceSize size, VkBuffer *buffer, VkDeviceMemory *memory, void *data = nullptr) - { - // Create the buffer handle - VkBufferCreateInfo bufferCreateInfo{}; - bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; - bufferCreateInfo.usage = usageFlags; - bufferCreateInfo.size = size; - bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - VK_CHECK_RESULT(vkCreateBuffer(logicalDevice, &bufferCreateInfo, nullptr, buffer)); - - // Create the memory backing up the buffer handle - VkMemoryRequirements memReqs; - VkMemoryAllocateInfo memAlloc{}; - memAlloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - vkGetBufferMemoryRequirements(logicalDevice, *buffer, &memReqs); - memAlloc.allocationSize = memReqs.size; - // Find a memory type index that fits the properties of the buffer - memAlloc.memoryTypeIndex = getMemoryType(memReqs.memoryTypeBits, memoryPropertyFlags); - VK_CHECK_RESULT(vkAllocateMemory(logicalDevice, &memAlloc, nullptr, memory)); - - // If a pointer to the buffer data has been passed, map the buffer and copy over the data - if (data != nullptr) - { - void *mapped; - VK_CHECK_RESULT(vkMapMemory(logicalDevice, *memory, 0, size, 0, &mapped)); - memcpy(mapped, data, size); - // If host coherency hasn't been requested, do a manual flush to make writes visible - if ((memoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) == 0) - { - VkMappedMemoryRange mappedRange{}; - mappedRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; - mappedRange.memory = *memory; - mappedRange.offset = 0; - mappedRange.size = size; - vkFlushMappedMemoryRanges(logicalDevice, 1, &mappedRange); - } - vkUnmapMemory(logicalDevice, *memory); - } - - // Attach the memory to the buffer object - VK_CHECK_RESULT(vkBindBufferMemory(logicalDevice, *buffer, *memory, 0)); - - return VK_SUCCESS; - } - - /** - * Create a command pool for allocation command buffers from - * - * @param queueFamilyIndex Family index of the queue to create the command pool for - * @param createFlags (Optional) Command pool creation flags (Defaults to VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT) - * - * @note Command buffers allocated from the created pool can only be submitted to a queue with the same family index - * - * @return A handle to the created command buffer - */ - VkCommandPool createCommandPool(uint32_t queueFamilyIndex, VkCommandPoolCreateFlags createFlags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT) - { - VkCommandPoolCreateInfo cmdPoolInfo = {}; - cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - cmdPoolInfo.queueFamilyIndex = queueFamilyIndex; - cmdPoolInfo.flags = createFlags; - VkCommandPool cmdPool; - VK_CHECK_RESULT(vkCreateCommandPool(logicalDevice, &cmdPoolInfo, nullptr, &cmdPool)); - return cmdPool; - } - - /** - * Allocate a command buffer from the command pool - * - * @param level Level of the new command buffer (primary or secondary) - * @param (Optional) begin If true, recording on the new command buffer will be started (vkBeginCommandBuffer) (Defaults to false) - * - * @return A handle to the allocated command buffer - */ - VkCommandBuffer createCommandBuffer(VkCommandBufferLevel level, bool begin = false) - { - VkCommandBufferAllocateInfo cmdBufAllocateInfo{}; - cmdBufAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - cmdBufAllocateInfo.commandPool = commandPool; - cmdBufAllocateInfo.level = level; - cmdBufAllocateInfo.commandBufferCount = 1; - - VkCommandBuffer cmdBuffer; - VK_CHECK_RESULT(vkAllocateCommandBuffers(logicalDevice, &cmdBufAllocateInfo, &cmdBuffer)); - - // If requested, also start recording for the new command buffer - if (begin) { - VkCommandBufferBeginInfo commandBufferBI{}; - commandBufferBI.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - VK_CHECK_RESULT(vkBeginCommandBuffer(cmdBuffer, &commandBufferBI)); - } - - return cmdBuffer; - } - - void beginCommandBuffer(VkCommandBuffer commandBuffer) - { - VkCommandBufferBeginInfo commandBufferBI{}; - commandBufferBI.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - VK_CHECK_RESULT(vkBeginCommandBuffer(commandBuffer, &commandBufferBI)); - } - - /** - * Finish command buffer recording and submit it to a queue - * - * @param commandBuffer Command buffer to flush - * @param queue Queue to submit the command buffer to - * @param free (Optional) Free the command buffer once it has been submitted (Defaults to true) - * - * @note The queue that the command buffer is submitted to must be from the same family index as the pool it was allocated from - * @note Uses a fence to ensure command buffer has finished executing - */ - void flushCommandBuffer(VkCommandBuffer commandBuffer, VkQueue queue, bool free = true) - { - VK_CHECK_RESULT(vkEndCommandBuffer(commandBuffer)); - - VkSubmitInfo submitInfo{}; - submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &commandBuffer; - - // Create fence to ensure that the command buffer has finished executing - VkFenceCreateInfo fenceInfo{}; - fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; - VkFence fence; - VK_CHECK_RESULT(vkCreateFence(logicalDevice, &fenceInfo, nullptr, &fence)); - - // Submit to the queue - VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, fence)); - // Wait for the fence to signal that command buffer has finished executing - VK_CHECK_RESULT(vkWaitForFences(logicalDevice, 1, &fence, VK_TRUE, 100000000000)); - - vkDestroyFence(logicalDevice, fence, nullptr); - - if (free) { - vkFreeCommandBuffers(logicalDevice, commandPool, 1, &commandBuffer); - } - } - }; -} diff --git a/src/base/VulkanTexture.cpp b/src/base/VulkanTexture.cpp index 52db439..cd61839 100644 --- a/src/base/VulkanTexture.cpp +++ b/src/base/VulkanTexture.cpp @@ -1,869 +1,902 @@ /* -* Vulkan texture loader -* -* Copyright(C) by Sascha Willems - www.saschawillems.de -* -* This code is licensed under the MIT license(MIT) (http://opensource.org/licenses/MIT) -*/ + * Vulkan texture loader + * + * Copyright(C) by Sascha Willems - www.saschawillems.de + * + * This code is licensed under the MIT license(MIT) (http://opensource.org/licenses/MIT) + */ #include "VulkanTexture.h" +#include "vulkan/vulkan_core.h" -namespace vks +namespace VulkanBase { - void Texture::updateDescriptor() - { - descriptor.sampler = sampler; - descriptor.imageView = view; - descriptor.imageLayout = imageLayout; - } - - void Texture::destroy() - { - vkDestroyImageView(device->logicalDevice, view, nullptr); - vkDestroyImage(device->logicalDevice, image, nullptr); - if (sampler) - { - vkDestroySampler(device->logicalDevice, sampler, nullptr); - } - vkFreeMemory(device->logicalDevice, deviceMemory, nullptr); - } - - ktxResult Texture::loadKTXFile(std::string filename, ktxTexture **target) - { - ktxResult result = KTX_SUCCESS; -#if defined(__ANDROID__) - AAsset* asset = AAssetManager_open(androidApp->activity->assetManager, filename.c_str(), AASSET_MODE_STREAMING); - if (!asset) { - vks::tools::exitFatal("Could not load texture from " + filename + "\n\nThe file may be part of the additional asset pack.\n\nRun \"download_assets.py\" in the repository root to download the latest version.", -1); - } - size_t size = AAsset_getLength(asset); - assert(size > 0); - ktx_uint8_t *textureData = new ktx_uint8_t[size]; - AAsset_read(asset, textureData, size); - AAsset_close(asset); - result = ktxTexture_CreateFromMemory(textureData, size, KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT, target); - delete[] textureData; -#else - if (!vks::tools::fileExists(filename)) { - vks::tools::exitFatal("Could not load texture from " + filename + "\n\nThe file may be part of the additional asset pack.\n\nRun \"download_assets.py\" in the repository root to download the latest version.", -1); - } - result = ktxTexture_CreateFromNamedFile(filename.c_str(), KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT, target); -#endif - return result; - } - - /** - * Load a 2D texture including all mip levels - * - * @param filename File to load (supports .ktx) - * @param format Vulkan format of the image data stored in the file - * @param device Vulkan device to create the texture on - * @param copyQueue Queue used for the texture staging copy commands (must support transfer) - * @param (Optional) imageUsageFlags Usage flags for the texture's image (defaults to VK_IMAGE_USAGE_SAMPLED_BIT) - * @param (Optional) imageLayout Usage layout for the texture (defaults VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) - * @param (Optional) forceLinear Force linear tiling (not advised, defaults to false) - * - */ - void Texture2D::loadFromFile(std::string filename, VkFormat format, vks::VulkanDevice *device, VkQueue copyQueue, VkImageUsageFlags imageUsageFlags, VkImageLayout imageLayout, bool forceLinear) - { - ktxTexture* ktxTexture; - ktxResult result = loadKTXFile(filename, &ktxTexture); - assert(result == KTX_SUCCESS); - - this->device = device; - width = ktxTexture->baseWidth; - height = ktxTexture->baseHeight; - mipLevels = ktxTexture->numLevels; - - ktx_uint8_t *ktxTextureData = ktxTexture_GetData(ktxTexture); - ktx_size_t ktxTextureSize = ktxTexture_GetSize(ktxTexture); - - // Get device properties for the requested texture format - VkFormatProperties formatProperties; - vkGetPhysicalDeviceFormatProperties(device->physicalDevice, format, &formatProperties); - - // Only use linear tiling if requested (and supported by the device) - // Support for linear tiling is mostly limited, so prefer to use - // optimal tiling instead - // On most implementations linear tiling will only support a very - // limited amount of formats and features (mip maps, cubemaps, arrays, etc.) - VkBool32 useStaging = !forceLinear; - - VkMemoryAllocateInfo memAllocInfo = vks::initializers::memoryAllocateInfo(); - VkMemoryRequirements memReqs; - - // Use a separate command buffer for texture loading - VkCommandBuffer copyCmd = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); - - if (useStaging) - { - // Create a host-visible staging buffer that contains the raw image data - VkBuffer stagingBuffer; - VkDeviceMemory stagingMemory; - - VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo(); - bufferCreateInfo.size = ktxTextureSize; - // This buffer is used as a transfer source for the buffer copy - bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; - bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - - VK_CHECK_RESULT(vkCreateBuffer(device->logicalDevice, &bufferCreateInfo, nullptr, &stagingBuffer)); - - // Get memory requirements for the staging buffer (alignment, memory type bits) - vkGetBufferMemoryRequirements(device->logicalDevice, stagingBuffer, &memReqs); - - memAllocInfo.allocationSize = memReqs.size; - // Get memory type index for a host visible buffer - memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); - - VK_CHECK_RESULT(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &stagingMemory)); - VK_CHECK_RESULT(vkBindBufferMemory(device->logicalDevice, stagingBuffer, stagingMemory, 0)); - - // Copy texture data into staging buffer - uint8_t *data; - VK_CHECK_RESULT(vkMapMemory(device->logicalDevice, stagingMemory, 0, memReqs.size, 0, (void **)&data)); - memcpy(data, ktxTextureData, ktxTextureSize); - vkUnmapMemory(device->logicalDevice, stagingMemory); - - // Setup buffer copy regions for each mip level - std::vector bufferCopyRegions; - - for (uint32_t i = 0; i < mipLevels; i++) - { - ktx_size_t offset; - KTX_error_code result = ktxTexture_GetImageOffset(ktxTexture, i, 0, 0, &offset); - assert(result == KTX_SUCCESS); - - VkBufferImageCopy bufferCopyRegion = {}; - bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - bufferCopyRegion.imageSubresource.mipLevel = i; - bufferCopyRegion.imageSubresource.baseArrayLayer = 0; - bufferCopyRegion.imageSubresource.layerCount = 1; - bufferCopyRegion.imageExtent.width = std::max(1u, ktxTexture->baseWidth >> i); - bufferCopyRegion.imageExtent.height = std::max(1u, ktxTexture->baseHeight >> i); - bufferCopyRegion.imageExtent.depth = 1; - bufferCopyRegion.bufferOffset = offset; - - bufferCopyRegions.push_back(bufferCopyRegion); - } - - // Create optimal tiled target image - VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo(); - imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; - imageCreateInfo.format = format; - imageCreateInfo.mipLevels = mipLevels; - imageCreateInfo.arrayLayers = 1; - imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; - imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - imageCreateInfo.extent = { width, height, 1 }; - imageCreateInfo.usage = imageUsageFlags; - // Ensure that the TRANSFER_DST bit is set for staging - if (!(imageCreateInfo.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT)) - { - imageCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; - } - VK_CHECK_RESULT(vkCreateImage(device->logicalDevice, &imageCreateInfo, nullptr, &image)); - - vkGetImageMemoryRequirements(device->logicalDevice, image, &memReqs); - - memAllocInfo.allocationSize = memReqs.size; - - memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - VK_CHECK_RESULT(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &deviceMemory)); - VK_CHECK_RESULT(vkBindImageMemory(device->logicalDevice, image, deviceMemory, 0)); - - VkImageSubresourceRange subresourceRange = {}; - subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - subresourceRange.baseMipLevel = 0; - subresourceRange.levelCount = mipLevels; - subresourceRange.layerCount = 1; - - // Image barrier for optimal image (target) - // Optimal image will be used as destination for the copy - vks::tools::setImageLayout( - copyCmd, - image, - VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - subresourceRange); - - // Copy mip levels from staging buffer - vkCmdCopyBufferToImage( - copyCmd, - stagingBuffer, - image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - static_cast(bufferCopyRegions.size()), - bufferCopyRegions.data() - ); - - // Change texture image layout to shader read after all mip levels have been copied - this->imageLayout = imageLayout; - vks::tools::setImageLayout( - copyCmd, - image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - imageLayout, - subresourceRange); - - device->flushCommandBuffer(copyCmd, copyQueue); - - // Clean up staging resources - vkFreeMemory(device->logicalDevice, stagingMemory, nullptr); - vkDestroyBuffer(device->logicalDevice, stagingBuffer, nullptr); - } - else - { - // Prefer using optimal tiling, as linear tiling - // may support only a small set of features - // depending on implementation (e.g. no mip maps, only one layer, etc.) - - // Check if this support is supported for linear tiling - assert(formatProperties.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT); - - VkImage mappableImage; - VkDeviceMemory mappableMemory; - - VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo(); - imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; - imageCreateInfo.format = format; - imageCreateInfo.extent = { width, height, 1 }; - imageCreateInfo.mipLevels = 1; - imageCreateInfo.arrayLayers = 1; - imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; - imageCreateInfo.tiling = VK_IMAGE_TILING_LINEAR; - imageCreateInfo.usage = imageUsageFlags; - imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - - // Load mip map level 0 to linear tiling image - VK_CHECK_RESULT(vkCreateImage(device->logicalDevice, &imageCreateInfo, nullptr, &mappableImage)); - - // Get memory requirements for this image - // like size and alignment - vkGetImageMemoryRequirements(device->logicalDevice, mappableImage, &memReqs); - // Set memory allocation size to required memory size - memAllocInfo.allocationSize = memReqs.size; - - // Get memory type that can be mapped to host memory - memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); - - // Allocate host memory - VK_CHECK_RESULT(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &mappableMemory)); - - // Bind allocated image for use - VK_CHECK_RESULT(vkBindImageMemory(device->logicalDevice, mappableImage, mappableMemory, 0)); - - // Get sub resource layout - // Mip map count, array layer, etc. - VkImageSubresource subRes = {}; - subRes.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - subRes.mipLevel = 0; - - VkSubresourceLayout subResLayout; - void *data; - - // Get sub resources layout - // Includes row pitch, size offsets, etc. - vkGetImageSubresourceLayout(device->logicalDevice, mappableImage, &subRes, &subResLayout); - - // Map image memory - VK_CHECK_RESULT(vkMapMemory(device->logicalDevice, mappableMemory, 0, memReqs.size, 0, &data)); - - // Copy image data into memory - memcpy(data, ktxTextureData, memReqs.size); - - vkUnmapMemory(device->logicalDevice, mappableMemory); - - // Linear tiled images don't need to be staged - // and can be directly used as textures - image = mappableImage; - deviceMemory = mappableMemory; - this->imageLayout = imageLayout; - - // Setup image memory barrier - vks::tools::setImageLayout(copyCmd, image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, imageLayout); - - device->flushCommandBuffer(copyCmd, copyQueue); - } - - ktxTexture_Destroy(ktxTexture); - - // Create a default sampler - VkSamplerCreateInfo samplerCreateInfo = {}; - samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; - samplerCreateInfo.magFilter = VK_FILTER_LINEAR; - samplerCreateInfo.minFilter = VK_FILTER_LINEAR; - samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; - samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; - samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; - samplerCreateInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; - samplerCreateInfo.mipLodBias = 0.0f; - samplerCreateInfo.compareOp = VK_COMPARE_OP_NEVER; - samplerCreateInfo.minLod = 0.0f; - // Max level-of-detail should match mip level count - samplerCreateInfo.maxLod = (useStaging) ? (float)mipLevels : 0.0f; - // Only enable anisotropic filtering if enabled on the device - samplerCreateInfo.maxAnisotropy = device->enabledFeatures.samplerAnisotropy ? device->properties.limits.maxSamplerAnisotropy : 1.0f; - samplerCreateInfo.anisotropyEnable = device->enabledFeatures.samplerAnisotropy; - samplerCreateInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; - VK_CHECK_RESULT(vkCreateSampler(device->logicalDevice, &samplerCreateInfo, nullptr, &sampler)); - - // Create image view - // Textures are not directly accessed by the shaders and - // are abstracted by image views containing additional - // information and sub resource ranges - VkImageViewCreateInfo viewCreateInfo = {}; - viewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - viewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - viewCreateInfo.format = format; - viewCreateInfo.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; - // Linear tiling usually won't support mip maps - // Only set mip map count if optimal tiling is used - viewCreateInfo.subresourceRange.levelCount = (useStaging) ? mipLevels : 1; - viewCreateInfo.image = image; - VK_CHECK_RESULT(vkCreateImageView(device->logicalDevice, &viewCreateInfo, nullptr, &view)); - - // Update descriptor image info member that can be used for setting up descriptor sets - updateDescriptor(); - } - - /** - * Creates a 2D texture from a buffer - * - * @param buffer Buffer containing texture data to upload - * @param bufferSize Size of the buffer in machine units - * @param width Width of the texture to create - * @param height Height of the texture to create - * @param format Vulkan format of the image data stored in the file - * @param device Vulkan device to create the texture on - * @param copyQueue Queue used for the texture staging copy commands (must support transfer) - * @param (Optional) filter Texture filtering for the sampler (defaults to VK_FILTER_LINEAR) - * @param (Optional) imageUsageFlags Usage flags for the texture's image (defaults to VK_IMAGE_USAGE_SAMPLED_BIT) - * @param (Optional) imageLayout Usage layout for the texture (defaults VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) - */ - void Texture2D::fromBuffer(void* buffer, VkDeviceSize bufferSize, VkFormat format, uint32_t texWidth, uint32_t texHeight, vks::VulkanDevice *device, VkQueue copyQueue, VkFilter filter, VkImageUsageFlags imageUsageFlags, VkImageLayout imageLayout) - { - assert(buffer); - - this->device = device; - width = texWidth; - height = texHeight; - mipLevels = 1; - - VkMemoryAllocateInfo memAllocInfo = vks::initializers::memoryAllocateInfo(); - VkMemoryRequirements memReqs; - - // Use a separate command buffer for texture loading - VkCommandBuffer copyCmd = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); - - // Create a host-visible staging buffer that contains the raw image data - VkBuffer stagingBuffer; - VkDeviceMemory stagingMemory; - - VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo(); - bufferCreateInfo.size = bufferSize; - // This buffer is used as a transfer source for the buffer copy - bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; - bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - - VK_CHECK_RESULT(vkCreateBuffer(device->logicalDevice, &bufferCreateInfo, nullptr, &stagingBuffer)); - - // Get memory requirements for the staging buffer (alignment, memory type bits) - vkGetBufferMemoryRequirements(device->logicalDevice, stagingBuffer, &memReqs); - - memAllocInfo.allocationSize = memReqs.size; - // Get memory type index for a host visible buffer - memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); - - VK_CHECK_RESULT(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &stagingMemory)); - VK_CHECK_RESULT(vkBindBufferMemory(device->logicalDevice, stagingBuffer, stagingMemory, 0)); - - // Copy texture data into staging buffer - uint8_t *data; - VK_CHECK_RESULT(vkMapMemory(device->logicalDevice, stagingMemory, 0, memReqs.size, 0, (void **)&data)); - memcpy(data, buffer, bufferSize); - vkUnmapMemory(device->logicalDevice, stagingMemory); - - VkBufferImageCopy bufferCopyRegion = {}; - bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - bufferCopyRegion.imageSubresource.mipLevel = 0; - bufferCopyRegion.imageSubresource.baseArrayLayer = 0; - bufferCopyRegion.imageSubresource.layerCount = 1; - bufferCopyRegion.imageExtent.width = width; - bufferCopyRegion.imageExtent.height = height; - bufferCopyRegion.imageExtent.depth = 1; - bufferCopyRegion.bufferOffset = 0; - - // Create optimal tiled target image - VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo(); - imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; - imageCreateInfo.format = format; - imageCreateInfo.mipLevels = mipLevels; - imageCreateInfo.arrayLayers = 1; - imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; - imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - imageCreateInfo.extent = { width, height, 1 }; - imageCreateInfo.usage = imageUsageFlags; - // Ensure that the TRANSFER_DST bit is set for staging - if (!(imageCreateInfo.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT)) - { - imageCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; - } - VK_CHECK_RESULT(vkCreateImage(device->logicalDevice, &imageCreateInfo, nullptr, &image)); - - vkGetImageMemoryRequirements(device->logicalDevice, image, &memReqs); - - memAllocInfo.allocationSize = memReqs.size; - - memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - VK_CHECK_RESULT(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &deviceMemory)); - VK_CHECK_RESULT(vkBindImageMemory(device->logicalDevice, image, deviceMemory, 0)); - - VkImageSubresourceRange subresourceRange = {}; - subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - subresourceRange.baseMipLevel = 0; - subresourceRange.levelCount = mipLevels; - subresourceRange.layerCount = 1; - - // Image barrier for optimal image (target) - // Optimal image will be used as destination for the copy - vks::tools::setImageLayout( - copyCmd, - image, - VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - subresourceRange); - - // Copy mip levels from staging buffer - vkCmdCopyBufferToImage( - copyCmd, - stagingBuffer, - image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 1, - &bufferCopyRegion - ); - - // Change texture image layout to shader read after all mip levels have been copied - this->imageLayout = imageLayout; - vks::tools::setImageLayout( - copyCmd, - image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - imageLayout, - subresourceRange); - - device->flushCommandBuffer(copyCmd, copyQueue); - - // Clean up staging resources - vkFreeMemory(device->logicalDevice, stagingMemory, nullptr); - vkDestroyBuffer(device->logicalDevice, stagingBuffer, nullptr); - - // Create sampler - VkSamplerCreateInfo samplerCreateInfo = {}; - samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; - samplerCreateInfo.magFilter = filter; - samplerCreateInfo.minFilter = filter; - samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; - samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; - samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; - samplerCreateInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; - samplerCreateInfo.mipLodBias = 0.0f; - samplerCreateInfo.compareOp = VK_COMPARE_OP_NEVER; - samplerCreateInfo.minLod = 0.0f; - samplerCreateInfo.maxLod = 0.0f; - samplerCreateInfo.maxAnisotropy = 1.0f; - VK_CHECK_RESULT(vkCreateSampler(device->logicalDevice, &samplerCreateInfo, nullptr, &sampler)); - - // Create image view - VkImageViewCreateInfo viewCreateInfo = {}; - viewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - viewCreateInfo.pNext = NULL; - viewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - viewCreateInfo.format = format; - viewCreateInfo.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; - viewCreateInfo.subresourceRange.levelCount = 1; - viewCreateInfo.image = image; - VK_CHECK_RESULT(vkCreateImageView(device->logicalDevice, &viewCreateInfo, nullptr, &view)); - - // Update descriptor image info member that can be used for setting up descriptor sets - updateDescriptor(); - } - - /** - * Load a 2D texture array including all mip levels - * - * @param filename File to load (supports .ktx) - * @param format Vulkan format of the image data stored in the file - * @param device Vulkan device to create the texture on - * @param copyQueue Queue used for the texture staging copy commands (must support transfer) - * @param (Optional) imageUsageFlags Usage flags for the texture's image (defaults to VK_IMAGE_USAGE_SAMPLED_BIT) - * @param (Optional) imageLayout Usage layout for the texture (defaults VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) - * - */ - void Texture2DArray::loadFromFile(std::string filename, VkFormat format, vks::VulkanDevice *device, VkQueue copyQueue, VkImageUsageFlags imageUsageFlags, VkImageLayout imageLayout) - { - ktxTexture* ktxTexture; - ktxResult result = loadKTXFile(filename, &ktxTexture); - assert(result == KTX_SUCCESS); - - this->device = device; - width = ktxTexture->baseWidth; - height = ktxTexture->baseHeight; - layerCount = ktxTexture->numLayers; - mipLevels = ktxTexture->numLevels; - - ktx_uint8_t *ktxTextureData = ktxTexture_GetData(ktxTexture); - ktx_size_t ktxTextureSize = ktxTexture_GetSize(ktxTexture); - - VkMemoryAllocateInfo memAllocInfo = vks::initializers::memoryAllocateInfo(); - VkMemoryRequirements memReqs; - - // Create a host-visible staging buffer that contains the raw image data - VkBuffer stagingBuffer; - VkDeviceMemory stagingMemory; - - VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo(); - bufferCreateInfo.size = ktxTextureSize; - // This buffer is used as a transfer source for the buffer copy - bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; - bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - - VK_CHECK_RESULT(vkCreateBuffer(device->logicalDevice, &bufferCreateInfo, nullptr, &stagingBuffer)); - - // Get memory requirements for the staging buffer (alignment, memory type bits) - vkGetBufferMemoryRequirements(device->logicalDevice, stagingBuffer, &memReqs); - - memAllocInfo.allocationSize = memReqs.size; - // Get memory type index for a host visible buffer - memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); - - VK_CHECK_RESULT(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &stagingMemory)); - VK_CHECK_RESULT(vkBindBufferMemory(device->logicalDevice, stagingBuffer, stagingMemory, 0)); - - // Copy texture data into staging buffer - uint8_t *data; - VK_CHECK_RESULT(vkMapMemory(device->logicalDevice, stagingMemory, 0, memReqs.size, 0, (void **)&data)); - memcpy(data, ktxTextureData, ktxTextureSize); - vkUnmapMemory(device->logicalDevice, stagingMemory); - - // Setup buffer copy regions for each layer including all of its miplevels - std::vector bufferCopyRegions; - - for (uint32_t layer = 0; layer < layerCount; layer++) - { - for (uint32_t level = 0; level < mipLevels; level++) - { - ktx_size_t offset; - KTX_error_code result = ktxTexture_GetImageOffset(ktxTexture, level, layer, 0, &offset); - assert(result == KTX_SUCCESS); - - VkBufferImageCopy bufferCopyRegion = {}; - bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - bufferCopyRegion.imageSubresource.mipLevel = level; - bufferCopyRegion.imageSubresource.baseArrayLayer = layer; - bufferCopyRegion.imageSubresource.layerCount = 1; - bufferCopyRegion.imageExtent.width = ktxTexture->baseWidth >> level; - bufferCopyRegion.imageExtent.height = ktxTexture->baseHeight >> level; - bufferCopyRegion.imageExtent.depth = 1; - bufferCopyRegion.bufferOffset = offset; - - bufferCopyRegions.push_back(bufferCopyRegion); - } - } - - // Create optimal tiled target image - VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo(); - imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; - imageCreateInfo.format = format; - imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; - imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - imageCreateInfo.extent = { width, height, 1 }; - imageCreateInfo.usage = imageUsageFlags; - // Ensure that the TRANSFER_DST bit is set for staging - if (!(imageCreateInfo.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT)) - { - imageCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; - } - imageCreateInfo.arrayLayers = layerCount; - imageCreateInfo.mipLevels = mipLevels; - - VK_CHECK_RESULT(vkCreateImage(device->logicalDevice, &imageCreateInfo, nullptr, &image)); - - vkGetImageMemoryRequirements(device->logicalDevice, image, &memReqs); - - memAllocInfo.allocationSize = memReqs.size; - memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - - VK_CHECK_RESULT(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &deviceMemory)); - VK_CHECK_RESULT(vkBindImageMemory(device->logicalDevice, image, deviceMemory, 0)); - - // Use a separate command buffer for texture loading - VkCommandBuffer copyCmd = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); - - // Image barrier for optimal image (target) - // Set initial layout for all array layers (faces) of the optimal (target) tiled texture - VkImageSubresourceRange subresourceRange = {}; - subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - subresourceRange.baseMipLevel = 0; - subresourceRange.levelCount = mipLevels; - subresourceRange.layerCount = layerCount; - - vks::tools::setImageLayout( - copyCmd, - image, - VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - subresourceRange); - - // Copy the layers and mip levels from the staging buffer to the optimal tiled image - vkCmdCopyBufferToImage( - copyCmd, - stagingBuffer, - image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - static_cast(bufferCopyRegions.size()), - bufferCopyRegions.data()); - - // Change texture image layout to shader read after all faces have been copied - this->imageLayout = imageLayout; - vks::tools::setImageLayout( - copyCmd, - image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - imageLayout, - subresourceRange); - - device->flushCommandBuffer(copyCmd, copyQueue); - - // Create sampler - VkSamplerCreateInfo samplerCreateInfo = vks::initializers::samplerCreateInfo(); - samplerCreateInfo.magFilter = VK_FILTER_LINEAR; - samplerCreateInfo.minFilter = VK_FILTER_LINEAR; - samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; - samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; - samplerCreateInfo.addressModeV = samplerCreateInfo.addressModeU; - samplerCreateInfo.addressModeW = samplerCreateInfo.addressModeU; - samplerCreateInfo.mipLodBias = 0.0f; - samplerCreateInfo.maxAnisotropy = device->enabledFeatures.samplerAnisotropy ? device->properties.limits.maxSamplerAnisotropy : 1.0f; - samplerCreateInfo.anisotropyEnable = device->enabledFeatures.samplerAnisotropy; - samplerCreateInfo.compareOp = VK_COMPARE_OP_NEVER; - samplerCreateInfo.minLod = 0.0f; - samplerCreateInfo.maxLod = (float)mipLevels; - samplerCreateInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; - VK_CHECK_RESULT(vkCreateSampler(device->logicalDevice, &samplerCreateInfo, nullptr, &sampler)); - - // Create image view - VkImageViewCreateInfo viewCreateInfo = vks::initializers::imageViewCreateInfo(); - viewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; - viewCreateInfo.format = format; - viewCreateInfo.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; - viewCreateInfo.subresourceRange.layerCount = layerCount; - viewCreateInfo.subresourceRange.levelCount = mipLevels; - viewCreateInfo.image = image; - VK_CHECK_RESULT(vkCreateImageView(device->logicalDevice, &viewCreateInfo, nullptr, &view)); - - // Clean up staging resources - ktxTexture_Destroy(ktxTexture); - vkFreeMemory(device->logicalDevice, stagingMemory, nullptr); - vkDestroyBuffer(device->logicalDevice, stagingBuffer, nullptr); - - // Update descriptor image info member that can be used for setting up descriptor sets - updateDescriptor(); - } - - /** - * Load a cubemap texture including all mip levels from a single file - * - * @param filename File to load (supports .ktx) - * @param format Vulkan format of the image data stored in the file - * @param device Vulkan device to create the texture on - * @param copyQueue Queue used for the texture staging copy commands (must support transfer) - * @param (Optional) imageUsageFlags Usage flags for the texture's image (defaults to VK_IMAGE_USAGE_SAMPLED_BIT) - * @param (Optional) imageLayout Usage layout for the texture (defaults VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) - * - */ - void TextureCubeMap::loadFromFile(std::string filename, VkFormat format, vks::VulkanDevice *device, VkQueue copyQueue, VkImageUsageFlags imageUsageFlags, VkImageLayout imageLayout) - { - ktxTexture* ktxTexture; - ktxResult result = loadKTXFile(filename, &ktxTexture); - assert(result == KTX_SUCCESS); - - this->device = device; - width = ktxTexture->baseWidth; - height = ktxTexture->baseHeight; - mipLevels = ktxTexture->numLevels; - - ktx_uint8_t *ktxTextureData = ktxTexture_GetData(ktxTexture); - ktx_size_t ktxTextureSize = ktxTexture_GetSize(ktxTexture); - - VkMemoryAllocateInfo memAllocInfo = vks::initializers::memoryAllocateInfo(); - VkMemoryRequirements memReqs; - - // Create a host-visible staging buffer that contains the raw image data - VkBuffer stagingBuffer; - VkDeviceMemory stagingMemory; - - VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo(); - bufferCreateInfo.size = ktxTextureSize; - // This buffer is used as a transfer source for the buffer copy - bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; - bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - - VK_CHECK_RESULT(vkCreateBuffer(device->logicalDevice, &bufferCreateInfo, nullptr, &stagingBuffer)); - - // Get memory requirements for the staging buffer (alignment, memory type bits) - vkGetBufferMemoryRequirements(device->logicalDevice, stagingBuffer, &memReqs); - - memAllocInfo.allocationSize = memReqs.size; - // Get memory type index for a host visible buffer - memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); - - VK_CHECK_RESULT(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &stagingMemory)); - VK_CHECK_RESULT(vkBindBufferMemory(device->logicalDevice, stagingBuffer, stagingMemory, 0)); - - // Copy texture data into staging buffer - uint8_t *data; - VK_CHECK_RESULT(vkMapMemory(device->logicalDevice, stagingMemory, 0, memReqs.size, 0, (void **)&data)); - memcpy(data, ktxTextureData, ktxTextureSize); - vkUnmapMemory(device->logicalDevice, stagingMemory); - - // Setup buffer copy regions for each face including all of its mip levels - std::vector bufferCopyRegions; - - for (uint32_t face = 0; face < 6; face++) - { - for (uint32_t level = 0; level < mipLevels; level++) - { - ktx_size_t offset; - KTX_error_code result = ktxTexture_GetImageOffset(ktxTexture, level, 0, face, &offset); - assert(result == KTX_SUCCESS); - - VkBufferImageCopy bufferCopyRegion = {}; - bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - bufferCopyRegion.imageSubresource.mipLevel = level; - bufferCopyRegion.imageSubresource.baseArrayLayer = face; - bufferCopyRegion.imageSubresource.layerCount = 1; - bufferCopyRegion.imageExtent.width = ktxTexture->baseWidth >> level; - bufferCopyRegion.imageExtent.height = ktxTexture->baseHeight >> level; - bufferCopyRegion.imageExtent.depth = 1; - bufferCopyRegion.bufferOffset = offset; - - bufferCopyRegions.push_back(bufferCopyRegion); - } - } - - // Create optimal tiled target image - VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo(); - imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; - imageCreateInfo.format = format; - imageCreateInfo.mipLevels = mipLevels; - imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; - imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - imageCreateInfo.extent = { width, height, 1 }; - imageCreateInfo.usage = imageUsageFlags; - // Ensure that the TRANSFER_DST bit is set for staging - if (!(imageCreateInfo.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT)) - { - imageCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; - } - // Cube faces count as array layers in Vulkan - imageCreateInfo.arrayLayers = 6; - // This flag is required for cube map images - imageCreateInfo.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; - - - VK_CHECK_RESULT(vkCreateImage(device->logicalDevice, &imageCreateInfo, nullptr, &image)); - - vkGetImageMemoryRequirements(device->logicalDevice, image, &memReqs); - - memAllocInfo.allocationSize = memReqs.size; - memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - - VK_CHECK_RESULT(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &deviceMemory)); - VK_CHECK_RESULT(vkBindImageMemory(device->logicalDevice, image, deviceMemory, 0)); - - // Use a separate command buffer for texture loading - VkCommandBuffer copyCmd = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); - - // Image barrier for optimal image (target) - // Set initial layout for all array layers (faces) of the optimal (target) tiled texture - VkImageSubresourceRange subresourceRange = {}; - subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - subresourceRange.baseMipLevel = 0; - subresourceRange.levelCount = mipLevels; - subresourceRange.layerCount = 6; - - vks::tools::setImageLayout( - copyCmd, - image, - VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - subresourceRange); - - // Copy the cube map faces from the staging buffer to the optimal tiled image - vkCmdCopyBufferToImage( - copyCmd, - stagingBuffer, - image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - static_cast(bufferCopyRegions.size()), - bufferCopyRegions.data()); - - // Change texture image layout to shader read after all faces have been copied - this->imageLayout = imageLayout; - vks::tools::setImageLayout( - copyCmd, - image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - imageLayout, - subresourceRange); - - device->flushCommandBuffer(copyCmd, copyQueue); - - // Create sampler - VkSamplerCreateInfo samplerCreateInfo = vks::initializers::samplerCreateInfo(); - samplerCreateInfo.magFilter = VK_FILTER_LINEAR; - samplerCreateInfo.minFilter = VK_FILTER_LINEAR; - samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; - samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; - samplerCreateInfo.addressModeV = samplerCreateInfo.addressModeU; - samplerCreateInfo.addressModeW = samplerCreateInfo.addressModeU; - samplerCreateInfo.mipLodBias = 0.0f; - samplerCreateInfo.maxAnisotropy = device->enabledFeatures.samplerAnisotropy ? device->properties.limits.maxSamplerAnisotropy : 1.0f; - samplerCreateInfo.anisotropyEnable = device->enabledFeatures.samplerAnisotropy; - samplerCreateInfo.compareOp = VK_COMPARE_OP_NEVER; - samplerCreateInfo.minLod = 0.0f; - samplerCreateInfo.maxLod = (float)mipLevels; - samplerCreateInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; - VK_CHECK_RESULT(vkCreateSampler(device->logicalDevice, &samplerCreateInfo, nullptr, &sampler)); - - // Create image view - VkImageViewCreateInfo viewCreateInfo = vks::initializers::imageViewCreateInfo(); - viewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_CUBE; - viewCreateInfo.format = format; - viewCreateInfo.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; - viewCreateInfo.subresourceRange.layerCount = 6; - viewCreateInfo.subresourceRange.levelCount = mipLevels; - viewCreateInfo.image = image; - VK_CHECK_RESULT(vkCreateImageView(device->logicalDevice, &viewCreateInfo, nullptr, &view)); - - // Clean up staging resources - ktxTexture_Destroy(ktxTexture); - vkFreeMemory(device->logicalDevice, stagingMemory, nullptr); - vkDestroyBuffer(device->logicalDevice, stagingBuffer, nullptr); - - // Update descriptor image info member that can be used for setting up descriptor sets - updateDescriptor(); - } - +void Texture::updateDescriptor() +{ + descriptor.sampler = sampler; + descriptor.imageView = view; + descriptor.imageLayout = imageLayout; } + +void Texture::destroy() +{ + VkDevice logicalDevice = device->getLogicalDevice(); + vkDestroyImageView(logicalDevice, view, nullptr); + vkDestroyImage(logicalDevice, image, nullptr); + if (sampler) + { + vkDestroySampler(logicalDevice, sampler, nullptr); + } + vkFreeMemory(logicalDevice, deviceMemory, nullptr); +} + +ktxResult Texture::loadKTXFile(std::string filename, ktxTexture **target) +{ + ktxResult result = KTX_SUCCESS; +#if defined(__ANDROID__) + AAsset *asset = AAssetManager_open(androidApp->activity->assetManager, filename.c_str(), AASSET_MODE_STREAMING); + if (!asset) + { + vks::tools::exitFatal("Could not load texture from " + filename + "\n\nThe file may be part of the additional asset pack.\n\nRun \"download_assets.py\" in the repository root to download the latest version.", -1); + } + size_t size = AAsset_getLength(asset); + assert(size > 0); + ktx_uint8_t *textureData = new ktx_uint8_t[size]; + AAsset_read(asset, textureData, size); + AAsset_close(asset); + result = ktxTexture_CreateFromMemory(textureData, size, KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT, target); + delete[] textureData; +#else + if (!vks::tools::fileExists(filename)) + { + vks::tools::exitFatal("Could not load texture from " + filename + "\n\nThe file may be part of the additional asset pack.\n\nRun \"download_assets.py\" in the repository root to download the latest version.", -1); + } + result = ktxTexture_CreateFromNamedFile(filename.c_str(), KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT, target); +#endif + return result; +} + +/** + * Load a 2D texture including all mip levels + * + * @param filename File to load (supports .ktx) + * @param format Vulkan format of the image data stored in the file + * @param device Vulkan device to create the texture on + * @param copyQueue Queue used for the texture staging copy commands (must support transfer) + * @param (Optional) imageUsageFlags Usage flags for the texture's image (defaults to VK_IMAGE_USAGE_SAMPLED_BIT) + * @param (Optional) imageLayout Usage layout for the texture (defaults VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) + * @param (Optional) forceLinear Force linear tiling (not advised, defaults to false) + * + */ +void Texture2D::loadFromFile(std::string filename, VkFormat format, VulkanBase::VulkanDevice *device, VkQueue copyQueue, VkImageUsageFlags imageUsageFlags, VkImageLayout imageLayout, bool forceLinear) +{ + ktxTexture *ktxTexture; + ktxResult result = loadKTXFile(filename, &ktxTexture); + assert(result == KTX_SUCCESS); + + this->device = device; + VkDevice logicalDevice = device->getLogicalDevice(); + width = ktxTexture->baseWidth; + height = ktxTexture->baseHeight; + mipLevels = ktxTexture->numLevels; + + ktx_uint8_t *ktxTextureData = ktxTexture_GetData(ktxTexture); + ktx_size_t ktxTextureSize = ktxTexture_GetSize(ktxTexture); + + // Get device properties for the requested texture format + VkFormatProperties formatProperties; + vkGetPhysicalDeviceFormatProperties(device->getPhysicalDevice(), format, &formatProperties); + + // Only use linear tiling if requested (and supported by the device) + // Support for linear tiling is mostly limited, so prefer to use + // optimal tiling instead + // On most implementations linear tiling will only support a very + // limited amount of formats and features (mip maps, cubemaps, arrays, etc.) + VkBool32 useStaging = !forceLinear; + + VkMemoryAllocateInfo memAllocInfo = vks::initializers::memoryAllocateInfo(); + VkMemoryRequirements memReqs; + + // Use a separate command buffer for texture loading + VkCommandBuffer copyCmd = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); + + if (useStaging) + { + // Create a host-visible staging buffer that contains the raw image data + VkBuffer stagingBuffer; + VkDeviceMemory stagingMemory; + + VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo(); + bufferCreateInfo.size = ktxTextureSize; + // This buffer is used as a transfer source for the buffer copy + bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + VK_CHECK_RESULT(vkCreateBuffer(logicalDevice, &bufferCreateInfo, nullptr, &stagingBuffer)); + + // Get memory requirements for the staging buffer (alignment, memory type bits) + vkGetBufferMemoryRequirements(logicalDevice, stagingBuffer, &memReqs); + + memAllocInfo.allocationSize = memReqs.size; + // Get memory type index for a host visible buffer + memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + + VK_CHECK_RESULT(vkAllocateMemory(logicalDevice, &memAllocInfo, nullptr, &stagingMemory)); + VK_CHECK_RESULT(vkBindBufferMemory(logicalDevice, stagingBuffer, stagingMemory, 0)); + + // Copy texture data into staging buffer + uint8_t *data; + VK_CHECK_RESULT(vkMapMemory(logicalDevice, stagingMemory, 0, memReqs.size, 0, (void **)&data)); + memcpy(data, ktxTextureData, ktxTextureSize); + vkUnmapMemory(logicalDevice, stagingMemory); + + // Setup buffer copy regions for each mip level + std::vector bufferCopyRegions; + + for (uint32_t i = 0; i < mipLevels; i++) + { + ktx_size_t offset; + KTX_error_code result = ktxTexture_GetImageOffset(ktxTexture, i, 0, 0, &offset); + assert(result == KTX_SUCCESS); + + VkBufferImageCopy bufferCopyRegion = {}; + bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + bufferCopyRegion.imageSubresource.mipLevel = i; + bufferCopyRegion.imageSubresource.baseArrayLayer = 0; + bufferCopyRegion.imageSubresource.layerCount = 1; + bufferCopyRegion.imageExtent.width = std::max(1u, ktxTexture->baseWidth >> i); + bufferCopyRegion.imageExtent.height = std::max(1u, ktxTexture->baseHeight >> i); + bufferCopyRegion.imageExtent.depth = 1; + bufferCopyRegion.bufferOffset = offset; + + bufferCopyRegions.push_back(bufferCopyRegion); + } + + // Create optimal tiled target image + VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo(); + imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; + imageCreateInfo.format = format; + imageCreateInfo.mipLevels = mipLevels; + imageCreateInfo.arrayLayers = 1; + imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageCreateInfo.extent = {width, height, 1}; + imageCreateInfo.usage = imageUsageFlags; + // Ensure that the TRANSFER_DST bit is set for staging + if (!(imageCreateInfo.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT)) + { + imageCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; + } + VK_CHECK_RESULT(vkCreateImage(logicalDevice, &imageCreateInfo, nullptr, &image)); + + vkGetImageMemoryRequirements(logicalDevice, image, &memReqs); + + memAllocInfo.allocationSize = memReqs.size; + + memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + VK_CHECK_RESULT(vkAllocateMemory(logicalDevice, &memAllocInfo, nullptr, &deviceMemory)); + VK_CHECK_RESULT(vkBindImageMemory(logicalDevice, image, deviceMemory, 0)); + + VkImageSubresourceRange subresourceRange = {}; + subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + subresourceRange.baseMipLevel = 0; + subresourceRange.levelCount = mipLevels; + subresourceRange.layerCount = 1; + + // Image barrier for optimal image (target) + // Optimal image will be used as destination for the copy + vks::tools::setImageLayout( + copyCmd, + image, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + subresourceRange); + + // Copy mip levels from staging buffer + vkCmdCopyBufferToImage( + copyCmd, + stagingBuffer, + image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + static_cast(bufferCopyRegions.size()), + bufferCopyRegions.data()); + + // Change texture image layout to shader read after all mip levels have been copied + this->imageLayout = imageLayout; + vks::tools::setImageLayout( + copyCmd, + image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + imageLayout, + subresourceRange); + + device->flushCommandBuffer(copyCmd, copyQueue); + + // Clean up staging resources + vkFreeMemory(logicalDevice, stagingMemory, nullptr); + vkDestroyBuffer(logicalDevice, stagingBuffer, nullptr); + } + else + { + // Prefer using optimal tiling, as linear tiling + // may support only a small set of features + // depending on implementation (e.g. no mip maps, only one layer, etc.) + + // Check if this support is supported for linear tiling + assert(formatProperties.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT); + + VkImage mappableImage; + VkDeviceMemory mappableMemory; + + VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo(); + imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; + imageCreateInfo.format = format; + imageCreateInfo.extent = {width, height, 1}; + imageCreateInfo.mipLevels = 1; + imageCreateInfo.arrayLayers = 1; + imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageCreateInfo.tiling = VK_IMAGE_TILING_LINEAR; + imageCreateInfo.usage = imageUsageFlags; + imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + + // Load mip map level 0 to linear tiling image + VK_CHECK_RESULT(vkCreateImage(logicalDevice, &imageCreateInfo, nullptr, &mappableImage)); + + // Get memory requirements for this image + // like size and alignment + vkGetImageMemoryRequirements(logicalDevice, mappableImage, &memReqs); + // Set memory allocation size to required memory size + memAllocInfo.allocationSize = memReqs.size; + + // Get memory type that can be mapped to host memory + memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + + // Allocate host memory + VK_CHECK_RESULT(vkAllocateMemory(logicalDevice, &memAllocInfo, nullptr, &mappableMemory)); + + // Bind allocated image for use + VK_CHECK_RESULT(vkBindImageMemory(logicalDevice, mappableImage, mappableMemory, 0)); + + // Get sub resource layout + // Mip map count, array layer, etc. + VkImageSubresource subRes = {}; + subRes.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + subRes.mipLevel = 0; + + VkSubresourceLayout subResLayout; + void *data; + + // Get sub resources layout + // Includes row pitch, size offsets, etc. + vkGetImageSubresourceLayout(logicalDevice, mappableImage, &subRes, &subResLayout); + + // Map image memory + VK_CHECK_RESULT(vkMapMemory(logicalDevice, mappableMemory, 0, memReqs.size, 0, &data)); + + // Copy image data into memory + memcpy(data, ktxTextureData, memReqs.size); + + vkUnmapMemory(logicalDevice, mappableMemory); + + // Linear tiled images don't need to be staged + // and can be directly used as textures + image = mappableImage; + deviceMemory = mappableMemory; + this->imageLayout = imageLayout; + + // Setup image memory barrier + vks::tools::setImageLayout(copyCmd, image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, imageLayout); + + device->flushCommandBuffer(copyCmd, copyQueue); + } + + ktxTexture_Destroy(ktxTexture); + + // Create a default sampler + VkSamplerCreateInfo samplerCreateInfo = {}; + samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + samplerCreateInfo.magFilter = VK_FILTER_LINEAR; + samplerCreateInfo.minFilter = VK_FILTER_LINEAR; + samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerCreateInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerCreateInfo.mipLodBias = 0.0f; + samplerCreateInfo.compareOp = VK_COMPARE_OP_NEVER; + samplerCreateInfo.minLod = 0.0f; + // Max level-of-detail should match mip level count + samplerCreateInfo.maxLod = (useStaging) ? (float)mipLevels : 0.0f; + // Only enable anisotropic filtering if enabled on the device + + if (device->getPhysicalDeviceFeatures().samplerAnisotropy) + { + samplerCreateInfo.maxAnisotropy = device->getPhysicalDeviceProperties().limits.maxSamplerAnisotropy; + } + else + { + samplerCreateInfo.maxAnisotropy = 1.0; + } + + samplerCreateInfo.anisotropyEnable = device->getPhysicalDeviceFeatures().samplerAnisotropy; + samplerCreateInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; + VK_CHECK_RESULT(vkCreateSampler(logicalDevice, &samplerCreateInfo, nullptr, &sampler)); + + // Create image view + // Textures are not directly accessed by the shaders and + // are abstracted by image views containing additional + // information and sub resource ranges + VkImageViewCreateInfo viewCreateInfo = {}; + viewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + viewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + viewCreateInfo.format = format; + viewCreateInfo.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; + // Linear tiling usually won't support mip maps + // Only set mip map count if optimal tiling is used + viewCreateInfo.subresourceRange.levelCount = (useStaging) ? mipLevels : 1; + viewCreateInfo.image = image; + VK_CHECK_RESULT(vkCreateImageView(logicalDevice, &viewCreateInfo, nullptr, &view)); + + // Update descriptor image info member that can be used for setting up descriptor sets + updateDescriptor(); +} + +/** + * Creates a 2D texture from a buffer + * + * @param buffer Buffer containing texture data to upload + * @param bufferSize Size of the buffer in machine units + * @param width Width of the texture to create + * @param height Height of the texture to create + * @param format Vulkan format of the image data stored in the file + * @param device Vulkan device to create the texture on + * @param copyQueue Queue used for the texture staging copy commands (must support transfer) + * @param (Optional) filter Texture filtering for the sampler (defaults to VK_FILTER_LINEAR) + * @param (Optional) imageUsageFlags Usage flags for the texture's image (defaults to VK_IMAGE_USAGE_SAMPLED_BIT) + * @param (Optional) imageLayout Usage layout for the texture (defaults VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) + */ +void Texture2D::loadFromBuffer(void *buffer, VkDeviceSize bufferSize, VkFormat format, uint32_t texWidth, uint32_t texHeight, VulkanBase::VulkanDevice *device, VkQueue copyQueue, VkFilter filter, VkImageUsageFlags imageUsageFlags, VkImageLayout imageLayout) +{ + assert(buffer); + + this->device = device; + width = texWidth; + height = texHeight; + mipLevels = 1; + + VkDevice logicalDevice = device->getLogicalDevice(); + + VkMemoryAllocateInfo memAllocInfo = vks::initializers::memoryAllocateInfo(); + VkMemoryRequirements memReqs; + + // Use a separate command buffer for texture loading + VkCommandBuffer copyCmd = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); + + // Create a host-visible staging buffer that contains the raw image data + VkBuffer stagingBuffer; + VkDeviceMemory stagingMemory; + + VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo(); + bufferCreateInfo.size = bufferSize; + // This buffer is used as a transfer source for the buffer copy + bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + VK_CHECK_RESULT(vkCreateBuffer(logicalDevice, &bufferCreateInfo, nullptr, &stagingBuffer)); + + // Get memory requirements for the staging buffer (alignment, memory type bits) + vkGetBufferMemoryRequirements(logicalDevice, stagingBuffer, &memReqs); + + memAllocInfo.allocationSize = memReqs.size; + // Get memory type index for a host visible buffer + memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + + VK_CHECK_RESULT(vkAllocateMemory(logicalDevice, &memAllocInfo, nullptr, &stagingMemory)); + VK_CHECK_RESULT(vkBindBufferMemory(logicalDevice, stagingBuffer, stagingMemory, 0)); + + // Copy texture data into staging buffer + uint8_t *data; + VK_CHECK_RESULT(vkMapMemory(logicalDevice, stagingMemory, 0, memReqs.size, 0, (void **)&data)); + memcpy(data, buffer, bufferSize); + vkUnmapMemory(logicalDevice, stagingMemory); + + VkBufferImageCopy bufferCopyRegion = {}; + bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + bufferCopyRegion.imageSubresource.mipLevel = 0; + bufferCopyRegion.imageSubresource.baseArrayLayer = 0; + bufferCopyRegion.imageSubresource.layerCount = 1; + bufferCopyRegion.imageExtent.width = width; + bufferCopyRegion.imageExtent.height = height; + bufferCopyRegion.imageExtent.depth = 1; + bufferCopyRegion.bufferOffset = 0; + + // Create optimal tiled target image + VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo(); + imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; + imageCreateInfo.format = format; + imageCreateInfo.mipLevels = mipLevels; + imageCreateInfo.arrayLayers = 1; + imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageCreateInfo.extent = {width, height, 1}; + imageCreateInfo.usage = imageUsageFlags; + // Ensure that the TRANSFER_DST bit is set for staging + if (!(imageCreateInfo.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT)) + { + imageCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; + } + VK_CHECK_RESULT(vkCreateImage(logicalDevice, &imageCreateInfo, nullptr, &image)); + + vkGetImageMemoryRequirements(logicalDevice, image, &memReqs); + + memAllocInfo.allocationSize = memReqs.size; + + memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + VK_CHECK_RESULT(vkAllocateMemory(logicalDevice, &memAllocInfo, nullptr, &deviceMemory)); + VK_CHECK_RESULT(vkBindImageMemory(logicalDevice, image, deviceMemory, 0)); + + VkImageSubresourceRange subresourceRange = {}; + subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + subresourceRange.baseMipLevel = 0; + subresourceRange.levelCount = mipLevels; + subresourceRange.layerCount = 1; + + // Image barrier for optimal image (target) + // Optimal image will be used as destination for the copy + vks::tools::setImageLayout( + copyCmd, + image, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + subresourceRange); + + // Copy mip levels from staging buffer + vkCmdCopyBufferToImage( + copyCmd, + stagingBuffer, + image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, + &bufferCopyRegion); + + // Change texture image layout to shader read after all mip levels have been copied + this->imageLayout = imageLayout; + vks::tools::setImageLayout( + copyCmd, + image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + imageLayout, + subresourceRange); + + device->flushCommandBuffer(copyCmd, copyQueue); + + // Clean up staging resources + vkFreeMemory(logicalDevice, stagingMemory, nullptr); + vkDestroyBuffer(logicalDevice, stagingBuffer, nullptr); + + // Create sampler + VkSamplerCreateInfo samplerCreateInfo = {}; + samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + samplerCreateInfo.magFilter = filter; + samplerCreateInfo.minFilter = filter; + samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerCreateInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerCreateInfo.mipLodBias = 0.0f; + samplerCreateInfo.compareOp = VK_COMPARE_OP_NEVER; + samplerCreateInfo.minLod = 0.0f; + samplerCreateInfo.maxLod = 0.0f; + samplerCreateInfo.maxAnisotropy = 1.0f; + VK_CHECK_RESULT(vkCreateSampler(logicalDevice, &samplerCreateInfo, nullptr, &sampler)); + + // Create image view + VkImageViewCreateInfo viewCreateInfo = {}; + viewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + viewCreateInfo.pNext = NULL; + viewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + viewCreateInfo.format = format; + viewCreateInfo.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; + viewCreateInfo.subresourceRange.levelCount = 1; + viewCreateInfo.image = image; + VK_CHECK_RESULT(vkCreateImageView(logicalDevice, &viewCreateInfo, nullptr, &view)); + + // Update descriptor image info member that can be used for setting up descriptor sets + updateDescriptor(); +} + +/** + * Load a 2D texture array including all mip levels + * + * @param filename File to load (supports .ktx) + * @param format Vulkan format of the image data stored in the file + * @param device Vulkan device to create the texture on + * @param copyQueue Queue used for the texture staging copy commands (must support transfer) + * @param (Optional) imageUsageFlags Usage flags for the texture's image (defaults to VK_IMAGE_USAGE_SAMPLED_BIT) + * @param (Optional) imageLayout Usage layout for the texture (defaults VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) + * + */ +void Texture2DArray::loadFromFile(std::string filename, VkFormat format, VulkanBase::VulkanDevice *device, VkQueue copyQueue, VkImageUsageFlags imageUsageFlags, VkImageLayout imageLayout) +{ + ktxTexture *ktxTexture; + ktxResult result = loadKTXFile(filename, &ktxTexture); + assert(result == KTX_SUCCESS); + + this->device = device; + width = ktxTexture->baseWidth; + height = ktxTexture->baseHeight; + layerCount = ktxTexture->numLayers; + mipLevels = ktxTexture->numLevels; + + ktx_uint8_t *ktxTextureData = ktxTexture_GetData(ktxTexture); + ktx_size_t ktxTextureSize = ktxTexture_GetSize(ktxTexture); + + VkDevice logicalDevice = device->getLogicalDevice(); + + VkMemoryAllocateInfo memAllocInfo = vks::initializers::memoryAllocateInfo(); + VkMemoryRequirements memReqs; + + // Create a host-visible staging buffer that contains the raw image data + VkBuffer stagingBuffer; + VkDeviceMemory stagingMemory; + + VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo(); + bufferCreateInfo.size = ktxTextureSize; + // This buffer is used as a transfer source for the buffer copy + bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + VK_CHECK_RESULT(vkCreateBuffer(logicalDevice, &bufferCreateInfo, nullptr, &stagingBuffer)); + + // Get memory requirements for the staging buffer (alignment, memory type bits) + vkGetBufferMemoryRequirements(logicalDevice, stagingBuffer, &memReqs); + + memAllocInfo.allocationSize = memReqs.size; + // Get memory type index for a host visible buffer + memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + + VK_CHECK_RESULT(vkAllocateMemory(logicalDevice, &memAllocInfo, nullptr, &stagingMemory)); + VK_CHECK_RESULT(vkBindBufferMemory(logicalDevice, stagingBuffer, stagingMemory, 0)); + + // Copy texture data into staging buffer + uint8_t *data; + VK_CHECK_RESULT(vkMapMemory(logicalDevice, stagingMemory, 0, memReqs.size, 0, (void **)&data)); + memcpy(data, ktxTextureData, ktxTextureSize); + vkUnmapMemory(logicalDevice, stagingMemory); + + // Setup buffer copy regions for each layer including all of its miplevels + std::vector bufferCopyRegions; + + for (uint32_t layer = 0; layer < layerCount; layer++) + { + for (uint32_t level = 0; level < mipLevels; level++) + { + ktx_size_t offset; + KTX_error_code result = ktxTexture_GetImageOffset(ktxTexture, level, layer, 0, &offset); + assert(result == KTX_SUCCESS); + + VkBufferImageCopy bufferCopyRegion = {}; + bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + bufferCopyRegion.imageSubresource.mipLevel = level; + bufferCopyRegion.imageSubresource.baseArrayLayer = layer; + bufferCopyRegion.imageSubresource.layerCount = 1; + bufferCopyRegion.imageExtent.width = ktxTexture->baseWidth >> level; + bufferCopyRegion.imageExtent.height = ktxTexture->baseHeight >> level; + bufferCopyRegion.imageExtent.depth = 1; + bufferCopyRegion.bufferOffset = offset; + + bufferCopyRegions.push_back(bufferCopyRegion); + } + } + + // Create optimal tiled target image + VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo(); + imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; + imageCreateInfo.format = format; + imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageCreateInfo.extent = {width, height, 1}; + imageCreateInfo.usage = imageUsageFlags; + // Ensure that the TRANSFER_DST bit is set for staging + if (!(imageCreateInfo.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT)) + { + imageCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; + } + imageCreateInfo.arrayLayers = layerCount; + imageCreateInfo.mipLevels = mipLevels; + + VK_CHECK_RESULT(vkCreateImage(logicalDevice, &imageCreateInfo, nullptr, &image)); + + vkGetImageMemoryRequirements(logicalDevice, image, &memReqs); + + memAllocInfo.allocationSize = memReqs.size; + memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + + VK_CHECK_RESULT(vkAllocateMemory(logicalDevice, &memAllocInfo, nullptr, &deviceMemory)); + VK_CHECK_RESULT(vkBindImageMemory(logicalDevice, image, deviceMemory, 0)); + + // Use a separate command buffer for texture loading + VkCommandBuffer copyCmd = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); + + // Image barrier for optimal image (target) + // Set initial layout for all array layers (faces) of the optimal (target) tiled texture + VkImageSubresourceRange subresourceRange = {}; + subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + subresourceRange.baseMipLevel = 0; + subresourceRange.levelCount = mipLevels; + subresourceRange.layerCount = layerCount; + + vks::tools::setImageLayout( + copyCmd, + image, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + subresourceRange); + + // Copy the layers and mip levels from the staging buffer to the optimal tiled image + vkCmdCopyBufferToImage( + copyCmd, + stagingBuffer, + image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + static_cast(bufferCopyRegions.size()), + bufferCopyRegions.data()); + + // Change texture image layout to shader read after all faces have been copied + this->imageLayout = imageLayout; + vks::tools::setImageLayout( + copyCmd, + image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + imageLayout, + subresourceRange); + + device->flushCommandBuffer(copyCmd, copyQueue); + + // Create sampler + VkSamplerCreateInfo samplerCreateInfo = vks::initializers::samplerCreateInfo(); + samplerCreateInfo.magFilter = VK_FILTER_LINEAR; + samplerCreateInfo.minFilter = VK_FILTER_LINEAR; + samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerCreateInfo.addressModeV = samplerCreateInfo.addressModeU; + samplerCreateInfo.addressModeW = samplerCreateInfo.addressModeU; + samplerCreateInfo.mipLodBias = 0.0f; + if (device->getPhysicalDeviceFeatures().samplerAnisotropy) + { + samplerCreateInfo.maxAnisotropy = device->getPhysicalDeviceProperties().limits.maxSamplerAnisotropy; + } + else + { + samplerCreateInfo.maxAnisotropy = 1.0; + } + + samplerCreateInfo.anisotropyEnable = device->getPhysicalDeviceFeatures().samplerAnisotropy; + samplerCreateInfo.compareOp = VK_COMPARE_OP_NEVER; + samplerCreateInfo.minLod = 0.0f; + samplerCreateInfo.maxLod = (float)mipLevels; + samplerCreateInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; + VK_CHECK_RESULT(vkCreateSampler(logicalDevice, &samplerCreateInfo, nullptr, &sampler)); + + // Create image view + VkImageViewCreateInfo viewCreateInfo = vks::initializers::imageViewCreateInfo(); + viewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; + viewCreateInfo.format = format; + viewCreateInfo.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; + viewCreateInfo.subresourceRange.layerCount = layerCount; + viewCreateInfo.subresourceRange.levelCount = mipLevels; + viewCreateInfo.image = image; + VK_CHECK_RESULT(vkCreateImageView(logicalDevice, &viewCreateInfo, nullptr, &view)); + + // Clean up staging resources + ktxTexture_Destroy(ktxTexture); + vkFreeMemory(logicalDevice, stagingMemory, nullptr); + vkDestroyBuffer(logicalDevice, stagingBuffer, nullptr); + + // Update descriptor image info member that can be used for setting up descriptor sets + updateDescriptor(); +} + +/** + * Load a cubemap texture including all mip levels from a single file + * + * @param filename File to load (supports .ktx) + * @param format Vulkan format of the image data stored in the file + * @param device Vulkan device to create the texture on + * @param copyQueue Queue used for the texture staging copy commands (must support transfer) + * @param (Optional) imageUsageFlags Usage flags for the texture's image (defaults to VK_IMAGE_USAGE_SAMPLED_BIT) + * @param (Optional) imageLayout Usage layout for the texture (defaults VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) + * + */ +void TextureCubeMap::loadFromFile(std::string filename, VkFormat format, VulkanBase::VulkanDevice *device, VkQueue copyQueue, VkImageUsageFlags imageUsageFlags, VkImageLayout imageLayout) +{ + ktxTexture *ktxTexture; + ktxResult result = loadKTXFile(filename, &ktxTexture); + assert(result == KTX_SUCCESS); + + this->device = device; + width = ktxTexture->baseWidth; + height = ktxTexture->baseHeight; + mipLevels = ktxTexture->numLevels; + + ktx_uint8_t *ktxTextureData = ktxTexture_GetData(ktxTexture); + ktx_size_t ktxTextureSize = ktxTexture_GetSize(ktxTexture); + + VkDevice logicalDevice = device->getLogicalDevice(); + + VkMemoryAllocateInfo memAllocInfo = vks::initializers::memoryAllocateInfo(); + VkMemoryRequirements memReqs; + + // Create a host-visible staging buffer that contains the raw image data + VkBuffer stagingBuffer; + VkDeviceMemory stagingMemory; + + VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo(); + bufferCreateInfo.size = ktxTextureSize; + // This buffer is used as a transfer source for the buffer copy + bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + VK_CHECK_RESULT(vkCreateBuffer(logicalDevice, &bufferCreateInfo, nullptr, &stagingBuffer)); + + // Get memory requirements for the staging buffer (alignment, memory type bits) + vkGetBufferMemoryRequirements(logicalDevice, stagingBuffer, &memReqs); + + memAllocInfo.allocationSize = memReqs.size; + // Get memory type index for a host visible buffer + memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + + VK_CHECK_RESULT(vkAllocateMemory(logicalDevice, &memAllocInfo, nullptr, &stagingMemory)); + VK_CHECK_RESULT(vkBindBufferMemory(logicalDevice, stagingBuffer, stagingMemory, 0)); + + // Copy texture data into staging buffer + uint8_t *data; + VK_CHECK_RESULT(vkMapMemory(logicalDevice, stagingMemory, 0, memReqs.size, 0, (void **)&data)); + memcpy(data, ktxTextureData, ktxTextureSize); + vkUnmapMemory(logicalDevice, stagingMemory); + + // Setup buffer copy regions for each face including all of its mip levels + std::vector bufferCopyRegions; + + for (uint32_t face = 0; face < 6; face++) + { + for (uint32_t level = 0; level < mipLevels; level++) + { + ktx_size_t offset; + KTX_error_code result = ktxTexture_GetImageOffset(ktxTexture, level, 0, face, &offset); + assert(result == KTX_SUCCESS); + + VkBufferImageCopy bufferCopyRegion = {}; + bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + bufferCopyRegion.imageSubresource.mipLevel = level; + bufferCopyRegion.imageSubresource.baseArrayLayer = face; + bufferCopyRegion.imageSubresource.layerCount = 1; + bufferCopyRegion.imageExtent.width = ktxTexture->baseWidth >> level; + bufferCopyRegion.imageExtent.height = ktxTexture->baseHeight >> level; + bufferCopyRegion.imageExtent.depth = 1; + bufferCopyRegion.bufferOffset = offset; + + bufferCopyRegions.push_back(bufferCopyRegion); + } + } + + // Create optimal tiled target image + VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo(); + imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; + imageCreateInfo.format = format; + imageCreateInfo.mipLevels = mipLevels; + imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageCreateInfo.extent = {width, height, 1}; + imageCreateInfo.usage = imageUsageFlags; + // Ensure that the TRANSFER_DST bit is set for staging + if (!(imageCreateInfo.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT)) + { + imageCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; + } + // Cube faces count as array layers in Vulkan + imageCreateInfo.arrayLayers = 6; + // This flag is required for cube map images + imageCreateInfo.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; + + VK_CHECK_RESULT(vkCreateImage(logicalDevice, &imageCreateInfo, nullptr, &image)); + + vkGetImageMemoryRequirements(logicalDevice, image, &memReqs); + + memAllocInfo.allocationSize = memReqs.size; + memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + + VK_CHECK_RESULT(vkAllocateMemory(logicalDevice, &memAllocInfo, nullptr, &deviceMemory)); + VK_CHECK_RESULT(vkBindImageMemory(logicalDevice, image, deviceMemory, 0)); + + // Use a separate command buffer for texture loading + VkCommandBuffer copyCmd = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); + + // Image barrier for optimal image (target) + // Set initial layout for all array layers (faces) of the optimal (target) tiled texture + VkImageSubresourceRange subresourceRange = {}; + subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + subresourceRange.baseMipLevel = 0; + subresourceRange.levelCount = mipLevels; + subresourceRange.layerCount = 6; + + vks::tools::setImageLayout( + copyCmd, + image, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + subresourceRange); + + // Copy the cube map faces from the staging buffer to the optimal tiled image + vkCmdCopyBufferToImage( + copyCmd, + stagingBuffer, + image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + static_cast(bufferCopyRegions.size()), + bufferCopyRegions.data()); + + // Change texture image layout to shader read after all faces have been copied + this->imageLayout = imageLayout; + vks::tools::setImageLayout( + copyCmd, + image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + imageLayout, + subresourceRange); + + device->flushCommandBuffer(copyCmd, copyQueue); + + // Create sampler + VkSamplerCreateInfo samplerCreateInfo = vks::initializers::samplerCreateInfo(); + samplerCreateInfo.magFilter = VK_FILTER_LINEAR; + samplerCreateInfo.minFilter = VK_FILTER_LINEAR; + samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerCreateInfo.addressModeV = samplerCreateInfo.addressModeU; + samplerCreateInfo.addressModeW = samplerCreateInfo.addressModeU; + samplerCreateInfo.mipLodBias = 0.0f; + if (device->getPhysicalDeviceFeatures().samplerAnisotropy) + { + samplerCreateInfo.maxAnisotropy = device->getPhysicalDeviceProperties().limits.maxSamplerAnisotropy; + } + else + { + samplerCreateInfo.maxAnisotropy = 1.0; + } + + samplerCreateInfo.anisotropyEnable = device->getPhysicalDeviceFeatures().samplerAnisotropy; + samplerCreateInfo.compareOp = VK_COMPARE_OP_NEVER; + samplerCreateInfo.minLod = 0.0f; + samplerCreateInfo.maxLod = (float)mipLevels; + samplerCreateInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; + VK_CHECK_RESULT(vkCreateSampler(logicalDevice, &samplerCreateInfo, nullptr, &sampler)); + + // Create image view + VkImageViewCreateInfo viewCreateInfo = vks::initializers::imageViewCreateInfo(); + viewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_CUBE; + viewCreateInfo.format = format; + viewCreateInfo.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; + viewCreateInfo.subresourceRange.layerCount = 6; + viewCreateInfo.subresourceRange.levelCount = mipLevels; + viewCreateInfo.image = image; + VK_CHECK_RESULT(vkCreateImageView(logicalDevice, &viewCreateInfo, nullptr, &view)); + + // Clean up staging resources + ktxTexture_Destroy(ktxTexture); + vkFreeMemory(logicalDevice, stagingMemory, nullptr); + vkDestroyBuffer(logicalDevice, stagingBuffer, nullptr); + + // Update descriptor image info member that can be used for setting up descriptor sets + updateDescriptor(); +} + +} // namespace VulkanBase diff --git a/src/base/VulkanTexture.h b/src/base/VulkanTexture.h index 91784ec..e63f54d 100644 --- a/src/base/VulkanTexture.h +++ b/src/base/VulkanTexture.h @@ -1,97 +1,90 @@ -/* -* Vulkan texture loader -* -* Copyright(C) by Sascha Willems - www.saschawillems.de -* -* This code is licensed under the MIT license(MIT) (http://opensource.org/licenses/MIT) -*/ +#ifndef VULKANTEXTURE_H +#define VULKANTEXTURE_H -#pragma once +#include "VulkanBase_Marco.h" + +#include "VulkanBuffer.h" +#include "VulkanDevice.h" +#include "VulkanTools.h" + +#include +#include +#include #include #include #include #include -#include "vulkan/vulkan.h" +VULKANBASE_NAMESPACE_BEGIN -#include -#include - -#include "VulkanBuffer.h" -#include "VulkanDevice.hpp" -//#include "VulkanTools.h" - -#if defined(__ANDROID__) -# include -#endif - -namespace vks -{ class Texture { - public: - vks::VulkanDevice * device; - VkImage image; - VkImageLayout imageLayout; - VkDeviceMemory deviceMemory; - VkImageView view; - uint32_t width, height; - uint32_t mipLevels; - uint32_t layerCount; - VkDescriptorImageInfo descriptor; - VkSampler sampler; +public: + VulkanBase::VulkanDevice *device; + VkImage image; + VkImageLayout imageLayout; + VkDeviceMemory deviceMemory; + VkImageView view; + uint32_t width, height; + uint32_t mipLevels; + uint32_t layerCount; + VkDescriptorImageInfo descriptor; + VkSampler sampler; - void updateDescriptor(); - void destroy(); - ktxResult loadKTXFile(std::string filename, ktxTexture **target); + void updateDescriptor(); + void destroy(); + ktxResult loadKTXFile(std::string filename, ktxTexture **target); }; class Texture2D : public Texture { - public: - void loadFromFile( - std::string filename, - VkFormat format, - vks::VulkanDevice *device, - VkQueue copyQueue, - VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT, - VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - bool forceLinear = false); - void fromBuffer( - void * buffer, - VkDeviceSize bufferSize, - VkFormat format, - uint32_t texWidth, - uint32_t texHeight, - vks::VulkanDevice *device, - VkQueue copyQueue, - VkFilter filter = VK_FILTER_LINEAR, - VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT, - VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); +public: + void loadFromFile( + std::string filename, + VkFormat format, + VulkanBase::VulkanDevice *device, + VkQueue copyQueue, + VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT, + VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + bool forceLinear = false); + void loadFromBuffer( + void *buffer, + VkDeviceSize bufferSize, + VkFormat format, + uint32_t texWidth, + uint32_t texHeight, + VulkanBase::VulkanDevice *device, + VkQueue copyQueue, + VkFilter filter = VK_FILTER_LINEAR, + VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT, + VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); }; class Texture2DArray : public Texture { - public: - void loadFromFile( - std::string filename, - VkFormat format, - vks::VulkanDevice *device, - VkQueue copyQueue, - VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT, - VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); +public: + void loadFromFile( + std::string filename, + VkFormat format, + VulkanBase::VulkanDevice *device, + VkQueue copyQueue, + VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT, + VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); }; class TextureCubeMap : public Texture { - public: - void loadFromFile( - std::string filename, - VkFormat format, - vks::VulkanDevice *device, - VkQueue copyQueue, - VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT, - VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); +public: + void loadFromFile( + std::string filename, + VkFormat format, + VulkanBase::VulkanDevice *device, + VkQueue copyQueue, + VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT, + VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); }; -} // namespace vks + +VULKANBASE_NAMESPACE_END + +#endif diff --git a/src/base/VulkanTexture.hpp b/src/base/VulkanTexture.hpp deleted file mode 100644 index 29b2b5b..0000000 --- a/src/base/VulkanTexture.hpp +++ /dev/null @@ -1,630 +0,0 @@ -/* -* Vulkan texture loader -* -* Copyright(C) 2016-2017 by Sascha Willems - www.saschawillems.de -* -* This code is licensed under the MIT license(MIT) (http://opensource.org/licenses/MIT) -*/ - -#pragma once - -#include -#include -#include -#include - -#include "vulkan/vulkan.h" -#include "VulkanTools.h" -#include "VulkanDevice.hpp" -#define GLM_ENABLE_EXPERIMENTAL -#include - - -namespace vks -{ - class Texture { - public: - vks::VulkanDevice *device; - VkImage image = VK_NULL_HANDLE; - VkImageLayout imageLayout; - VkDeviceMemory deviceMemory; - VkImageView view; - uint32_t width, height; - uint32_t mipLevels; - uint32_t layerCount; - VkDescriptorImageInfo descriptor; - VkSampler sampler; - - void updateDescriptor() - { - descriptor.sampler = sampler; - descriptor.imageView = view; - descriptor.imageLayout = imageLayout; - } - - void destroy() - { - vkDestroyImageView(device->logicalDevice, view, nullptr); - vkDestroyImage(device->logicalDevice, image, nullptr); - if (sampler) - { - vkDestroySampler(device->logicalDevice, sampler, nullptr); - } - vkFreeMemory(device->logicalDevice, deviceMemory, nullptr); - } - }; - - class Texture2D : public Texture { - public: - void loadFromFile( - std::string filename, - VkFormat format, - vks::VulkanDevice *device, - VkQueue copyQueue, - VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT, - VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) - { - - gli::texture2d tex2D(gli::load(filename.c_str())); - - assert(!tex2D.empty()); - - this->device = device; - width = static_cast(tex2D[0].extent().x); - height = static_cast(tex2D[0].extent().y); - mipLevels = static_cast(tex2D.levels()); - - // Get device properites for the requested texture format - VkFormatProperties formatProperties; - vkGetPhysicalDeviceFormatProperties(device->physicalDevice, format, &formatProperties); - - VkMemoryAllocateInfo memAllocInfo{}; - memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - VkMemoryRequirements memReqs; - - // Use a separate command buffer for texture loading - VkCommandBuffer copyCmd = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); - - // Create a host-visible staging buffer that contains the raw image data - VkBuffer stagingBuffer; - VkDeviceMemory stagingMemory; - - VkBufferCreateInfo bufferCreateInfo{}; - bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; - bufferCreateInfo.size = tex2D.size(); - // This buffer is used as a transfer source for the buffer copy - bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; - bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - - VK_CHECK_RESULT(vkCreateBuffer(device->logicalDevice, &bufferCreateInfo, nullptr, &stagingBuffer)); - - // Get memory requirements for the staging buffer (alignment, memory type bits) - vkGetBufferMemoryRequirements(device->logicalDevice, stagingBuffer, &memReqs); - - memAllocInfo.allocationSize = memReqs.size; - // Get memory type index for a host visible buffer - memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); - - VK_CHECK_RESULT(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &stagingMemory)); - VK_CHECK_RESULT(vkBindBufferMemory(device->logicalDevice, stagingBuffer, stagingMemory, 0)); - - // Copy texture data into staging buffer - uint8_t *data; - VK_CHECK_RESULT(vkMapMemory(device->logicalDevice, stagingMemory, 0, memReqs.size, 0, (void **)&data)); - memcpy(data, tex2D.data(), tex2D.size()); - vkUnmapMemory(device->logicalDevice, stagingMemory); - - // Setup buffer copy regions for each mip level - std::vector bufferCopyRegions; - uint32_t offset = 0; - - for (uint32_t i = 0; i < mipLevels; i++) - { - VkBufferImageCopy bufferCopyRegion = {}; - bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - bufferCopyRegion.imageSubresource.mipLevel = i; - bufferCopyRegion.imageSubresource.baseArrayLayer = 0; - bufferCopyRegion.imageSubresource.layerCount = 1; - bufferCopyRegion.imageExtent.width = static_cast(tex2D[i].extent().x); - bufferCopyRegion.imageExtent.height = static_cast(tex2D[i].extent().y); - bufferCopyRegion.imageExtent.depth = 1; - bufferCopyRegion.bufferOffset = offset; - - bufferCopyRegions.push_back(bufferCopyRegion); - - offset += static_cast(tex2D[i].size()); - } - - // Create optimal tiled target image - VkImageCreateInfo imageCreateInfo{}; - imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; - imageCreateInfo.format = format; - imageCreateInfo.mipLevels = mipLevels; - imageCreateInfo.arrayLayers = 1; - imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; - imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - imageCreateInfo.extent = { width, height, 1 }; - imageCreateInfo.usage = imageUsageFlags; - // Ensure that the TRANSFER_DST bit is set for staging - if (!(imageCreateInfo.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT)) - { - imageCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; - } - VK_CHECK_RESULT(vkCreateImage(device->logicalDevice, &imageCreateInfo, nullptr, &image)); - - vkGetImageMemoryRequirements(device->logicalDevice, image, &memReqs); - - memAllocInfo.allocationSize = memReqs.size; - - memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - VK_CHECK_RESULT(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &deviceMemory)); - VK_CHECK_RESULT(vkBindImageMemory(device->logicalDevice, image, deviceMemory, 0)); - - VkImageSubresourceRange subresourceRange = {}; - subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - subresourceRange.baseMipLevel = 0; - subresourceRange.levelCount = mipLevels; - subresourceRange.layerCount = 1; - - // Image barrier for optimal image (target) - // Optimal image will be used as destination for the copy - { - VkImageMemoryBarrier imageMemoryBarrier{}; - imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; - imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - imageMemoryBarrier.srcAccessMask = 0; - imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - imageMemoryBarrier.image = image; - imageMemoryBarrier.subresourceRange = subresourceRange; - vkCmdPipelineBarrier(copyCmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); - } - - // Copy mip levels from staging buffer - vkCmdCopyBufferToImage( - copyCmd, - stagingBuffer, - image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - static_cast(bufferCopyRegions.size()), - bufferCopyRegions.data() - ); - - // Change texture image layout to shader read after all mip levels have been copied - this->imageLayout = imageLayout; - { - VkImageMemoryBarrier imageMemoryBarrier{}; - imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - imageMemoryBarrier.newLayout = imageLayout; - imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - imageMemoryBarrier.image = image; - imageMemoryBarrier.subresourceRange = subresourceRange; - vkCmdPipelineBarrier(copyCmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); - } - - device->flushCommandBuffer(copyCmd, copyQueue); - - // Clean up staging resources - vkFreeMemory(device->logicalDevice, stagingMemory, nullptr); - vkDestroyBuffer(device->logicalDevice, stagingBuffer, nullptr); - - VkSamplerCreateInfo samplerCreateInfo{}; - samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; - samplerCreateInfo.magFilter = VK_FILTER_LINEAR; - samplerCreateInfo.minFilter = VK_FILTER_LINEAR; - samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; - samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; - samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; - samplerCreateInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; - samplerCreateInfo.mipLodBias = 0.0f; - samplerCreateInfo.compareOp = VK_COMPARE_OP_NEVER; - samplerCreateInfo.minLod = 0.0f; - samplerCreateInfo.maxLod = (float)mipLevels; - samplerCreateInfo.maxAnisotropy = device->enabledFeatures.samplerAnisotropy ? device->properties.limits.maxSamplerAnisotropy : 1.0f; - samplerCreateInfo.anisotropyEnable = device->enabledFeatures.samplerAnisotropy; - samplerCreateInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; - VK_CHECK_RESULT(vkCreateSampler(device->logicalDevice, &samplerCreateInfo, nullptr, &sampler)); - - VkImageViewCreateInfo viewCreateInfo{}; - viewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - viewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - viewCreateInfo.format = format; - viewCreateInfo.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; - viewCreateInfo.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; - viewCreateInfo.subresourceRange.levelCount = mipLevels; - viewCreateInfo.image = image; - VK_CHECK_RESULT(vkCreateImageView(device->logicalDevice, &viewCreateInfo, nullptr, &view)); - - updateDescriptor(); - } - - void loadFromBuffer( - void* buffer, - VkDeviceSize bufferSize, - VkFormat format, - uint32_t width, - uint32_t height, - vks::VulkanDevice *device, - VkQueue copyQueue, - VkFilter filter = VK_FILTER_LINEAR, - VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT, - VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) - { - assert(buffer); - - this->device = device; - width = width; - height = height; - mipLevels = 1; - - VkMemoryAllocateInfo memAllocInfo{}; - memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - VkMemoryRequirements memReqs; - // Use a separate command buffer for texture loading - VkCommandBuffer copyCmd = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); - - // Create a host-visible staging buffer that contains the raw image data - VkBuffer stagingBuffer; - VkDeviceMemory stagingMemory; - - VkBufferCreateInfo bufferCreateInfo{}; - bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; - bufferCreateInfo.size = bufferSize; - // This buffer is used as a transfer source for the buffer copy - bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; - bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - - VK_CHECK_RESULT(vkCreateBuffer(device->logicalDevice, &bufferCreateInfo, nullptr, &stagingBuffer)); - - // Get memory requirements for the staging buffer (alignment, memory type bits) - vkGetBufferMemoryRequirements(device->logicalDevice, stagingBuffer, &memReqs); - - memAllocInfo.allocationSize = memReqs.size; - // Get memory type index for a host visible buffer - memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); - - VK_CHECK_RESULT(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &stagingMemory)); - VK_CHECK_RESULT(vkBindBufferMemory(device->logicalDevice, stagingBuffer, stagingMemory, 0)); - - // Copy texture data into staging buffer - uint8_t *data; - VK_CHECK_RESULT(vkMapMemory(device->logicalDevice, stagingMemory, 0, memReqs.size, 0, (void **)&data)); - memcpy(data, buffer, bufferSize); - vkUnmapMemory(device->logicalDevice, stagingMemory); - - VkBufferImageCopy bufferCopyRegion = {}; - bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - bufferCopyRegion.imageSubresource.mipLevel = 0; - bufferCopyRegion.imageSubresource.baseArrayLayer = 0; - bufferCopyRegion.imageSubresource.layerCount = 1; - bufferCopyRegion.imageExtent.width = width; - bufferCopyRegion.imageExtent.height = height; - bufferCopyRegion.imageExtent.depth = 1; - bufferCopyRegion.bufferOffset = 0; - - // Create optimal tiled target image - VkImageCreateInfo imageCreateInfo{}; - imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; - imageCreateInfo.format = format; - imageCreateInfo.mipLevels = mipLevels; - imageCreateInfo.arrayLayers = 1; - imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; - imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - imageCreateInfo.extent = { width, height, 1 }; - imageCreateInfo.usage = imageUsageFlags; - // Ensure that the TRANSFER_DST bit is set for staging - if (!(imageCreateInfo.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT)) - { - imageCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; - } - VK_CHECK_RESULT(vkCreateImage(device->logicalDevice, &imageCreateInfo, nullptr, &image)); - - vkGetImageMemoryRequirements(device->logicalDevice, image, &memReqs); - - memAllocInfo.allocationSize = memReqs.size; - - memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - VK_CHECK_RESULT(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &deviceMemory)); - VK_CHECK_RESULT(vkBindImageMemory(device->logicalDevice, image, deviceMemory, 0)); - - VkImageSubresourceRange subresourceRange = {}; - subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - subresourceRange.baseMipLevel = 0; - subresourceRange.levelCount = mipLevels; - subresourceRange.layerCount = 1; - - { - VkImageMemoryBarrier imageMemoryBarrier{}; - imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; - imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - imageMemoryBarrier.srcAccessMask = 0; - imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - imageMemoryBarrier.image = image; - imageMemoryBarrier.subresourceRange = subresourceRange; - vkCmdPipelineBarrier(copyCmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); - } - - vkCmdCopyBufferToImage( - copyCmd, - stagingBuffer, - image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 1, - &bufferCopyRegion - ); - - this->imageLayout = imageLayout; - { - VkImageMemoryBarrier imageMemoryBarrier{}; - imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - imageMemoryBarrier.newLayout = imageLayout; - imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - imageMemoryBarrier.image = image; - imageMemoryBarrier.subresourceRange = subresourceRange; - vkCmdPipelineBarrier(copyCmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); - } - - device->flushCommandBuffer(copyCmd, copyQueue); - - // Clean up staging resources - vkFreeMemory(device->logicalDevice, stagingMemory, nullptr); - vkDestroyBuffer(device->logicalDevice, stagingBuffer, nullptr); - - // Create sampler - VkSamplerCreateInfo samplerCreateInfo = {}; - samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; - samplerCreateInfo.magFilter = filter; - samplerCreateInfo.minFilter = filter; - samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; - samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; - samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; - samplerCreateInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; - samplerCreateInfo.mipLodBias = 0.0f; - samplerCreateInfo.compareOp = VK_COMPARE_OP_NEVER; - samplerCreateInfo.minLod = 0.0f; - samplerCreateInfo.maxLod = 0.0f; - samplerCreateInfo.maxAnisotropy = 1.0f; - VK_CHECK_RESULT(vkCreateSampler(device->logicalDevice, &samplerCreateInfo, nullptr, &sampler)); - - // Create image view - VkImageViewCreateInfo viewCreateInfo = {}; - viewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - viewCreateInfo.pNext = NULL; - viewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - viewCreateInfo.format = format; - viewCreateInfo.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; - viewCreateInfo.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; - viewCreateInfo.subresourceRange.levelCount = 1; - viewCreateInfo.image = image; - VK_CHECK_RESULT(vkCreateImageView(device->logicalDevice, &viewCreateInfo, nullptr, &view)); - - // Update descriptor image info member that can be used for setting up descriptor sets - updateDescriptor(); - } - }; - - class TextureCubeMap : public Texture { - public: - void loadFromFile( - std::string filename, - VkFormat format, - vks::VulkanDevice *device, - VkQueue copyQueue, - VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT, - VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) - { -#if defined(__ANDROID__) - // Textures are stored inside the apk on Android (compressed) - // So they need to be loaded via the asset manager - AAsset* asset = AAssetManager_open(androidApp->activity->assetManager, filename.c_str(), AASSET_MODE_STREAMING); - if (!asset) { - LOGE("Could not load texture %s", filename.c_str()); - exit(-1); - } - size_t size = AAsset_getLength(asset); - assert(size > 0); - - void *textureData = malloc(size); - AAsset_read(asset, textureData, size); - AAsset_close(asset); - - gli::texture_cube texCube(gli::load((const char*)textureData, size)); - - free(textureData); -#else - gli::texture_cube texCube(gli::load(filename)); -#endif - assert(!texCube.empty()); - - this->device = device; - width = static_cast(texCube.extent().x); - height = static_cast(texCube.extent().y); - mipLevels = static_cast(texCube.levels()); - - VkMemoryAllocateInfo memAllocInfo{}; - memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - VkMemoryRequirements memReqs; - - // Create a host-visible staging buffer that contains the raw image data - VkBuffer stagingBuffer; - VkDeviceMemory stagingMemory; - - VkBufferCreateInfo bufferCreateInfo{}; - bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; - bufferCreateInfo.size = texCube.size(); - // This buffer is used as a transfer source for the buffer copy - bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; - bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - - VK_CHECK_RESULT(vkCreateBuffer(device->logicalDevice, &bufferCreateInfo, nullptr, &stagingBuffer)); - - // Get memory requirements for the staging buffer (alignment, memory type bits) - vkGetBufferMemoryRequirements(device->logicalDevice, stagingBuffer, &memReqs); - - memAllocInfo.allocationSize = memReqs.size; - // Get memory type index for a host visible buffer - memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); - - VK_CHECK_RESULT(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &stagingMemory)); - VK_CHECK_RESULT(vkBindBufferMemory(device->logicalDevice, stagingBuffer, stagingMemory, 0)); - - // Copy texture data into staging buffer - uint8_t *data; - VK_CHECK_RESULT(vkMapMemory(device->logicalDevice, stagingMemory, 0, memReqs.size, 0, (void **)&data)); - memcpy(data, texCube.data(), texCube.size()); - vkUnmapMemory(device->logicalDevice, stagingMemory); - - // Setup buffer copy regions for each face including all of it's miplevels - std::vector bufferCopyRegions; - size_t offset = 0; - - for (uint32_t face = 0; face < 6; face++) { - for (uint32_t level = 0; level < mipLevels; level++) { - VkBufferImageCopy bufferCopyRegion = {}; - bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - bufferCopyRegion.imageSubresource.mipLevel = level; - bufferCopyRegion.imageSubresource.baseArrayLayer = face; - bufferCopyRegion.imageSubresource.layerCount = 1; - bufferCopyRegion.imageExtent.width = static_cast(texCube[face][level].extent().x); - bufferCopyRegion.imageExtent.height = static_cast(texCube[face][level].extent().y); - bufferCopyRegion.imageExtent.depth = 1; - bufferCopyRegion.bufferOffset = offset; - - bufferCopyRegions.push_back(bufferCopyRegion); - - // Increase offset into staging buffer for next level / face - offset += texCube[face][level].size(); - } - } - - // Create optimal tiled target image - VkImageCreateInfo imageCreateInfo{}; - imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; - imageCreateInfo.format = format; - imageCreateInfo.mipLevels = mipLevels; - imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; - imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - imageCreateInfo.extent = { width, height, 1 }; - imageCreateInfo.usage = imageUsageFlags; - // Ensure that the TRANSFER_DST bit is set for staging - if (!(imageCreateInfo.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT)) { - imageCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; - } - // Cube faces count as array layers in Vulkan - imageCreateInfo.arrayLayers = 6; - // This flag is required for cube map images - imageCreateInfo.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; - - - VK_CHECK_RESULT(vkCreateImage(device->logicalDevice, &imageCreateInfo, nullptr, &image)); - - vkGetImageMemoryRequirements(device->logicalDevice, image, &memReqs); - - memAllocInfo.allocationSize = memReqs.size; - memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - - VK_CHECK_RESULT(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &deviceMemory)); - VK_CHECK_RESULT(vkBindImageMemory(device->logicalDevice, image, deviceMemory, 0)); - - // Use a separate command buffer for texture loading - VkCommandBuffer copyCmd = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); - - // Image barrier for optimal image (target) - // Set initial layout for all array layers (faces) of the optimal (target) tiled texture - VkImageSubresourceRange subresourceRange = {}; - subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - subresourceRange.baseMipLevel = 0; - subresourceRange.levelCount = mipLevels; - subresourceRange.layerCount = 6; - - { - VkImageMemoryBarrier imageMemoryBarrier{}; - imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; - imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - imageMemoryBarrier.srcAccessMask = 0; - imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - imageMemoryBarrier.image = image; - imageMemoryBarrier.subresourceRange = subresourceRange; - vkCmdPipelineBarrier(copyCmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); - } - - // Copy the cube map faces from the staging buffer to the optimal tiled image - vkCmdCopyBufferToImage( - copyCmd, - stagingBuffer, - image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - static_cast(bufferCopyRegions.size()), - bufferCopyRegions.data()); - - // Change texture image layout to shader read after all faces have been copied - this->imageLayout = imageLayout; - { - VkImageMemoryBarrier imageMemoryBarrier{}; - imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - imageMemoryBarrier.newLayout = imageLayout; - imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - imageMemoryBarrier.image = image; - imageMemoryBarrier.subresourceRange = subresourceRange; - vkCmdPipelineBarrier(copyCmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); - } - - device->flushCommandBuffer(copyCmd, copyQueue); - - // Create sampler - VkSamplerCreateInfo samplerCreateInfo{}; - samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; - samplerCreateInfo.magFilter = VK_FILTER_LINEAR; - samplerCreateInfo.minFilter = VK_FILTER_LINEAR; - samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; - samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; - samplerCreateInfo.addressModeV = samplerCreateInfo.addressModeU; - samplerCreateInfo.addressModeW = samplerCreateInfo.addressModeU; - samplerCreateInfo.mipLodBias = 0.0f; - samplerCreateInfo.maxAnisotropy = device->enabledFeatures.samplerAnisotropy ? device->properties.limits.maxSamplerAnisotropy : 1.0f; - samplerCreateInfo.anisotropyEnable = device->enabledFeatures.samplerAnisotropy; - samplerCreateInfo.compareOp = VK_COMPARE_OP_NEVER; - samplerCreateInfo.minLod = 0.0f; - samplerCreateInfo.maxLod = (float)mipLevels; - samplerCreateInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; - VK_CHECK_RESULT(vkCreateSampler(device->logicalDevice, &samplerCreateInfo, nullptr, &sampler)); - - // Create image view - VkImageViewCreateInfo viewCreateInfo{}; - viewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - viewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_CUBE; - viewCreateInfo.format = format; - viewCreateInfo.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; - viewCreateInfo.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; - viewCreateInfo.subresourceRange.layerCount = 6; - viewCreateInfo.subresourceRange.levelCount = mipLevels; - viewCreateInfo.image = image; - VK_CHECK_RESULT(vkCreateImageView(device->logicalDevice, &viewCreateInfo, nullptr, &view)); - - // Clean up staging resources - vkFreeMemory(device->logicalDevice, stagingMemory, nullptr); - vkDestroyBuffer(device->logicalDevice, stagingBuffer, nullptr); - - // Update descriptor image info member that can be used for setting up descriptor sets - updateDescriptor(); - } - }; - -} \ No newline at end of file diff --git a/src/render/VulkanTextureSampler.cpp b/src/base/VulkanTextureSampler.cpp similarity index 100% rename from src/render/VulkanTextureSampler.cpp rename to src/base/VulkanTextureSampler.cpp diff --git a/src/render/VulkanTextureSampler.h b/src/base/VulkanTextureSampler.h similarity index 100% rename from src/render/VulkanTextureSampler.h rename to src/base/VulkanTextureSampler.h diff --git a/src/base/VulkanTools.h b/src/base/VulkanTools.h index a5ed699..de4f15f 100644 --- a/src/base/VulkanTools.h +++ b/src/base/VulkanTools.h @@ -1,25 +1,27 @@  #pragma once -#include "vulkan/vulkan.h" #include "VulkanInitializers.hpp" +#include "vulkan/vulkan.h" -#include -#include -#include + +#include +#include #include #include -#include -#include -#include #include +#include #include -#include -#include +#include +#include +#include +#include + #if defined(_WIN32) -#include #include #include +#include + #elif defined(__ANDROID__) #include "VulkanAndroid.h" #include @@ -32,114 +34,114 @@ // Macro to check and display Vulkan return results #if defined(__ANDROID__) -#define VK_CHECK_RESULT(f) \ -{ \ - VkResult res = (f); \ - if (res != VK_SUCCESS) \ - { \ - LOGE("Fatal : VkResult is \" %s \" in %s at line %d", vks::tools::errorString(res).c_str(), __FILE__, __LINE__); \ - assert(res == VK_SUCCESS); \ - } \ -} +#define VK_CHECK_RESULT(f) \ + { \ + VkResult res = (f); \ + if (res != VK_SUCCESS) \ + { \ + LOGE("Fatal : VkResult is \" %s \" in %s at line %d", vks::tools::errorString(res).c_str(), __FILE__, __LINE__); \ + assert(res == VK_SUCCESS); \ + } \ + } #else -#define VK_CHECK_RESULT(f) \ -{ \ - VkResult res = (f); \ - if (res != VK_SUCCESS) \ - { \ - std::cout << "Fatal : VkResult is \"" << vks::tools::errorString(res) << "\" in " << __FILE__ << " at line " << __LINE__ << "\n"; \ - assert(res == VK_SUCCESS); \ - } \ -} +#define VK_CHECK_RESULT(f) \ + { \ + VkResult res = (f); \ + if (res != VK_SUCCESS) \ + { \ + std::cout << "Fatal : VkResult is \"" << vks::tools::errorString(res) << "\" in " << __FILE__ << " at line " << __LINE__ << "\n"; \ + assert(res == VK_SUCCESS); \ + } \ + } #endif -#define GET_INSTANCE_PROC_ADDR(inst, entrypoint) \ -{ \ - fp##entrypoint = reinterpret_cast(vkGetInstanceProcAddr(inst, "vk"#entrypoint)); \ - if (fp##entrypoint == NULL) \ - { \ - exit(1); \ - } \ -} +#define GET_INSTANCE_PROC_ADDR(inst, entrypoint) \ + { \ + fp##entrypoint = reinterpret_cast(vkGetInstanceProcAddr(inst, "vk" #entrypoint)); \ + if (fp##entrypoint == NULL) \ + { \ + exit(1); \ + } \ + } -#define GET_DEVICE_PROC_ADDR(dev, entrypoint) \ -{ \ - fp##entrypoint = reinterpret_cast(vkGetDeviceProcAddr(dev, "vk"#entrypoint)); \ - if (fp##entrypoint == NULL) \ - { \ - exit(1); \ - } \ -} +#define GET_DEVICE_PROC_ADDR(dev, entrypoint) \ + { \ + fp##entrypoint = reinterpret_cast(vkGetDeviceProcAddr(dev, "vk" #entrypoint)); \ + if (fp##entrypoint == NULL) \ + { \ + exit(1); \ + } \ + } const std::string getAssetPath(); namespace vks { - namespace tools - { - /** @brief Disable message boxes on fatal errors */ - extern bool errorModeSilent; +namespace tools +{ +/** @brief Disable message boxes on fatal errors */ +extern bool errorModeSilent; - /** @brief Returns an error code as a string */ - std::string errorString(VkResult errorCode); +/** @brief Returns an error code as a string */ +std::string errorString(VkResult errorCode); - /** @brief Returns the device type as a string */ - std::string physicalDeviceTypeString(VkPhysicalDeviceType type); +/** @brief Returns the device type as a string */ +std::string physicalDeviceTypeString(VkPhysicalDeviceType type); - // Selected a suitable supported depth format starting with 32 bit down to 16 bit - // Returns false if none of the depth formats in the list is supported by the device - VkBool32 getSupportedDepthFormat(VkPhysicalDevice physicalDevice, VkFormat *depthFormat); +// Selected a suitable supported depth format starting with 32 bit down to 16 bit +// Returns false if none of the depth formats in the list is supported by the device +VkBool32 getSupportedDepthFormat(VkPhysicalDevice physicalDevice, VkFormat *depthFormat); - // Returns tru a given format support LINEAR filtering - VkBool32 formatIsFilterable(VkPhysicalDevice physicalDevice, VkFormat format, VkImageTiling tiling); - // Returns true if a given format has a stencil part - VkBool32 formatHasStencil(VkFormat format); +// Returns tru a given format support LINEAR filtering +VkBool32 formatIsFilterable(VkPhysicalDevice physicalDevice, VkFormat format, VkImageTiling tiling); +// Returns true if a given format has a stencil part +VkBool32 formatHasStencil(VkFormat format); - // Put an image memory barrier for setting an image layout on the sub resource into the given command buffer - void setImageLayout( - VkCommandBuffer cmdbuffer, - VkImage image, - VkImageLayout oldImageLayout, - VkImageLayout newImageLayout, - VkImageSubresourceRange subresourceRange, - VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, - VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); - // Uses a fixed sub resource layout with first mip level and layer - void setImageLayout( - VkCommandBuffer cmdbuffer, - VkImage image, - VkImageAspectFlags aspectMask, - VkImageLayout oldImageLayout, - VkImageLayout newImageLayout, - VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, - VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); +// Put an image memory barrier for setting an image layout on the sub resource into the given command buffer +void setImageLayout( + VkCommandBuffer cmdbuffer, + VkImage image, + VkImageLayout oldImageLayout, + VkImageLayout newImageLayout, + VkImageSubresourceRange subresourceRange, + VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); +// Uses a fixed sub resource layout with first mip level and layer +void setImageLayout( + VkCommandBuffer cmdbuffer, + VkImage image, + VkImageAspectFlags aspectMask, + VkImageLayout oldImageLayout, + VkImageLayout newImageLayout, + VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); - /** @brief Insert an image memory barrier into the command buffer */ - void insertImageMemoryBarrier( - VkCommandBuffer cmdbuffer, - VkImage image, - VkAccessFlags srcAccessMask, - VkAccessFlags dstAccessMask, - VkImageLayout oldImageLayout, - VkImageLayout newImageLayout, - VkPipelineStageFlags srcStageMask, - VkPipelineStageFlags dstStageMask, - VkImageSubresourceRange subresourceRange); +/** @brief Insert an image memory barrier into the command buffer */ +void insertImageMemoryBarrier( + VkCommandBuffer cmdbuffer, + VkImage image, + VkAccessFlags srcAccessMask, + VkAccessFlags dstAccessMask, + VkImageLayout oldImageLayout, + VkImageLayout newImageLayout, + VkPipelineStageFlags srcStageMask, + VkPipelineStageFlags dstStageMask, + VkImageSubresourceRange subresourceRange); - // Display error message and exit on fatal error - void exitFatal(const std::string& message, int32_t exitCode); - void exitFatal(const std::string& message, VkResult resultCode); +// Display error message and exit on fatal error +void exitFatal(const std::string &message, int32_t exitCode); +void exitFatal(const std::string &message, VkResult resultCode); - // Load a SPIR-V shader (binary) +// Load a SPIR-V shader (binary) #if defined(__ANDROID__) - VkShaderModule loadShader(AAssetManager* assetManager, const char *fileName, VkDevice device); +VkShaderModule loadShader(AAssetManager *assetManager, const char *fileName, VkDevice device); #else - VkShaderModule loadShader(const char *fileName, VkDevice device); +VkShaderModule loadShader(const char *fileName, VkDevice device); #endif - /** @brief Checks if a file exists */ - bool fileExists(const std::string &filename); +/** @brief Checks if a file exists */ +bool fileExists(const std::string &filename); - uint32_t alignedSize(uint32_t value, uint32_t alignment); - } -} +uint32_t alignedSize(uint32_t value, uint32_t alignment); +} // namespace tools +} // namespace vks diff --git a/src/base/VulkanUtils.hpp b/src/base/VulkanUtils.hpp deleted file mode 100644 index 74fd425..0000000 --- a/src/base/VulkanUtils.hpp +++ /dev/null @@ -1,176 +0,0 @@ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include "vulkan/vulkan.h" -#include "VulkanDevice.hpp" - - -/* - Vulkan buffer object -*/ -struct Buffer { - VkDevice device; - VkBuffer buffer = VK_NULL_HANDLE; - VkDeviceMemory memory = VK_NULL_HANDLE; - VkDescriptorBufferInfo descriptor; - int32_t count = 0; - void *mapped = nullptr; - void create(vks::VulkanDevice *device, VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, VkDeviceSize size, bool map = true) { - this->device = device->logicalDevice; - device->createBuffer(usageFlags, memoryPropertyFlags, size, &buffer, &memory); - descriptor = { buffer, 0, size }; - if (map) { - VK_CHECK_RESULT(vkMapMemory(device->logicalDevice, memory, 0, size, 0, &mapped)); - } - } - void destroy() { - if (mapped) { - unmap(); - } - vkDestroyBuffer(device, buffer, nullptr); - vkFreeMemory(device, memory, nullptr); - buffer = VK_NULL_HANDLE; - memory = VK_NULL_HANDLE; - } - void map() { - VK_CHECK_RESULT(vkMapMemory(device, memory, 0, VK_WHOLE_SIZE, 0, &mapped)); - } - void unmap() { - if (mapped) { - vkUnmapMemory(device, memory); - mapped = nullptr; - } - } - void flush(VkDeviceSize size = VK_WHOLE_SIZE) { - VkMappedMemoryRange mappedRange{}; - mappedRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; - mappedRange.memory = memory; - mappedRange.size = size; - VK_CHECK_RESULT(vkFlushMappedMemoryRanges(device, 1, &mappedRange)); - } -}; - -VkPipelineShaderStageCreateInfo loadShader(VkDevice device, std::string filename, VkShaderStageFlagBits stage) -{ - VkPipelineShaderStageCreateInfo shaderStage{}; - shaderStage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - shaderStage.stage = stage; - shaderStage.pName = "main"; -#if defined(VK_USE_PLATFORM_ANDROID_KHR) - std::string assetpath = "shaders/" + filename; - AAsset* asset = AAssetManager_open(androidApp->activity->assetManager, assetpath.c_str(), AASSET_MODE_STREAMING); - assert(asset); - size_t size = AAsset_getLength(asset); - assert(size > 0); - char *shaderCode = new char[size]; - AAsset_read(asset, shaderCode, size); - AAsset_close(asset); - VkShaderModule shaderModule; - VkShaderModuleCreateInfo moduleCreateInfo; - moduleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - moduleCreateInfo.pNext = NULL; - moduleCreateInfo.codeSize = size; - moduleCreateInfo.pCode = (uint32_t*)shaderCode; - moduleCreateInfo.flags = 0; - VK_CHECK_RESULT(vkCreateShaderModule(device, &moduleCreateInfo, NULL, &shaderStage.module)); - delete[] shaderCode; -#else - std::ifstream is(filename, std::ios::binary | std::ios::in | std::ios::ate); - - if (is.is_open()) { - size_t size = is.tellg(); - is.seekg(0, std::ios::beg); - char* shaderCode = new char[size]; - is.read(shaderCode, size); - is.close(); - assert(size > 0); - VkShaderModuleCreateInfo moduleCreateInfo{}; - moduleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - moduleCreateInfo.codeSize = size; - moduleCreateInfo.pCode = (uint32_t*)shaderCode; - vkCreateShaderModule(device, &moduleCreateInfo, NULL, &shaderStage.module); - delete[] shaderCode; - } - else { - std::cerr << "Error: Could not open shader file \"" << filename << "\"" << std::endl; - shaderStage.module = VK_NULL_HANDLE; - } - -#endif - assert(shaderStage.module != VK_NULL_HANDLE); - return shaderStage; -} - -void readDirectory(const std::string& directory, const std::string &pattern, std::map &filelist, bool recursive) -{ -#if defined(VK_USE_PLATFORM_ANDROID_KHR) - AAssetDir* assetDir = AAssetManager_openDir(androidApp->activity->assetManager, directory.c_str()); - AAssetDir_rewind(assetDir); - const char* assetName; - while ((assetName = AAssetDir_getNextFileName(assetDir)) != 0) { - std::string filename(assetName); - filename.erase(filename.find_last_of("."), std::string::npos); - filelist[filename] = directory + "/" + assetName; - } - AAssetDir_close(assetDir); -#elif defined(VK_USE_PLATFORM_WIN32_KHR) - std::string searchpattern(directory + "/" + pattern); - WIN32_FIND_DATA data; - HANDLE hFind; - if ((hFind = FindFirstFile(searchpattern.c_str(), &data)) != INVALID_HANDLE_VALUE) { - do { - std::string filename(data.cFileName); - filename.erase(filename.find_last_of("."), std::string::npos); - filelist[filename] = directory + "/" + data.cFileName; - } while (FindNextFile(hFind, &data) != 0); - FindClose(hFind); - } - if (recursive) { - std::string dirpattern = directory + "/*"; - if ((hFind = FindFirstFile(dirpattern.c_str(), &data)) != INVALID_HANDLE_VALUE) { - do { - if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - char subdir[MAX_PATH]; - strcpy(subdir, directory.c_str()); - strcat(subdir, "/"); - strcat(subdir, data.cFileName); - if ((strcmp(data.cFileName, ".") != 0) && (strcmp(data.cFileName, "..") != 0)) { - readDirectory(subdir, pattern, filelist, recursive); - } - } - } while (FindNextFile(hFind, &data) != 0); - FindClose(hFind); - } - } -#elif defined(__linux__) - std::string patternExt = pattern; - patternExt.erase(0, pattern.find_last_of(".")); - struct dirent *entry; - DIR *dir = opendir(directory.c_str()); - if (dir == NULL) { - return; - } - while ((entry = readdir(dir)) != NULL) { - if (entry->d_type == DT_REG) { - std::string filename(entry->d_name); - if (filename.find(patternExt) != std::string::npos) { - filename.erase(filename.find_last_of("."), std::string::npos); - filelist[filename] = directory + "/" + entry->d_name; - } - } - if (recursive && (entry->d_type == DT_DIR)) { - std::string subdir = directory + "/" + entry->d_name; - if ((strcmp(entry->d_name, ".") != 0) && (strcmp(entry->d_name, "..") != 0)) { - readDirectory(subdir, pattern, filelist, recursive); - } - } - } - closedir(dir); -#endif -} \ No newline at end of file diff --git a/src/base/ui.hpp b/src/base/ui.hpp deleted file mode 100644 index a31ba72..0000000 --- a/src/base/ui.hpp +++ /dev/null @@ -1,356 +0,0 @@ - -#include "VulkanDevice.hpp" -#include "VulkanUtils.hpp" -#include "VulkanTexture.hpp" - -#if defined(__ANDROID__) -#include -#endif - -#include - -#include -#include -#include - -#define GLM_FORCE_RADIANS -#define GLM_FORCE_DEPTH_ZERO_TO_ONE -#include -#include - -#include -#include -#include - - -struct UI { -private: - VkDevice device; -public: - Buffer vertexBuffer, indexBuffer; - vks::Texture2D fontTexture; - VkPipelineLayout pipelineLayout; - VkPipeline pipeline; - VkDescriptorPool descriptorPool; - VkDescriptorSetLayout descriptorSetLayout; - VkDescriptorSet descriptorSet; - - struct PushConstBlock { - glm::vec2 scale; - glm::vec2 translate; - } pushConstBlock; - - UI(vks::VulkanDevice *vulkanDevice, VkRenderPass renderPass, VkQueue queue, VkPipelineCache pipelineCache, VkSampleCountFlagBits multiSampleCount) { - - this->device = vulkanDevice->logicalDevice; - - ImGui::CreateContext(); - - /* - Font texture loading - */ - ImGuiIO& io = ImGui::GetIO(); - - /// enable keyborad and gamepad input - io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; - io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; - - unsigned char* fontData; - int texWidth, texHeight; - std::string ttfFilePath = getAssetPath() + "/STXINWEI.TTF"; - io.Fonts->AddFontFromFileTTF(ttfFilePath.data(), 16.0f,NULL, io.Fonts->GetGlyphRangesChineseSimplifiedCommon()); - io.Fonts->GetTexDataAsRGBA32(&fontData, &texWidth, &texHeight); - fontTexture.loadFromBuffer(fontData, texWidth * texHeight * 4 * sizeof(char), VK_FORMAT_R8G8B8A8_UNORM, texWidth, texHeight, vulkanDevice, queue); - - /* - Setup - */ - ImGuiStyle& style = ImGui::GetStyle(); - style.FrameBorderSize = 0.0f; - style.WindowBorderSize = 0.0f; - - /* - Descriptor pool - */ - std::vector poolSizes = { - { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1 } - }; - VkDescriptorPoolCreateInfo descriptorPoolCI{}; - descriptorPoolCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; - descriptorPoolCI.poolSizeCount = 1; - descriptorPoolCI.pPoolSizes = poolSizes.data(); - descriptorPoolCI.maxSets = 1; - VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolCI, nullptr, &descriptorPool)); - - /* - Descriptor set layout - */ - VkDescriptorSetLayoutBinding setLayoutBinding{ 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr }; - VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCI{}; - descriptorSetLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; - descriptorSetLayoutCI.pBindings = &setLayoutBinding; - descriptorSetLayoutCI.bindingCount = 1; - VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorSetLayoutCI, nullptr, &descriptorSetLayout)); - - /* - Descriptor set - */ - VkDescriptorSetAllocateInfo descriptorSetAllocInfo{}; - descriptorSetAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; - descriptorSetAllocInfo.descriptorPool = descriptorPool; - descriptorSetAllocInfo.pSetLayouts = &descriptorSetLayout; - descriptorSetAllocInfo.descriptorSetCount = 1; - VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorSetAllocInfo, &descriptorSet)); - VkWriteDescriptorSet writeDescriptorSet{}; - writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - writeDescriptorSet.descriptorCount = 1; - writeDescriptorSet.dstSet = descriptorSet; - writeDescriptorSet.dstBinding = 0; - writeDescriptorSet.pImageInfo = &fontTexture.descriptor; - vkUpdateDescriptorSets(device, 1, &writeDescriptorSet, 0, nullptr); - - /* - Pipeline layout - */ - VkPushConstantRange pushConstantRange{ VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(PushConstBlock) }; - - VkPipelineLayoutCreateInfo pipelineLayoutCI{}; - pipelineLayoutCI.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - pipelineLayoutCI.pushConstantRangeCount = 1; - pipelineLayoutCI.pPushConstantRanges = &pushConstantRange; - pipelineLayoutCI.setLayoutCount = 1; - pipelineLayoutCI.pSetLayouts = &descriptorSetLayout; - pipelineLayoutCI.pushConstantRangeCount = 1; - pipelineLayoutCI.pPushConstantRanges = &pushConstantRange; - VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCI, nullptr, &pipelineLayout)); - - /* - Pipeline - */ - VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCI{}; - inputAssemblyStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; - inputAssemblyStateCI.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; - - VkPipelineRasterizationStateCreateInfo rasterizationStateCI{}; - rasterizationStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; - rasterizationStateCI.polygonMode = VK_POLYGON_MODE_FILL; - rasterizationStateCI.cullMode = VK_CULL_MODE_NONE; - rasterizationStateCI.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; - rasterizationStateCI.lineWidth = 1.0f; - - VkPipelineColorBlendAttachmentState blendAttachmentState{}; - blendAttachmentState.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; - blendAttachmentState.blendEnable = VK_TRUE; - blendAttachmentState.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; - blendAttachmentState.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; - blendAttachmentState.colorBlendOp = VK_BLEND_OP_ADD; - blendAttachmentState.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; - blendAttachmentState.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; - blendAttachmentState.alphaBlendOp = VK_BLEND_OP_ADD; - - VkPipelineColorBlendStateCreateInfo colorBlendStateCI{}; - colorBlendStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; - colorBlendStateCI.attachmentCount = 1; - colorBlendStateCI.pAttachments = &blendAttachmentState; - - VkPipelineDepthStencilStateCreateInfo depthStencilStateCI{}; - depthStencilStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; - depthStencilStateCI.depthTestEnable = VK_FALSE; - depthStencilStateCI.depthWriteEnable = VK_FALSE; - depthStencilStateCI.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; - depthStencilStateCI.front = depthStencilStateCI.back; - depthStencilStateCI.back.compareOp = VK_COMPARE_OP_ALWAYS; - - VkPipelineViewportStateCreateInfo viewportStateCI{}; - viewportStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; - viewportStateCI.viewportCount = 1; - viewportStateCI.scissorCount = 1; - - VkPipelineMultisampleStateCreateInfo multisampleStateCI{}; - multisampleStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; - - if (multiSampleCount > VK_SAMPLE_COUNT_1_BIT) { - multisampleStateCI.rasterizationSamples = multiSampleCount; - } - - std::vector dynamicStateEnables = { - VK_DYNAMIC_STATE_VIEWPORT, - VK_DYNAMIC_STATE_SCISSOR - }; - VkPipelineDynamicStateCreateInfo dynamicStateCI{}; - dynamicStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; - dynamicStateCI.pDynamicStates = dynamicStateEnables.data(); - dynamicStateCI.dynamicStateCount = static_cast(dynamicStateEnables.size()); - - VkVertexInputBindingDescription vertexInputBinding = { 0, 20, VK_VERTEX_INPUT_RATE_VERTEX }; - std::vector vertexInputAttributes = { - { 0, 0, VK_FORMAT_R32G32_SFLOAT, 0 }, - { 1, 0, VK_FORMAT_R32G32_SFLOAT, sizeof(float) * 2 }, - { 2, 0, VK_FORMAT_R8G8B8A8_UNORM, sizeof(float) * 4 }, - }; - VkPipelineVertexInputStateCreateInfo vertexInputStateCI{}; - vertexInputStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; - vertexInputStateCI.vertexBindingDescriptionCount = 1; - vertexInputStateCI.pVertexBindingDescriptions = &vertexInputBinding; - vertexInputStateCI.vertexAttributeDescriptionCount = static_cast(vertexInputAttributes.size()); - vertexInputStateCI.pVertexAttributeDescriptions = vertexInputAttributes.data(); - - std::array shaderStages; - - VkGraphicsPipelineCreateInfo pipelineCI{}; - pipelineCI.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - pipelineCI.layout = pipelineLayout; - pipelineCI.renderPass = renderPass; - pipelineCI.pInputAssemblyState = &inputAssemblyStateCI; - pipelineCI.pVertexInputState = &vertexInputStateCI; - 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.layout = pipelineLayout; - std::string uiVertShaderPath = getAssetPath() + "shaders/ui.vert.spv"; - std::string uiFragShaderPath = getAssetPath() + "shaders/ui.frag.spv"; - shaderStages = { - - loadShader(device, uiVertShaderPath.data(), VK_SHADER_STAGE_VERTEX_BIT), - loadShader(device, uiFragShaderPath.data(), VK_SHADER_STAGE_FRAGMENT_BIT) - }; - VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipeline)); - - for (auto shaderStage : shaderStages) { - vkDestroyShaderModule(device, shaderStage.module, nullptr); - } - } - - ~UI() { - ImGui::DestroyContext(); - vertexBuffer.destroy(); - indexBuffer.destroy(); - vkDestroyPipeline(device, pipeline, nullptr); - vkDestroyPipelineLayout(device, pipelineLayout, nullptr); - vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); - vkDestroyDescriptorPool(device, descriptorPool, nullptr); - } - - void draw(VkCommandBuffer cmdBuffer) { - vkCmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); - vkCmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr); - - const VkDeviceSize offsets[1] = { 0 }; - vkCmdBindVertexBuffers(cmdBuffer, 0, 1, &vertexBuffer.buffer, offsets); - vkCmdBindIndexBuffer(cmdBuffer, indexBuffer.buffer, 0, VK_INDEX_TYPE_UINT16); - - vkCmdPushConstants(cmdBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(UI::PushConstBlock), &pushConstBlock); - - ImDrawData* imDrawData = ImGui::GetDrawData(); - int32_t vertexOffset = 0; - int32_t indexOffset = 0; - for (int32_t j = 0; j < imDrawData->CmdListsCount; j++) { - const ImDrawList* cmd_list = imDrawData->CmdLists[j]; - for (int32_t k = 0; k < cmd_list->CmdBuffer.Size; k++) { - const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[k]; - VkRect2D scissorRect; - scissorRect.offset.x = std::max((int32_t)(pcmd->ClipRect.x), 0); - scissorRect.offset.y = std::max((int32_t)(pcmd->ClipRect.y), 0); - scissorRect.extent.width = (uint32_t)(pcmd->ClipRect.z - pcmd->ClipRect.x); - scissorRect.extent.height = (uint32_t)(pcmd->ClipRect.w - pcmd->ClipRect.y); - vkCmdSetScissor(cmdBuffer, 0, 1, &scissorRect); - vkCmdDrawIndexed(cmdBuffer, pcmd->ElemCount, 1, indexOffset, vertexOffset, 0); - indexOffset += pcmd->ElemCount; - } - vertexOffset += cmd_list->VtxBuffer.Size; - } - } - - template - bool checkbox(const char* caption, T *value) { - bool val = (*value == 1); - bool res = ImGui::Checkbox(caption, &val); - *value = val; - return res; - } - bool header(const char *caption) { - return ImGui::CollapsingHeader(caption, ImGuiTreeNodeFlags_DefaultOpen); - } - bool slider(const char* caption, float* value, float min, float max) { - return ImGui::SliderFloat(caption, value, min, max); - } - bool combo(const char *caption, int32_t *itemindex, std::vector items) { - if (items.empty()) { - return false; - } - std::vector charitems; - charitems.reserve(items.size()); - for (size_t i = 0; i < items.size(); i++) { - charitems.push_back(items[i].c_str()); - } - uint32_t itemCount = static_cast(charitems.size()); - return ImGui::Combo(caption, itemindex, &charitems[0], itemCount, itemCount); - } - bool combo(const char *caption, std::string &selectedkey, std::map items) { - bool selectionChanged = false; - if (ImGui::BeginCombo(caption, selectedkey.c_str())) { - for (auto it = items.begin(); it != items.end(); ++it) { - const bool isSelected = it->first == selectedkey; - if (ImGui::Selectable(it->first.c_str(), isSelected)) { - selectionChanged = it->first != selectedkey; - selectedkey = it->first; - } - if (isSelected) { - ImGui::SetItemDefaultFocus(); - } - } - ImGui::EndCombo(); - } - return selectionChanged; - } - bool button(const char *caption) { - return ImGui::Button(caption); - } - bool beginChild(const char* caption, ImVec2 size, bool border) - { - return ImGui::BeginChild(caption, size, border); - } - - void endChild() - { - return ImGui::EndChild(); - } - - // menu GUI - bool beginMainMenuBar() { - return ImGui::BeginMainMenuBar(); - } - - bool beginMenu(const char* caption) - { - return ImGui::BeginMenu(caption); - } - - bool menuItem(const char* caption) - { - return ImGui::MenuItem(caption); - } - void endMenu() - { - return ImGui::EndMenu(); - } - - - void endMainMenuBar() { - return ImGui::EndMainMenuBar(); - } - - void text(const char *formatstr, ...) { - va_list args; - va_start(args, formatstr); - ImGui::TextV(formatstr, args); - va_end(args); - } -}; \ No newline at end of file diff --git a/src/base/vulkanexamplebase.cpp b/src/base/vulkanexamplebase.cpp index 4e83331..d354156 100644 --- a/src/base/vulkanexamplebase.cpp +++ b/src/base/vulkanexamplebase.cpp @@ -1,1373 +1,1462 @@ /* -* Vulkan Example base class, stripped down version -* -* Copyright (C) 2016-2018 by Sascha Willems - www.saschawillems.de -* -* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) -*/ + * Vulkan Example base class, stripped down version + * + * Copyright (C) 2016-2018 by Sascha Willems - www.saschawillems.de + * + * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + */ #include "VulkanExampleBase.h" +std::vector VulkanExampleBase::args; -std::vector VulkanExampleBase::args; - -VKAPI_ATTR VkBool32 VKAPI_CALL debugMessageCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject, size_t location, int32_t msgCode, const char * pLayerPrefix, const char * pMsg, void * pUserData) +VKAPI_ATTR VkBool32 VKAPI_CALL debugMessageCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject, size_t location, int32_t msgCode, const char *pLayerPrefix, const char *pMsg, void *pUserData) { - std::string prefix(""); - if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) { - prefix += "ERROR:"; - }; - if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT) { - prefix += "WARNING:"; - }; - if (flags & VK_DEBUG_REPORT_DEBUG_BIT_EXT) { - prefix += "DEBUG:"; - } - std::stringstream debugMessage; - debugMessage << prefix << " [" << pLayerPrefix << "] Code " << msgCode << " : " << pMsg; + std::string prefix(""); + if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) + { + prefix += "ERROR:"; + }; + if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT) + { + prefix += "WARNING:"; + }; + if (flags & VK_DEBUG_REPORT_DEBUG_BIT_EXT) + { + prefix += "DEBUG:"; + } + std::stringstream debugMessage; + debugMessage << prefix << " [" << pLayerPrefix << "] Code " << msgCode << " : " << pMsg; #if defined(__ANDROID__) - LOGD("%s", debugMessage.str().c_str()); + LOGD("%s", debugMessage.str().c_str()); #else - std::cout << debugMessage.str() << "\n"; + std::cout << debugMessage.str() << "\n"; #endif - fflush(stdout); - return VK_FALSE; + fflush(stdout); + return VK_FALSE; } VkResult VulkanExampleBase::createInstance(bool enableValidation) { - this->settings.validation = enableValidation; + this->settings.validation = enableValidation; - // Validation can also be forced via a define + // Validation can also be forced via a define #if defined(_VALIDATION) - this->settings.validation = true; -#endif + this->settings.validation = true; +#endif - VkApplicationInfo appInfo = {}; - appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; - appInfo.pApplicationName = name.c_str(); - appInfo.pEngineName = name.c_str(); - appInfo.apiVersion = VK_API_VERSION_1_0; + VkApplicationInfo appInfo = {}; + appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + appInfo.pApplicationName = name.c_str(); + appInfo.pEngineName = name.c_str(); + appInfo.apiVersion = VK_API_VERSION_1_0; - std::vector instanceExtensions = { }; + std::vector instanceExtensions = {}; - if (settings.headless) - { - instanceExtensions.push_back("VK_EXT_headless_surface"); - } - else - { - instanceExtensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME); - // Enable surface extensions depending on os + if (settings.headless) + { + instanceExtensions.push_back("VK_EXT_headless_surface"); + } + else + { + instanceExtensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME); + // Enable surface extensions depending on os #if defined(_WIN32) - instanceExtensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); + instanceExtensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); #elif defined(VK_USE_PLATFORM_ANDROID_KHR) - instanceExtensions.push_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME); + instanceExtensions.push_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME); #elif defined(_DIRECT2DISPLAY) - instanceExtensions.push_back(VK_KHR_DISPLAY_EXTENSION_NAME); + instanceExtensions.push_back(VK_KHR_DISPLAY_EXTENSION_NAME); #elif defined(VK_USE_PLATFORM_WAYLAND_KHR) - instanceExtensions.push_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME); + instanceExtensions.push_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME); #elif defined(VK_USE_PLATFORM_XCB_KHR) - instanceExtensions.push_back(VK_KHR_XCB_SURFACE_EXTENSION_NAME); + instanceExtensions.push_back(VK_KHR_XCB_SURFACE_EXTENSION_NAME); #elif defined(VK_USE_PLATFORM_MACOS_MVK) - instanceExtensions.push_back(VK_MVK_MACOS_SURFACE_EXTENSION_NAME); + instanceExtensions.push_back(VK_MVK_MACOS_SURFACE_EXTENSION_NAME); #endif #if defined(VK_USE_PLATFORM_MACOS_MVK) && (VK_HEADER_VERSION >= 216) - instanceExtensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); - instanceExtensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); + instanceExtensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); + instanceExtensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); #endif + } - } - - - - VkInstanceCreateInfo instanceCreateInfo = {}; - instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; - instanceCreateInfo.pNext = NULL; - instanceCreateInfo.pApplicationInfo = &appInfo; + VkInstanceCreateInfo instanceCreateInfo = {}; + instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + instanceCreateInfo.pNext = NULL; + instanceCreateInfo.pApplicationInfo = &appInfo; #if defined(VK_USE_PLATFORM_MACOS_MVK) && (VK_HEADER_VERSION >= 216) instanceCreateInfo.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; #endif - if (instanceExtensions.size() > 0) - { - if (settings.validation) { - instanceExtensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); - } - instanceCreateInfo.enabledExtensionCount = (uint32_t)instanceExtensions.size(); - instanceCreateInfo.ppEnabledExtensionNames = instanceExtensions.data(); - } - std::vector validationLayerNames; - if (settings.validation) { - validationLayerNames.push_back("VK_LAYER_KHRONOS_validation"); - instanceCreateInfo.enabledLayerCount = (uint32_t)validationLayerNames.size(); - instanceCreateInfo.ppEnabledLayerNames = validationLayerNames.data(); - } - return vkCreateInstance(&instanceCreateInfo, nullptr, &instance); - - + if (instanceExtensions.size() > 0) + { + if (settings.validation) + { + instanceExtensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); + } + instanceCreateInfo.enabledExtensionCount = (uint32_t)instanceExtensions.size(); + instanceCreateInfo.ppEnabledExtensionNames = instanceExtensions.data(); + } + std::vector validationLayerNames; + if (settings.validation) + { + validationLayerNames.push_back("VK_LAYER_KHRONOS_validation"); + instanceCreateInfo.enabledLayerCount = (uint32_t)validationLayerNames.size(); + instanceCreateInfo.ppEnabledLayerNames = validationLayerNames.data(); + } + return vkCreateInstance(&instanceCreateInfo, nullptr, &instance); } void VulkanExampleBase::prepare() { - /* - Swapchain - */ - initSwapchain(); - setupSwapChain(); + /* + Swapchain + */ + initSwapchain(); + setupSwapChain(); #if defined(VK_USE_PLATFORM_ANDROID_KHR) - width = swapChain.extent.width; - height = swapChain.extent.height; + width = swapChain.extent.width; + height = swapChain.extent.height; #endif - /* - Command pool - */ - VkCommandPoolCreateInfo cmdPoolInfo = {}; - cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - cmdPoolInfo.queueFamilyIndex = swapChain.queueNodeIndex; - cmdPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - VK_CHECK_RESULT(vkCreateCommandPool(device, &cmdPoolInfo, nullptr, &cmdPool)); + /* + Command pool + */ + VkCommandPoolCreateInfo cmdPoolInfo = {}; + cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + cmdPoolInfo.queueFamilyIndex = swapChain.queueNodeIndex; + cmdPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + VK_CHECK_RESULT(vkCreateCommandPool(device, &cmdPoolInfo, nullptr, &cmdPool)); - /* - Render pass - */ + /* + Render pass + */ - if (settings.multiSampling) { - std::array attachments = {}; + if (settings.multiSampling) + { + std::array attachments = {}; - // Multisampled attachment that we render to - attachments[0].format = swapChain.colorFormat; - attachments[0].samples = settings.sampleCount; - attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; - attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + // Multisampled attachment that we render to + attachments[0].format = swapChain.colorFormat; + attachments[0].samples = settings.sampleCount; + attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - // This is the frame buffer attachment to where the multisampled image - // will be resolved to and which will be presented to the swapchain - attachments[1].format = swapChain.colorFormat; - attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; - attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE; - attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachments[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - attachments[1].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + // This is the frame buffer attachment to where the multisampled image + // will be resolved to and which will be presented to the swapchain + attachments[1].format = swapChain.colorFormat; + attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachments[1].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - // Multisampled depth attachment we render to - attachments[2].format = depthFormat; - attachments[2].samples = settings.sampleCount; - attachments[2].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - attachments[2].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachments[2].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachments[2].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachments[2].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - attachments[2].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + // Multisampled depth attachment we render to + attachments[2].format = depthFormat; + attachments[2].samples = settings.sampleCount; + attachments[2].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[2].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[2].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[2].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[2].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachments[2].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - // Depth resolve attachment - attachments[3].format = depthFormat; - attachments[3].samples = VK_SAMPLE_COUNT_1_BIT; - attachments[3].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachments[3].storeOp = VK_ATTACHMENT_STORE_OP_STORE; - attachments[3].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachments[3].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachments[3].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - attachments[3].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + // Depth resolve attachment + attachments[3].format = depthFormat; + attachments[3].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[3].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[3].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[3].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[3].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[3].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachments[3].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - VkAttachmentReference colorReference = {}; - colorReference.attachment = 0; - colorReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + VkAttachmentReference colorReference = {}; + colorReference.attachment = 0; + colorReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - VkAttachmentReference depthReference = {}; - depthReference.attachment = 2; - depthReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + VkAttachmentReference depthReference = {}; + depthReference.attachment = 2; + depthReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - // Resolve attachment reference for the color attachment - VkAttachmentReference resolveReference = {}; - resolveReference.attachment = 1; - resolveReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + // Resolve attachment reference for the color attachment + VkAttachmentReference resolveReference = {}; + resolveReference.attachment = 1; + resolveReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - VkSubpassDescription subpass = {}; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.colorAttachmentCount = 1; - subpass.pColorAttachments = &colorReference; - // Pass our resolve attachments to the sub pass - subpass.pResolveAttachments = &resolveReference; - subpass.pDepthStencilAttachment = &depthReference; + VkSubpassDescription subpass = {}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &colorReference; + // Pass our resolve attachments to the sub pass + subpass.pResolveAttachments = &resolveReference; + subpass.pDepthStencilAttachment = &depthReference; - std::array dependencies; + std::array dependencies; - dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; - dependencies[0].dstSubpass = 0; - dependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; - dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT; - dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; + dependencies[0].dstSubpass = 0; + dependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT; + dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; - dependencies[1].srcSubpass = 0; - dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL; - dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; - dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - dependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; - dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + dependencies[1].srcSubpass = 0; + dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL; + dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; + dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; - VkRenderPassCreateInfo renderPassCI = {}; - renderPassCI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - renderPassCI.attachmentCount = static_cast(attachments.size()); - renderPassCI.pAttachments = attachments.data(); - renderPassCI.subpassCount = 1; - renderPassCI.pSubpasses = &subpass; - renderPassCI.dependencyCount = 2; - renderPassCI.pDependencies = dependencies.data(); - VK_CHECK_RESULT(vkCreateRenderPass(device, &renderPassCI, nullptr, &renderPass)); - } - else { - std::array attachments = {}; - // Color attachment - attachments[0].format = swapChain.colorFormat; - attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; - attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; - attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - // Depth attachment - attachments[1].format = depthFormat; - attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; - attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE; - attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachments[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + VkRenderPassCreateInfo renderPassCI = {}; + renderPassCI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassCI.attachmentCount = static_cast(attachments.size()); + renderPassCI.pAttachments = attachments.data(); + renderPassCI.subpassCount = 1; + renderPassCI.pSubpasses = &subpass; + renderPassCI.dependencyCount = 2; + renderPassCI.pDependencies = dependencies.data(); + VK_CHECK_RESULT(vkCreateRenderPass(device, &renderPassCI, nullptr, &renderPass)); + } + else + { + std::array attachments = {}; + // Color attachment + attachments[0].format = swapChain.colorFormat; + attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + // Depth attachment + attachments[1].format = depthFormat; + attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - VkAttachmentReference colorReference = {}; - colorReference.attachment = 0; - colorReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + VkAttachmentReference colorReference = {}; + colorReference.attachment = 0; + colorReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - VkAttachmentReference depthReference = {}; - depthReference.attachment = 1; - depthReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + VkAttachmentReference depthReference = {}; + depthReference.attachment = 1; + depthReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - VkSubpassDescription subpassDescription = {}; - subpassDescription.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpassDescription.colorAttachmentCount = 1; - subpassDescription.pColorAttachments = &colorReference; - subpassDescription.pDepthStencilAttachment = &depthReference; - subpassDescription.inputAttachmentCount = 0; - subpassDescription.pInputAttachments = nullptr; - subpassDescription.preserveAttachmentCount = 0; - subpassDescription.pPreserveAttachments = nullptr; - subpassDescription.pResolveAttachments = nullptr; + VkSubpassDescription subpassDescription = {}; + subpassDescription.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpassDescription.colorAttachmentCount = 1; + subpassDescription.pColorAttachments = &colorReference; + subpassDescription.pDepthStencilAttachment = &depthReference; + subpassDescription.inputAttachmentCount = 0; + subpassDescription.pInputAttachments = nullptr; + subpassDescription.preserveAttachmentCount = 0; + subpassDescription.pPreserveAttachments = nullptr; + subpassDescription.pResolveAttachments = nullptr; - // Subpass dependencies for layout transitions - std::array dependencies; + // Subpass dependencies for layout transitions + std::array dependencies; - dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; - dependencies[0].dstSubpass = 0; - dependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; - dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT; - dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; + dependencies[0].dstSubpass = 0; + dependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT; + dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; - dependencies[1].srcSubpass = 0; - dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL; - dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; - dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - dependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; - dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + dependencies[1].srcSubpass = 0; + dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL; + dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; + dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; - VkRenderPassCreateInfo renderPassCI{}; - renderPassCI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - renderPassCI.attachmentCount = static_cast(attachments.size()); - renderPassCI.pAttachments = attachments.data(); - renderPassCI.subpassCount = 1; - renderPassCI.pSubpasses = &subpassDescription; - renderPassCI.dependencyCount = static_cast(dependencies.size()); - renderPassCI.pDependencies = dependencies.data(); - VK_CHECK_RESULT(vkCreateRenderPass(device, &renderPassCI, nullptr, &renderPass)); - } + VkRenderPassCreateInfo renderPassCI{}; + renderPassCI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassCI.attachmentCount = static_cast(attachments.size()); + renderPassCI.pAttachments = attachments.data(); + renderPassCI.subpassCount = 1; + renderPassCI.pSubpasses = &subpassDescription; + renderPassCI.dependencyCount = static_cast(dependencies.size()); + renderPassCI.pDependencies = dependencies.data(); + VK_CHECK_RESULT(vkCreateRenderPass(device, &renderPassCI, nullptr, &renderPass)); + } - /* - Pipeline cache - */ - VkPipelineCacheCreateInfo pipelineCacheCreateInfo{}; - pipelineCacheCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO; - VK_CHECK_RESULT(vkCreatePipelineCache(device, &pipelineCacheCreateInfo, nullptr, &pipelineCache)); + /* + Pipeline cache + */ + VkPipelineCacheCreateInfo pipelineCacheCreateInfo{}; + pipelineCacheCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO; + VK_CHECK_RESULT(vkCreatePipelineCache(device, &pipelineCacheCreateInfo, nullptr, &pipelineCache)); - /* - Frame buffer - */ - setupFrameBuffer(); + /* + Frame buffer + */ + setupFrameBuffer(); } -void VulkanExampleBase::fileDropped(std::string filename) { } +void VulkanExampleBase::fileDropped(std::string filename) {} void VulkanExampleBase::renderFrame() { - auto tStart = std::chrono::high_resolution_clock::now(); + auto tStart = std::chrono::high_resolution_clock::now(); - render(); - - frameCounter++; - auto tEnd = std::chrono::high_resolution_clock::now(); - auto tDiff = std::chrono::duration(tEnd - tStart).count(); - frameTimer = (float)tDiff / 1000.0f; - camera.update(frameTimer); - fpsTimer += (float)tDiff; - if (fpsTimer > 1000.0f) { - lastFPS = static_cast((float)frameCounter * (1000.0f / fpsTimer)); - fpsTimer = 0.0f; - frameCounter = 0; - } + render(); + + frameCounter++; + auto tEnd = std::chrono::high_resolution_clock::now(); + auto tDiff = std::chrono::duration(tEnd - tStart).count(); + frameTimer = (float)tDiff / 1000.0f; + camera.update(frameTimer); + fpsTimer += (float)tDiff; + if (fpsTimer > 1000.0f) + { + lastFPS = static_cast((float)frameCounter * (1000.0f / fpsTimer)); + fpsTimer = 0.0f; + frameCounter = 0; + } } void VulkanExampleBase::renderLoop() { - destWidth = width; - destHeight = height; + destWidth = width; + destHeight = height; #if defined(_WIN32) - MSG msg; - bool quitMessageReceived = false; - while (!quitMessageReceived) { - while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - if (msg.message == WM_QUIT) { - quitMessageReceived = true; - break; - } - } - if (!IsIconic(window)) { - renderFrame(); - } - } + MSG msg; + bool quitMessageReceived = false; + while (!quitMessageReceived) + { + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + if (msg.message == WM_QUIT) + { + quitMessageReceived = true; + break; + } + } + if (!IsIconic(window)) + { + renderFrame(); + } + } #elif defined(VK_USE_PLATFORM_ANDROID_KHR) - while (1) - { - int ident; - int events; - struct android_poll_source* source; - bool destroy = false; + while (1) + { + int ident; + int events; + struct android_poll_source *source; + bool destroy = false; - focused = true; + focused = true; - while ((ident = ALooper_pollAll(focused ? 0 : -1, NULL, &events, (void**)&source)) >= 0) - { - if (source != NULL) - { - source->process(androidApp, source); - } - if (androidApp->destroyRequested != 0) - { - LOGD("Android app destroy requested"); - destroy = true; - break; - } - } + while ((ident = ALooper_pollAll(focused ? 0 : -1, NULL, &events, (void **)&source)) >= 0) + { + if (source != NULL) + { + source->process(androidApp, source); + } + if (androidApp->destroyRequested != 0) + { + LOGD("Android app destroy requested"); + destroy = true; + break; + } + } - // App destruction requested - // Exit loop, example will be destroyed in application main - if (destroy) - { - break; - } + // App destruction requested + // Exit loop, example will be destroyed in application main + if (destroy) + { + break; + } - // Render frame - if (prepared) - { - auto tStart = std::chrono::high_resolution_clock::now(); - render(); - frameCounter++; - auto tEnd = std::chrono::high_resolution_clock::now(); - auto tDiff = std::chrono::duration(tEnd - tStart).count(); - frameTimer = tDiff / 1000.0f; - camera.update(frameTimer); - fpsTimer += (float)tDiff; - if (fpsTimer > 1000.0f) - { - lastFPS = (float)frameCounter * (1000.0f / fpsTimer); - fpsTimer = 0.0f; - frameCounter = 0; - } + // Render frame + if (prepared) + { + auto tStart = std::chrono::high_resolution_clock::now(); + render(); + frameCounter++; + auto tEnd = std::chrono::high_resolution_clock::now(); + auto tDiff = std::chrono::duration(tEnd - tStart).count(); + frameTimer = tDiff / 1000.0f; + camera.update(frameTimer); + fpsTimer += (float)tDiff; + if (fpsTimer > 1000.0f) + { + lastFPS = (float)frameCounter * (1000.0f / fpsTimer); + fpsTimer = 0.0f; + frameCounter = 0; + } - // Check gamepad state - const float deadZone = 0.0015f; - // todo : check if gamepad is present - // todo : time based and relative axis positions - if (camera.type != Camera::CameraType::firstperson) - { - // Rotate - if (std::abs(gamePadState.axisLeft.x) > deadZone) { - camera.rotate(glm::vec3(0.0f, gamePadState.axisLeft.x * 0.5f, 0.0f)); - } - if (std::abs(gamePadState.axisLeft.y) > deadZone) { - camera.rotate(glm::vec3(gamePadState.axisLeft.y * 0.5f, 0.0f, 0.0f)); - } - } else { - camera.updatePad(gamePadState.axisLeft, gamePadState.axisRight, frameTimer); - } - } - } + // Check gamepad state + const float deadZone = 0.0015f; + // todo : check if gamepad is present + // todo : time based and relative axis positions + if (camera.type != Camera::CameraType::firstperson) + { + // Rotate + if (std::abs(gamePadState.axisLeft.x) > deadZone) + { + camera.rotate(glm::vec3(0.0f, gamePadState.axisLeft.x * 0.5f, 0.0f)); + } + if (std::abs(gamePadState.axisLeft.y) > deadZone) + { + camera.rotate(glm::vec3(gamePadState.axisLeft.y * 0.5f, 0.0f, 0.0f)); + } + } + else + { + camera.updatePad(gamePadState.axisLeft, gamePadState.axisRight, frameTimer); + } + } + } #elif defined(_DIRECT2DISPLAY) - while (!quit) - { - auto tStart = std::chrono::high_resolution_clock::now(); - render(); - frameCounter++; - auto tEnd = std::chrono::high_resolution_clock::now(); - auto tDiff = std::chrono::duration(tEnd - tStart).count(); - frameTimer = tDiff / 1000.0f; - camera.update(frameTimer); - fpsTimer += (float)tDiff; - if (fpsTimer > 1000.0f) - { - lastFPS = (float)frameCounter * (1000.0f / fpsTimer); - fpsTimer = 0.0f; - frameCounter = 0; - } - } + while (!quit) + { + auto tStart = std::chrono::high_resolution_clock::now(); + render(); + frameCounter++; + auto tEnd = std::chrono::high_resolution_clock::now(); + auto tDiff = std::chrono::duration(tEnd - tStart).count(); + frameTimer = tDiff / 1000.0f; + camera.update(frameTimer); + fpsTimer += (float)tDiff; + if (fpsTimer > 1000.0f) + { + lastFPS = (float)frameCounter * (1000.0f / fpsTimer); + fpsTimer = 0.0f; + frameCounter = 0; + } + } #elif defined(VK_USE_PLATFORM_WAYLAND_KHR) - while (!quit) - { - auto tStart = std::chrono::high_resolution_clock::now(); + while (!quit) + { + auto tStart = std::chrono::high_resolution_clock::now(); - while (wl_display_prepare_read(display) != 0) - wl_display_dispatch_pending(display); - wl_display_flush(display); - wl_display_read_events(display); - wl_display_dispatch_pending(display); + while (wl_display_prepare_read(display) != 0) + wl_display_dispatch_pending(display); + wl_display_flush(display); + wl_display_read_events(display); + wl_display_dispatch_pending(display); - render(); - frameCounter++; - auto tEnd = std::chrono::high_resolution_clock::now(); - auto tDiff = std::chrono::duration(tEnd - tStart).count(); - frameTimer = tDiff / 1000.0f; - camera.update(frameTimer); - fpsTimer += (float)tDiff; - if (fpsTimer > 1000.0f) - { - wl_shell_surface_set_title(shell_surface, title.c_str()); - lastFPS = (float)frameCounter * (1000.0f / fpsTimer); - fpsTimer = 0.0f; - frameCounter = 0; - } - } + render(); + frameCounter++; + auto tEnd = std::chrono::high_resolution_clock::now(); + auto tDiff = std::chrono::duration(tEnd - tStart).count(); + frameTimer = tDiff / 1000.0f; + camera.update(frameTimer); + fpsTimer += (float)tDiff; + if (fpsTimer > 1000.0f) + { + wl_shell_surface_set_title(shell_surface, title.c_str()); + lastFPS = (float)frameCounter * (1000.0f / fpsTimer); + fpsTimer = 0.0f; + frameCounter = 0; + } + } #elif defined(VK_USE_PLATFORM_XCB_KHR) - xcb_flush(connection); - while (!quit) - { - auto tStart = std::chrono::high_resolution_clock::now(); - xcb_generic_event_t *event; - while ((event = xcb_poll_for_event(connection))) - { - handleEvent(event); - free(event); - } - render(); - frameCounter++; - auto tEnd = std::chrono::high_resolution_clock::now(); - auto tDiff = std::chrono::duration(tEnd - tStart).count(); - frameTimer = tDiff / 1000.0f; - camera.update(frameTimer); - fpsTimer += (float)tDiff; - if (fpsTimer > 1000.0f) - { - xcb_change_property(connection, XCB_PROP_MODE_REPLACE, - window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, - title.size(), title.c_str()); - lastFPS = (float)frameCounter * (1000.0f / fpsTimer); - fpsTimer = 0.0f; - frameCounter = 0; - } - } + xcb_flush(connection); + while (!quit) + { + auto tStart = std::chrono::high_resolution_clock::now(); + xcb_generic_event_t *event; + while ((event = xcb_poll_for_event(connection))) + { + handleEvent(event); + free(event); + } + render(); + frameCounter++; + auto tEnd = std::chrono::high_resolution_clock::now(); + auto tDiff = std::chrono::duration(tEnd - tStart).count(); + frameTimer = tDiff / 1000.0f; + camera.update(frameTimer); + fpsTimer += (float)tDiff; + if (fpsTimer > 1000.0f) + { + xcb_change_property(connection, XCB_PROP_MODE_REPLACE, + window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, + title.size(), title.c_str()); + lastFPS = (float)frameCounter * (1000.0f / fpsTimer); + fpsTimer = 0.0f; + frameCounter = 0; + } + } #elif defined(VK_USE_PLATFORM_MACOS_MVK) - [NSApp run]; + [NSApp run]; #endif - // Flush device to make sure all resources can be freed - vkDeviceWaitIdle(device); + // Flush device to make sure all resources can be freed + vkDeviceWaitIdle(device); } VulkanExampleBase::VulkanExampleBase() { - char* numConvPtr; - // Parse command line arguments - for (size_t i = 0; i < args.size(); i++) - { - if (args[i] == std::string("-validation")) { - settings.validation = true; - } - if (args[i] == std::string("-vsync")) { - settings.vsync = true; - } - if ((args[i] == std::string("-f")) || (args[i] == std::string("--fullscreen"))) { - settings.fullscreen = true; - } - if ((args[i] == std::string("-w")) || (args[i] == std::string("--width"))) { - uint32_t w = strtol(args[i + 1], &numConvPtr, 10); - if (numConvPtr != args[i + 1]) { width = w; }; - } - if ((args[i] == std::string("-h")) || (args[i] == std::string("--height"))) { - uint32_t h = strtol(args[i + 1], &numConvPtr, 10); - if (numConvPtr != args[i + 1]) { height = h; }; - } - } - + char *numConvPtr; + // Parse command line arguments + for (size_t i = 0; i < args.size(); i++) + { + if (args[i] == std::string("-validation")) + { + settings.validation = true; + } + if (args[i] == std::string("-vsync")) + { + settings.vsync = true; + } + if ((args[i] == std::string("-f")) || (args[i] == std::string("--fullscreen"))) + { + settings.fullscreen = true; + } + if ((args[i] == std::string("-w")) || (args[i] == std::string("--width"))) + { + uint32_t w = strtol(args[i + 1], &numConvPtr, 10); + if (numConvPtr != args[i + 1]) + { + width = w; + }; + } + if ((args[i] == std::string("-h")) || (args[i] == std::string("--height"))) + { + uint32_t h = strtol(args[i + 1], &numConvPtr, 10); + if (numConvPtr != args[i + 1]) + { + height = h; + }; + } + } + #if defined(VK_USE_PLATFORM_ANDROID_KHR) - // Vulkan library is loaded dynamically on Android - bool libLoaded = vks::android::loadVulkanLibrary(); - assert(libLoaded); + // Vulkan library is loaded dynamically on Android + bool libLoaded = vks::android::loadVulkanLibrary(); + assert(libLoaded); #elif defined(_DIRECT2DISPLAY) #elif defined(VK_USE_PLATFORM_WAYLAND_KHR) - initWaylandConnection(); + initWaylandConnection(); #elif defined(VK_USE_PLATFORM_XCB_KHR) - initxcbConnection(); + initxcbConnection(); #endif #if defined(_WIN32) - AllocConsole(); - AttachConsole(GetCurrentProcessId()); - FILE *stream; - freopen_s(&stream, "CONOUT$", "w+", stdout); - freopen_s(&stream, "CONOUT$", "w+", stderr); - SetConsoleTitle(TEXT("Vulkan validation output")); + AllocConsole(); + AttachConsole(GetCurrentProcessId()); + FILE *stream; + freopen_s(&stream, "CONOUT$", "w+", stdout); + freopen_s(&stream, "CONOUT$", "w+", stderr); + SetConsoleTitle(TEXT("Vulkan validation output")); #endif } VulkanExampleBase::~VulkanExampleBase() { - // Clean up Vulkan resources - swapChain.cleanup(); - vkDestroyDescriptorPool(device, descriptorPool, nullptr); - vkDestroyRenderPass(device, renderPass, nullptr); - for (uint32_t i = 0; i < frameBuffers.size(); i++) { - vkDestroyFramebuffer(device, frameBuffers[i], nullptr); - } - vkDestroyImageView(device, depthStencil.view, nullptr); - vkDestroyImage(device, depthStencil.image, nullptr); - vkFreeMemory(device, depthStencil.mem, nullptr); - vkDestroyPipelineCache(device, pipelineCache, nullptr); - vkDestroyCommandPool(device, cmdPool, nullptr); - if (settings.multiSampling) { - vkDestroyImage(device, multisampleTarget.color.image, nullptr); - vkDestroyImageView(device, multisampleTarget.color.view, nullptr); - vkFreeMemory(device, multisampleTarget.color.memory, nullptr); - vkDestroyImage(device, multisampleTarget.depth.image, nullptr); - vkDestroyImageView(device, multisampleTarget.depth.view, nullptr); - vkFreeMemory(device, multisampleTarget.depth.memory, nullptr); - } - delete vulkanDevice; - if (settings.validation) { - vkDestroyDebugReportCallback(instance, debugReportCallback, nullptr); - } - vkDestroyInstance(instance, nullptr); + // Clean up Vulkan resources + swapChain.cleanup(); + vkDestroyDescriptorPool(device, descriptorPool, nullptr); + vkDestroyRenderPass(device, renderPass, nullptr); + for (uint32_t i = 0; i < frameBuffers.size(); i++) + { + vkDestroyFramebuffer(device, frameBuffers[i], nullptr); + } + vkDestroyImageView(device, depthStencil.view, nullptr); + vkDestroyImage(device, depthStencil.image, nullptr); + vkFreeMemory(device, depthStencil.mem, nullptr); + vkDestroyPipelineCache(device, pipelineCache, nullptr); + vkDestroyCommandPool(device, cmdPool, nullptr); + if (settings.multiSampling) + { + vkDestroyImage(device, multisampleTarget.color.image, nullptr); + vkDestroyImageView(device, multisampleTarget.color.view, nullptr); + vkFreeMemory(device, multisampleTarget.color.memory, nullptr); + vkDestroyImage(device, multisampleTarget.depth.image, nullptr); + vkDestroyImageView(device, multisampleTarget.depth.view, nullptr); + vkFreeMemory(device, multisampleTarget.depth.memory, nullptr); + } + delete vulkanDevice; + if (settings.validation) + { + vkDestroyDebugReportCallback(instance, debugReportCallback, nullptr); + } + vkDestroyInstance(instance, nullptr); #if defined(_DIRECT2DISPLAY) #elif defined(VK_USE_PLATFORM_WAYLAND_KHR) - wl_shell_surface_destroy(shell_surface); - wl_surface_destroy(surface); - if (keyboard) - wl_keyboard_destroy(keyboard); - if (pointer) - wl_pointer_destroy(pointer); - wl_seat_destroy(seat); - wl_shell_destroy(shell); - wl_compositor_destroy(compositor); - wl_registry_destroy(registry); - wl_display_disconnect(display); + wl_shell_surface_destroy(shell_surface); + wl_surface_destroy(surface); + if (keyboard) + wl_keyboard_destroy(keyboard); + if (pointer) + wl_pointer_destroy(pointer); + wl_seat_destroy(seat); + wl_shell_destroy(shell); + wl_compositor_destroy(compositor); + wl_registry_destroy(registry); + wl_display_disconnect(display); #elif defined(VK_USE_PLATFORM_ANDROID_KHR) - // todo : android cleanup (if required) + // todo : android cleanup (if required) #elif defined(VK_USE_PLATFORM_XCB_KHR) - xcb_destroy_window(connection, window); - xcb_disconnect(connection); + xcb_destroy_window(connection, window); + xcb_disconnect(connection); #endif } void VulkanExampleBase::initVulkan() { - VkResult err; + VkResult err; - /* - Instance creation - */ - err = createInstance(settings.validation); - if (err) { - std::cerr << "Could not create Vulkan instance!" << std::endl; - exit(err); - } + /* + Instance creation + */ + err = createInstance(settings.validation); + if (err) + { + std::cerr << "Could not create Vulkan instance!" << std::endl; + exit(err); + } #if defined(VK_USE_PLATFORM_ANDROID_KHR) - vks::android::loadVulkanFunctions(instance); + vks::android::loadVulkanFunctions(instance); #endif - /* - Validation layers - */ - if (settings.validation) { - vkCreateDebugReportCallback = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT")); - vkDestroyDebugReportCallback = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT")); - VkDebugReportCallbackCreateInfoEXT debugCreateInfo{}; - debugCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT; - debugCreateInfo.pfnCallback = (PFN_vkDebugReportCallbackEXT)debugMessageCallback; - debugCreateInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; - VK_CHECK_RESULT(vkCreateDebugReportCallback(instance, &debugCreateInfo, nullptr, &debugReportCallback)); - } + /* + Validation layers + */ + if (settings.validation) + { + vkCreateDebugReportCallback = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT")); + vkDestroyDebugReportCallback = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT")); + VkDebugReportCallbackCreateInfoEXT debugCreateInfo{}; + debugCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT; + debugCreateInfo.pfnCallback = (PFN_vkDebugReportCallbackEXT)debugMessageCallback; + debugCreateInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; + VK_CHECK_RESULT(vkCreateDebugReportCallback(instance, &debugCreateInfo, nullptr, &debugReportCallback)); + } - /* - GPU selection - */ - uint32_t gpuCount = 0; - VK_CHECK_RESULT(vkEnumeratePhysicalDevices(instance, &gpuCount, nullptr)); - assert(gpuCount > 0); - std::vector physicalDevices(gpuCount); - err = vkEnumeratePhysicalDevices(instance, &gpuCount, physicalDevices.data()); - if (err) { - std::cerr << "Could not enumerate physical devices!" << std::endl; - exit(err); - } - uint32_t selectedDevice = 0; -#if !defined(VK_USE_PLATFORM_ANDROID_KHR) - for (size_t i = 0; i < args.size(); i++) { - if ((args[i] == std::string("-g")) || (args[i] == std::string("--gpu"))) { - char* endptr; - selectedPhysicalDeviceIndex = strtol(args[i + 1], &endptr, 10); - if (endptr != args[i + 1]) { - if (selectedPhysicalDeviceIndex > gpuCount - 1) { - std::cerr << "Selected device index " << selectedPhysicalDeviceIndex << " is out of range, reverting to device 0 (use -listgpus to show available Vulkan devices)" << std::endl; - } else { - std::cout << "Selected Vulkan device " << selectedPhysicalDeviceIndex << std::endl; - selectedDevice = selectedPhysicalDeviceIndex; - } - }; - break; - } - } + /* + GPU selection + */ + uint32_t gpuCount = 0; + VK_CHECK_RESULT(vkEnumeratePhysicalDevices(instance, &gpuCount, nullptr)); + assert(gpuCount > 0); + std::vector physicalDevices(gpuCount); + err = vkEnumeratePhysicalDevices(instance, &gpuCount, physicalDevices.data()); + if (err) + { + std::cerr << "Could not enumerate physical devices!" << std::endl; + exit(err); + } + uint32_t selectedDevice = 0; +#if !defined(VK_USE_PLATFORM_ANDROID_KHR) + for (size_t i = 0; i < args.size(); i++) + { + if ((args[i] == std::string("-g")) || (args[i] == std::string("--gpu"))) + { + char *endptr; + selectedPhysicalDeviceIndex = strtol(args[i + 1], &endptr, 10); + if (endptr != args[i + 1]) + { + if (selectedPhysicalDeviceIndex > gpuCount - 1) + { + std::cerr << "Selected device index " << selectedPhysicalDeviceIndex << " is out of range, reverting to device 0 (use -listgpus to show available Vulkan devices)" << std::endl; + } + else + { + std::cout << "Selected Vulkan device " << selectedPhysicalDeviceIndex << std::endl; + selectedDevice = selectedPhysicalDeviceIndex; + } + }; + break; + } + } #endif - physicalDevice = physicalDevices[selectedDevice]; + physicalDevice = physicalDevices[selectedDevice]; - vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties); - vkGetPhysicalDeviceFeatures(physicalDevice, &deviceFeatures); - vkGetPhysicalDeviceMemoryProperties(physicalDevice, &deviceMemoryProperties); + vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties); + vkGetPhysicalDeviceFeatures(physicalDevice, &deviceFeatures); + vkGetPhysicalDeviceMemoryProperties(physicalDevice, &deviceMemoryProperties); - /* - Device creation - */ - vulkanDevice = new vks::VulkanDevice(physicalDevice); - VkPhysicalDeviceFeatures enabledFeatures{}; - if (deviceFeatures.samplerAnisotropy) { - enabledFeatures.samplerAnisotropy = VK_TRUE; - } - std::vector enabledExtensions{}; - VkResult res = vulkanDevice->createLogicalDevice(enabledFeatures, enabledExtensions); - if (res != VK_SUCCESS) { - std::cerr << "Could not create Vulkan device!" << std::endl; - exit(res); - } - device = vulkanDevice->logicalDevice; + /* + Device creation + */ + vulkanDevice = new VulkanBase::VulkanDevice(physicalDevice); + VkPhysicalDeviceFeatures enabledFeatures{}; + if (deviceFeatures.samplerAnisotropy) + { + enabledFeatures.samplerAnisotropy = VK_TRUE; + } + std::vector enabledExtensions{}; + VkResult res = vulkanDevice->createLogicalDevice(enabledFeatures, enabledExtensions); + if (res != VK_SUCCESS) + { + std::cerr << "Could not create Vulkan device!" << std::endl; + exit(res); + } + device = vulkanDevice->getLogicalDevice(); - /* - Graphics queue - */ - vkGetDeviceQueue(device, vulkanDevice->queueFamilyIndices.graphics, 0, &queue); + /* + Graphics queue + */ + vkGetDeviceQueue(device, vulkanDevice->getGraphicsQueueFamilyIndex(), 0, &queue); - /* - Suitable depth format - */ - std::vector depthFormats = { VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D32_SFLOAT, VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_D16_UNORM_S8_UINT, VK_FORMAT_D16_UNORM }; - VkBool32 validDepthFormat = false; - for (auto& format : depthFormats) { - VkFormatProperties formatProps; - vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &formatProps); - if (formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) { - depthFormat = format; - validDepthFormat = true; - break; - } - } - assert(validDepthFormat); + /* + Suitable depth format + */ + std::vector depthFormats = {VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D32_SFLOAT, VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_D16_UNORM_S8_UINT, VK_FORMAT_D16_UNORM}; + VkBool32 validDepthFormat = false; + for (auto &format : depthFormats) + { + VkFormatProperties formatProps; + vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &formatProps); + if (formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) + { + depthFormat = format; + validDepthFormat = true; + break; + } + } + assert(validDepthFormat); - swapChain.connect(instance, physicalDevice, device); + swapChain.connect(instance, physicalDevice, device); #if defined(VK_USE_PLATFORM_ANDROID_KHR) - // Get Android device name and manufacturer (to display along GPU name) - androidProduct = ""; - char prop[PROP_VALUE_MAX+1]; - int len = __system_property_get("ro.product.manufacturer", prop); - if (len > 0) { - androidProduct += std::string(prop) + " "; - }; - len = __system_property_get("ro.product.model", prop); - if (len > 0) { - androidProduct += std::string(prop); - }; - LOGD("androidProduct = %s", androidProduct.c_str()); -#endif + // Get Android device name and manufacturer (to display along GPU name) + androidProduct = ""; + char prop[PROP_VALUE_MAX + 1]; + int len = __system_property_get("ro.product.manufacturer", prop); + if (len > 0) + { + androidProduct += std::string(prop) + " "; + }; + len = __system_property_get("ro.product.model", prop); + if (len > 0) + { + androidProduct += std::string(prop); + }; + LOGD("androidProduct = %s", androidProduct.c_str()); +#endif } #if defined(_WIN32) HWND VulkanExampleBase::setupWindow(HINSTANCE hinstance, WNDPROC wndproc) { - this->windowInstance = hinstance; + this->windowInstance = hinstance; - WNDCLASSEX wndClass; + WNDCLASSEX wndClass; - wndClass.cbSize = sizeof(WNDCLASSEX); - wndClass.style = CS_HREDRAW | CS_VREDRAW; - wndClass.lpfnWndProc = wndproc; - wndClass.cbClsExtra = 0; - wndClass.cbWndExtra = 0; - wndClass.hInstance = hinstance; - wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); - wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); - wndClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); - wndClass.lpszMenuName = NULL; - wndClass.lpszClassName = name.c_str(); - wndClass.hIconSm = LoadIcon(NULL, IDI_WINLOGO); + wndClass.cbSize = sizeof(WNDCLASSEX); + wndClass.style = CS_HREDRAW | CS_VREDRAW; + wndClass.lpfnWndProc = wndproc; + wndClass.cbClsExtra = 0; + wndClass.cbWndExtra = 0; + wndClass.hInstance = hinstance; + wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); + wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); + wndClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); + wndClass.lpszMenuName = NULL; + wndClass.lpszClassName = name.c_str(); + wndClass.hIconSm = LoadIcon(NULL, IDI_WINLOGO); - if (!RegisterClassEx(&wndClass)) { - std::cout << "Could not register window class!\n"; - fflush(stdout); - exit(1); - } + if (!RegisterClassEx(&wndClass)) + { + std::cout << "Could not register window class!\n"; + fflush(stdout); + exit(1); + } - int screenWidth = GetSystemMetrics(SM_CXSCREEN); - int screenHeight = GetSystemMetrics(SM_CYSCREEN); + int screenWidth = GetSystemMetrics(SM_CXSCREEN); + int screenHeight = GetSystemMetrics(SM_CYSCREEN); - if (settings.fullscreen) { - DEVMODE dmScreenSettings; - memset(&dmScreenSettings, 0, sizeof(dmScreenSettings)); - dmScreenSettings.dmSize = sizeof(dmScreenSettings); - dmScreenSettings.dmPelsWidth = screenWidth; - dmScreenSettings.dmPelsHeight = screenHeight; - dmScreenSettings.dmBitsPerPel = 32; - dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; - if ((width != (uint32_t)screenWidth) && (height != (uint32_t)screenHeight)) { - if (ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) { - if (MessageBox(NULL, "Fullscreen Mode not supported!\n Switch to window mode?", "Error", MB_YESNO | MB_ICONEXCLAMATION) == IDYES) { - settings.fullscreen = false; - } else { - return nullptr; - } - } - } - } + if (settings.fullscreen) + { + DEVMODE dmScreenSettings; + memset(&dmScreenSettings, 0, sizeof(dmScreenSettings)); + dmScreenSettings.dmSize = sizeof(dmScreenSettings); + dmScreenSettings.dmPelsWidth = screenWidth; + dmScreenSettings.dmPelsHeight = screenHeight; + dmScreenSettings.dmBitsPerPel = 32; + dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; + if ((width != (uint32_t)screenWidth) && (height != (uint32_t)screenHeight)) + { + if (ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) + { + if (MessageBox(NULL, "Fullscreen Mode not supported!\n Switch to window mode?", "Error", MB_YESNO | MB_ICONEXCLAMATION) == IDYES) + { + settings.fullscreen = false; + } + else + { + return nullptr; + } + } + } + } - DWORD dwExStyle; - DWORD dwStyle; + DWORD dwExStyle; + DWORD dwStyle; - if (settings.fullscreen) { - dwExStyle = WS_EX_APPWINDOW; - dwStyle = WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; - } else { - dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; - dwStyle = WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; - } + if (settings.fullscreen) + { + dwExStyle = WS_EX_APPWINDOW; + dwStyle = WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; + } + else + { + dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; + dwStyle = WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; + } - RECT windowRect; - windowRect.left = 0L; - windowRect.top = 0L; - windowRect.right = settings.fullscreen ? (long)screenWidth : (long)width; - windowRect.bottom = settings.fullscreen ? (long)screenHeight : (long)height; + RECT windowRect; + windowRect.left = 0L; + windowRect.top = 0L; + windowRect.right = settings.fullscreen ? (long)screenWidth : (long)width; + windowRect.bottom = settings.fullscreen ? (long)screenHeight : (long)height; - AdjustWindowRectEx(&windowRect, dwStyle, FALSE, dwExStyle); + AdjustWindowRectEx(&windowRect, dwStyle, FALSE, dwExStyle); - window = CreateWindowEx(WS_EX_ACCEPTFILES, - name.c_str(), - title.c_str(), - dwStyle | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, - 0, - 0, - windowRect.right - windowRect.left, - windowRect.bottom - windowRect.top, - NULL, - NULL, - hinstance, - NULL); + window = CreateWindowEx(WS_EX_ACCEPTFILES, + name.c_str(), + title.c_str(), + dwStyle | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, + 0, + 0, + windowRect.right - windowRect.left, + windowRect.bottom - windowRect.top, + NULL, + NULL, + hinstance, + NULL); - if (!settings.fullscreen) { - uint32_t x = (GetSystemMetrics(SM_CXSCREEN) - windowRect.right) / 2; - uint32_t y = (GetSystemMetrics(SM_CYSCREEN) - windowRect.bottom) / 2; - SetWindowPos(window, 0, x, y, 0, 0, SWP_NOZORDER | SWP_NOSIZE); - } + if (!settings.fullscreen) + { + uint32_t x = (GetSystemMetrics(SM_CXSCREEN) - windowRect.right) / 2; + uint32_t y = (GetSystemMetrics(SM_CYSCREEN) - windowRect.bottom) / 2; + SetWindowPos(window, 0, x, y, 0, 0, SWP_NOZORDER | SWP_NOSIZE); + } - if (!window) { - printf("Could not create window!\n"); - fflush(stdout); - return nullptr; - exit(1); - } + if (!window) + { + printf("Could not create window!\n"); + fflush(stdout); + return nullptr; + exit(1); + } - ShowWindow(window, SW_SHOW); - SetForegroundWindow(window); - SetFocus(window); + ShowWindow(window, SW_SHOW); + SetForegroundWindow(window); + SetFocus(window); - return window; + return window; } void VulkanExampleBase::handleMessages(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { - switch (uMsg) - { - case WM_CLOSE: - prepared = false; - DestroyWindow(hWnd); - PostQuitMessage(0); - break; - case WM_PAINT: - ValidateRect(window, NULL); - break; - case WM_KEYDOWN: - switch (wParam) - { - case KEY_P: - paused = !paused; - break; - case KEY_ESCAPE: - PostQuitMessage(0); - break; - } + switch (uMsg) + { + case WM_CLOSE: + prepared = false; + DestroyWindow(hWnd); + PostQuitMessage(0); + break; + case WM_PAINT: + ValidateRect(window, NULL); + break; + case WM_KEYDOWN: + switch (wParam) + { + case KEY_P: + paused = !paused; + break; + case KEY_ESCAPE: + PostQuitMessage(0); + break; + } - if (camera.firstperson) - { - switch (wParam) - { - case KEY_W: - camera.keys.up = true; - break; - case KEY_S: - camera.keys.down = true; - break; - case KEY_A: - camera.keys.left = true; - break; - case KEY_D: - camera.keys.right = true; - break; - } - } + if (camera.firstperson) + { + switch (wParam) + { + case KEY_W: + camera.keys.up = true; + break; + case KEY_S: + camera.keys.down = true; + break; + case KEY_A: + camera.keys.left = true; + break; + case KEY_D: + camera.keys.right = true; + break; + } + } - break; - case WM_KEYUP: - if (camera.firstperson) - { - switch (wParam) - { - case KEY_W: - camera.keys.up = false; - break; - case KEY_S: - camera.keys.down = false; - break; - case KEY_A: - camera.keys.left = false; - break; - case KEY_D: - camera.keys.right = false; - break; - } - } - break; - case WM_LBUTTONDOWN: - mousePos = glm::vec2((float)LOWORD(lParam), (float)HIWORD(lParam)); - mouseButtons.left = true; - break; - case WM_RBUTTONDOWN: - mousePos = glm::vec2((float)LOWORD(lParam), (float)HIWORD(lParam)); - mouseButtons.right = true; - break; - case WM_MBUTTONDOWN: - mousePos = glm::vec2((float)LOWORD(lParam), (float)HIWORD(lParam)); - mouseButtons.middle = true; - break; - case WM_LBUTTONUP: - mouseButtons.left = false; - break; - case WM_RBUTTONUP: - mouseButtons.right = false; - break; - case WM_MBUTTONUP: - mouseButtons.middle = false; - break; - case WM_MOUSEWHEEL: - { - short wheelDelta = GET_WHEEL_DELTA_WPARAM(wParam); - camera.translate(glm::vec3(0.0f, 0.0f, -(float)wheelDelta * 0.005f * camera.movementSpeed)); - break; - } - case WM_MOUSEMOVE: - { - handleMouseMove(LOWORD(lParam), HIWORD(lParam)); - break; - } - case WM_SIZE: - if ((prepared) && (wParam != SIZE_MINIMIZED)) { - if ((resizing) || ((wParam == SIZE_MAXIMIZED) || (wParam == SIZE_RESTORED))) { - destWidth = LOWORD(lParam); - destHeight = HIWORD(lParam); - windowResize(); - } - } - break; - case WM_ENTERSIZEMOVE: - resizing = true; - break; - case WM_EXITSIZEMOVE: - resizing = false; - break; - case WM_DROPFILES: - { - std::string fname; - HDROP hDrop = reinterpret_cast(wParam); - // extract files here - char filename[MAX_PATH]; - uint32_t count = DragQueryFileA(hDrop, -1, nullptr, 0); - for (uint32_t i = 0; i < count; ++i) { - if (DragQueryFileA(hDrop, i, filename, MAX_PATH)) { - fname = filename; - } - break; - } - DragFinish(hDrop); - fileDropped(fname); - break; - } - } + break; + case WM_KEYUP: + if (camera.firstperson) + { + switch (wParam) + { + case KEY_W: + camera.keys.up = false; + break; + case KEY_S: + camera.keys.down = false; + break; + case KEY_A: + camera.keys.left = false; + break; + case KEY_D: + camera.keys.right = false; + break; + } + } + break; + case WM_LBUTTONDOWN: + mousePos = glm::vec2((float)LOWORD(lParam), (float)HIWORD(lParam)); + mouseButtons.left = true; + break; + case WM_RBUTTONDOWN: + mousePos = glm::vec2((float)LOWORD(lParam), (float)HIWORD(lParam)); + mouseButtons.right = true; + break; + case WM_MBUTTONDOWN: + mousePos = glm::vec2((float)LOWORD(lParam), (float)HIWORD(lParam)); + mouseButtons.middle = true; + break; + case WM_LBUTTONUP: + mouseButtons.left = false; + break; + case WM_RBUTTONUP: + mouseButtons.right = false; + break; + case WM_MBUTTONUP: + mouseButtons.middle = false; + break; + case WM_MOUSEWHEEL: + { + short wheelDelta = GET_WHEEL_DELTA_WPARAM(wParam); + camera.translate(glm::vec3(0.0f, 0.0f, -(float)wheelDelta * 0.005f * camera.movementSpeed)); + break; + } + case WM_MOUSEMOVE: + { + handleMouseMove(LOWORD(lParam), HIWORD(lParam)); + break; + } + case WM_SIZE: + if ((prepared) && (wParam != SIZE_MINIMIZED)) + { + if ((resizing) || ((wParam == SIZE_MAXIMIZED) || (wParam == SIZE_RESTORED))) + { + destWidth = LOWORD(lParam); + destHeight = HIWORD(lParam); + windowResize(); + } + } + break; + case WM_ENTERSIZEMOVE: + resizing = true; + break; + case WM_EXITSIZEMOVE: + resizing = false; + break; + case WM_DROPFILES: + { + std::string fname; + HDROP hDrop = reinterpret_cast(wParam); + // extract files here + char filename[MAX_PATH]; + uint32_t count = DragQueryFileA(hDrop, -1, nullptr, 0); + for (uint32_t i = 0; i < count; ++i) + { + if (DragQueryFileA(hDrop, i, filename, MAX_PATH)) + { + fname = filename; + } + break; + } + DragFinish(hDrop); + fileDropped(fname); + break; + } + } } #elif defined(VK_USE_PLATFORM_ANDROID_KHR) -int32_t VulkanExampleBase::handleAppInput(struct android_app* app, AInputEvent* event) +int32_t VulkanExampleBase::handleAppInput(struct android_app *app, AInputEvent *event) { - ImGuiIO& io = ImGui::GetIO(); - bool uiMouseCapture = io.WantCaptureMouse; + ImGuiIO &io = ImGui::GetIO(); + bool uiMouseCapture = io.WantCaptureMouse; - VulkanExampleBase* vulkanExample = reinterpret_cast(app->userData); - if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) - { - int32_t eventSource = AInputEvent_getSource(event); - switch (eventSource) { - case AINPUT_SOURCE_JOYSTICK: { - // Left thumbstick - vulkanExample->gamePadState.axisLeft.x = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_X, 0); - vulkanExample->gamePadState.axisLeft.y = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_Y, 0); - // Right thumbstick - vulkanExample->gamePadState.axisRight.x = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_Z, 0); - vulkanExample->gamePadState.axisRight.y = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_RZ, 0); - break; - } + VulkanExampleBase *vulkanExample = reinterpret_cast(app->userData); + if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) + { + int32_t eventSource = AInputEvent_getSource(event); + switch (eventSource) + { + case AINPUT_SOURCE_JOYSTICK: + { + // Left thumbstick + vulkanExample->gamePadState.axisLeft.x = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_X, 0); + vulkanExample->gamePadState.axisLeft.y = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_Y, 0); + // Right thumbstick + vulkanExample->gamePadState.axisRight.x = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_Z, 0); + vulkanExample->gamePadState.axisRight.y = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_RZ, 0); + break; + } - // FIXME: Reusing code for TOUCHSCREEN seemingly works well for MOUSE event source, - // but it would be better to provide a dedicated event handling logic for MOUSE event source. - case AINPUT_SOURCE_MOUSE: - case AINPUT_SOURCE_TOUCHSCREEN: { - int32_t action = AMotionEvent_getAction(event); - int32_t pointerCount = AMotionEvent_getPointerCount(event); - int32_t flags = action & AMOTION_EVENT_ACTION_MASK; + // FIXME: Reusing code for TOUCHSCREEN seemingly works well for MOUSE event source, + // but it would be better to provide a dedicated event handling logic for MOUSE event source. + case AINPUT_SOURCE_MOUSE: + case AINPUT_SOURCE_TOUCHSCREEN: + { + int32_t action = AMotionEvent_getAction(event); + int32_t pointerCount = AMotionEvent_getPointerCount(event); + int32_t flags = action & AMOTION_EVENT_ACTION_MASK; - switch (flags) { - case AMOTION_EVENT_ACTION_DOWN: - case AMOTION_EVENT_ACTION_POINTER_DOWN: { - for (uint32_t i = 0; i < pointerCount; i++) { - vulkanExample->touchPoints[i].x = AMotionEvent_getX(event, i); - vulkanExample->touchPoints[i].y = AMotionEvent_getY(event, i); - }; - int32_t pointerIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; - if (pointerIndex < 2) { - vulkanExample->touchPoints[pointerIndex].down = true; - } - if (pointerCount < 2) { - // Detect single tap - int64_t eventTime = AMotionEvent_getEventTime(event); - int64_t downTime = AMotionEvent_getDownTime(event); - if (eventTime - downTime <= vks::android::TAP_TIMEOUT) { - float deadZone = (160.f / vks::android::screenDensity) * vks::android::TAP_SLOP * vks::android::TAP_SLOP; - float x = AMotionEvent_getX(event, 0) - vulkanExample->touchPoints[0].x; - float y = AMotionEvent_getY(event, 0) - vulkanExample->touchPoints[0].y; - if ((x * x + y * y) < deadZone) { - vulkanExample->mousePos.x = vulkanExample->touchPoints[0].x; - vulkanExample->mousePos.y = vulkanExample->touchPoints[0].y; - vulkanExample->mouseButtons.left = true; - } - }; - } - break; - } - case AMOTION_EVENT_ACTION_UP: - case AMOTION_EVENT_ACTION_POINTER_UP: { - int32_t pointerIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; - if (pointerIndex < 2) { - vulkanExample->touchPoints[pointerIndex].down = false; - } - if (pointerCount < 2) { - vulkanExample->touchPoints[1].down = false; - } - break; - } - case AMOTION_EVENT_ACTION_MOVE: { - // Pinch and zoom - if (!uiMouseCapture && vulkanExample->touchPoints[0].down && vulkanExample->touchPoints[1].down) { - for (uint32_t i = 0; i < pointerCount; i++) { - if (vulkanExample->touchPoints[i].down) { - vulkanExample->touchPoints[i].x = AMotionEvent_getX(event, i); - vulkanExample->touchPoints[i].y = AMotionEvent_getY(event, i); - } - }; - float dx = vulkanExample->touchPoints[1].x - vulkanExample->touchPoints[0].x; - float dy = vulkanExample->touchPoints[1].y - vulkanExample->touchPoints[0].y; - float d = sqrt(dx * dx + dy * dy); - if (d < vulkanExample->pinchDist) { - vulkanExample->camera.translate(glm::vec3(0.0f, 0.0f, 0.03f)); - }; - if (d > vulkanExample->pinchDist) { - vulkanExample->camera.translate(glm::vec3(0.0f, 0.0f, -0.03f)); - }; - vulkanExample->pinchDist = d; - } else { - // Rotate - if (!uiMouseCapture && vulkanExample->touchPoints[0].down) { - int32_t eventX = AMotionEvent_getX(event, 0); - int32_t eventY = AMotionEvent_getY(event, 0); + switch (flags) + { + case AMOTION_EVENT_ACTION_DOWN: + case AMOTION_EVENT_ACTION_POINTER_DOWN: + { + for (uint32_t i = 0; i < pointerCount; i++) + { + vulkanExample->touchPoints[i].x = AMotionEvent_getX(event, i); + vulkanExample->touchPoints[i].y = AMotionEvent_getY(event, i); + }; + int32_t pointerIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; + if (pointerIndex < 2) + { + vulkanExample->touchPoints[pointerIndex].down = true; + } + if (pointerCount < 2) + { + // Detect single tap + int64_t eventTime = AMotionEvent_getEventTime(event); + int64_t downTime = AMotionEvent_getDownTime(event); + if (eventTime - downTime <= vks::android::TAP_TIMEOUT) + { + float deadZone = (160.f / vks::android::screenDensity) * vks::android::TAP_SLOP * vks::android::TAP_SLOP; + float x = AMotionEvent_getX(event, 0) - vulkanExample->touchPoints[0].x; + float y = AMotionEvent_getY(event, 0) - vulkanExample->touchPoints[0].y; + if ((x * x + y * y) < deadZone) + { + vulkanExample->mousePos.x = vulkanExample->touchPoints[0].x; + vulkanExample->mousePos.y = vulkanExample->touchPoints[0].y; + vulkanExample->mouseButtons.left = true; + } + }; + } + break; + } + case AMOTION_EVENT_ACTION_UP: + case AMOTION_EVENT_ACTION_POINTER_UP: + { + int32_t pointerIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; + if (pointerIndex < 2) + { + vulkanExample->touchPoints[pointerIndex].down = false; + } + if (pointerCount < 2) + { + vulkanExample->touchPoints[1].down = false; + } + break; + } + case AMOTION_EVENT_ACTION_MOVE: + { + // Pinch and zoom + if (!uiMouseCapture && vulkanExample->touchPoints[0].down && vulkanExample->touchPoints[1].down) + { + for (uint32_t i = 0; i < pointerCount; i++) + { + if (vulkanExample->touchPoints[i].down) + { + vulkanExample->touchPoints[i].x = AMotionEvent_getX(event, i); + vulkanExample->touchPoints[i].y = AMotionEvent_getY(event, i); + } + }; + float dx = vulkanExample->touchPoints[1].x - vulkanExample->touchPoints[0].x; + float dy = vulkanExample->touchPoints[1].y - vulkanExample->touchPoints[0].y; + float d = sqrt(dx * dx + dy * dy); + if (d < vulkanExample->pinchDist) + { + vulkanExample->camera.translate(glm::vec3(0.0f, 0.0f, 0.03f)); + }; + if (d > vulkanExample->pinchDist) + { + vulkanExample->camera.translate(glm::vec3(0.0f, 0.0f, -0.03f)); + }; + vulkanExample->pinchDist = d; + } + else + { + // Rotate + if (!uiMouseCapture && vulkanExample->touchPoints[0].down) + { + int32_t eventX = AMotionEvent_getX(event, 0); + int32_t eventY = AMotionEvent_getY(event, 0); - float deltaX = (vulkanExample->touchPoints[0].y - eventY) * vulkanExample->camera.rotationSpeed * 0.5f; - float deltaY = (vulkanExample->touchPoints[0].x - eventX) * vulkanExample->camera.rotationSpeed * 0.5f; + float deltaX = (vulkanExample->touchPoints[0].y - eventY) * vulkanExample->camera.rotationSpeed * 0.5f; + float deltaY = (vulkanExample->touchPoints[0].x - eventX) * vulkanExample->camera.rotationSpeed * 0.5f; - vulkanExample->camera.rotate(glm::vec3(deltaX, 0.0f, 0.0f)); - vulkanExample->camera.rotate(glm::vec3(0.0f, -deltaY, 0.0f)); + vulkanExample->camera.rotate(glm::vec3(deltaX, 0.0f, 0.0f)); + vulkanExample->camera.rotate(glm::vec3(0.0f, -deltaY, 0.0f)); - vulkanExample->touchPoints[0].x = eventX; - vulkanExample->touchPoints[0].y = eventY; - } - } - break; - } - default: - return 1; - } - } + vulkanExample->touchPoints[0].x = eventX; + vulkanExample->touchPoints[0].y = eventY; + } + } + break; + } + default: + return 1; + } + } - return 1; - } - } + return 1; + } + } - if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY) - { - int32_t keyCode = AKeyEvent_getKeyCode((const AInputEvent*)event); - int32_t action = AKeyEvent_getAction((const AInputEvent*)event); - int32_t button = 0; + if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY) + { + int32_t keyCode = AKeyEvent_getKeyCode((const AInputEvent *)event); + int32_t action = AKeyEvent_getAction((const AInputEvent *)event); + int32_t button = 0; - if (action == AKEY_EVENT_ACTION_UP) - return 0; + if (action == AKEY_EVENT_ACTION_UP) + return 0; - switch (keyCode) - { - case AKEYCODE_BUTTON_START: - vulkanExample->paused = !vulkanExample->paused; - break; - }; + switch (keyCode) + { + case AKEYCODE_BUTTON_START: + vulkanExample->paused = !vulkanExample->paused; + break; + }; - LOGD("Button %d pressed", keyCode); - } + LOGD("Button %d pressed", keyCode); + } - return 0; + return 0; } -void VulkanExampleBase::handleAppCommand(android_app * app, int32_t cmd) +void VulkanExampleBase::handleAppCommand(android_app *app, int32_t cmd) { - assert(app->userData != NULL); - VulkanExampleBase* vulkanExample = reinterpret_cast(app->userData); - switch (cmd) - { - case APP_CMD_SAVE_STATE: - LOGD("APP_CMD_SAVE_STATE"); - /* - vulkanExample->app->savedState = malloc(sizeof(struct saved_state)); - *((struct saved_state*)vulkanExample->app->savedState) = vulkanExample->state; - vulkanExample->app->savedStateSize = sizeof(struct saved_state); - */ - break; - case APP_CMD_INIT_WINDOW: - LOGD("APP_CMD_INIT_WINDOW"); - if (androidApp->window != NULL) - { - vulkanExample->initVulkan(); - vulkanExample->prepare(); - assert(vulkanExample->prepared); - } - else - { - LOGE("No window assigned!"); - } - break; - case APP_CMD_LOST_FOCUS: - LOGD("APP_CMD_LOST_FOCUS"); - vulkanExample->focused = false; - break; - case APP_CMD_GAINED_FOCUS: - LOGD("APP_CMD_GAINED_FOCUS"); - vulkanExample->focused = true; - break; - case APP_CMD_TERM_WINDOW: - // Window is hidden or closed, clean up resources - LOGD("APP_CMD_TERM_WINDOW"); - vulkanExample->swapChain.cleanup(); - break; - } + assert(app->userData != NULL); + VulkanExampleBase *vulkanExample = reinterpret_cast(app->userData); + switch (cmd) + { + case APP_CMD_SAVE_STATE: + LOGD("APP_CMD_SAVE_STATE"); + /* + vulkanExample->app->savedState = malloc(sizeof(struct saved_state)); + *((struct saved_state*)vulkanExample->app->savedState) = vulkanExample->state; + vulkanExample->app->savedStateSize = sizeof(struct saved_state); + */ + break; + case APP_CMD_INIT_WINDOW: + LOGD("APP_CMD_INIT_WINDOW"); + if (androidApp->window != NULL) + { + vulkanExample->initVulkan(); + vulkanExample->prepare(); + assert(vulkanExample->prepared); + } + else + { + LOGE("No window assigned!"); + } + break; + case APP_CMD_LOST_FOCUS: + LOGD("APP_CMD_LOST_FOCUS"); + vulkanExample->focused = false; + break; + case APP_CMD_GAINED_FOCUS: + LOGD("APP_CMD_GAINED_FOCUS"); + vulkanExample->focused = true; + break; + case APP_CMD_TERM_WINDOW: + // Window is hidden or closed, clean up resources + LOGD("APP_CMD_TERM_WINDOW"); + vulkanExample->swapChain.cleanup(); + break; + } } #elif defined(_DIRECT2DISPLAY) #elif defined(VK_USE_PLATFORM_WAYLAND_KHR) -/*static*/void VulkanExampleBase::registryGlobalCb(void *data, - wl_registry *registry, uint32_t name, const char *interface, - uint32_t version) +/*static*/ void VulkanExampleBase::registryGlobalCb(void *data, + wl_registry *registry, uint32_t name, const char *interface, + uint32_t version) { - VulkanExampleBase *self = reinterpret_cast(data); - self->registryGlobal(registry, name, interface, version); + VulkanExampleBase *self = reinterpret_cast(data); + self->registryGlobal(registry, name, interface, version); } -/*static*/void VulkanExampleBase::seatCapabilitiesCb(void *data, wl_seat *seat, - uint32_t caps) +/*static*/ void VulkanExampleBase::seatCapabilitiesCb(void *data, wl_seat *seat, + uint32_t caps) { - VulkanExampleBase *self = reinterpret_cast(data); - self->seatCapabilities(seat, caps); + VulkanExampleBase *self = reinterpret_cast(data); + self->seatCapabilities(seat, caps); } -/*static*/void VulkanExampleBase::pointerEnterCb(void *data, - wl_pointer *pointer, uint32_t serial, wl_surface *surface, - wl_fixed_t sx, wl_fixed_t sy) +/*static*/ void VulkanExampleBase::pointerEnterCb(void *data, + wl_pointer *pointer, uint32_t serial, wl_surface *surface, + wl_fixed_t sx, wl_fixed_t sy) { } -/*static*/void VulkanExampleBase::pointerLeaveCb(void *data, - wl_pointer *pointer, uint32_t serial, wl_surface *surface) +/*static*/ void VulkanExampleBase::pointerLeaveCb(void *data, + wl_pointer *pointer, uint32_t serial, wl_surface *surface) { } -/*static*/void VulkanExampleBase::pointerMotionCb(void *data, - wl_pointer *pointer, uint32_t time, wl_fixed_t sx, wl_fixed_t sy) +/*static*/ void VulkanExampleBase::pointerMotionCb(void *data, + wl_pointer *pointer, uint32_t time, wl_fixed_t sx, wl_fixed_t sy) { - VulkanExampleBase *self = reinterpret_cast(data); - self->pointerMotion(pointer, time, sx, sy); + VulkanExampleBase *self = reinterpret_cast(data); + self->pointerMotion(pointer, time, sx, sy); } void VulkanExampleBase::pointerMotion(wl_pointer *pointer, uint32_t time, wl_fixed_t sx, wl_fixed_t sy) { - handleMouseMove(wl_fixed_to_int(sx), wl_fixed_to_int(sy)); + handleMouseMove(wl_fixed_to_int(sx), wl_fixed_to_int(sy)); } -/*static*/void VulkanExampleBase::pointerButtonCb(void *data, - wl_pointer *pointer, uint32_t serial, uint32_t time, uint32_t button, - uint32_t state) +/*static*/ void VulkanExampleBase::pointerButtonCb(void *data, + wl_pointer *pointer, uint32_t serial, uint32_t time, uint32_t button, + uint32_t state) { - VulkanExampleBase *self = reinterpret_cast(data); - self->pointerButton(pointer, serial, time, button, state); + VulkanExampleBase *self = reinterpret_cast(data); + self->pointerButton(pointer, serial, time, button, state); } void VulkanExampleBase::pointerButton(struct wl_pointer *pointer, - uint32_t serial, uint32_t time, uint32_t button, uint32_t state) + uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { - switch (button) - { - case BTN_LEFT: - mouseButtons.left = !!state; - break; - case BTN_MIDDLE: - mouseButtons.middle = !!state; - break; - case BTN_RIGHT: - mouseButtons.right = !!state; - break; - default: - break; - } + switch (button) + { + case BTN_LEFT: + mouseButtons.left = !!state; + break; + case BTN_MIDDLE: + mouseButtons.middle = !!state; + break; + case BTN_RIGHT: + mouseButtons.right = !!state; + break; + default: + break; + } } -/*static*/void VulkanExampleBase::pointerAxisCb(void *data, - wl_pointer *pointer, uint32_t time, uint32_t axis, - wl_fixed_t value) +/*static*/ void VulkanExampleBase::pointerAxisCb(void *data, + wl_pointer *pointer, uint32_t time, uint32_t axis, + wl_fixed_t value) { - VulkanExampleBase *self = reinterpret_cast(data); - self->pointerAxis(pointer, time, axis, value); + VulkanExampleBase *self = reinterpret_cast(data); + self->pointerAxis(pointer, time, axis, value); } void VulkanExampleBase::pointerAxis(wl_pointer *pointer, uint32_t time, - uint32_t axis, wl_fixed_t value) + uint32_t axis, wl_fixed_t value) { - double d = wl_fixed_to_double(value); - switch (axis) - { - case REL_X: - camera.translate(glm::vec3(0.0f, 0.0f, -d * 0.005f * camera.movementSpeed)); - break; - default: - break; - } + double d = wl_fixed_to_double(value); + switch (axis) + { + case REL_X: + camera.translate(glm::vec3(0.0f, 0.0f, -d * 0.005f * camera.movementSpeed)); + break; + default: + break; + } } -/*static*/void VulkanExampleBase::keyboardKeymapCb(void *data, - struct wl_keyboard *keyboard, uint32_t format, int fd, uint32_t size) +/*static*/ void VulkanExampleBase::keyboardKeymapCb(void *data, + struct wl_keyboard *keyboard, uint32_t format, int fd, uint32_t size) { } -/*static*/void VulkanExampleBase::keyboardEnterCb(void *data, - struct wl_keyboard *keyboard, uint32_t serial, - struct wl_surface *surface, struct wl_array *keys) +/*static*/ void VulkanExampleBase::keyboardEnterCb(void *data, + struct wl_keyboard *keyboard, uint32_t serial, + struct wl_surface *surface, struct wl_array *keys) { } -/*static*/void VulkanExampleBase::keyboardLeaveCb(void *data, - struct wl_keyboard *keyboard, uint32_t serial, - struct wl_surface *surface) +/*static*/ void VulkanExampleBase::keyboardLeaveCb(void *data, + struct wl_keyboard *keyboard, uint32_t serial, + struct wl_surface *surface) { } -/*static*/void VulkanExampleBase::keyboardKeyCb(void *data, - struct wl_keyboard *keyboard, uint32_t serial, uint32_t time, - uint32_t key, uint32_t state) +/*static*/ void VulkanExampleBase::keyboardKeyCb(void *data, + struct wl_keyboard *keyboard, uint32_t serial, uint32_t time, + uint32_t key, uint32_t state) { - VulkanExampleBase *self = reinterpret_cast(data); - self->keyboardKey(keyboard, serial, time, key, state); + VulkanExampleBase *self = reinterpret_cast(data); + self->keyboardKey(keyboard, serial, time, key, state); } void VulkanExampleBase::keyboardKey(struct wl_keyboard *keyboard, - uint32_t serial, uint32_t time, uint32_t key, uint32_t state) + uint32_t serial, uint32_t time, uint32_t key, uint32_t state) { - switch (key) - { - case KEY_W: - camera.keys.up = !!state; - break; - case KEY_S: - camera.keys.down = !!state; - break; - case KEY_A: - camera.keys.left = !!state; - break; - case KEY_D: - camera.keys.right = !!state; - break; - case KEY_P: - if (state) - paused = !paused; - break; - case KEY_ESC: - quit = true; - break; - } + switch (key) + { + case KEY_W: + camera.keys.up = !!state; + break; + case KEY_S: + camera.keys.down = !!state; + break; + case KEY_A: + camera.keys.left = !!state; + break; + case KEY_D: + camera.keys.right = !!state; + break; + case KEY_P: + if (state) + paused = !paused; + break; + case KEY_ESC: + quit = true; + break; + } } -/*static*/void VulkanExampleBase::keyboardModifiersCb(void *data, - struct wl_keyboard *keyboard, uint32_t serial, uint32_t mods_depressed, - uint32_t mods_latched, uint32_t mods_locked, uint32_t group) +/*static*/ void VulkanExampleBase::keyboardModifiersCb(void *data, + struct wl_keyboard *keyboard, uint32_t serial, uint32_t mods_depressed, + uint32_t mods_latched, uint32_t mods_locked, uint32_t group) { } void VulkanExampleBase::seatCapabilities(wl_seat *seat, uint32_t caps) { - if ((caps & WL_SEAT_CAPABILITY_POINTER) && !pointer) - { - pointer = wl_seat_get_pointer(seat); - static const struct wl_pointer_listener pointer_listener = - { pointerEnterCb, pointerLeaveCb, pointerMotionCb, pointerButtonCb, - pointerAxisCb, }; - wl_pointer_add_listener(pointer, &pointer_listener, this); - } - else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && pointer) - { - wl_pointer_destroy(pointer); - pointer = nullptr; - } + if ((caps & WL_SEAT_CAPABILITY_POINTER) && !pointer) + { + pointer = wl_seat_get_pointer(seat); + static const struct wl_pointer_listener pointer_listener = + { + pointerEnterCb, + pointerLeaveCb, + pointerMotionCb, + pointerButtonCb, + pointerAxisCb, + }; + wl_pointer_add_listener(pointer, &pointer_listener, this); + } + else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && pointer) + { + wl_pointer_destroy(pointer); + pointer = nullptr; + } - if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !keyboard) - { - keyboard = wl_seat_get_keyboard(seat); - static const struct wl_keyboard_listener keyboard_listener = - { keyboardKeymapCb, keyboardEnterCb, keyboardLeaveCb, keyboardKeyCb, - keyboardModifiersCb, }; - wl_keyboard_add_listener(keyboard, &keyboard_listener, this); - } - else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && keyboard) - { - wl_keyboard_destroy(keyboard); - keyboard = nullptr; - } + if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !keyboard) + { + keyboard = wl_seat_get_keyboard(seat); + static const struct wl_keyboard_listener keyboard_listener = + { + keyboardKeymapCb, + keyboardEnterCb, + keyboardLeaveCb, + keyboardKeyCb, + keyboardModifiersCb, + }; + wl_keyboard_add_listener(keyboard, &keyboard_listener, this); + } + else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && keyboard) + { + wl_keyboard_destroy(keyboard); + keyboard = nullptr; + } } void VulkanExampleBase::registryGlobal(wl_registry *registry, uint32_t name, - const char *interface, uint32_t version) + const char *interface, uint32_t version) { - if (strcmp(interface, "wl_compositor") == 0) - { - compositor = (wl_compositor *) wl_registry_bind(registry, name, - &wl_compositor_interface, 3); - } - else if (strcmp(interface, "wl_shell") == 0) - { - shell = (wl_shell *) wl_registry_bind(registry, name, - &wl_shell_interface, 1); - } - else if (strcmp(interface, "wl_seat") == 0) - { - seat = (wl_seat *) wl_registry_bind(registry, name, &wl_seat_interface, - 1); + if (strcmp(interface, "wl_compositor") == 0) + { + compositor = (wl_compositor *)wl_registry_bind(registry, name, + &wl_compositor_interface, 3); + } + else if (strcmp(interface, "wl_shell") == 0) + { + shell = (wl_shell *)wl_registry_bind(registry, name, + &wl_shell_interface, 1); + } + else if (strcmp(interface, "wl_seat") == 0) + { + seat = (wl_seat *)wl_registry_bind(registry, name, &wl_seat_interface, + 1); - static const struct wl_seat_listener seat_listener = - { seatCapabilitiesCb, }; - wl_seat_add_listener(seat, &seat_listener, this); - } + static const struct wl_seat_listener seat_listener = + { + seatCapabilitiesCb, + }; + wl_seat_add_listener(seat, &seat_listener, this); + } } -/*static*/void VulkanExampleBase::registryGlobalRemoveCb(void *data, - struct wl_registry *registry, uint32_t name) +/*static*/ void VulkanExampleBase::registryGlobalRemoveCb(void *data, + struct wl_registry *registry, uint32_t name) { } void VulkanExampleBase::initWaylandConnection() { - display = wl_display_connect(NULL); - if (!display) - { - std::cout << "Could not connect to Wayland display!\n"; - fflush(stdout); - exit(1); - } + display = wl_display_connect(NULL); + if (!display) + { + std::cout << "Could not connect to Wayland display!\n"; + fflush(stdout); + exit(1); + } - registry = wl_display_get_registry(display); - if (!registry) - { - std::cout << "Could not get Wayland registry!\n"; - fflush(stdout); - exit(1); - } + registry = wl_display_get_registry(display); + if (!registry) + { + std::cout << "Could not get Wayland registry!\n"; + fflush(stdout); + exit(1); + } - static const struct wl_registry_listener registry_listener = - { registryGlobalCb, registryGlobalRemoveCb }; - wl_registry_add_listener(registry, ®istry_listener, this); - wl_display_dispatch(display); - wl_display_roundtrip(display); - if (!compositor || !shell || !seat) - { - std::cout << "Could not bind Wayland protocols!\n"; - fflush(stdout); - exit(1); - } + static const struct wl_registry_listener registry_listener = + {registryGlobalCb, registryGlobalRemoveCb}; + wl_registry_add_listener(registry, ®istry_listener, this); + wl_display_dispatch(display); + wl_display_roundtrip(display); + if (!compositor || !shell || !seat) + { + std::cout << "Could not bind Wayland protocols!\n"; + fflush(stdout); + exit(1); + } } static void PingCb(void *data, struct wl_shell_surface *shell_surface, - uint32_t serial) + uint32_t serial) { - wl_shell_surface_pong(shell_surface, serial); + wl_shell_surface_pong(shell_surface, serial); } static void ConfigureCb(void *data, struct wl_shell_surface *shell_surface, - uint32_t edges, int32_t width, int32_t height) + uint32_t edges, int32_t width, int32_t height) { } @@ -1377,219 +1466,221 @@ static void PopupDoneCb(void *data, struct wl_shell_surface *shell_surface) wl_shell_surface *VulkanExampleBase::setupWindow() { - surface = wl_compositor_create_surface(compositor); - shell_surface = wl_shell_get_shell_surface(shell, surface); + surface = wl_compositor_create_surface(compositor); + shell_surface = wl_shell_get_shell_surface(shell, surface); - static const struct wl_shell_surface_listener shell_surface_listener = - { PingCb, ConfigureCb, PopupDoneCb }; + static const struct wl_shell_surface_listener shell_surface_listener = + {PingCb, ConfigureCb, PopupDoneCb}; - wl_shell_surface_add_listener(shell_surface, &shell_surface_listener, this); - wl_shell_surface_set_toplevel(shell_surface); - wl_shell_surface_set_title(shell_surface, title.c_str()); - return shell_surface; + wl_shell_surface_add_listener(shell_surface, &shell_surface_listener, this); + wl_shell_surface_set_toplevel(shell_surface); + wl_shell_surface_set_title(shell_surface, title.c_str()); + return shell_surface; } #elif defined(VK_USE_PLATFORM_XCB_KHR) -static inline xcb_intern_atom_reply_t* intern_atom_helper(xcb_connection_t *conn, bool only_if_exists, const char *str) +static inline xcb_intern_atom_reply_t *intern_atom_helper(xcb_connection_t *conn, bool only_if_exists, const char *str) { - xcb_intern_atom_cookie_t cookie = xcb_intern_atom(conn, only_if_exists, strlen(str), str); - return xcb_intern_atom_reply(conn, cookie, NULL); + xcb_intern_atom_cookie_t cookie = xcb_intern_atom(conn, only_if_exists, strlen(str), str); + return xcb_intern_atom_reply(conn, cookie, NULL); } // Set up a window using XCB and request event types xcb_window_t VulkanExampleBase::setupWindow() { - uint32_t value_mask, value_list[32]; + uint32_t value_mask, value_list[32]; - window = xcb_generate_id(connection); + window = xcb_generate_id(connection); - value_mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; - value_list[0] = screen->black_pixel; - value_list[1] = - XCB_EVENT_MASK_KEY_RELEASE | - XCB_EVENT_MASK_KEY_PRESS | - XCB_EVENT_MASK_EXPOSURE | - XCB_EVENT_MASK_STRUCTURE_NOTIFY | - XCB_EVENT_MASK_POINTER_MOTION | - XCB_EVENT_MASK_BUTTON_PRESS | - XCB_EVENT_MASK_BUTTON_RELEASE; + value_mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; + value_list[0] = screen->black_pixel; + value_list[1] = + XCB_EVENT_MASK_KEY_RELEASE | + XCB_EVENT_MASK_KEY_PRESS | + XCB_EVENT_MASK_EXPOSURE | + XCB_EVENT_MASK_STRUCTURE_NOTIFY | + XCB_EVENT_MASK_POINTER_MOTION | + XCB_EVENT_MASK_BUTTON_PRESS | + XCB_EVENT_MASK_BUTTON_RELEASE; - if (settings.fullscreen) - { - width = destWidth = screen->width_in_pixels; - height = destHeight = screen->height_in_pixels; - } + if (settings.fullscreen) + { + width = destWidth = screen->width_in_pixels; + height = destHeight = screen->height_in_pixels; + } - xcb_create_window(connection, - XCB_COPY_FROM_PARENT, - window, screen->root, - 0, 0, width, height, 0, - XCB_WINDOW_CLASS_INPUT_OUTPUT, - screen->root_visual, - value_mask, value_list); + xcb_create_window(connection, + XCB_COPY_FROM_PARENT, + window, screen->root, + 0, 0, width, height, 0, + XCB_WINDOW_CLASS_INPUT_OUTPUT, + screen->root_visual, + value_mask, value_list); - /* Magic code that will send notification when window is destroyed */ - xcb_intern_atom_reply_t* reply = intern_atom_helper(connection, true, "WM_PROTOCOLS"); - atom_wm_delete_window = intern_atom_helper(connection, false, "WM_DELETE_WINDOW"); + /* Magic code that will send notification when window is destroyed */ + xcb_intern_atom_reply_t *reply = intern_atom_helper(connection, true, "WM_PROTOCOLS"); + atom_wm_delete_window = intern_atom_helper(connection, false, "WM_DELETE_WINDOW"); - xcb_change_property(connection, XCB_PROP_MODE_REPLACE, - window, (*reply).atom, 4, 32, 1, - &(*atom_wm_delete_window).atom); + xcb_change_property(connection, XCB_PROP_MODE_REPLACE, + window, (*reply).atom, 4, 32, 1, + &(*atom_wm_delete_window).atom); - xcb_change_property(connection, XCB_PROP_MODE_REPLACE, - window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, - title.size(), title.c_str()); + xcb_change_property(connection, XCB_PROP_MODE_REPLACE, + window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, + title.size(), title.c_str()); - free(reply); + free(reply); - if (settings.fullscreen) - { - xcb_intern_atom_reply_t *atom_wm_state = intern_atom_helper(connection, false, "_NET_WM_STATE"); - xcb_intern_atom_reply_t *atom_wm_fullscreen = intern_atom_helper(connection, false, "_NET_WM_STATE_FULLSCREEN"); - xcb_change_property(connection, - XCB_PROP_MODE_REPLACE, - window, atom_wm_state->atom, - XCB_ATOM_ATOM, 32, 1, - &(atom_wm_fullscreen->atom)); - free(atom_wm_fullscreen); - free(atom_wm_state); - } + if (settings.fullscreen) + { + xcb_intern_atom_reply_t *atom_wm_state = intern_atom_helper(connection, false, "_NET_WM_STATE"); + xcb_intern_atom_reply_t *atom_wm_fullscreen = intern_atom_helper(connection, false, "_NET_WM_STATE_FULLSCREEN"); + xcb_change_property(connection, + XCB_PROP_MODE_REPLACE, + window, atom_wm_state->atom, + XCB_ATOM_ATOM, 32, 1, + &(atom_wm_fullscreen->atom)); + free(atom_wm_fullscreen); + free(atom_wm_state); + } - xcb_map_window(connection, window); + xcb_map_window(connection, window); - return(window); + return (window); } // Initialize XCB connection void VulkanExampleBase::initxcbConnection() { - const xcb_setup_t *setup; - xcb_screen_iterator_t iter; - int scr; + const xcb_setup_t *setup; + xcb_screen_iterator_t iter; + int scr; - connection = xcb_connect(NULL, &scr); - if (connection == NULL) { - printf("Could not find a compatible Vulkan ICD!\n"); - fflush(stdout); - exit(1); - } + connection = xcb_connect(NULL, &scr); + if (connection == NULL) + { + printf("Could not find a compatible Vulkan ICD!\n"); + fflush(stdout); + exit(1); + } - setup = xcb_get_setup(connection); - iter = xcb_setup_roots_iterator(setup); - while (scr-- > 0) - xcb_screen_next(&iter); - screen = iter.data; + setup = xcb_get_setup(connection); + iter = xcb_setup_roots_iterator(setup); + while (scr-- > 0) + xcb_screen_next(&iter); + screen = iter.data; } void VulkanExampleBase::handleEvent(const xcb_generic_event_t *event) { - switch (event->response_type & 0x7f) - { - case XCB_CLIENT_MESSAGE: - if ((*(xcb_client_message_event_t*)event).data.data32[0] == - (*atom_wm_delete_window).atom) { - quit = true; - } - break; - case XCB_MOTION_NOTIFY: - { - xcb_motion_notify_event_t *motion = (xcb_motion_notify_event_t *)event; - handleMouseMove((int32_t)motion->event_x, (int32_t)motion->event_y); - break; - } - break; - case XCB_BUTTON_PRESS: - { - xcb_button_press_event_t *press = (xcb_button_press_event_t *)event; - if (press->detail == XCB_BUTTON_INDEX_1) - mouseButtons.left = true; - if (press->detail == XCB_BUTTON_INDEX_2) - mouseButtons.middle = true; - if (press->detail == XCB_BUTTON_INDEX_3) - mouseButtons.right = true; - } - break; - case XCB_BUTTON_RELEASE: - { - xcb_button_press_event_t *press = (xcb_button_press_event_t *)event; - if (press->detail == XCB_BUTTON_INDEX_1) - mouseButtons.left = false; - if (press->detail == XCB_BUTTON_INDEX_2) - mouseButtons.middle = false; - if (press->detail == XCB_BUTTON_INDEX_3) - mouseButtons.right = false; - } - break; - case XCB_KEY_PRESS: - { - const xcb_key_release_event_t *keyEvent = (const xcb_key_release_event_t *)event; - switch (keyEvent->detail) - { - case KEY_W: - camera.keys.up = true; - break; - case KEY_S: - camera.keys.down = true; - break; - case KEY_A: - camera.keys.left = true; - break; - case KEY_D: - camera.keys.right = true; - break; - case KEY_P: - paused = !paused; - break; - } - } - break; - case XCB_KEY_RELEASE: - { - const xcb_key_release_event_t *keyEvent = (const xcb_key_release_event_t *)event; - switch (keyEvent->detail) - { - case KEY_W: - camera.keys.up = false; - break; - case KEY_S: - camera.keys.down = false; - break; - case KEY_A: - camera.keys.left = false; - break; - case KEY_D: - camera.keys.right = false; - break; - case KEY_ESCAPE: - quit = true; - break; - } - } - break; - case XCB_DESTROY_NOTIFY: - quit = true; - break; - case XCB_CONFIGURE_NOTIFY: - { - const xcb_configure_notify_event_t *cfgEvent = (const xcb_configure_notify_event_t *)event; - if ((prepared) && ((cfgEvent->width != width) || (cfgEvent->height != height))) - { - destWidth = cfgEvent->width; - destHeight = cfgEvent->height; - if ((destWidth > 0) && (destHeight > 0)) - { - windowResize(); - } - } - } - break; - default: - break; - } + switch (event->response_type & 0x7f) + { + case XCB_CLIENT_MESSAGE: + if ((*(xcb_client_message_event_t *)event).data.data32[0] == + (*atom_wm_delete_window).atom) + { + quit = true; + } + break; + case XCB_MOTION_NOTIFY: + { + xcb_motion_notify_event_t *motion = (xcb_motion_notify_event_t *)event; + handleMouseMove((int32_t)motion->event_x, (int32_t)motion->event_y); + break; + } + break; + case XCB_BUTTON_PRESS: + { + xcb_button_press_event_t *press = (xcb_button_press_event_t *)event; + if (press->detail == XCB_BUTTON_INDEX_1) + mouseButtons.left = true; + if (press->detail == XCB_BUTTON_INDEX_2) + mouseButtons.middle = true; + if (press->detail == XCB_BUTTON_INDEX_3) + mouseButtons.right = true; + } + break; + case XCB_BUTTON_RELEASE: + { + xcb_button_press_event_t *press = (xcb_button_press_event_t *)event; + if (press->detail == XCB_BUTTON_INDEX_1) + mouseButtons.left = false; + if (press->detail == XCB_BUTTON_INDEX_2) + mouseButtons.middle = false; + if (press->detail == XCB_BUTTON_INDEX_3) + mouseButtons.right = false; + } + break; + case XCB_KEY_PRESS: + { + const xcb_key_release_event_t *keyEvent = (const xcb_key_release_event_t *)event; + switch (keyEvent->detail) + { + case KEY_W: + camera.keys.up = true; + break; + case KEY_S: + camera.keys.down = true; + break; + case KEY_A: + camera.keys.left = true; + break; + case KEY_D: + camera.keys.right = true; + break; + case KEY_P: + paused = !paused; + break; + } + } + break; + case XCB_KEY_RELEASE: + { + const xcb_key_release_event_t *keyEvent = (const xcb_key_release_event_t *)event; + switch (keyEvent->detail) + { + case KEY_W: + camera.keys.up = false; + break; + case KEY_S: + camera.keys.down = false; + break; + case KEY_A: + camera.keys.left = false; + break; + case KEY_D: + camera.keys.right = false; + break; + case KEY_ESCAPE: + quit = true; + break; + } + } + break; + case XCB_DESTROY_NOTIFY: + quit = true; + break; + case XCB_CONFIGURE_NOTIFY: + { + const xcb_configure_notify_event_t *cfgEvent = (const xcb_configure_notify_event_t *)event; + if ((prepared) && ((cfgEvent->width != width) || (cfgEvent->height != height))) + { + destWidth = cfgEvent->width; + destHeight = cfgEvent->height; + if ((destWidth > 0) && (destHeight > 0)) + { + windowResize(); + } + } + } + break; + default: + break; + } } #elif defined(VK_USE_PLATFORM_MACOS_MVK) -@interface AppDelegate : NSObject +@interface AppDelegate : NSObject { } @@ -1601,477 +1692,494 @@ void VulkanExampleBase::handleEvent(const xcb_generic_event_t *event) - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender { - return YES; + return YES; } @end CVReturn OnDisplayLinkOutput(CVDisplayLinkRef displayLink, const CVTimeStamp *inNow, const CVTimeStamp *inOutputTime, - CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *displayLinkContext) + CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *displayLinkContext) { - @autoreleasepool - { - auto vulkanExampleBase = static_cast(displayLinkContext); - vulkanExampleBase->renderFrame(); - } - return kCVReturnSuccess; + @autoreleasepool + { + auto vulkanExampleBase = static_cast(displayLinkContext); + vulkanExampleBase->renderFrame(); + } + return kCVReturnSuccess; } -@interface View : NSView +@interface View : NSView { @public - VulkanExampleBase* vulkanExampleBase; + VulkanExampleBase *vulkanExampleBase; } @end @implementation View { - CVDisplayLinkRef displayLink; + CVDisplayLinkRef displayLink; } - (instancetype)initWithFrame:(NSRect)frameRect { - self = [super initWithFrame:(frameRect)]; - if (self) - { - self.wantsLayer = YES; - self.layer = [CAMetalLayer layer]; - } - return self; + self = [super initWithFrame:(frameRect)]; + if (self) + { + self.wantsLayer = YES; + self.layer = [CAMetalLayer layer]; + } + return self; } - (void)viewDidMoveToWindow { - CVDisplayLinkCreateWithActiveCGDisplays(&displayLink); - CVDisplayLinkSetOutputCallback(displayLink, &OnDisplayLinkOutput, vulkanExampleBase); - CVDisplayLinkStart(displayLink); + CVDisplayLinkCreateWithActiveCGDisplays(&displayLink); + CVDisplayLinkSetOutputCallback(displayLink, &OnDisplayLinkOutput, vulkanExampleBase); + CVDisplayLinkStart(displayLink); } - (BOOL)acceptsFirstResponder { - return YES; + return YES; } -- (void)keyDown:(NSEvent*)event +- (void)keyDown:(NSEvent *)event { - switch (event.keyCode) - { - case kVK_ANSI_P: - vulkanExampleBase->paused = !vulkanExampleBase->paused; - break; - case kVK_Escape: - [NSApp terminate:nil]; - break; - default: - break; - } + switch (event.keyCode) + { + case kVK_ANSI_P: + vulkanExampleBase->paused = !vulkanExampleBase->paused; + break; + case kVK_Escape: + [NSApp terminate:nil]; + break; + default: + break; + } } -- (void)keyUp:(NSEvent*)event +- (void)keyUp:(NSEvent *)event { - if (vulkanExampleBase->camera.firstperson) - { - switch (event.keyCode) - { - case kVK_ANSI_W: - vulkanExampleBase->camera.keys.up = false; - break; - case kVK_ANSI_S: - vulkanExampleBase->camera.keys.down = false; - break; - case kVK_ANSI_A: - vulkanExampleBase->camera.keys.left = false; - break; - case kVK_ANSI_D: - vulkanExampleBase->camera.keys.right = false; - break; - default: - break; - } - } + if (vulkanExampleBase->camera.firstperson) + { + switch (event.keyCode) + { + case kVK_ANSI_W: + vulkanExampleBase->camera.keys.up = false; + break; + case kVK_ANSI_S: + vulkanExampleBase->camera.keys.down = false; + break; + case kVK_ANSI_A: + vulkanExampleBase->camera.keys.left = false; + break; + case kVK_ANSI_D: + vulkanExampleBase->camera.keys.right = false; + break; + default: + break; + } + } } - (void)mouseDown:(NSEvent *)event { - NSPoint location = [event locationInWindow]; - NSPoint point = [self convertPoint:location fromView:nil]; - vulkanExampleBase->mousePos = glm::vec2(point.x, point.y); - vulkanExampleBase->mouseButtons.left = true; + NSPoint location = [event locationInWindow]; + NSPoint point = [self convertPoint:location fromView:nil]; + vulkanExampleBase->mousePos = glm::vec2(point.x, point.y); + vulkanExampleBase->mouseButtons.left = true; } - (void)mouseUp:(NSEvent *)event { - NSPoint location = [event locationInWindow]; - NSPoint point = [self convertPoint:location fromView:nil]; - vulkanExampleBase->mousePos = glm::vec2(point.x, point.y); - vulkanExampleBase->mouseButtons.left = false; + NSPoint location = [event locationInWindow]; + NSPoint point = [self convertPoint:location fromView:nil]; + vulkanExampleBase->mousePos = glm::vec2(point.x, point.y); + vulkanExampleBase->mouseButtons.left = false; } - (void)otherMouseDown:(NSEvent *)event { - vulkanExampleBase->mouseButtons.right = true; + vulkanExampleBase->mouseButtons.right = true; } - (void)otherMouseUp:(NSEvent *)event { - vulkanExampleBase->mouseButtons.right = false; + vulkanExampleBase->mouseButtons.right = false; } - (void)mouseDragged:(NSEvent *)event { - NSPoint location = [event locationInWindow]; - NSPoint point = [self convertPoint:location fromView:nil]; - vulkanExampleBase->mouseDragged(point.x, point.y); + NSPoint location = [event locationInWindow]; + NSPoint point = [self convertPoint:location fromView:nil]; + vulkanExampleBase->mouseDragged(point.x, point.y); } - (void)mouseMoved:(NSEvent *)event { - NSPoint location = [event locationInWindow]; - NSPoint point = [self convertPoint:location fromView:nil]; - vulkanExampleBase->mouseDragged(point.x, point.y); + NSPoint location = [event locationInWindow]; + NSPoint point = [self convertPoint:location fromView:nil]; + vulkanExampleBase->mouseDragged(point.x, point.y); } - (void)scrollWheel:(NSEvent *)event { - short wheelDelta = [event deltaY]; - vulkanExampleBase->camera.translate(glm::vec3(0.0f, 0.0f, - -(float)wheelDelta * 0.05f * vulkanExampleBase->camera.movementSpeed)); + short wheelDelta = [event deltaY]; + vulkanExampleBase->camera.translate(glm::vec3(0.0f, 0.0f, + -(float)wheelDelta * 0.05f * vulkanExampleBase->camera.movementSpeed)); } - (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)frameSize { - CVDisplayLinkStop(displayLink); - vulkanExampleBase->windowWillResize(frameSize.width, frameSize.height); - return frameSize; + CVDisplayLinkStop(displayLink); + vulkanExampleBase->windowWillResize(frameSize.width, frameSize.height); + return frameSize; } - (void)windowDidResize:(NSNotification *)notification { - vulkanExampleBase->windowDidResize(); - CVDisplayLinkStart(displayLink); + vulkanExampleBase->windowDidResize(); + CVDisplayLinkStart(displayLink); } - (BOOL)windowShouldClose:(NSWindow *)sender { - return TRUE; + return TRUE; } - (void)windowWillClose:(NSNotification *)notification { - CVDisplayLinkStop(displayLink); + CVDisplayLinkStop(displayLink); } @end -NSWindow* VulkanExampleBase::setupWindow() +NSWindow *VulkanExampleBase::setupWindow() { - NSApp = [NSApplication sharedApplication]; - [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; - [NSApp setDelegate:[AppDelegate new]]; + NSApp = [NSApplication sharedApplication]; + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + [NSApp setDelegate:[AppDelegate new]]; - const auto kContentRect = NSMakeRect(0.0f, 0.0f, width, height); + const auto kContentRect = NSMakeRect(0.0f, 0.0f, width, height); - window = [[NSWindow alloc] initWithContentRect:kContentRect - styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskResizable - backing:NSBackingStoreBuffered - defer:NO]; - [window setTitle:@(title.c_str())]; - [window setAcceptsMouseMovedEvents:YES]; - [window center]; - [window makeKeyAndOrderFront:nil]; + window = [[NSWindow alloc] initWithContentRect:kContentRect + styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskResizable + backing:NSBackingStoreBuffered + defer:NO]; + [window setTitle:@(title.c_str())]; + [window setAcceptsMouseMovedEvents:YES]; + [window center]; + [window makeKeyAndOrderFront:nil]; - auto view = [[View alloc] initWithFrame:kContentRect]; - view->vulkanExampleBase = this; + auto view = [[View alloc] initWithFrame:kContentRect]; + view->vulkanExampleBase = this; - [window setDelegate:view]; - [window setContentView:view]; + [window setDelegate:view]; + [window setContentView:view]; - return window; + return window; } void VulkanExampleBase::mouseDragged(float x, float y) { - handleMouseMove(static_cast(x), static_cast(y)); + handleMouseMove(static_cast(x), static_cast(y)); } void VulkanExampleBase::windowWillResize(float x, float y) { - resizing = true; - if (prepared) - { - destWidth = x; - destHeight = y; - windowResize(); - } - std::cout << "resize" << std::endl; + resizing = true; + if (prepared) + { + destWidth = x; + destHeight = y; + windowResize(); + } + std::cout << "resize" << std::endl; } void VulkanExampleBase::windowDidResize() { - std::cout << "done" << std::endl; - resizing = false; + std::cout << "done" << std::endl; + resizing = false; } #endif -void VulkanExampleBase::windowResized() {} +void VulkanExampleBase::windowResized() +{ +} void VulkanExampleBase::setupFrameBuffer() { - /* - MSAA - */ - if (settings.multiSampling) { - // Check if device supports requested sample count for color and depth frame buffer - //assert((deviceProperties.limits.framebufferColorSampleCounts >= sampleCount) && (deviceProperties.limits.framebufferDepthSampleCounts >= sampleCount)); + /* + MSAA + */ + if (settings.multiSampling) + { + // Check if device supports requested sample count for color and depth frame buffer + // assert((deviceProperties.limits.framebufferColorSampleCounts >= sampleCount) && (deviceProperties.limits.framebufferDepthSampleCounts >= sampleCount)); - VkImageCreateInfo imageCI{}; - imageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - imageCI.imageType = VK_IMAGE_TYPE_2D; - imageCI.format = swapChain.colorFormat; - imageCI.extent.width = width; - imageCI.extent.height = height; - imageCI.extent.depth = 1; - imageCI.mipLevels = 1; - imageCI.arrayLayers = 1; - imageCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - imageCI.tiling = VK_IMAGE_TILING_OPTIMAL; - imageCI.samples = settings.sampleCount; - imageCI.usage = VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - imageCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - VK_CHECK_RESULT(vkCreateImage(device, &imageCI, nullptr, &multisampleTarget.color.image)); + VkImageCreateInfo imageCI{}; + imageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + imageCI.imageType = VK_IMAGE_TYPE_2D; + imageCI.format = swapChain.colorFormat; + imageCI.extent.width = width; + imageCI.extent.height = height; + imageCI.extent.depth = 1; + imageCI.mipLevels = 1; + imageCI.arrayLayers = 1; + imageCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + imageCI.tiling = VK_IMAGE_TILING_OPTIMAL; + imageCI.samples = settings.sampleCount; + imageCI.usage = VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + imageCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + VK_CHECK_RESULT(vkCreateImage(device, &imageCI, nullptr, &multisampleTarget.color.image)); - VkMemoryRequirements memReqs; - vkGetImageMemoryRequirements(device, multisampleTarget.color.image, &memReqs); - VkMemoryAllocateInfo memAllocInfo{}; - memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - memAllocInfo.allocationSize = memReqs.size; - VkBool32 lazyMemTypePresent; - memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT, &lazyMemTypePresent); - if (!lazyMemTypePresent) { - memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - } - VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &multisampleTarget.color.memory)); - vkBindImageMemory(device, multisampleTarget.color.image, multisampleTarget.color.memory, 0); + VkMemoryRequirements memReqs; + vkGetImageMemoryRequirements(device, multisampleTarget.color.image, &memReqs); + VkMemoryAllocateInfo memAllocInfo{}; + memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + memAllocInfo.allocationSize = memReqs.size; + VkBool32 lazyMemTypePresent; + memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT, &lazyMemTypePresent); + if (!lazyMemTypePresent) + { + memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + } + VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &multisampleTarget.color.memory)); + vkBindImageMemory(device, multisampleTarget.color.image, multisampleTarget.color.memory, 0); - // Create image view for the MSAA target - VkImageViewCreateInfo imageViewCI{}; - imageViewCI.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - imageViewCI.image = multisampleTarget.color.image; - imageViewCI.viewType = VK_IMAGE_VIEW_TYPE_2D; - imageViewCI.format = swapChain.colorFormat; - imageViewCI.components.r = VK_COMPONENT_SWIZZLE_R; - imageViewCI.components.g = VK_COMPONENT_SWIZZLE_G; - imageViewCI.components.b = VK_COMPONENT_SWIZZLE_B; - imageViewCI.components.a = VK_COMPONENT_SWIZZLE_A; - imageViewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - imageViewCI.subresourceRange.levelCount = 1; - imageViewCI.subresourceRange.layerCount = 1; - VK_CHECK_RESULT(vkCreateImageView(device, &imageViewCI, nullptr, &multisampleTarget.color.view)); + // Create image view for the MSAA target + VkImageViewCreateInfo imageViewCI{}; + imageViewCI.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + imageViewCI.image = multisampleTarget.color.image; + imageViewCI.viewType = VK_IMAGE_VIEW_TYPE_2D; + imageViewCI.format = swapChain.colorFormat; + imageViewCI.components.r = VK_COMPONENT_SWIZZLE_R; + imageViewCI.components.g = VK_COMPONENT_SWIZZLE_G; + imageViewCI.components.b = VK_COMPONENT_SWIZZLE_B; + imageViewCI.components.a = VK_COMPONENT_SWIZZLE_A; + imageViewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imageViewCI.subresourceRange.levelCount = 1; + imageViewCI.subresourceRange.layerCount = 1; + VK_CHECK_RESULT(vkCreateImageView(device, &imageViewCI, nullptr, &multisampleTarget.color.view)); - // Depth target - imageCI.imageType = VK_IMAGE_TYPE_2D; - imageCI.format = depthFormat; - imageCI.extent.width = width; - imageCI.extent.height = height; - imageCI.extent.depth = 1; - imageCI.mipLevels = 1; - imageCI.arrayLayers = 1; - imageCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - imageCI.tiling = VK_IMAGE_TILING_OPTIMAL; - imageCI.samples = settings.sampleCount; - imageCI.usage = VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; - imageCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - VK_CHECK_RESULT(vkCreateImage(device, &imageCI, nullptr, &multisampleTarget.depth.image)); + // Depth target + imageCI.imageType = VK_IMAGE_TYPE_2D; + imageCI.format = depthFormat; + imageCI.extent.width = width; + imageCI.extent.height = height; + imageCI.extent.depth = 1; + imageCI.mipLevels = 1; + imageCI.arrayLayers = 1; + imageCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + imageCI.tiling = VK_IMAGE_TILING_OPTIMAL; + imageCI.samples = settings.sampleCount; + imageCI.usage = VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + imageCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + VK_CHECK_RESULT(vkCreateImage(device, &imageCI, nullptr, &multisampleTarget.depth.image)); - vkGetImageMemoryRequirements(device, multisampleTarget.depth.image, &memReqs); - memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - memAllocInfo.allocationSize = memReqs.size; - memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT, &lazyMemTypePresent); - if (!lazyMemTypePresent) { - memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - } - VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &multisampleTarget.depth.memory)); - vkBindImageMemory(device, multisampleTarget.depth.image, multisampleTarget.depth.memory, 0); + vkGetImageMemoryRequirements(device, multisampleTarget.depth.image, &memReqs); + memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + memAllocInfo.allocationSize = memReqs.size; + memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT, &lazyMemTypePresent); + if (!lazyMemTypePresent) + { + memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + } + VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &multisampleTarget.depth.memory)); + vkBindImageMemory(device, multisampleTarget.depth.image, multisampleTarget.depth.memory, 0); - // Create image view for the MSAA target - imageViewCI.image = multisampleTarget.depth.image; - imageViewCI.viewType = VK_IMAGE_VIEW_TYPE_2D; - imageViewCI.format = depthFormat; - imageViewCI.components.r = VK_COMPONENT_SWIZZLE_R; - imageViewCI.components.g = VK_COMPONENT_SWIZZLE_G; - imageViewCI.components.b = VK_COMPONENT_SWIZZLE_B; - imageViewCI.components.a = VK_COMPONENT_SWIZZLE_A; - imageViewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; - imageViewCI.subresourceRange.levelCount = 1; - imageViewCI.subresourceRange.layerCount = 1; - VK_CHECK_RESULT(vkCreateImageView(device, &imageViewCI, nullptr, &multisampleTarget.depth.view)); - } + // Create image view for the MSAA target + imageViewCI.image = multisampleTarget.depth.image; + imageViewCI.viewType = VK_IMAGE_VIEW_TYPE_2D; + imageViewCI.format = depthFormat; + imageViewCI.components.r = VK_COMPONENT_SWIZZLE_R; + imageViewCI.components.g = VK_COMPONENT_SWIZZLE_G; + imageViewCI.components.b = VK_COMPONENT_SWIZZLE_B; + imageViewCI.components.a = VK_COMPONENT_SWIZZLE_A; + imageViewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + imageViewCI.subresourceRange.levelCount = 1; + imageViewCI.subresourceRange.layerCount = 1; + VK_CHECK_RESULT(vkCreateImageView(device, &imageViewCI, nullptr, &multisampleTarget.depth.view)); + } + // Depth/Stencil attachment is the same for all frame buffers - // Depth/Stencil attachment is the same for all frame buffers + VkImageCreateInfo image = {}; + image.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + image.pNext = NULL; + image.imageType = VK_IMAGE_TYPE_2D; + image.format = depthFormat; + image.extent = {width, height, 1}; + image.mipLevels = 1; + image.arrayLayers = 1; + image.samples = VK_SAMPLE_COUNT_1_BIT; + image.tiling = VK_IMAGE_TILING_OPTIMAL; + image.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + image.flags = 0; - VkImageCreateInfo image = {}; - image.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - image.pNext = NULL; - image.imageType = VK_IMAGE_TYPE_2D; - image.format = depthFormat; - image.extent = { width, height, 1 }; - image.mipLevels = 1; - image.arrayLayers = 1; - image.samples = VK_SAMPLE_COUNT_1_BIT; - image.tiling = VK_IMAGE_TILING_OPTIMAL; - image.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; - image.flags = 0; + VkMemoryAllocateInfo mem_alloc = {}; + mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + mem_alloc.pNext = NULL; + mem_alloc.allocationSize = 0; + mem_alloc.memoryTypeIndex = 0; - VkMemoryAllocateInfo mem_alloc = {}; - mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - mem_alloc.pNext = NULL; - mem_alloc.allocationSize = 0; - mem_alloc.memoryTypeIndex = 0; + VkImageViewCreateInfo depthStencilView = {}; + depthStencilView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + depthStencilView.pNext = NULL; + depthStencilView.viewType = VK_IMAGE_VIEW_TYPE_2D; + depthStencilView.format = depthFormat; + depthStencilView.flags = 0; + depthStencilView.subresourceRange = {}; + depthStencilView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + depthStencilView.subresourceRange.baseMipLevel = 0; + depthStencilView.subresourceRange.levelCount = 1; + depthStencilView.subresourceRange.baseArrayLayer = 0; + depthStencilView.subresourceRange.layerCount = 1; - VkImageViewCreateInfo depthStencilView = {}; - depthStencilView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - depthStencilView.pNext = NULL; - depthStencilView.viewType = VK_IMAGE_VIEW_TYPE_2D; - depthStencilView.format = depthFormat; - depthStencilView.flags = 0; - depthStencilView.subresourceRange = {}; - depthStencilView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; - depthStencilView.subresourceRange.baseMipLevel = 0; - depthStencilView.subresourceRange.levelCount = 1; - depthStencilView.subresourceRange.baseArrayLayer = 0; - depthStencilView.subresourceRange.layerCount = 1; + VkMemoryRequirements memReqs; + VK_CHECK_RESULT(vkCreateImage(device, &image, nullptr, &depthStencil.image)); + vkGetImageMemoryRequirements(device, depthStencil.image, &memReqs); + mem_alloc.allocationSize = memReqs.size; + mem_alloc.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + VK_CHECK_RESULT(vkAllocateMemory(device, &mem_alloc, nullptr, &depthStencil.mem)); + VK_CHECK_RESULT(vkBindImageMemory(device, depthStencil.image, depthStencil.mem, 0)); - VkMemoryRequirements memReqs; - VK_CHECK_RESULT(vkCreateImage(device, &image, nullptr, &depthStencil.image)); - vkGetImageMemoryRequirements(device, depthStencil.image, &memReqs); - mem_alloc.allocationSize = memReqs.size; - mem_alloc.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - VK_CHECK_RESULT(vkAllocateMemory(device, &mem_alloc, nullptr, &depthStencil.mem)); - VK_CHECK_RESULT(vkBindImageMemory(device, depthStencil.image, depthStencil.mem, 0)); + depthStencilView.image = depthStencil.image; + VK_CHECK_RESULT(vkCreateImageView(device, &depthStencilView, nullptr, &depthStencil.view)); - depthStencilView.image = depthStencil.image; - VK_CHECK_RESULT(vkCreateImageView(device, &depthStencilView, nullptr, &depthStencil.view)); + // - // + VkImageView attachments[4]; - VkImageView attachments[4]; + if (settings.multiSampling) + { + attachments[0] = multisampleTarget.color.view; + attachments[2] = multisampleTarget.depth.view; + attachments[3] = depthStencil.view; + } + else + { + attachments[1] = depthStencil.view; + } - if (settings.multiSampling) { - attachments[0] = multisampleTarget.color.view; - attachments[2] = multisampleTarget.depth.view; - attachments[3] = depthStencil.view; - } - else { - attachments[1] = depthStencil.view; - } + VkFramebufferCreateInfo frameBufferCI{}; + frameBufferCI.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + frameBufferCI.pNext = NULL; + frameBufferCI.renderPass = renderPass; + frameBufferCI.attachmentCount = settings.multiSampling ? 4 : 2; + frameBufferCI.pAttachments = attachments; + frameBufferCI.width = width; + frameBufferCI.height = height; + frameBufferCI.layers = 1; - VkFramebufferCreateInfo frameBufferCI{}; - frameBufferCI.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; - frameBufferCI.pNext = NULL; - frameBufferCI.renderPass = renderPass; - frameBufferCI.attachmentCount = settings.multiSampling ? 4 :2; - frameBufferCI.pAttachments = attachments; - frameBufferCI.width = width; - frameBufferCI.height = height; - frameBufferCI.layers = 1; - - // Create frame buffers for every swap chain image - frameBuffers.resize(swapChain.imageCount); - for (uint32_t i = 0; i < frameBuffers.size(); i++) { - if (settings.multiSampling) { - attachments[1] = swapChain.buffers[i].view; - } - else { - attachments[0] = swapChain.buffers[i].view; - } - VK_CHECK_RESULT(vkCreateFramebuffer(device, &frameBufferCI, nullptr, &frameBuffers[i])); - } + // Create frame buffers for every swap chain image + frameBuffers.resize(swapChain.imageCount); + for (uint32_t i = 0; i < frameBuffers.size(); i++) + { + if (settings.multiSampling) + { + attachments[1] = swapChain.buffers[i].view; + } + else + { + attachments[0] = swapChain.buffers[i].view; + } + VK_CHECK_RESULT(vkCreateFramebuffer(device, &frameBufferCI, nullptr, &frameBuffers[i])); + } } void VulkanExampleBase::windowResize() { - if (!prepared) { - return; - } - prepared = false; + if (!prepared) + { + return; + } + prepared = false; - vkDeviceWaitIdle(device); - width = destWidth; - height = destHeight; - setupSwapChain(); - if (settings.multiSampling) { - vkDestroyImageView(device, multisampleTarget.color.view, nullptr); - vkDestroyImage(device, multisampleTarget.color.image, nullptr); - vkFreeMemory(device, multisampleTarget.color.memory, nullptr); - vkDestroyImageView(device, multisampleTarget.depth.view, nullptr); - vkDestroyImage(device, multisampleTarget.depth.image, nullptr); - vkFreeMemory(device, multisampleTarget.depth.memory, nullptr); - } - vkDestroyImageView(device, depthStencil.view, nullptr); - vkDestroyImage(device, depthStencil.image, nullptr); - vkFreeMemory(device, depthStencil.mem, nullptr); - for (uint32_t i = 0; i < frameBuffers.size(); i++) { - vkDestroyFramebuffer(device, frameBuffers[i], nullptr); - } - setupFrameBuffer(); - vkDeviceWaitIdle(device); + vkDeviceWaitIdle(device); + width = destWidth; + height = destHeight; + setupSwapChain(); + if (settings.multiSampling) + { + vkDestroyImageView(device, multisampleTarget.color.view, nullptr); + vkDestroyImage(device, multisampleTarget.color.image, nullptr); + vkFreeMemory(device, multisampleTarget.color.memory, nullptr); + vkDestroyImageView(device, multisampleTarget.depth.view, nullptr); + vkDestroyImage(device, multisampleTarget.depth.image, nullptr); + vkFreeMemory(device, multisampleTarget.depth.memory, nullptr); + } + vkDestroyImageView(device, depthStencil.view, nullptr); + vkDestroyImage(device, depthStencil.image, nullptr); + vkFreeMemory(device, depthStencil.mem, nullptr); + for (uint32_t i = 0; i < frameBuffers.size(); i++) + { + vkDestroyFramebuffer(device, frameBuffers[i], nullptr); + } + setupFrameBuffer(); + vkDeviceWaitIdle(device); - camera.updateAspectRatio((float)width / (float)height); - windowResized(); + camera.updateAspectRatio((float)width / (float)height); + windowResized(); - prepared = true; + prepared = true; } void VulkanExampleBase::handleMouseMove(int32_t x, int32_t y) { - int32_t dx = (int32_t)mousePos.x - x; - int32_t dy = (int32_t)mousePos.y - y; - - ImGuiIO& io = ImGui::GetIO(); - bool handled = io.WantCaptureMouse; + int32_t dx = (int32_t)mousePos.x - x; + int32_t dy = (int32_t)mousePos.y - y; - if (handled) { - mousePos = glm::vec2((float)x, (float)y); - return; - } + ImGuiIO &io = ImGui::GetIO(); + bool handled = io.WantCaptureMouse; - if (handled) { - mousePos = glm::vec2((float)x, (float)y); - return; - } + if (handled) + { + mousePos = glm::vec2((float)x, (float)y); + return; + } - if (mouseButtons.left) { - camera.rotate(glm::vec3(dy * camera.rotationSpeed, -dx * camera.rotationSpeed, 0.0f)); - } - if (mouseButtons.right) { - camera.translate(glm::vec3(-0.0f, 0.0f, dy * .005f * camera.movementSpeed)); - } - if (mouseButtons.middle) { - camera.translate(glm::vec3(-dx * 0.01f, dy * 0.01f, 0.0f)); - } - mousePos = glm::vec2((float)x, (float)y); + if (handled) + { + mousePos = glm::vec2((float)x, (float)y); + return; + } + + if (mouseButtons.left) + { + camera.rotate(glm::vec3(dy * camera.rotationSpeed, -dx * camera.rotationSpeed, 0.0f)); + } + if (mouseButtons.right) + { + camera.translate(glm::vec3(-0.0f, 0.0f, dy * .005f * camera.movementSpeed)); + } + if (mouseButtons.middle) + { + camera.translate(glm::vec3(-dx * 0.01f, dy * 0.01f, 0.0f)); + } + mousePos = glm::vec2((float)x, (float)y); } void VulkanExampleBase::initSwapchain() { #if defined(_WIN32) - swapChain.initSurface(windowInstance, window); -#elif defined(VK_USE_PLATFORM_ANDROID_KHR) - swapChain.initSurface(androidApp->window); + swapChain.initSurface(windowInstance, window); +#elif defined(VK_USE_PLATFORM_ANDROID_KHR) + swapChain.initSurface(androidApp->window); #elif defined(_DIRECT2DISPLAY) - swapChain.initSurface(width, height); + swapChain.initSurface(width, height); #elif defined(VK_USE_PLATFORM_WAYLAND_KHR) - swapChain.initSurface(display, surface); + swapChain.initSurface(display, surface); #elif defined(VK_USE_PLATFORM_XCB_KHR) - swapChain.initSurface(connection, window); + swapChain.initSurface(connection, window); #elif defined(VK_USE_PLATFORM_MACOS_MVK) - swapChain.initSurface((__bridge void*)[window contentView]); + swapChain.initSurface((__bridge void *)[window contentView]); #endif } void VulkanExampleBase::setupSwapChain() { - swapChain.create(&width, &height, settings.vsync); + swapChain.create(&width, &height, settings.vsync); } diff --git a/src/base/vulkanexamplebase.h b/src/base/vulkanexamplebase.h index bca0e0e..6227c87 100644 --- a/src/base/vulkanexamplebase.h +++ b/src/base/vulkanexamplebase.h @@ -1,24 +1,26 @@ /* -* Vulkan Example base class -* -* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de -* -* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) -*/ + * Vulkan Example base class + * + * Copyright (C) 2016 by Sascha Willems - www.saschawillems.de + * + * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + */ #pragma once #ifdef _WIN32 #pragma comment(linker, "/subsystem:windows") -#include #include #include +#include + #elif defined(VK_USE_PLATFORM_ANDROID_KHR) -#include +#include "VulkanAndroid.h" #include +#include #include #include -#include "VulkanAndroid.h" + #elif defined(VK_USE_PLATFORM_WAYLAND_KHR) #include #elif defined(_DIRECT2DISPLAY) @@ -26,24 +28,25 @@ #elif defined(VK_USE_PLATFORM_XCB_KHR) #include #elif defined(VK_USE_PLATFORM_MACOS_MVK) -#include #include -#include +#include #include +#include + #endif -#include #include +#include #include #define GLM_FORCE_RADIANS #define GLM_FORCE_DEPTH_ZERO_TO_ONE #define GLM_ENABLE_EXPERIMENTAL -#include -#include -#include #include +#include #include +#include +#include #include "vulkan/vulkan.h" @@ -51,218 +54,228 @@ #include "camera.hpp" #include "keycodes.hpp" -#include "VulkanDevice.hpp" +#include "VulkanDevice.h" #include "VulkanSwapChain.hpp" #include "imgui.h" class VulkanExampleBase { -private: - float fpsTimer = 0.0f; - uint32_t frameCounter = 0; - uint32_t destWidth; - uint32_t destHeight; - bool resizing = false; - void handleMouseMove(int32_t x, int32_t y); - PFN_vkCreateDebugReportCallbackEXT vkCreateDebugReportCallback; - PFN_vkDestroyDebugReportCallbackEXT vkDestroyDebugReportCallback; - VkDebugReportCallbackEXT debugReportCallback; - struct MultisampleTarget { - struct { - VkImage image; - VkImageView view; - VkDeviceMemory memory; - } color; - struct { - VkImage image; - VkImageView view; - VkDeviceMemory memory; - } depth; - } multisampleTarget; +private: + float fpsTimer = 0.0f; + uint32_t frameCounter = 0; + uint32_t destWidth; + uint32_t destHeight; + bool resizing = false; + void handleMouseMove(int32_t x, int32_t y); + PFN_vkCreateDebugReportCallbackEXT vkCreateDebugReportCallback; + PFN_vkDestroyDebugReportCallbackEXT vkDestroyDebugReportCallback; + VkDebugReportCallbackEXT debugReportCallback; + struct MultisampleTarget + { + struct + { + VkImage image; + VkImageView view; + VkDeviceMemory memory; + } color; + struct + { + VkImage image; + VkImageView view; + VkDeviceMemory memory; + } depth; + } multisampleTarget; + protected: - VkInstance instance; - VkPhysicalDevice physicalDevice; - VkPhysicalDeviceProperties deviceProperties; - VkPhysicalDeviceFeatures deviceFeatures; - VkPhysicalDeviceMemoryProperties deviceMemoryProperties; - VkDevice device; - vks::VulkanDevice *vulkanDevice; - VkQueue queue; - VkFormat depthFormat; - VkCommandPool cmdPool; - VkRenderPass renderPass; - std::vectorframeBuffers; - uint32_t currentBuffer = 0; - VkDescriptorPool descriptorPool; - VkPipelineCache pipelineCache; - VulkanSwapChain swapChain; - std::string title = "Vulkan Example"; - std::string name = "vulkanExample"; - void windowResize(); -public: - static std::vector args; - uint32_t selectedPhysicalDeviceIndex = 0; - bool prepared = false; - uint32_t width = 1280; - uint32_t height = 720; - float frameTimer = 1.0f; - Camera camera; - glm::vec2 mousePos; - bool paused = false; - uint32_t lastFPS = 0; + VkInstance instance; + VkPhysicalDevice physicalDevice; + VkPhysicalDeviceProperties deviceProperties; + VkPhysicalDeviceFeatures deviceFeatures; + VkPhysicalDeviceMemoryProperties deviceMemoryProperties; + VkDevice device; + VulkanBase::VulkanDevice *vulkanDevice; + VkQueue queue; + VkFormat depthFormat; + VkCommandPool cmdPool; + VkRenderPass renderPass; + std::vector frameBuffers; + uint32_t currentBuffer = 0; + VkDescriptorPool descriptorPool; + VkPipelineCache pipelineCache; + VulkanSwapChain swapChain; + std::string title = "Vulkan Example"; + std::string name = "vulkanExample"; + void windowResize(); - struct Settings { - bool validation = false; // 校验层开关 - bool fullscreen = false; // 全屏开关 - bool vsync = false; // 垂直同步开关 - bool multiSampling = true; // 多重采样 - bool rotateModel = false; // 模型自旋转(暂时失效) - bool headless = false; // 无头开关 - bool outputPNGimage = false; - bool enableSaveToImageSequeue = true; // 图片序列开关(暂时弃用) - uint32_t outputFrameCount = 100; // 图片序列结束帧 - bool takeScreenShot = false; // 截屏(暂时弃用) - uint32_t startFrameCount = 1; // 图片序列开始帧 +public: + static std::vector args; + uint32_t selectedPhysicalDeviceIndex = 0; + bool prepared = false; + uint32_t width = 1280; + uint32_t height = 720; + float frameTimer = 1.0f; + Camera camera; + glm::vec2 mousePos; + bool paused = false; + uint32_t lastFPS = 0; - uint32_t videoFrameRate = 25; + struct Settings + { + bool validation = false; // 校验层开关 + bool fullscreen = false; // 全屏开关 + bool vsync = false; // 垂直同步开关 + bool multiSampling = true; // 多重采样 + bool rotateModel = false; // 模型自旋转(暂时失效) + bool headless = false; // 无头开关 + bool outputPNGimage = false; + bool enableSaveToImageSequeue = true; // 图片序列开关(暂时弃用) + uint32_t outputFrameCount = 100; // 图片序列结束帧 + bool takeScreenShot = false; // 截屏(暂时弃用) + uint32_t startFrameCount = 1; // 图片序列开始帧 - VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_4_BIT; // 多重采样倍率 - } settings; - - struct DepthStencil { - VkImage image; - VkDeviceMemory mem; - VkImageView view; - } depthStencil; + uint32_t videoFrameRate = 25; - struct GamePadState { - glm::vec2 axisLeft = glm::vec2(0.0f); - glm::vec2 axisRight = glm::vec2(0.0f); - } gamePadState; + VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_4_BIT; // 多重采样倍率 + } settings; - struct MouseButtons { - bool left = false; - bool right = false; - bool middle = false; - } mouseButtons; + struct DepthStencil + { + VkImage image; + VkDeviceMemory mem; + VkImageView view; + } depthStencil; - // OS specific + struct GamePadState + { + glm::vec2 axisLeft = glm::vec2(0.0f); + glm::vec2 axisRight = glm::vec2(0.0f); + } gamePadState; + + struct MouseButtons + { + bool left = false; + bool right = false; + bool middle = false; + } mouseButtons; + + // OS specific #if defined(_WIN32) - HWND window; - HINSTANCE windowInstance; + HWND window; + HINSTANCE windowInstance; #elif defined(VK_USE_PLATFORM_ANDROID_KHR) - // true if application has focused, false if moved to background - bool focused = false; - std::string androidProduct; - struct TouchPoint { - int32_t id; - float x; - float y; - bool down = false; - }; - float pinchDist = 0.0f; - std::array touchPoints; + // true if application has focused, false if moved to background + bool focused = false; + std::string androidProduct; + struct TouchPoint + { + int32_t id; + float x; + float y; + bool down = false; + }; + float pinchDist = 0.0f; + std::array touchPoints; #elif defined(VK_USE_PLATFORM_WAYLAND_KHR) - wl_display *display = nullptr; - wl_registry *registry = nullptr; - wl_compositor *compositor = nullptr; - wl_shell *shell = nullptr; - wl_seat *seat = nullptr; - wl_pointer *pointer = nullptr; - wl_keyboard *keyboard = nullptr; - wl_surface *surface = nullptr; - wl_shell_surface *shell_surface = nullptr; - bool quit = false; + wl_display *display = nullptr; + wl_registry *registry = nullptr; + wl_compositor *compositor = nullptr; + wl_shell *shell = nullptr; + wl_seat *seat = nullptr; + wl_pointer *pointer = nullptr; + wl_keyboard *keyboard = nullptr; + wl_surface *surface = nullptr; + wl_shell_surface *shell_surface = nullptr; + bool quit = false; #elif defined(_DIRECT2DISPLAY) - bool quit = false; + bool quit = false; #elif defined(VK_USE_PLATFORM_XCB_KHR) - bool quit = false; - xcb_connection_t *connection; - xcb_screen_t *screen; - xcb_window_t window; - xcb_intern_atom_reply_t *atom_wm_delete_window; + bool quit = false; + xcb_connection_t *connection; + xcb_screen_t *screen; + xcb_window_t window; + xcb_intern_atom_reply_t *atom_wm_delete_window; #elif defined(VK_USE_PLATFORM_MACOS_MVK) - NSWindow* window; + NSWindow *window; #endif #if defined(_WIN32) - HWND setupWindow(HINSTANCE hinstance, WNDPROC wndproc); - void handleMessages(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + HWND setupWindow(HINSTANCE hinstance, WNDPROC wndproc); + void handleMessages(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); #elif defined(VK_USE_PLATFORM_ANDROID_KHR) - static int32_t handleAppInput(struct android_app* app, AInputEvent* event); - static void handleAppCommand(android_app* app, int32_t cmd); + static int32_t handleAppInput(struct android_app *app, AInputEvent *event); + static void handleAppCommand(android_app *app, int32_t cmd); #elif defined(VK_USE_PLATFORM_WAYLAND_KHR) - wl_shell_surface *setupWindow(); - void initWaylandConnection(); - static void registryGlobalCb(void *data, struct wl_registry *registry, - uint32_t name, const char *interface, uint32_t version); - void registryGlobal(struct wl_registry *registry, uint32_t name, - const char *interface, uint32_t version); - static void registryGlobalRemoveCb(void *data, struct wl_registry *registry, - uint32_t name); - static void seatCapabilitiesCb(void *data, wl_seat *seat, uint32_t caps); - void seatCapabilities(wl_seat *seat, uint32_t caps); - static void pointerEnterCb(void *data, struct wl_pointer *pointer, - uint32_t serial, struct wl_surface *surface, wl_fixed_t sx, - wl_fixed_t sy); - static void pointerLeaveCb(void *data, struct wl_pointer *pointer, - uint32_t serial, struct wl_surface *surface); - static void pointerMotionCb(void *data, struct wl_pointer *pointer, - uint32_t time, wl_fixed_t sx, wl_fixed_t sy); - void pointerMotion(struct wl_pointer *pointer, - uint32_t time, wl_fixed_t sx, wl_fixed_t sy); - static void pointerButtonCb(void *data, struct wl_pointer *wl_pointer, - uint32_t serial, uint32_t time, uint32_t button, uint32_t state); - void pointerButton(struct wl_pointer *wl_pointer, - uint32_t serial, uint32_t time, uint32_t button, uint32_t state); - static void pointerAxisCb(void *data, struct wl_pointer *wl_pointer, - uint32_t time, uint32_t axis, wl_fixed_t value); - void pointerAxis(struct wl_pointer *wl_pointer, - uint32_t time, uint32_t axis, wl_fixed_t value); - static void keyboardKeymapCb(void *data, struct wl_keyboard *keyboard, - uint32_t format, int fd, uint32_t size); - static void keyboardEnterCb(void *data, struct wl_keyboard *keyboard, - uint32_t serial, struct wl_surface *surface, struct wl_array *keys); - static void keyboardLeaveCb(void *data, struct wl_keyboard *keyboard, - uint32_t serial, struct wl_surface *surface); - static void keyboardKeyCb(void *data, struct wl_keyboard *keyboard, - uint32_t serial, uint32_t time, uint32_t key, uint32_t state); - void keyboardKey(struct wl_keyboard *keyboard, - uint32_t serial, uint32_t time, uint32_t key, uint32_t state); - static void keyboardModifiersCb(void *data, struct wl_keyboard *keyboard, - uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, - uint32_t mods_locked, uint32_t group); + wl_shell_surface *setupWindow(); + void initWaylandConnection(); + static void registryGlobalCb(void *data, struct wl_registry *registry, + uint32_t name, const char *interface, uint32_t version); + void registryGlobal(struct wl_registry *registry, uint32_t name, + const char *interface, uint32_t version); + static void registryGlobalRemoveCb(void *data, struct wl_registry *registry, + uint32_t name); + static void seatCapabilitiesCb(void *data, wl_seat *seat, uint32_t caps); + void seatCapabilities(wl_seat *seat, uint32_t caps); + static void pointerEnterCb(void *data, struct wl_pointer *pointer, + uint32_t serial, struct wl_surface *surface, wl_fixed_t sx, + wl_fixed_t sy); + static void pointerLeaveCb(void *data, struct wl_pointer *pointer, + uint32_t serial, struct wl_surface *surface); + static void pointerMotionCb(void *data, struct wl_pointer *pointer, + uint32_t time, wl_fixed_t sx, wl_fixed_t sy); + void pointerMotion(struct wl_pointer *pointer, + uint32_t time, wl_fixed_t sx, wl_fixed_t sy); + static void pointerButtonCb(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, uint32_t time, uint32_t button, uint32_t state); + void pointerButton(struct wl_pointer *wl_pointer, + uint32_t serial, uint32_t time, uint32_t button, uint32_t state); + static void pointerAxisCb(void *data, struct wl_pointer *wl_pointer, + uint32_t time, uint32_t axis, wl_fixed_t value); + void pointerAxis(struct wl_pointer *wl_pointer, + uint32_t time, uint32_t axis, wl_fixed_t value); + static void keyboardKeymapCb(void *data, struct wl_keyboard *keyboard, + uint32_t format, int fd, uint32_t size); + static void keyboardEnterCb(void *data, struct wl_keyboard *keyboard, + uint32_t serial, struct wl_surface *surface, struct wl_array *keys); + static void keyboardLeaveCb(void *data, struct wl_keyboard *keyboard, + uint32_t serial, struct wl_surface *surface); + static void keyboardKeyCb(void *data, struct wl_keyboard *keyboard, + uint32_t serial, uint32_t time, uint32_t key, uint32_t state); + void keyboardKey(struct wl_keyboard *keyboard, + uint32_t serial, uint32_t time, uint32_t key, uint32_t state); + static void keyboardModifiersCb(void *data, struct wl_keyboard *keyboard, + uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, + uint32_t mods_locked, uint32_t group); #elif defined(_DIRECT2DISPLAY) // #elif defined(VK_USE_PLATFORM_XCB_KHR) - xcb_window_t setupWindow(); - void initxcbConnection(); - void handleEvent(const xcb_generic_event_t *event); + xcb_window_t setupWindow(); + void initxcbConnection(); + void handleEvent(const xcb_generic_event_t *event); #elif defined(VK_USE_PLATFORM_MACOS_MVK) - NSWindow* setupWindow(); - void mouseDragged(float x, float y); - void windowWillResize(float x, float y); - void windowDidResize(); + NSWindow *setupWindow(); + void mouseDragged(float x, float y); + void windowWillResize(float x, float y); + void windowDidResize(); #endif - VulkanExampleBase(); - virtual ~VulkanExampleBase(); - - void initVulkan(); + VulkanExampleBase(); + virtual ~VulkanExampleBase(); - virtual VkResult createInstance(bool enableValidation); - virtual void render() = 0; - virtual void windowResized(); - virtual void setupFrameBuffer(); - virtual void prepare(); - virtual void fileDropped(std::string filename); + void initVulkan(); - void initSwapchain(); - void setupSwapChain(); + virtual VkResult createInstance(bool enableValidation); + virtual void render() = 0; + virtual void windowResized(); + virtual void setupFrameBuffer(); + virtual void prepare(); + virtual void fileDropped(std::string filename); - void renderLoop(); - void renderFrame(); + void initSwapchain(); + void setupSwapChain(); + + void renderLoop(); + void renderFrame(); }; diff --git a/src/gltf/glTFMainModel.cpp b/src/gltf/glTFMainModel.cpp index ac63b09..5d47b4a 100644 --- a/src/gltf/glTFMainModel.cpp +++ b/src/gltf/glTFMainModel.cpp @@ -1,6 +1,6 @@ #include "glTFMainModel.h" -#include #include +#include GLTFLOADER_NAMESPACE_BEGIN @@ -12,927 +12,1063 @@ glTFMainModel::~glTFMainModel() { } -void glTFMainModel::destroy(VkDevice device) +ModelBuffer &glTFMainModel::getModelVertex() { - if (m_vertices.buffer != VK_NULL_HANDLE) { - vkDestroyBuffer(device, m_vertices.buffer, nullptr); - vkFreeMemory(device, m_vertices.memory, nullptr); - m_vertices.buffer = VK_NULL_HANDLE; - } - if (m_indices.buffer != VK_NULL_HANDLE) { - vkDestroyBuffer(device, m_indices.buffer, nullptr); - vkFreeMemory(device, m_indices.memory, nullptr); - m_indices.buffer = VK_NULL_HANDLE; - } - for (glTFTexture texture : m_textures) { - texture.destroy(); - } - m_textures.resize(0); - - for (glTFNode* node : m_nodes) { - delete node; - } - m_nodes.resize(0); - - m_textureSamplers.resize(0); - m_materials.resize(0); - m_animations.resize(0); - m_linearNodes.resize(0); - m_extensions.resize(0); - - for (glTFSkin* skin : m_skins) { - delete skin; - } - - m_skins.resize(0); + return m_vertices; +} +ModelBuffer &glTFMainModel::getModelIndex() +{ + return m_indices; } - -void glTFMainModel::getNodeProperty(const tinygltf::Node& node, const tinygltf::Model& model, size_t& vertexCount, size_t& indexCount) +void glTFMainModel::destroy(VkDevice device) { - if (node.children.size() > 0) { - for (size_t i = 0; i < node.children.size(); i++) { - getNodeProperty(model.nodes[node.children[i]], model, vertexCount, indexCount); - } - } - if (node.mesh > -1) { - const tinygltf::Mesh mesh = model.meshes[node.mesh]; - for (size_t i = 0; i < mesh.primitives.size(); i++) { - auto primitive = mesh.primitives[i]; - vertexCount += model.accessors[primitive.attributes.find("POSITION")->second].count; - if (primitive.indices > -1) { - indexCount += model.accessors[primitive.indices].count; - } - } - } + if (m_vertices.buffer != VK_NULL_HANDLE) + { + vkDestroyBuffer(device, m_vertices.buffer, nullptr); + vkFreeMemory(device, m_vertices.memory, nullptr); + m_vertices.buffer = VK_NULL_HANDLE; + } + if (m_indices.buffer != VK_NULL_HANDLE) + { + vkDestroyBuffer(device, m_indices.buffer, nullptr); + vkFreeMemory(device, m_indices.memory, nullptr); + m_indices.buffer = VK_NULL_HANDLE; + } + for (glTFTexture texture : m_textures) + { + texture.destroy(); + } + m_textures.resize(0); + + for (glTFNode *node : m_nodes) + { + delete node; + } + m_nodes.resize(0); + + m_textureSamplers.resize(0); + m_materials.resize(0); + m_animations.resize(0); + m_linearNodes.resize(0); + m_extensions.resize(0); + + for (glTFSkin *skin : m_skins) + { + delete skin; + } + + m_skins.resize(0); +} + +void glTFMainModel::getNodeProperty(const tinygltf::Node &node, const tinygltf::Model &model, size_t &vertexCount, size_t &indexCount) +{ + if (node.children.size() > 0) + { + for (size_t i = 0; i < node.children.size(); i++) + { + getNodeProperty(model.nodes[node.children[i]], model, vertexCount, indexCount); + } + } + if (node.mesh > -1) + { + const tinygltf::Mesh mesh = model.meshes[node.mesh]; + for (size_t i = 0; i < mesh.primitives.size(); i++) + { + auto primitive = mesh.primitives[i]; + vertexCount += model.accessors[primitive.attributes.find("POSITION")->second].count; + if (primitive.indices > -1) + { + indexCount += model.accessors[primitive.indices].count; + } + } + } } void glTFMainModel::getSceneDimensions() { - // Calculate binary volume hierarchy for all nodes in the scene - for (glTFNode* node : m_linearNodes) { - calculateBoundingBox(node, nullptr); - } + // Calculate binary volume hierarchy for all nodes in the scene + for (glTFNode *node : m_linearNodes) + { + calculateBoundingBox(node, nullptr); + } - m_dimensions.min = glm::vec3(FLT_MAX); - m_dimensions.max = glm::vec3(-FLT_MAX); + m_dimensions.min = glm::vec3(FLT_MAX); + m_dimensions.max = glm::vec3(-FLT_MAX); - for (auto node : m_linearNodes) { - glTFBoundingBox bvh = node->getBvh(); - if (bvh.isValid()) { - m_dimensions.min = glm::min(m_dimensions.min, bvh.getMin()); - m_dimensions.max = glm::max(m_dimensions.max, bvh.getMax()); - } - } + for (auto node : m_linearNodes) + { + glTFBoundingBox bvh = node->getBvh(); + if (bvh.isValid()) + { + m_dimensions.min = glm::min(m_dimensions.min, bvh.getMin()); + m_dimensions.max = glm::max(m_dimensions.max, bvh.getMax()); + } + } - // Calculate scene aabb - m_aabb = glm::scale(glm::mat4(1.0f), glm::vec3(m_dimensions.max[0] - m_dimensions.min[0], m_dimensions.max[1] - m_dimensions.min[1], m_dimensions.max[2] - m_dimensions.min[2])); - m_aabb[3][0] = m_dimensions.min[0]; - m_aabb[3][1] = m_dimensions.min[1]; - m_aabb[3][2] = m_dimensions.min[2]; + // Calculate scene aabb + m_aabb = glm::scale(glm::mat4(1.0f), glm::vec3(m_dimensions.max[0] - m_dimensions.min[0], m_dimensions.max[1] - m_dimensions.min[1], m_dimensions.max[2] - m_dimensions.min[2])); + m_aabb[3][0] = m_dimensions.min[0]; + m_aabb[3][1] = m_dimensions.min[1]; + m_aabb[3][2] = m_dimensions.min[2]; } VkSamplerAddressMode glTFMainModel::getVkWrapMode(int32_t wrapMode) { - switch (wrapMode) { - case -1: - case 10497: - return VK_SAMPLER_ADDRESS_MODE_REPEAT; - case 33071: - return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; - case 33648: - return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT; - } + switch (wrapMode) + { + case -1: + case 10497: + return VK_SAMPLER_ADDRESS_MODE_REPEAT; + case 33071: + return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + case 33648: + return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT; + } - std::cerr << "Unknown wrap mode for getVkWrapMode: " << wrapMode << std::endl; - return VK_SAMPLER_ADDRESS_MODE_REPEAT; + std::cerr << "Unknown wrap mode for getVkWrapMode: " << wrapMode << std::endl; + return VK_SAMPLER_ADDRESS_MODE_REPEAT; } VkFilter glTFMainModel::getVkFilterMode(int32_t filterMode) { - switch (filterMode) { - case -1: - case 9728: - return VK_FILTER_NEAREST; - case 9729: - return VK_FILTER_LINEAR; - case 9984: - return VK_FILTER_NEAREST; - case 9985: - return VK_FILTER_NEAREST; - case 9986: - return VK_FILTER_LINEAR; - case 9987: - return VK_FILTER_LINEAR; - } + switch (filterMode) + { + case -1: + case 9728: + return VK_FILTER_NEAREST; + case 9729: + return VK_FILTER_LINEAR; + case 9984: + return VK_FILTER_NEAREST; + case 9985: + return VK_FILTER_NEAREST; + case 9986: + return VK_FILTER_LINEAR; + case 9987: + return VK_FILTER_LINEAR; + } - std::cerr << "Unknown filter mode for getVkFilterMode: " << filterMode << std::endl; - return VK_FILTER_NEAREST; + std::cerr << "Unknown filter mode for getVkFilterMode: " << filterMode << std::endl; + return VK_FILTER_NEAREST; } -void glTFMainModel::loadNode(glTFNode* parent, const tinygltf::Node& node, uint32_t nodeIndex, const tinygltf::Model& model, LoaderInfo& loaderInfo, float globalscale) +void glTFMainModel::loadNode(glTFNode *parent, const tinygltf::Node &node, uint32_t nodeIndex, const tinygltf::Model &model, LoaderInfo &loaderInfo, float globalscale) { - glTFNode* newNode = new glTFNode{}; - newNode->setIndex(nodeIndex); - newNode->setParent(parent); - newNode->setName(node.name); - newNode->setSkinIndex(node.skin); - newNode->setMatrix(glm::mat4(1.0f)); + glTFNode *newNode = new glTFNode{}; + newNode->setIndex(nodeIndex); + newNode->setParent(parent); + newNode->setName(node.name); + newNode->setSkinIndex(node.skin); + newNode->setMatrix(glm::mat4(1.0f)); - // Generate local node matrix - glm::vec3 translation = glm::vec3(0.0f); - if (node.translation.size() == 3) { - translation = glm::make_vec3(node.translation.data()); - newNode->setTranslation(translation); - } - glm::mat4 rotation = glm::mat4(1.0f); - if (node.rotation.size() == 4) { - glm::quat q = glm::make_quat(node.rotation.data()); - newNode->setRotation(glm::mat4(q)); - } - glm::vec3 scale = glm::vec3(1.0f); - if (node.scale.size() == 3) { - scale = glm::make_vec3(node.scale.data()); - newNode->setScale(scale); - } - if (node.matrix.size() == 16) { - newNode->setMatrix(glm::make_mat4x4(node.matrix.data())); - }; + // Generate local node matrix + glm::vec3 translation = glm::vec3(0.0f); + if (node.translation.size() == 3) + { + translation = glm::make_vec3(node.translation.data()); + newNode->setTranslation(translation); + } + glm::mat4 rotation = glm::mat4(1.0f); + if (node.rotation.size() == 4) + { + glm::quat q = glm::make_quat(node.rotation.data()); + newNode->setRotation(glm::mat4(q)); + } + glm::vec3 scale = glm::vec3(1.0f); + if (node.scale.size() == 3) + { + scale = glm::make_vec3(node.scale.data()); + newNode->setScale(scale); + } + if (node.matrix.size() == 16) + { + newNode->setMatrix(glm::make_mat4x4(node.matrix.data())); + }; - // Node with children - if (node.children.size() > 0) { - for (size_t i = 0; i < node.children.size(); i++) { - loadNode(newNode, model.nodes[node.children[i]], node.children[i], model, loaderInfo, globalscale); - } - } + // Node with children + if (node.children.size() > 0) + { + for (size_t i = 0; i < node.children.size(); i++) + { + loadNode(newNode, model.nodes[node.children[i]], node.children[i], model, loaderInfo, globalscale); + } + } - // Node contains mesh data - if (node.mesh > -1) { - const tinygltf::Mesh mesh = model.meshes[node.mesh]; - glTFMesh* newMesh = new glTFMesh(m_device, newNode->getMatrix()); - for (size_t j = 0; j < mesh.primitives.size(); j++) { - const tinygltf::Primitive& primitive = mesh.primitives[j]; - uint32_t vertexStart = static_cast(loaderInfo.vertexPos); - uint32_t indexStart = static_cast(loaderInfo.indexPos); - uint32_t indexCount = 0; - uint32_t vertexCount = 0; - glm::vec3 posMin{}; - glm::vec3 posMax{}; - bool hasSkin = false; - bool hasIndices = primitive.indices > -1; - // Vertices - { - const float* bufferPos = nullptr; - const float* bufferNormals = nullptr; - const float* bufferTexCoordSet0 = nullptr; - const float* bufferTexCoordSet1 = nullptr; - const float* bufferColorSet0 = nullptr; - const void* bufferJoints = nullptr; - const float* bufferWeights = nullptr; + // Node contains mesh data + if (node.mesh > -1) + { + const tinygltf::Mesh mesh = model.meshes[node.mesh]; + glTFMesh *newMesh = new glTFMesh(m_device, newNode->getMatrix()); + for (size_t j = 0; j < mesh.primitives.size(); j++) + { + const tinygltf::Primitive &primitive = mesh.primitives[j]; + uint32_t vertexStart = static_cast(loaderInfo.vertexPos); + uint32_t indexStart = static_cast(loaderInfo.indexPos); + uint32_t indexCount = 0; + uint32_t vertexCount = 0; + glm::vec3 posMin{}; + glm::vec3 posMax{}; + bool hasSkin = false; + bool hasIndices = primitive.indices > -1; + // Vertices + { + const float *bufferPos = nullptr; + const float *bufferNormals = nullptr; + const float *bufferTexCoordSet0 = nullptr; + const float *bufferTexCoordSet1 = nullptr; + const float *bufferColorSet0 = nullptr; + const void *bufferJoints = nullptr; + const float *bufferWeights = nullptr; - int posByteStride; - int normByteStride; - int uv0ByteStride; - int uv1ByteStride; - int color0ByteStride; - int jointByteStride; - int weightByteStride; + int posByteStride; + int normByteStride; + int uv0ByteStride; + int uv1ByteStride; + int color0ByteStride; + int jointByteStride; + int weightByteStride; - int jointComponentType; + int jointComponentType; - // Position attribute is required - assert(primitive.attributes.find("POSITION") != primitive.attributes.end()); + // Position attribute is required + assert(primitive.attributes.find("POSITION") != primitive.attributes.end()); - const tinygltf::Accessor& posAccessor = model.accessors[primitive.attributes.find("POSITION")->second]; - const tinygltf::BufferView& posView = model.bufferViews[posAccessor.bufferView]; - bufferPos = reinterpret_cast(&(model.buffers[posView.buffer].data[posAccessor.byteOffset + posView.byteOffset])); - posMin = glm::vec3(posAccessor.minValues[0], posAccessor.minValues[1], posAccessor.minValues[2]); - posMax = glm::vec3(posAccessor.maxValues[0], posAccessor.maxValues[1], posAccessor.maxValues[2]); - vertexCount = static_cast(posAccessor.count); - posByteStride = posAccessor.ByteStride(posView) ? (posAccessor.ByteStride(posView) / sizeof(float)) : tinygltf::GetNumComponentsInType(TINYGLTF_TYPE_VEC3); + const tinygltf::Accessor &posAccessor = model.accessors[primitive.attributes.find("POSITION")->second]; + const tinygltf::BufferView &posView = model.bufferViews[posAccessor.bufferView]; + bufferPos = reinterpret_cast(&(model.buffers[posView.buffer].data[posAccessor.byteOffset + posView.byteOffset])); + posMin = glm::vec3(posAccessor.minValues[0], posAccessor.minValues[1], posAccessor.minValues[2]); + posMax = glm::vec3(posAccessor.maxValues[0], posAccessor.maxValues[1], posAccessor.maxValues[2]); + vertexCount = static_cast(posAccessor.count); + posByteStride = posAccessor.ByteStride(posView) ? (posAccessor.ByteStride(posView) / sizeof(float)) : tinygltf::GetNumComponentsInType(TINYGLTF_TYPE_VEC3); - if (primitive.attributes.find("NORMAL") != primitive.attributes.end()) { - const tinygltf::Accessor& normAccessor = model.accessors[primitive.attributes.find("NORMAL")->second]; - const tinygltf::BufferView& normView = model.bufferViews[normAccessor.bufferView]; - bufferNormals = reinterpret_cast(&(model.buffers[normView.buffer].data[normAccessor.byteOffset + normView.byteOffset])); - normByteStride = normAccessor.ByteStride(normView) ? (normAccessor.ByteStride(normView) / sizeof(float)) : tinygltf::GetNumComponentsInType(TINYGLTF_TYPE_VEC3); - } + if (primitive.attributes.find("NORMAL") != primitive.attributes.end()) + { + const tinygltf::Accessor &normAccessor = model.accessors[primitive.attributes.find("NORMAL")->second]; + const tinygltf::BufferView &normView = model.bufferViews[normAccessor.bufferView]; + bufferNormals = reinterpret_cast(&(model.buffers[normView.buffer].data[normAccessor.byteOffset + normView.byteOffset])); + normByteStride = normAccessor.ByteStride(normView) ? (normAccessor.ByteStride(normView) / sizeof(float)) : tinygltf::GetNumComponentsInType(TINYGLTF_TYPE_VEC3); + } - // UVs - if (primitive.attributes.find("TEXCOORD_0") != primitive.attributes.end()) { - const tinygltf::Accessor& uvAccessor = model.accessors[primitive.attributes.find("TEXCOORD_0")->second]; - const tinygltf::BufferView& uvView = model.bufferViews[uvAccessor.bufferView]; - bufferTexCoordSet0 = reinterpret_cast(&(model.buffers[uvView.buffer].data[uvAccessor.byteOffset + uvView.byteOffset])); - uv0ByteStride = uvAccessor.ByteStride(uvView) ? (uvAccessor.ByteStride(uvView) / sizeof(float)) : tinygltf::GetNumComponentsInType(TINYGLTF_TYPE_VEC2); - } - if (primitive.attributes.find("TEXCOORD_1") != primitive.attributes.end()) { - const tinygltf::Accessor& uvAccessor = model.accessors[primitive.attributes.find("TEXCOORD_1")->second]; - const tinygltf::BufferView& uvView = model.bufferViews[uvAccessor.bufferView]; - bufferTexCoordSet1 = reinterpret_cast(&(model.buffers[uvView.buffer].data[uvAccessor.byteOffset + uvView.byteOffset])); - uv1ByteStride = uvAccessor.ByteStride(uvView) ? (uvAccessor.ByteStride(uvView) / sizeof(float)) : tinygltf::GetNumComponentsInType(TINYGLTF_TYPE_VEC2); - } + // UVs + if (primitive.attributes.find("TEXCOORD_0") != primitive.attributes.end()) + { + const tinygltf::Accessor &uvAccessor = model.accessors[primitive.attributes.find("TEXCOORD_0")->second]; + const tinygltf::BufferView &uvView = model.bufferViews[uvAccessor.bufferView]; + bufferTexCoordSet0 = reinterpret_cast(&(model.buffers[uvView.buffer].data[uvAccessor.byteOffset + uvView.byteOffset])); + uv0ByteStride = uvAccessor.ByteStride(uvView) ? (uvAccessor.ByteStride(uvView) / sizeof(float)) : tinygltf::GetNumComponentsInType(TINYGLTF_TYPE_VEC2); + } + if (primitive.attributes.find("TEXCOORD_1") != primitive.attributes.end()) + { + const tinygltf::Accessor &uvAccessor = model.accessors[primitive.attributes.find("TEXCOORD_1")->second]; + const tinygltf::BufferView &uvView = model.bufferViews[uvAccessor.bufferView]; + bufferTexCoordSet1 = reinterpret_cast(&(model.buffers[uvView.buffer].data[uvAccessor.byteOffset + uvView.byteOffset])); + uv1ByteStride = uvAccessor.ByteStride(uvView) ? (uvAccessor.ByteStride(uvView) / sizeof(float)) : tinygltf::GetNumComponentsInType(TINYGLTF_TYPE_VEC2); + } - // Vertex colors - if (primitive.attributes.find("COLOR_0") != primitive.attributes.end()) { - const tinygltf::Accessor& accessor = model.accessors[primitive.attributes.find("COLOR_0")->second]; - const tinygltf::BufferView& view = model.bufferViews[accessor.bufferView]; - bufferColorSet0 = reinterpret_cast(&(model.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset])); - color0ByteStride = accessor.ByteStride(view) ? (accessor.ByteStride(view) / sizeof(float)) : tinygltf::GetNumComponentsInType(TINYGLTF_TYPE_VEC3); - } + // Vertex colors + if (primitive.attributes.find("COLOR_0") != primitive.attributes.end()) + { + const tinygltf::Accessor &accessor = model.accessors[primitive.attributes.find("COLOR_0")->second]; + const tinygltf::BufferView &view = model.bufferViews[accessor.bufferView]; + bufferColorSet0 = reinterpret_cast(&(model.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset])); + color0ByteStride = accessor.ByteStride(view) ? (accessor.ByteStride(view) / sizeof(float)) : tinygltf::GetNumComponentsInType(TINYGLTF_TYPE_VEC3); + } - // Skinning - // Joints - if (primitive.attributes.find("JOINTS_0") != primitive.attributes.end()) { - const tinygltf::Accessor& jointAccessor = model.accessors[primitive.attributes.find("JOINTS_0")->second]; - const tinygltf::BufferView& jointView = model.bufferViews[jointAccessor.bufferView]; - bufferJoints = &(model.buffers[jointView.buffer].data[jointAccessor.byteOffset + jointView.byteOffset]); - jointComponentType = jointAccessor.componentType; - jointByteStride = jointAccessor.ByteStride(jointView) ? (jointAccessor.ByteStride(jointView) / tinygltf::GetComponentSizeInBytes(jointComponentType)) : tinygltf::GetNumComponentsInType(TINYGLTF_TYPE_VEC4); - } + // Skinning + // Joints + if (primitive.attributes.find("JOINTS_0") != primitive.attributes.end()) + { + const tinygltf::Accessor &jointAccessor = model.accessors[primitive.attributes.find("JOINTS_0")->second]; + const tinygltf::BufferView &jointView = model.bufferViews[jointAccessor.bufferView]; + bufferJoints = &(model.buffers[jointView.buffer].data[jointAccessor.byteOffset + jointView.byteOffset]); + jointComponentType = jointAccessor.componentType; + jointByteStride = jointAccessor.ByteStride(jointView) ? (jointAccessor.ByteStride(jointView) / tinygltf::GetComponentSizeInBytes(jointComponentType)) : tinygltf::GetNumComponentsInType(TINYGLTF_TYPE_VEC4); + } - if (primitive.attributes.find("WEIGHTS_0") != primitive.attributes.end()) { - const tinygltf::Accessor& weightAccessor = model.accessors[primitive.attributes.find("WEIGHTS_0")->second]; - const tinygltf::BufferView& weightView = model.bufferViews[weightAccessor.bufferView]; - bufferWeights = reinterpret_cast(&(model.buffers[weightView.buffer].data[weightAccessor.byteOffset + weightView.byteOffset])); - weightByteStride = weightAccessor.ByteStride(weightView) ? (weightAccessor.ByteStride(weightView) / sizeof(float)) : tinygltf::GetNumComponentsInType(TINYGLTF_TYPE_VEC4); - } + if (primitive.attributes.find("WEIGHTS_0") != primitive.attributes.end()) + { + const tinygltf::Accessor &weightAccessor = model.accessors[primitive.attributes.find("WEIGHTS_0")->second]; + const tinygltf::BufferView &weightView = model.bufferViews[weightAccessor.bufferView]; + bufferWeights = reinterpret_cast(&(model.buffers[weightView.buffer].data[weightAccessor.byteOffset + weightView.byteOffset])); + weightByteStride = weightAccessor.ByteStride(weightView) ? (weightAccessor.ByteStride(weightView) / sizeof(float)) : tinygltf::GetNumComponentsInType(TINYGLTF_TYPE_VEC4); + } - hasSkin = (bufferJoints && bufferWeights); + hasSkin = (bufferJoints && bufferWeights); - for (size_t v = 0; v < posAccessor.count; v++) { - Vertex& vert = loaderInfo.vertexBuffer[loaderInfo.vertexPos]; - vert.pos = glm::vec4(glm::make_vec3(&bufferPos[v * posByteStride]), 1.0f); - vert.normal = glm::normalize(glm::vec3(bufferNormals ? glm::make_vec3(&bufferNormals[v * normByteStride]) : glm::vec3(0.0f))); - vert.uv0 = bufferTexCoordSet0 ? glm::make_vec2(&bufferTexCoordSet0[v * uv0ByteStride]) : glm::vec3(0.0f); - vert.uv1 = bufferTexCoordSet1 ? glm::make_vec2(&bufferTexCoordSet1[v * uv1ByteStride]) : glm::vec3(0.0f); - vert.color = bufferColorSet0 ? glm::make_vec4(&bufferColorSet0[v * color0ByteStride]) : glm::vec4(1.0f); + for (size_t v = 0; v < posAccessor.count; v++) + { + Vertex &vert = loaderInfo.vertexBuffer[loaderInfo.vertexPos]; + vert.pos = glm::vec4(glm::make_vec3(&bufferPos[v * posByteStride]), 1.0f); + vert.normal = glm::normalize(glm::vec3(bufferNormals ? glm::make_vec3(&bufferNormals[v * normByteStride]) : glm::vec3(0.0f))); + vert.uv0 = bufferTexCoordSet0 ? glm::make_vec2(&bufferTexCoordSet0[v * uv0ByteStride]) : glm::vec3(0.0f); + vert.uv1 = bufferTexCoordSet1 ? glm::make_vec2(&bufferTexCoordSet1[v * uv1ByteStride]) : glm::vec3(0.0f); + vert.color = bufferColorSet0 ? glm::make_vec4(&bufferColorSet0[v * color0ByteStride]) : glm::vec4(1.0f); - if (hasSkin) - { - switch (jointComponentType) { - case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: { - const uint16_t* buf = static_cast(bufferJoints); - vert.joint0 = glm::vec4(glm::make_vec4(&buf[v * jointByteStride])); - break; - } - case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE: { - const uint8_t* buf = static_cast(bufferJoints); - vert.joint0 = glm::vec4(glm::make_vec4(&buf[v * jointByteStride])); - break; - } - default: - // Not supported by spec - std::cerr << "Joint component type " << jointComponentType << " not supported!" << std::endl; - break; - } - } - else { - vert.joint0 = glm::vec4(0.0f); - } - vert.weight0 = hasSkin ? glm::make_vec4(&bufferWeights[v * weightByteStride]) : glm::vec4(0.0f); - // Fix for all zero weights - if (glm::length(vert.weight0) == 0.0f) { - vert.weight0 = glm::vec4(1.0f, 0.0f, 0.0f, 0.0f); - } - loaderInfo.vertexPos++; - } - } - // Indices - if (hasIndices) - { - const tinygltf::Accessor& accessor = model.accessors[primitive.indices > -1 ? primitive.indices : 0]; - const tinygltf::BufferView& bufferView = model.bufferViews[accessor.bufferView]; - const tinygltf::Buffer& buffer = model.buffers[bufferView.buffer]; + if (hasSkin) + { + switch (jointComponentType) + { + case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: + { + const uint16_t *buf = static_cast(bufferJoints); + vert.joint0 = glm::vec4(glm::make_vec4(&buf[v * jointByteStride])); + break; + } + case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE: + { + const uint8_t *buf = static_cast(bufferJoints); + vert.joint0 = glm::vec4(glm::make_vec4(&buf[v * jointByteStride])); + break; + } + default: + // Not supported by spec + std::cerr << "Joint component type " << jointComponentType << " not supported!" << std::endl; + break; + } + } + else + { + vert.joint0 = glm::vec4(0.0f); + } + vert.weight0 = hasSkin ? glm::make_vec4(&bufferWeights[v * weightByteStride]) : glm::vec4(0.0f); + // Fix for all zero weights + if (glm::length(vert.weight0) == 0.0f) + { + vert.weight0 = glm::vec4(1.0f, 0.0f, 0.0f, 0.0f); + } + loaderInfo.vertexPos++; + } + } + // Indices + if (hasIndices) + { + const tinygltf::Accessor &accessor = model.accessors[primitive.indices > -1 ? primitive.indices : 0]; + const tinygltf::BufferView &bufferView = model.bufferViews[accessor.bufferView]; + const tinygltf::Buffer &buffer = model.buffers[bufferView.buffer]; - indexCount = static_cast(accessor.count); - const void* dataPtr = &(buffer.data[accessor.byteOffset + bufferView.byteOffset]); + indexCount = static_cast(accessor.count); + const void *dataPtr = &(buffer.data[accessor.byteOffset + bufferView.byteOffset]); - switch (accessor.componentType) { - case TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT: { - const uint32_t* buf = static_cast(dataPtr); - for (size_t index = 0; index < accessor.count; index++) { - loaderInfo.indexBuffer[loaderInfo.indexPos] = buf[index] + vertexStart; - loaderInfo.indexPos++; - } - break; - } - case TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT: { - const uint16_t* buf = static_cast(dataPtr); - for (size_t index = 0; index < accessor.count; index++) { - loaderInfo.indexBuffer[loaderInfo.indexPos] = buf[index] + vertexStart; - loaderInfo.indexPos++; - } - break; - } - case TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE: { - const uint8_t* buf = static_cast(dataPtr); - for (size_t index = 0; index < accessor.count; index++) { - loaderInfo.indexBuffer[loaderInfo.indexPos] = buf[index] + vertexStart; - loaderInfo.indexPos++; - } - break; - } - default: - std::cerr << "Index component type " << accessor.componentType << " not supported!" << std::endl; - return; - } - } - glTFPrimitive* newPrimitive = new glTFPrimitive(indexStart, indexCount, vertexCount, primitive.material > -1 ? m_materials[primitive.material] : m_materials.back()); - newPrimitive->setBoundingBox(posMin, posMax); - newMesh->pushPrimitiveBack(newPrimitive); - } - // Mesh BB from BBs of primitives - for (auto p : newMesh->getPrimitives()) { - if (p->getBoundingBox().isValid() && !newMesh->getBoundingBox().isValid()) { - newMesh->setBoundingBox(p->getBoundingBox()); - newMesh->getBoundingBox().setValid(true); - } - newMesh->getBoundingBox().setMin(glm::min(newMesh->getBoundingBox().getMin(), p->getBoundingBox().getMin())); - newMesh->getBoundingBox().setMax(glm::max(newMesh->getBoundingBox().getMax(), p->getBoundingBox().getMax())) ; - } - newNode->setMesh(newMesh); - } - if (parent) { - parent->pushChildrenBack(newNode); - } - else { - m_nodes.push_back(newNode); - } - m_linearNodes.push_back(newNode); + switch (accessor.componentType) + { + case TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT: + { + const uint32_t *buf = static_cast(dataPtr); + for (size_t index = 0; index < accessor.count; index++) + { + loaderInfo.indexBuffer[loaderInfo.indexPos] = buf[index] + vertexStart; + loaderInfo.indexPos++; + } + break; + } + case TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT: + { + const uint16_t *buf = static_cast(dataPtr); + for (size_t index = 0; index < accessor.count; index++) + { + loaderInfo.indexBuffer[loaderInfo.indexPos] = buf[index] + vertexStart; + loaderInfo.indexPos++; + } + break; + } + case TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE: + { + const uint8_t *buf = static_cast(dataPtr); + for (size_t index = 0; index < accessor.count; index++) + { + loaderInfo.indexBuffer[loaderInfo.indexPos] = buf[index] + vertexStart; + loaderInfo.indexPos++; + } + break; + } + default: + std::cerr << "Index component type " << accessor.componentType << " not supported!" << std::endl; + return; + } + } + glTFPrimitive *newPrimitive = new glTFPrimitive(indexStart, indexCount, vertexCount, primitive.material > -1 ? m_materials[primitive.material] : m_materials.back()); + newPrimitive->setBoundingBox(posMin, posMax); + newMesh->pushPrimitiveBack(newPrimitive); + } + // Mesh BB from BBs of primitives + for (auto p : newMesh->getPrimitives()) + { + if (p->getBoundingBox().isValid() && !newMesh->getBoundingBox().isValid()) + { + newMesh->setBoundingBox(p->getBoundingBox()); + newMesh->getBoundingBox().setValid(true); + } + newMesh->getBoundingBox().setMin(glm::min(newMesh->getBoundingBox().getMin(), p->getBoundingBox().getMin())); + newMesh->getBoundingBox().setMax(glm::max(newMesh->getBoundingBox().getMax(), p->getBoundingBox().getMax())); + } + newNode->setMesh(newMesh); + } + if (parent) + { + parent->pushChildrenBack(newNode); + } + else + { + m_nodes.push_back(newNode); + } + m_linearNodes.push_back(newNode); } -void glTFMainModel::loadSkins(tinygltf::Model& gltfModel) +void glTFMainModel::loadSkins(tinygltf::Model &gltfModel) { - for (tinygltf::Skin& source : gltfModel.skins) { - glTFSkin* newSkin = new glTFSkin{}; - newSkin->setName(source.name); + for (tinygltf::Skin &source : gltfModel.skins) + { + glTFSkin *newSkin = new glTFSkin{}; + newSkin->setName(source.name); - // Find skeleton root node - if (source.skeleton > -1) { - newSkin->setSkeletonRoot(nodeFromIndex(source.skeleton)); - } + // Find skeleton root node + if (source.skeleton > -1) + { + newSkin->setSkeletonRoot(nodeFromIndex(source.skeleton)); + } - // Find joint nodes - for (int jointIndex : source.joints) { - glTFNode* node = nodeFromIndex(jointIndex); - if (node) { - newSkin->pushJointsBack(nodeFromIndex(jointIndex)); - } - } + // Find joint nodes + for (int jointIndex : source.joints) + { + glTFNode *node = nodeFromIndex(jointIndex); + if (node) + { + newSkin->pushJointsBack(nodeFromIndex(jointIndex)); + } + } - // Get inverse bind matrices from buffer - if (source.inverseBindMatrices > -1) { - const tinygltf::Accessor& accessor = gltfModel.accessors[source.inverseBindMatrices]; - const tinygltf::BufferView& bufferView = gltfModel.bufferViews[accessor.bufferView]; - const tinygltf::Buffer& buffer = gltfModel.buffers[bufferView.buffer]; - std::vector inverseMatrices = newSkin->getInverseBindMatrices(); - inverseMatrices.resize(accessor.count); + // Get inverse bind matrices from buffer + if (source.inverseBindMatrices > -1) + { + const tinygltf::Accessor &accessor = gltfModel.accessors[source.inverseBindMatrices]; + const tinygltf::BufferView &bufferView = gltfModel.bufferViews[accessor.bufferView]; + const tinygltf::Buffer &buffer = gltfModel.buffers[bufferView.buffer]; + std::vector inverseMatrices = newSkin->getInverseBindMatrices(); + inverseMatrices.resize(accessor.count); - newSkin->setInverseBindMatrices(inverseMatrices); - memcpy(newSkin->getInverseBindMatrices().data(), &buffer.data[accessor.byteOffset + bufferView.byteOffset], accessor.count * sizeof(glm::mat4)); - } + newSkin->setInverseBindMatrices(inverseMatrices); + memcpy(newSkin->getInverseBindMatrices().data(), &buffer.data[accessor.byteOffset + bufferView.byteOffset], accessor.count * sizeof(glm::mat4)); + } - m_skins.push_back(newSkin); - } + m_skins.push_back(newSkin); + } } -void glTFMainModel::loadTextures(tinygltf::Model& gltfModel, VulkanBase::VulkanDevice* device, VkQueue transferQueue) +void glTFMainModel::loadTextures(tinygltf::Model &gltfModel, VulkanBase::VulkanDevice *device, VkQueue transferQueue) { - for (tinygltf::Texture& tex : gltfModel.textures) { - tinygltf::Image image = gltfModel.images[tex.source]; - VulkanBase::VulkanTextureSampler textureSampler; - if (tex.sampler == -1) { - // No sampler specified, use a default one - textureSampler.setMaxFilter(VK_FILTER_LINEAR); - textureSampler.setMinFilter(VK_FILTER_LINEAR); - textureSampler.setAddressModeU(VK_SAMPLER_ADDRESS_MODE_REPEAT); - textureSampler.setAddressModeV(VK_SAMPLER_ADDRESS_MODE_REPEAT); - textureSampler.setAddressModeW(VK_SAMPLER_ADDRESS_MODE_REPEAT); - } - else { - textureSampler = m_textureSamplers[tex.sampler]; - } - glTFTexture texture; - texture.fromglTfImage(image, textureSampler, device, transferQueue); - m_textures.push_back(texture); - } + for (tinygltf::Texture &tex : gltfModel.textures) + { + tinygltf::Image image = gltfModel.images[tex.source]; + VulkanBase::VulkanTextureSampler textureSampler; + if (tex.sampler == -1) + { + // No sampler specified, use a default one + textureSampler.setMaxFilter(VK_FILTER_LINEAR); + textureSampler.setMinFilter(VK_FILTER_LINEAR); + textureSampler.setAddressModeU(VK_SAMPLER_ADDRESS_MODE_REPEAT); + textureSampler.setAddressModeV(VK_SAMPLER_ADDRESS_MODE_REPEAT); + textureSampler.setAddressModeW(VK_SAMPLER_ADDRESS_MODE_REPEAT); + } + else + { + textureSampler = m_textureSamplers[tex.sampler]; + } + glTFTexture texture; + texture.fromglTfImage(image, textureSampler, device, transferQueue); + m_textures.push_back(texture); + } } -void glTFMainModel::loadTextureSamplers(tinygltf::Model& gltfModel) +void glTFMainModel::loadTextureSamplers(tinygltf::Model &gltfModel) { - for (tinygltf::Sampler smpl : gltfModel.samplers) { - VulkanBase::VulkanTextureSampler sampler{}; - sampler.setMinFilter(getVkFilterMode(smpl.minFilter)); - sampler.setMaxFilter(getVkFilterMode(smpl.magFilter)); - sampler.setAddressModeU(getVkWrapMode(smpl.wrapS)); - sampler.setAddressModeV(getVkWrapMode(smpl.wrapT)); - sampler.setAddressModeW(sampler.getAddressModeV()); - m_textureSamplers.push_back(sampler); - } + for (tinygltf::Sampler smpl : gltfModel.samplers) + { + VulkanBase::VulkanTextureSampler sampler{}; + sampler.setMinFilter(getVkFilterMode(smpl.minFilter)); + sampler.setMaxFilter(getVkFilterMode(smpl.magFilter)); + sampler.setAddressModeU(getVkWrapMode(smpl.wrapS)); + sampler.setAddressModeV(getVkWrapMode(smpl.wrapT)); + sampler.setAddressModeW(sampler.getAddressModeV()); + m_textureSamplers.push_back(sampler); + } } -void glTFMainModel::loadMaterials(tinygltf::Model& gltfModel) +void glTFMainModel::loadMaterials(tinygltf::Model &gltfModel) { - for (tinygltf::Material& mat : gltfModel.materials) { - glTFMaterial material{}; - material.setDoublesided(mat.doubleSided); - if (mat.values.find("baseColorTexture") != mat.values.end()) { - material.getPbrBaseTexture()->setBaseColorTexture(&m_textures[mat.values["baseColorTexture"].TextureIndex()]); - material.getTextureCoordSet()->setBaseColor(mat.values["baseColorTexture"].TextureTexCoord()); - } - if (mat.values.find("metallicRoughnessTexture") != mat.values.end()) { - material.getPbrBaseTexture()->setMetallicRoughnessTexture(&m_textures[mat.values["metallicRoughnessTexture"].TextureIndex()]); - material.getTextureCoordSet()->setMetallicRoughness(mat.values["metallicRoughnessTexture"].TextureTexCoord()); - } - if (mat.values.find("roughnessFactor") != mat.values.end()) { - material.getPbrBaseTexture()->setRoughnessFactor(static_cast(mat.values["roughnessFactor"].Factor())); - } - if (mat.values.find("metallicFactor") != mat.values.end()) { - material.getPbrBaseTexture()->setMetallicFactor(static_cast(mat.values["metallicFactor"].Factor())); - } - if (mat.values.find("baseColorFactor") != mat.values.end()) { - material.getPbrBaseTexture()->setBaseColorFactor(glm::make_vec4(mat.values["baseColorFactor"].ColorFactor().data())); - } - if (mat.additionalValues.find("normalTexture") != mat.additionalValues.end()) { - material.getPbrBaseTexture()->setNormalTexture(&m_textures[mat.additionalValues["normalTexture"].TextureIndex()]); - material.getTextureCoordSet()->setNormal(mat.additionalValues["normalTexture"].TextureTexCoord()); - } - if (mat.additionalValues.find("emissiveTexture") != mat.additionalValues.end()) { - material.getPbrBaseTexture()->setEmissiveTexture(&m_textures[mat.additionalValues["emissiveTexture"].TextureIndex()]); - material.getTextureCoordSet()->setEmissive(mat.additionalValues["emissiveTexture"].TextureTexCoord()); - } - if (mat.additionalValues.find("occlusionTexture") != mat.additionalValues.end()) { - material.getPbrBaseTexture()->setOcclusionTexture(&m_textures[mat.additionalValues["occlusionTexture"].TextureIndex()]); - material.getTextureCoordSet()->setOcclusion(mat.additionalValues["occlusionTexture"].TextureTexCoord()); - } - if (mat.additionalValues.find("alphaMode") != mat.additionalValues.end()) { - tinygltf::Parameter param = mat.additionalValues["alphaMode"]; - if (param.string_value == "BLEND") { - material.setAlphaMode(AlphaMode::ALPHAMODE_BLEND); - } - if (param.string_value == "MASK") { - material.setAlphaCutOff(0.5f); - material.setAlphaMode(AlphaMode::ALPHAMODE_MASK); - } - } - if (mat.additionalValues.find("alphaCutoff") != mat.additionalValues.end()) { - material.setAlphaCutOff(static_cast(mat.additionalValues["alphaCutoff"].Factor())); - } - if (mat.additionalValues.find("emissiveFactor") != mat.additionalValues.end()) { - material.getPbrBaseTexture()->setEmissiveFactor(glm::vec4(glm::make_vec3(mat.additionalValues["emissiveFactor"].ColorFactor().data()), 1.0)); - } + for (tinygltf::Material &mat : gltfModel.materials) + { + glTFMaterial material{}; + material.setDoublesided(mat.doubleSided); + if (mat.values.find("baseColorTexture") != mat.values.end()) + { + material.getPbrBaseTexture()->setBaseColorTexture(&m_textures[mat.values["baseColorTexture"].TextureIndex()]); + material.getTextureCoordSet()->setBaseColor(mat.values["baseColorTexture"].TextureTexCoord()); + } + if (mat.values.find("metallicRoughnessTexture") != mat.values.end()) + { + material.getPbrBaseTexture()->setMetallicRoughnessTexture(&m_textures[mat.values["metallicRoughnessTexture"].TextureIndex()]); + material.getTextureCoordSet()->setMetallicRoughness(mat.values["metallicRoughnessTexture"].TextureTexCoord()); + } + if (mat.values.find("roughnessFactor") != mat.values.end()) + { + material.getPbrBaseTexture()->setRoughnessFactor(static_cast(mat.values["roughnessFactor"].Factor())); + } + if (mat.values.find("metallicFactor") != mat.values.end()) + { + material.getPbrBaseTexture()->setMetallicFactor(static_cast(mat.values["metallicFactor"].Factor())); + } + if (mat.values.find("baseColorFactor") != mat.values.end()) + { + material.getPbrBaseTexture()->setBaseColorFactor(glm::make_vec4(mat.values["baseColorFactor"].ColorFactor().data())); + } + if (mat.additionalValues.find("normalTexture") != mat.additionalValues.end()) + { + material.getPbrBaseTexture()->setNormalTexture(&m_textures[mat.additionalValues["normalTexture"].TextureIndex()]); + material.getTextureCoordSet()->setNormal(mat.additionalValues["normalTexture"].TextureTexCoord()); + } + if (mat.additionalValues.find("emissiveTexture") != mat.additionalValues.end()) + { + material.getPbrBaseTexture()->setEmissiveTexture(&m_textures[mat.additionalValues["emissiveTexture"].TextureIndex()]); + material.getTextureCoordSet()->setEmissive(mat.additionalValues["emissiveTexture"].TextureTexCoord()); + } + if (mat.additionalValues.find("occlusionTexture") != mat.additionalValues.end()) + { + material.getPbrBaseTexture()->setOcclusionTexture(&m_textures[mat.additionalValues["occlusionTexture"].TextureIndex()]); + material.getTextureCoordSet()->setOcclusion(mat.additionalValues["occlusionTexture"].TextureTexCoord()); + } + if (mat.additionalValues.find("alphaMode") != mat.additionalValues.end()) + { + tinygltf::Parameter param = mat.additionalValues["alphaMode"]; + if (param.string_value == "BLEND") + { + material.setAlphaMode(AlphaMode::ALPHAMODE_BLEND); + } + if (param.string_value == "MASK") + { + material.setAlphaCutOff(0.5f); + material.setAlphaMode(AlphaMode::ALPHAMODE_MASK); + } + } + if (mat.additionalValues.find("alphaCutoff") != mat.additionalValues.end()) + { + material.setAlphaCutOff(static_cast(mat.additionalValues["alphaCutoff"].Factor())); + } + if (mat.additionalValues.find("emissiveFactor") != mat.additionalValues.end()) + { + material.getPbrBaseTexture()->setEmissiveFactor(glm::vec4(glm::make_vec3(mat.additionalValues["emissiveFactor"].ColorFactor().data()), 1.0)); + } - // Extensions + // Extensions - if (mat.extensions.find("KHR_materials_pbrSpecularGlossiness") != mat.extensions.end()) { - auto ext = mat.extensions.find("KHR_materials_pbrSpecularGlossiness"); - if (ext->second.Has("specularGlossinessTexture")) { - auto index = ext->second.Get("specularGlossinessTexture").Get("index"); - material.getTextureExtension()->setSpecularGlossinessTexture(&m_textures[index.Get()]); - auto texCoordSet = ext->second.Get("specularGlossinessTexture").Get("texCoord"); - material.getTextureCoordSet()->setSpecularGlossiness(texCoordSet.Get()); - material.getPbrWorkFlow()->setSpecularGlossiness(true); - } - if (ext->second.Has("diffuseTexture")) { - auto index = ext->second.Get("diffuseTexture").Get("index"); - material.getTextureExtension()->setDiffuseTexture(&m_textures[index.Get()]); - } - if (ext->second.Has("diffuseFactor")) { - auto factor = ext->second.Get("diffuseFactor"); - glm::vec4 diffuseFactor(0.0); - for (uint32_t i = 0; i < factor.ArrayLen(); i++) { - auto val = factor.Get(i); - diffuseFactor[i] = val.IsNumber() ? (float)val.Get() : (float)val.Get(); - } - material.getTextureExtension()->setDiffuseFactor(diffuseFactor); - } - if (ext->second.Has("specularFactor")) { - auto factor = ext->second.Get("specularFactor"); - glm::vec3 specularFactor(0.0); - for (uint32_t i = 0; i < factor.ArrayLen(); i++) { - auto val = factor.Get(i); - specularFactor[i] = val.IsNumber() ? (float)val.Get() : (float)val.Get(); - } - material.getTextureExtension()->setSpecularFactor(specularFactor); - } - } + if (mat.extensions.find("KHR_materials_pbrSpecularGlossiness") != mat.extensions.end()) + { + auto ext = mat.extensions.find("KHR_materials_pbrSpecularGlossiness"); + if (ext->second.Has("specularGlossinessTexture")) + { + auto index = ext->second.Get("specularGlossinessTexture").Get("index"); + material.getTextureExtension()->setSpecularGlossinessTexture(&m_textures[index.Get()]); + auto texCoordSet = ext->second.Get("specularGlossinessTexture").Get("texCoord"); + material.getTextureCoordSet()->setSpecularGlossiness(texCoordSet.Get()); + material.getPbrWorkFlow()->setSpecularGlossiness(true); + } + if (ext->second.Has("diffuseTexture")) + { + auto index = ext->second.Get("diffuseTexture").Get("index"); + material.getTextureExtension()->setDiffuseTexture(&m_textures[index.Get()]); + } + if (ext->second.Has("diffuseFactor")) + { + auto factor = ext->second.Get("diffuseFactor"); + glm::vec4 diffuseFactor(0.0); + for (uint32_t i = 0; i < factor.ArrayLen(); i++) + { + auto val = factor.Get(i); + diffuseFactor[i] = val.IsNumber() ? (float)val.Get() : (float)val.Get(); + } + material.getTextureExtension()->setDiffuseFactor(diffuseFactor); + } + if (ext->second.Has("specularFactor")) + { + auto factor = ext->second.Get("specularFactor"); + glm::vec3 specularFactor(0.0); + for (uint32_t i = 0; i < factor.ArrayLen(); i++) + { + auto val = factor.Get(i); + specularFactor[i] = val.IsNumber() ? (float)val.Get() : (float)val.Get(); + } + material.getTextureExtension()->setSpecularFactor(specularFactor); + } + } - m_materials.push_back(material); - } - // Push a default material at the end of the list for meshes with no material assigned - m_materials.push_back(glTFMaterial()); + m_materials.push_back(material); + } + // Push a default material at the end of the list for meshes with no material assigned + m_materials.push_back(glTFMaterial()); } -void glTFMainModel::loadAnimations(tinygltf::Model& gltfModel) +void glTFMainModel::loadAnimations(tinygltf::Model &gltfModel) { - for (tinygltf::Animation& anim : gltfModel.animations) { - glTFAnimation animation{}; - animation.setName(anim.name); - if (anim.name.empty()) { - animation.setName(std::to_string(m_animations.size())); - } + for (tinygltf::Animation &anim : gltfModel.animations) + { + glTFAnimation animation{}; + animation.setName(anim.name); + if (anim.name.empty()) + { + animation.setName(std::to_string(m_animations.size())); + } - // Samplers - for (auto& samp : anim.samplers) { - glTFAnimationSampler sampler{}; + // Samplers + for (auto &samp : anim.samplers) + { + glTFAnimationSampler sampler{}; - if (samp.interpolation == "LINEAR") { - sampler.setAnimationInterpolationType(AnimationInterpolationType::LINEAR); - } - if (samp.interpolation == "STEP") { - sampler.setAnimationInterpolationType(AnimationInterpolationType::STEP); - } - if (samp.interpolation == "CUBICSPLINE") { - sampler.setAnimationInterpolationType(AnimationInterpolationType::CUBICSPLINE); - } + if (samp.interpolation == "LINEAR") + { + sampler.setAnimationInterpolationType(AnimationInterpolationType::LINEAR); + } + if (samp.interpolation == "STEP") + { + sampler.setAnimationInterpolationType(AnimationInterpolationType::STEP); + } + if (samp.interpolation == "CUBICSPLINE") + { + sampler.setAnimationInterpolationType(AnimationInterpolationType::CUBICSPLINE); + } - // Read sampler input time values - { - const tinygltf::Accessor& accessor = gltfModel.accessors[samp.input]; - const tinygltf::BufferView& bufferView = gltfModel.bufferViews[accessor.bufferView]; - const tinygltf::Buffer& buffer = gltfModel.buffers[bufferView.buffer]; + // Read sampler input time values + { + const tinygltf::Accessor &accessor = gltfModel.accessors[samp.input]; + const tinygltf::BufferView &bufferView = gltfModel.bufferViews[accessor.bufferView]; + const tinygltf::Buffer &buffer = gltfModel.buffers[bufferView.buffer]; - assert(accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT); + assert(accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT); - const void* dataPtr = &buffer.data[accessor.byteOffset + bufferView.byteOffset]; - const float* buf = static_cast(dataPtr); - for (size_t index = 0; index < accessor.count; index++) { + const void *dataPtr = &buffer.data[accessor.byteOffset + bufferView.byteOffset]; + const float *buf = static_cast(dataPtr); + for (size_t index = 0; index < accessor.count; index++) + { - sampler.pushInputBack(buf[index]); + sampler.pushInputBack(buf[index]); + } - } + for (auto input : sampler.getInputs()) + { + if (input < animation.getStart()) + { + animation.setStart(input); + }; + if (input > animation.getEnd()) + { + animation.setEnd(input); + } + } + } - for (auto input : sampler.getInputs()) { - if (input < animation.getStart()) { - animation.setStart(input); - }; - if (input > animation.getEnd()) { - animation.setEnd(input); - } - } - } + // Read sampler output T/R/S values + { + const tinygltf::Accessor &accessor = gltfModel.accessors[samp.output]; + const tinygltf::BufferView &bufferView = gltfModel.bufferViews[accessor.bufferView]; + const tinygltf::Buffer &buffer = gltfModel.buffers[bufferView.buffer]; - // Read sampler output T/R/S values - { - const tinygltf::Accessor& accessor = gltfModel.accessors[samp.output]; - const tinygltf::BufferView& bufferView = gltfModel.bufferViews[accessor.bufferView]; - const tinygltf::Buffer& buffer = gltfModel.buffers[bufferView.buffer]; + assert(accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT); - assert(accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT); + const void *dataPtr = &buffer.data[accessor.byteOffset + bufferView.byteOffset]; - const void* dataPtr = &buffer.data[accessor.byteOffset + bufferView.byteOffset]; + switch (accessor.type) + { + case TINYGLTF_TYPE_VEC3: + { + const glm::vec3 *buf = static_cast(dataPtr); + for (size_t index = 0; index < accessor.count; index++) + { - switch (accessor.type) { - case TINYGLTF_TYPE_VEC3: { - const glm::vec3* buf = static_cast(dataPtr); - for (size_t index = 0; index < accessor.count; index++) { + sampler.pushOutputsVec4Back(glm::vec4(buf[index], 0.0f)); + } + break; + } + case TINYGLTF_TYPE_VEC4: + { + const glm::vec4 *buf = static_cast(dataPtr); + for (size_t index = 0; index < accessor.count; index++) + { - sampler.pushOutputsVec4Back(glm::vec4(buf[index], 0.0f)); + sampler.pushOutputsVec4Back(buf[index]); + } + break; + } + default: + { + std::cout << "unknown type" << std::endl; + break; + } + } + } - } - break; - } - case TINYGLTF_TYPE_VEC4: { - const glm::vec4* buf = static_cast(dataPtr); - for (size_t index = 0; index < accessor.count; index++) { + animation.pushSamplersBack(sampler); + } - sampler.pushOutputsVec4Back(buf[index]); + // Channels + for (auto &source : anim.channels) + { + glTFAnimationChannel channel{}; - } - break; - } - default: { - std::cout << "unknown type" << std::endl; - break; - } - } - } + if (source.target_path == "rotation") + { + channel.setAnimationPathType(AnimationPathType::ROTATION); + } + if (source.target_path == "translation") + { + channel.setAnimationPathType(AnimationPathType::TRANSLATION); + } + if (source.target_path == "scale") + { + channel.setAnimationPathType(AnimationPathType::SCALE); + } + if (source.target_path == "weights") + { + std::cout << "weights not yet supported, skipping channel" << std::endl; + continue; + } + channel.setSamplerIndex(source.sampler); + channel.setNode(nodeFromIndex(source.target_node)); + if (!channel.getNode()) + { + continue; + } - animation.pushSamplersBack(sampler); + animation.pushChannelsBack(channel); + } - } - - // Channels - for (auto& source : anim.channels) { - glTFAnimationChannel channel{}; - - if (source.target_path == "rotation") { - channel.setAnimationPathType(AnimationPathType::ROTATION); - } - if (source.target_path == "translation") { - channel.setAnimationPathType(AnimationPathType::TRANSLATION); - } - if (source.target_path == "scale") { - channel.setAnimationPathType(AnimationPathType::SCALE); - } - if (source.target_path == "weights") { - std::cout << "weights not yet supported, skipping channel" << std::endl; - continue; - } - channel.setSamplerIndex(source.sampler); - channel.setNode(nodeFromIndex(source.target_node)); - if (!channel.getNode()) { - continue; - } - - animation.pushChannelsBack(channel); - } - - m_animations.push_back(animation); - } + m_animations.push_back(animation); + } } -void glTFMainModel::loadFromFile(std::string filename, VulkanBase::VulkanDevice* device, VkQueue transferQueue, float scale) +void glTFMainModel::loadFromFile(std::string filename, VulkanBase::VulkanDevice *device, VkQueue transferQueue, float scale) { - tinygltf::Model gltfModel; - tinygltf::TinyGLTF gltfContext; + tinygltf::Model gltfModel; + tinygltf::TinyGLTF gltfContext; - std::string error; - std::string warning; + std::string error; + std::string warning; - m_device = device; + m_device = device; - bool binary = false; - size_t extpos = filename.rfind('.', filename.length()); - if (extpos != std::string::npos) { - binary = (filename.substr(extpos + 1, filename.length() - extpos) == "glb"); - } + bool binary = false; + size_t extpos = filename.rfind('.', filename.length()); + if (extpos != std::string::npos) + { + binary = (filename.substr(extpos + 1, filename.length() - extpos) == "glb"); + } - bool fileLoaded = binary ? gltfContext.LoadBinaryFromFile(&gltfModel, &error, &warning, filename.c_str()) : gltfContext.LoadASCIIFromFile(&gltfModel, &error, &warning, filename.c_str()); + bool fileLoaded = binary ? gltfContext.LoadBinaryFromFile(&gltfModel, &error, &warning, filename.c_str()) : gltfContext.LoadASCIIFromFile(&gltfModel, &error, &warning, filename.c_str()); - LoaderInfo loaderInfo{}; - size_t vertexCount = 0; - size_t indexCount = 0; + LoaderInfo loaderInfo{}; + size_t vertexCount = 0; + size_t indexCount = 0; - if (fileLoaded) { - loadTextureSamplers(gltfModel); - loadTextures(gltfModel, device, transferQueue); - loadMaterials(gltfModel); + if (fileLoaded) + { + loadTextureSamplers(gltfModel); + loadTextures(gltfModel, device, transferQueue); + loadMaterials(gltfModel); - const tinygltf::Scene& scene = gltfModel.scenes[gltfModel.defaultScene > -1 ? gltfModel.defaultScene : 0]; + const tinygltf::Scene &scene = gltfModel.scenes[gltfModel.defaultScene > -1 ? gltfModel.defaultScene : 0]; - // Get vertex and index buffer sizes up-front - for (size_t i = 0; i < scene.nodes.size(); i++) { - getNodeProperty(gltfModel.nodes[scene.nodes[i]], gltfModel, vertexCount, indexCount); - } - loaderInfo.vertexBuffer = new Vertex[vertexCount]; - loaderInfo.indexBuffer = new uint32_t[indexCount]; + // Get vertex and index buffer sizes up-front + for (size_t i = 0; i < scene.nodes.size(); i++) + { + getNodeProperty(gltfModel.nodes[scene.nodes[i]], gltfModel, vertexCount, indexCount); + } + loaderInfo.vertexBuffer = new Vertex[vertexCount]; + loaderInfo.indexBuffer = new uint32_t[indexCount]; - // TODO: scene handling with no default scene - for (size_t i = 0; i < scene.nodes.size(); i++) { - const tinygltf::Node node = gltfModel.nodes[scene.nodes[i]]; - loadNode(nullptr, node, scene.nodes[i], gltfModel, loaderInfo, scale); - } - if (gltfModel.animations.size() > 0) { - loadAnimations(gltfModel); - } - loadSkins(gltfModel); + // TODO: scene handling with no default scene + for (size_t i = 0; i < scene.nodes.size(); i++) + { + const tinygltf::Node node = gltfModel.nodes[scene.nodes[i]]; + loadNode(nullptr, node, scene.nodes[i], gltfModel, loaderInfo, scale); + } + if (gltfModel.animations.size() > 0) + { + loadAnimations(gltfModel); + } + loadSkins(gltfModel); - for (auto node : m_linearNodes) { - // Assign skins - if (node->getSkinIndex() > -1) { - node->setSkin(m_skins[node->getSkinIndex()]); - } - // Initial pose - if (node->getMesh()) { - node->update(); - } - } - } - else { - // TODO: throw - std::cerr << "Could not load gltf file: " << error << std::endl; - return; - } + for (auto node : m_linearNodes) + { + // Assign skins + if (node->getSkinIndex() > -1) + { + node->setSkin(m_skins[node->getSkinIndex()]); + } + // Initial pose + if (node->getMesh()) + { + node->update(); + } + } + } + else + { + // TODO: throw + std::cerr << "Could not load gltf file: " << error << std::endl; + return; + } - m_extensions = gltfModel.extensionsUsed; + m_extensions = gltfModel.extensionsUsed; - size_t vertexBufferSize = vertexCount * sizeof(Vertex); - size_t indexBufferSize = indexCount * sizeof(uint32_t); + size_t vertexBufferSize = vertexCount * sizeof(Vertex); + size_t indexBufferSize = indexCount * sizeof(uint32_t); - assert(vertexBufferSize > 0); + assert(vertexBufferSize > 0); - struct StagingBuffer { - VkBuffer buffer; - VkDeviceMemory memory; - } vertexStaging, indexStaging; + struct StagingBuffer + { + VkBuffer buffer; + VkDeviceMemory memory; + } vertexStaging, indexStaging; - // Create staging buffers - // Vertex data - VK_CHECK_RESULT(device->createBuffer( - VK_BUFFER_USAGE_TRANSFER_SRC_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, - vertexBufferSize, - &vertexStaging.buffer, - &vertexStaging.memory, - loaderInfo.vertexBuffer)); - // Index data - if (indexBufferSize > 0) { - VK_CHECK_RESULT(device->createBuffer( - VK_BUFFER_USAGE_TRANSFER_SRC_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, - indexBufferSize, - &indexStaging.buffer, - &indexStaging.memory, - loaderInfo.indexBuffer)); - } + // Create staging buffers + // Vertex data + VK_CHECK_RESULT(device->createBuffer( + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + vertexBufferSize, + &vertexStaging.buffer, + &vertexStaging.memory, + loaderInfo.vertexBuffer)); + // Index data + if (indexBufferSize > 0) + { + VK_CHECK_RESULT(device->createBuffer( + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + indexBufferSize, + &indexStaging.buffer, + &indexStaging.memory, + loaderInfo.indexBuffer)); + } - // Create device local buffers - // Vertex buffer - VK_CHECK_RESULT(device->createBuffer( - VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, - vertexBufferSize, - &m_vertices.buffer, - &m_vertices.memory)); - // Index buffer - if (indexBufferSize > 0) { - VK_CHECK_RESULT(device->createBuffer( - VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, - indexBufferSize, - &m_indices.buffer, - &m_indices.memory)); - } + // Create device local buffers + // Vertex buffer + VK_CHECK_RESULT(device->createBuffer( + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + vertexBufferSize, + &m_vertices.buffer, + &m_vertices.memory)); + // Index buffer + if (indexBufferSize > 0) + { + VK_CHECK_RESULT(device->createBuffer( + VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + indexBufferSize, + &m_indices.buffer, + &m_indices.memory)); + } - // Copy from staging buffers - VkCommandBuffer copyCmd = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); + // Copy from staging buffers + VkCommandBuffer copyCmd = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); - VkBufferCopy copyRegion = {}; + VkBufferCopy copyRegion = {}; - copyRegion.size = vertexBufferSize; - vkCmdCopyBuffer(copyCmd, vertexStaging.buffer, m_vertices.buffer, 1, ©Region); + copyRegion.size = vertexBufferSize; + vkCmdCopyBuffer(copyCmd, vertexStaging.buffer, m_vertices.buffer, 1, ©Region); - if (indexBufferSize > 0) { - copyRegion.size = indexBufferSize; - vkCmdCopyBuffer(copyCmd, indexStaging.buffer, m_indices.buffer, 1, ©Region); - } + if (indexBufferSize > 0) + { + copyRegion.size = indexBufferSize; + vkCmdCopyBuffer(copyCmd, indexStaging.buffer, m_indices.buffer, 1, ©Region); + } - device->flushCommandBuffer(copyCmd, transferQueue, true); + device->flushCommandBuffer(copyCmd, transferQueue, true); - vkDestroyBuffer(device->getLogicalDevice(), vertexStaging.buffer, nullptr); - vkFreeMemory(device->getLogicalDevice(), vertexStaging.memory, nullptr); - if (indexBufferSize > 0) { - vkDestroyBuffer(device->getLogicalDevice(), indexStaging.buffer, nullptr); - vkFreeMemory(device->getLogicalDevice(), indexStaging.memory, nullptr); - } + vkDestroyBuffer(device->getLogicalDevice(), vertexStaging.buffer, nullptr); + vkFreeMemory(device->getLogicalDevice(), vertexStaging.memory, nullptr); + if (indexBufferSize > 0) + { + vkDestroyBuffer(device->getLogicalDevice(), indexStaging.buffer, nullptr); + vkFreeMemory(device->getLogicalDevice(), indexStaging.memory, nullptr); + } - delete[] loaderInfo.vertexBuffer; - delete[] loaderInfo.indexBuffer; + delete[] loaderInfo.vertexBuffer; + delete[] loaderInfo.indexBuffer; - getSceneDimensions(); + getSceneDimensions(); } -void glTFMainModel::drawNode(glTFNode* node, VkCommandBuffer commandBuffer) +void glTFMainModel::drawNode(glTFNode *node, VkCommandBuffer commandBuffer) { - if (node->getMesh()) { - for (glTFPrimitive* primitive : node->getMesh()->getPrimitives()) { - vkCmdDrawIndexed(commandBuffer, primitive->getIndexCount(), 1, primitive->getFirstIndex(), 0, 0); - } - } - for (auto& child : node->getChildren()) { - drawNode(child, commandBuffer); - } + if (node->getMesh()) + { + for (glTFPrimitive *primitive : node->getMesh()->getPrimitives()) + { + vkCmdDrawIndexed(commandBuffer, primitive->getIndexCount(), 1, primitive->getFirstIndex(), 0, 0); + } + } + for (auto &child : node->getChildren()) + { + drawNode(child, commandBuffer); + } } void glTFMainModel::draw(VkCommandBuffer commandBuffer) { - const VkDeviceSize offsets[1] = { 0 }; - vkCmdBindVertexBuffers(commandBuffer, 0, 1, &m_vertices.buffer, offsets); - vkCmdBindIndexBuffer(commandBuffer, m_indices.buffer, 0, VK_INDEX_TYPE_UINT32); - for (auto& node : m_nodes) { - drawNode(node, commandBuffer); - } + const VkDeviceSize offsets[1] = {0}; + vkCmdBindVertexBuffers(commandBuffer, 0, 1, &m_vertices.buffer, offsets); + vkCmdBindIndexBuffer(commandBuffer, m_indices.buffer, 0, VK_INDEX_TYPE_UINT32); + for (auto &node : m_nodes) + { + drawNode(node, commandBuffer); + } } -void glTFMainModel::calculateBoundingBox(glTFNode* node, glTFNode* parent) +void glTFMainModel::calculateBoundingBox(glTFNode *node, glTFNode *parent) { - glTFBoundingBox parentBvh = parent ? parent->getBvh() : glTFBoundingBox(m_dimensions.min, m_dimensions.max); + glTFBoundingBox parentBvh = parent ? parent->getBvh() : glTFBoundingBox(m_dimensions.min, m_dimensions.max); - glTFMesh* nodeMesh = node->getMesh(); + glTFMesh *nodeMesh = node->getMesh(); - if (nodeMesh) { - if (nodeMesh->getBoundingBox().isValid()) { - node->setAxisAlignedBoundingBox(nodeMesh->getBoundingBox().getAABB(node->getMatrix())); - - if (node->getChildren().size() == 0) { - glTFBoundingBox nodeAABB = node->getAxisAlignedBoundingBox(); - nodeAABB.setValid(true); - node->setBvh(nodeAABB); - } - } - } + if (nodeMesh) + { + if (nodeMesh->getBoundingBox().isValid()) + { + node->setAxisAlignedBoundingBox(nodeMesh->getBoundingBox().getAABB(node->getMatrix())); - parentBvh.setMin(glm::min(parentBvh.getMin(), node->getBvh().getMin())); - parentBvh.setMax(glm::min(parentBvh.getMax(), node->getBvh().getMax())); + if (node->getChildren().size() == 0) + { + glTFBoundingBox nodeAABB = node->getAxisAlignedBoundingBox(); + nodeAABB.setValid(true); + node->setBvh(nodeAABB); + } + } + } - for (auto& child : node->getChildren()) { - calculateBoundingBox(child, node); - } + parentBvh.setMin(glm::min(parentBvh.getMin(), node->getBvh().getMin())); + parentBvh.setMax(glm::min(parentBvh.getMax(), node->getBvh().getMax())); + + for (auto &child : node->getChildren()) + { + calculateBoundingBox(child, node); + } } void glTFMainModel::updateAnimation(uint32_t index, float time) { - if (m_animations.empty()) { - std::cout << ".glTF does not contain animation." << std::endl; - return; - } - if (index > static_cast(m_animations.size()) - 1) { - std::cout << "No animation with index " << index << std::endl; - return; - } - glTFAnimation& animation = m_animations[index]; + if (m_animations.empty()) + { + std::cout << ".glTF does not contain animation." << std::endl; + return; + } + if (index > static_cast(m_animations.size()) - 1) + { + std::cout << "No animation with index " << index << std::endl; + return; + } + glTFAnimation &animation = m_animations[index]; - bool updated = false; - for (auto& channel : animation.getChannels()) { - glTFAnimationSampler& sampler = animation.getSampler()[channel.getSamplerIndex()]; - if (sampler.getInputs().size() > sampler.getOutputsVec4().size()) { - continue; - } - std::vector samplerInputs = sampler.getInputs(); - for (size_t i = 0; i < sampler.getInputs().size() - 1; i++) { - if ((time >= samplerInputs[i]) && (time <= samplerInputs[i + 1])) { - float u = std::max(0.0f, time - samplerInputs[i]) / (samplerInputs[i + 1] - samplerInputs[i]); - if (u <= 1.0f) - { - switch (channel.getAnimationPathType()) { - case glTFLoader::AnimationPathType::TRANSLATION: - { - std::vector samplerOutPutVec4 = sampler.getOutputsVec4(); - glm::vec4 translate = glm::mix(samplerOutPutVec4[i], samplerOutPutVec4[i + 1], u); - glTFNode* channelNode = channel.getNode(); - channelNode->setTranslation(translate); - channel.setNode(channelNode); - break; - } - case glTFLoader::AnimationPathType::SCALE: - { - std::vector samplerOutPutVec4 = sampler.getOutputsVec4(); - glm::vec4 scaler = glm::mix(samplerOutPutVec4[i], samplerOutPutVec4[i + 1], u); - glTFNode* channelNode = channel.getNode(); - channelNode->setScale(scaler); - channel.setNode(channelNode); - break; - } - case glTFLoader::AnimationPathType::ROTATION: - { - std::vector samplerOutPutVec4 = sampler.getOutputsVec4(); - glm::quat q1; - q1.x = samplerOutPutVec4[i].x; - q1.y = samplerOutPutVec4[i].y; - q1.z = samplerOutPutVec4[i].z; - q1.w = samplerOutPutVec4[i].w; - glm::quat q2; - q2.x = samplerOutPutVec4[i + 1].x; - q2.y = samplerOutPutVec4[i + 1].y; - q2.z = samplerOutPutVec4[i + 1].z; - q2.w = samplerOutPutVec4[i + 1].w; + bool updated = false; + for (auto &channel : animation.getChannels()) + { + glTFAnimationSampler &sampler = animation.getSampler()[channel.getSamplerIndex()]; + if (sampler.getInputs().size() > sampler.getOutputsVec4().size()) + { + continue; + } + std::vector samplerInputs = sampler.getInputs(); + for (size_t i = 0; i < sampler.getInputs().size() - 1; i++) + { + if ((time >= samplerInputs[i]) && (time <= samplerInputs[i + 1])) + { + float u = std::max(0.0f, time - samplerInputs[i]) / (samplerInputs[i + 1] - samplerInputs[i]); + if (u <= 1.0f) + { + switch (channel.getAnimationPathType()) + { + case glTFLoader::AnimationPathType::TRANSLATION: + { + std::vector samplerOutPutVec4 = sampler.getOutputsVec4(); + glm::vec4 translate = glm::mix(samplerOutPutVec4[i], samplerOutPutVec4[i + 1], u); + glTFNode *channelNode = channel.getNode(); + channelNode->setTranslation(translate); + channel.setNode(channelNode); + break; + } + case glTFLoader::AnimationPathType::SCALE: + { + std::vector samplerOutPutVec4 = sampler.getOutputsVec4(); + glm::vec4 scaler = glm::mix(samplerOutPutVec4[i], samplerOutPutVec4[i + 1], u); + glTFNode *channelNode = channel.getNode(); + channelNode->setScale(scaler); + channel.setNode(channelNode); + break; + } + case glTFLoader::AnimationPathType::ROTATION: + { + std::vector samplerOutPutVec4 = sampler.getOutputsVec4(); + glm::quat q1; + q1.x = samplerOutPutVec4[i].x; + q1.y = samplerOutPutVec4[i].y; + q1.z = samplerOutPutVec4[i].z; + q1.w = samplerOutPutVec4[i].w; + glm::quat q2; + q2.x = samplerOutPutVec4[i + 1].x; + q2.y = samplerOutPutVec4[i + 1].y; + q2.z = samplerOutPutVec4[i + 1].z; + q2.w = samplerOutPutVec4[i + 1].w; - glTFNode* channelNode = channel.getNode(); - channelNode->setRotation(glm::normalize(glm::slerp(q1, q2, u))); - channel.setNode(channelNode); - break; - } - } - updated = true; - } - } - } - } - if (updated) { - for (auto& node : m_nodes) { - node->update(); - } - } + glTFNode *channelNode = channel.getNode(); + channelNode->setRotation(glm::normalize(glm::slerp(q1, q2, u))); + channel.setNode(channelNode); + break; + } + } + updated = true; + } + } + } + } + if (updated) + { + for (auto &node : m_nodes) + { + node->update(); + } + } } -glTFNode* glTFMainModel::findNode(glTFNode* parent, uint32_t index) +glTFNode *glTFMainModel::findNode(glTFNode *parent, uint32_t index) { - glTFNode* nodeFound = nullptr; - if (parent->getIndex() == index) { - return parent; - } - for (auto& child : parent->getChildren()) { - nodeFound = findNode(child, index); - if (nodeFound) { - break; - } - } - return nodeFound; + glTFNode *nodeFound = nullptr; + if (parent->getIndex() == index) + { + return parent; + } + for (auto &child : parent->getChildren()) + { + nodeFound = findNode(child, index); + if (nodeFound) + { + break; + } + } + return nodeFound; } -glTFNode* glTFMainModel::nodeFromIndex(uint32_t index) +glTFNode *glTFMainModel::nodeFromIndex(uint32_t index) { - glTFNode* nodeFound = nullptr; - for (auto& node : m_nodes) { - nodeFound = findNode(node, index); - if (nodeFound) { - break; - } - } - return nodeFound; + glTFNode *nodeFound = nullptr; + for (auto &node : m_nodes) + { + nodeFound = findNode(node, index); + if (nodeFound) + { + break; + } + } + return nodeFound; } GLTFLOADER_NAMESPACE_END - - diff --git a/src/gltf/glTFMainModel.h b/src/gltf/glTFMainModel.h index f888e3e..1cbb029 100644 --- a/src/gltf/glTFMainModel.h +++ b/src/gltf/glTFMainModel.h @@ -1,15 +1,21 @@ #ifndef GLTFMAINMODEL_H #define GLTFMAINMODEL_H +#ifndef TINYGLTF_NO_STB_IMAGE_WRITE +#define TINYGLTF_NO_STB_IMAGE_WRITE +#endif + #include "glTFModel_Marco.h" #include "glTFModel_common.h" #include "VulkanDevice.h" +#include "glTFAnimation.h" #include "glTFNode.h" #include "glTFSkin.h" #include "glTFTexture.h" -#include "glTFAnimation.h" + +#include #include #include @@ -17,68 +23,72 @@ GLTFLOADER_NAMESPACE_BEGIN +struct ModelBuffer +{ + VkBuffer buffer = VK_NULL_HANDLE; + VkDeviceMemory memory; +}; + +struct AABBDimensions +{ + glm::vec3 min = glm::vec3(FLT_MAX); + glm::vec3 max = glm::vec3(-FLT_MAX); +}; + class glTFMainModel { public: - glTFMainModel(); - ~glTFMainModel(); + glTFMainModel(); + ~glTFMainModel(); - VkSamplerAddressMode getVkWrapMode(int32_t wrapMode); - VkFilter getVkFilterMode(int32_t filterMode); - void getNodeProperty(const tinygltf::Node& node, const tinygltf::Model& model, size_t& vertexCount, size_t& indexCount); - void getSceneDimensions(); - void destroy(VkDevice device); - - void loadNode(glTFNode* parent, const tinygltf::Node& node, uint32_t nodeIndex, const tinygltf::Model& model, LoaderInfo& loaderInfo, float globalscale); - void loadSkins(tinygltf::Model& gltfModel); - void loadTextures(tinygltf::Model& gltfModel, VulkanBase::VulkanDevice* device, VkQueue transferQueue); - void loadTextureSamplers(tinygltf::Model& gltfModel); - void loadMaterials(tinygltf::Model& gltfModel); - void loadAnimations(tinygltf::Model& gltfModel); - void loadFromFile(std::string filename, VulkanBase::VulkanDevice* device, VkQueue transferQueue, float scale = 1.0f); + VkSamplerAddressMode getVkWrapMode(int32_t wrapMode); + VkFilter getVkFilterMode(int32_t filterMode); + void getNodeProperty(const tinygltf::Node &node, const tinygltf::Model &model, size_t &vertexCount, size_t &indexCount); + void getSceneDimensions(); - void drawNode(glTFNode* node, VkCommandBuffer commandBuffer); - void draw(VkCommandBuffer commandBuffer); + ModelBuffer &getModelVertex(); + ModelBuffer &getModelIndex(); - void calculateBoundingBox(glTFNode* node, glTFNode* parent); - void updateAnimation(uint32_t index, float time); - glTFNode* findNode(glTFNode* parent, uint32_t index); - glTFNode* nodeFromIndex(uint32_t index); + void destroy(VkDevice device); + + void loadNode(glTFNode *parent, const tinygltf::Node &node, uint32_t nodeIndex, const tinygltf::Model &model, LoaderInfo &loaderInfo, float globalscale); + void loadSkins(tinygltf::Model &gltfModel); + void loadTextures(tinygltf::Model &gltfModel, VulkanBase::VulkanDevice *device, VkQueue transferQueue); + void loadTextureSamplers(tinygltf::Model &gltfModel); + void loadMaterials(tinygltf::Model &gltfModel); + void loadAnimations(tinygltf::Model &gltfModel); + void loadFromFile(std::string filename, VulkanBase::VulkanDevice *device, VkQueue transferQueue, float scale = 1.0f); + + void drawNode(glTFNode *node, VkCommandBuffer commandBuffer); + void draw(VkCommandBuffer commandBuffer); + + void calculateBoundingBox(glTFNode *node, glTFNode *parent); + void updateAnimation(uint32_t index, float time); + glTFNode *findNode(glTFNode *parent, uint32_t index); + glTFNode *nodeFromIndex(uint32_t index); private: + VulkanBase::VulkanDevice *m_device; - VulkanBase::VulkanDevice* m_device; + ModelBuffer m_vertices; + ModelBuffer m_indices; - struct Vertices { - VkBuffer buffer = VK_NULL_HANDLE; - VkDeviceMemory memory; - } m_vertices; - struct Indices { - VkBuffer buffer = VK_NULL_HANDLE; - VkDeviceMemory memory; - } m_indices; + glm::mat4 m_aabb; - glm::mat4 m_aabb; + std::vector m_nodes; + std::vector m_linearNodes; - std::vector m_nodes; - std::vector m_linearNodes; + std::vector m_skins; - std::vector m_skins; - - std::vector m_textures; - std::vector m_textureSamplers; - std::vector m_materials; - std::vector m_animations; - std::vector m_extensions; - - struct Dimensions { - glm::vec3 min = glm::vec3(FLT_MAX); - glm::vec3 max = glm::vec3(-FLT_MAX); - } m_dimensions; + std::vector m_textures; + std::vector m_textureSamplers; + std::vector m_materials; + std::vector m_animations; + std::vector m_extensions; + AABBDimensions m_dimensions; }; - GLTFLOADER_NAMESPACE_END #endif // !GLTFMAINMODEL_h diff --git a/src/gltf/glTFMaterial.cpp b/src/gltf/glTFMaterial.cpp index ebde6b8..e74430f 100644 --- a/src/gltf/glTFMaterial.cpp +++ b/src/gltf/glTFMaterial.cpp @@ -1,6 +1,5 @@ #include "glTFMaterial.h" - GLTFLOADER_NAMESPACE_BEGIN glTFMaterial::glTFMaterial() @@ -13,72 +12,81 @@ glTFMaterial::~glTFMaterial() void glTFMaterial::setDoublesided(bool value) { - m_doubleSided = value; + m_doubleSided = value; } bool glTFMaterial::getDoublesided() { - return m_doubleSided; + return m_doubleSided; } void glTFMaterial::setAlphaMode(AlphaMode value) { - m_alphaMode = value; + m_alphaMode = value; } AlphaMode glTFMaterial::getAlphaMode() { - return m_alphaMode; + return m_alphaMode; } void glTFMaterial::setAlphaCutOff(float value) { - m_alphaCutoff = value; + m_alphaCutoff = value; } float glTFMaterial::getAlphaCutOff() { - return m_alphaCutoff; + return m_alphaCutoff; } -TextureCoordSet* glTFMaterial::getTextureCoordSet() +TextureCoordSet *glTFMaterial::getTextureCoordSet() { - return m_texCoordSets; + return m_texCoordSets; } -void glTFMaterial::setTextureCoordSet(TextureCoordSet* value) +void glTFMaterial::setTextureCoordSet(TextureCoordSet *value) { - m_texCoordSets = value; + m_texCoordSets = value; } -PbrTextureExtension* glTFMaterial::getTextureExtension() +PBR::PbrTextureExtension *glTFMaterial::getTextureExtension() { - return m_textureExtension; + return m_textureExtension; } -void glTFMaterial::setPbrTextureExtension(PbrTextureExtension* value) +void glTFMaterial::setPbrTextureExtension(PBR::PbrTextureExtension *value) { - m_textureExtension = value; + m_textureExtension = value; } -PbrBaseTexture* glTFMaterial::getPbrBaseTexture() +PBR::PbrBaseTexture *glTFMaterial::getPbrBaseTexture() { - return m_pbrBaseTexture; + return m_pbrBaseTexture; } -void glTFMaterial::setPbrBaseTexture(PbrBaseTexture* value) +void glTFMaterial::setPbrBaseTexture(PBR::PbrBaseTexture *value) { - m_pbrBaseTexture = value; + m_pbrBaseTexture = value; } -PbrWorkFlow* glTFMaterial::getPbrWorkFlow() +PBR::PbrWorkFlow *glTFMaterial::getPbrWorkFlow() { - return m_pbrWorkFlow; + return m_pbrWorkFlow; } -void glTFMaterial::setPbrWorkFlow() +void glTFMaterial::setPbrWorkFlow(PBR::PbrWorkFlow *value) { - m_pbrWorkFlow; + m_pbrWorkFlow = value; +} + +void glTFMaterial::setDescriptorSet(VkDescriptorSet value) +{ + m_descriptorSet = value; +} +VkDescriptorSet glTFMaterial::getDescriptorSet() +{ + return m_descriptorSet; } GLTFLOADER_NAMESPACE_END \ No newline at end of file diff --git a/src/gltf/glTFMaterial.h b/src/gltf/glTFMaterial.h index 1d02de0..e2b0d45 100644 --- a/src/gltf/glTFMaterial.h +++ b/src/gltf/glTFMaterial.h @@ -4,11 +4,11 @@ #include "glTFModel_Marco.h" #include "glTFModel_common.h" -#include "glTFTexture.h" +#include "PbrBaseTexture.h" #include "PbrTextureCoordSet.h" #include "PbrTextureExtension.h" -#include "PbrBaseTexture.h" #include "PbrWorkFlow.h" +#include "glTFTexture.h" #include @@ -17,50 +17,46 @@ GLTFLOADER_NAMESPACE_BEGIN class glTFMaterial { public: - glTFMaterial(); - ~glTFMaterial(); + glTFMaterial(); + ~glTFMaterial(); - void setDoublesided(bool value); - bool getDoublesided(); + void setDoublesided(bool value); + bool getDoublesided(); - void setAlphaMode(AlphaMode value); - AlphaMode getAlphaMode(); + void setAlphaMode(AlphaMode value); + AlphaMode getAlphaMode(); - void setAlphaCutOff(float value); - float getAlphaCutOff(); + void setAlphaCutOff(float value); + float getAlphaCutOff(); + void setDescriptorSet(VkDescriptorSet value); + VkDescriptorSet getDescriptorSet(); - TextureCoordSet* getTextureCoordSet(); - void setTextureCoordSet(TextureCoordSet* value); + TextureCoordSet *getTextureCoordSet(); + void setTextureCoordSet(TextureCoordSet *value); - PbrTextureExtension* getTextureExtension(); - void setPbrTextureExtension(PbrTextureExtension* value); + PBR::PbrTextureExtension *getTextureExtension(); + void setPbrTextureExtension(PBR::PbrTextureExtension *value); - PbrBaseTexture* getPbrBaseTexture(); - void setPbrBaseTexture(PbrBaseTexture* value); + PBR::PbrBaseTexture *getPbrBaseTexture(); + void setPbrBaseTexture(PBR::PbrBaseTexture *value); - PbrWorkFlow* getPbrWorkFlow(); - void setPbrWorkFlow(); + PBR::PbrWorkFlow *getPbrWorkFlow(); + void setPbrWorkFlow(PBR::PbrWorkFlow *value); private: + AlphaMode m_alphaMode = ALPHAMODE_OPAQUE; + float m_alphaCutoff = 1.0f; - AlphaMode m_alphaMode = ALPHAMODE_OPAQUE; - float m_alphaCutoff = 1.0f; - - PbrBaseTexture* m_pbrBaseTexture; - bool m_doubleSided = false; - TextureCoordSet* m_texCoordSets; - PbrTextureExtension* m_textureExtension; - PbrWorkFlow* m_pbrWorkFlow; - - VkDescriptorSet m_descriptorSet = VK_NULL_HANDLE; + PBR::PbrBaseTexture *m_pbrBaseTexture; + bool m_doubleSided = false; + TextureCoordSet *m_texCoordSets; + PBR::PbrTextureExtension *m_textureExtension; + PBR::PbrWorkFlow *m_pbrWorkFlow; + VkDescriptorSet m_descriptorSet = VK_NULL_HANDLE; }; - - - - GLTFLOADER_NAMESPACE_END #endif // !GLTFMATERIAL_H diff --git a/src/gltf/glTFModel.h b/src/gltf/glTFModel.h index 3e0b058..6fae447 100644 --- a/src/gltf/glTFModel.h +++ b/src/gltf/glTFModel.h @@ -32,9 +32,9 @@ #include "tiny_gltf.h" -#include "VulkanDevice.hpp" +#include "VulkanDevice.h" //#include "VulkanUtils.hpp" -#include "vulkan/vulkan.h" + #define ENABLE_VALIDATION false #define MAX_NUM_JOINTS 128u @@ -64,7 +64,7 @@ namespace glTFModel }; struct Texture { - vks::VulkanDevice* device; + VulkanBase::VulkanDevice* device; VkImage image; VkImageLayout imageLayout; VkDeviceMemory deviceMemory; @@ -77,7 +77,7 @@ namespace glTFModel void updateDescriptor(); void destroy(); // Load a texture from a glTF image (stored as vector of chars loaded via stb_image) and generate a full mip chaing for it - void fromglTfImage(tinygltf::Image& gltfimage, glTFModel::TextureSampler textureSampler, vks::VulkanDevice* device, VkQueue copyQueue); + void fromglTfImage(tinygltf::Image& gltfimage, glTFModel::TextureSampler textureSampler, VulkanBase::VulkanDevice* device, VkQueue copyQueue); }; struct Material { @@ -127,7 +127,7 @@ namespace glTFModel }; struct Mesh { - vks::VulkanDevice* device; + VulkanBase::VulkanDevice* device; std::vector primitives; BoundingBox bb; BoundingBox aabb; @@ -143,7 +143,7 @@ namespace glTFModel glm::mat4 jointMatrix[128]{}; float jointcount{ 0 }; } uniformBlock; - Mesh(vks::VulkanDevice* device, glm::mat4 matrix); + Mesh(VulkanBase::VulkanDevice* device, glm::mat4 matrix); ~Mesh(); void setBoundingBox(glm::vec3 min, glm::vec3 max); }; @@ -199,7 +199,7 @@ namespace glTFModel struct Model { - vks::VulkanDevice* device; + VulkanBase::VulkanDevice* device; struct Vertex { glm::vec3 pos; @@ -249,13 +249,13 @@ namespace glTFModel void loadNode(glTFModel::Node* parent, const tinygltf::Node& node, uint32_t nodeIndex, const tinygltf::Model& model, LoaderInfo& loaderInfo, float globalscale); void getNodeProps(const tinygltf::Node& node, const tinygltf::Model& model, size_t& vertexCount, size_t& indexCount); void loadSkins(tinygltf::Model& gltfModel); - void loadTextures(tinygltf::Model& gltfModel, vks::VulkanDevice* device, VkQueue transferQueue); + void loadTextures(tinygltf::Model& gltfModel, VulkanBase::VulkanDevice* device, VkQueue transferQueue); VkSamplerAddressMode getVkWrapMode(int32_t wrapMode); VkFilter getVkFilterMode(int32_t filterMode); void loadTextureSamplers(tinygltf::Model& gltfModel); void loadMaterials(tinygltf::Model& gltfModel); void loadAnimations(tinygltf::Model& gltfModel); - void loadFromFile(std::string filename, vks::VulkanDevice* device, VkQueue transferQueue, float scale = 1.0f); + void loadFromFile(std::string filename, VulkanBase::VulkanDevice* device, VkQueue transferQueue, float scale = 1.0f); void drawNode(Node* node, VkCommandBuffer commandBuffer); void draw(VkCommandBuffer commandBuffer); void calculateBoundingBox(Node* node, Node* parent); diff --git a/src/gltf/glTFPrimitive.cpp b/src/gltf/glTFPrimitive.cpp index ac58316..9b7ad79 100644 --- a/src/gltf/glTFPrimitive.cpp +++ b/src/gltf/glTFPrimitive.cpp @@ -2,15 +2,10 @@ GLTFLOADER_NAMESPACE_BEGIN - - -glTFPrimitive::glTFPrimitive(uint32_t firstIndex, uint32_t indexCount, uint32_t vertexCount, glTFMaterial& material) - :m_firstIndex(firstIndex) - ,m_indexCount(indexCount) - ,m_vertexCount(vertexCount) - ,m_material(material) +glTFPrimitive::glTFPrimitive(uint32_t firstIndex, uint32_t indexCount, uint32_t vertexCount, glTFMaterial &material) + : m_firstIndex(firstIndex), m_indexCount(indexCount), m_vertexCount(vertexCount), m_material(material) { - m_hasIndices = indexCount > 0; + m_hasIndices = indexCount > 0; } glTFPrimitive::~glTFPrimitive() @@ -19,33 +14,60 @@ glTFPrimitive::~glTFPrimitive() void glTFPrimitive::setBoundingBox(glm::vec3 min, glm::vec3 max) { - m_boundingBox.setBoundingBox(min, max); + m_boundingBox.setBoundingBox(min, max); } glTFBoundingBox glTFPrimitive::getBoundingBox() { - return m_boundingBox; + return m_boundingBox; } void glTFPrimitive::setIndexCount(unsigned int indexCount) { - m_indexCount = indexCount; + m_indexCount = indexCount; } unsigned int glTFPrimitive::getIndexCount() { - return m_indexCount; + return m_indexCount; } void glTFPrimitive::setFirstIndex(unsigned int firstIndex) { - m_firstIndex = firstIndex; + m_firstIndex = firstIndex; } unsigned int glTFPrimitive::getFirstIndex() { - return m_firstIndex; + return m_firstIndex; } +void glTFPrimitive::setGltfMaterial(glTFMaterial &material) +{ + m_material = material; +} + +glTFMaterial &glTFPrimitive::getGltfMaterial() +{ + return m_material; +} + +void glTFPrimitive::setHasIndices(bool hasIndices) +{ + m_hasIndices = hasIndices; +} +bool glTFPrimitive::getHasIndices() +{ + return m_hasIndices; +} + +void glTFPrimitive::setVertexCount(unsigned int vertexCount) +{ + m_vertexCount = vertexCount; +} +unsigned int glTFPrimitive::getVertexCount() +{ + return m_vertexCount; +} GLTFLOADER_NAMESPACE_END \ No newline at end of file diff --git a/src/gltf/glTFPrimitive.h b/src/gltf/glTFPrimitive.h index c9e5de0..843ffd6 100644 --- a/src/gltf/glTFPrimitive.h +++ b/src/gltf/glTFPrimitive.h @@ -3,44 +3,47 @@ #include "glTFModel_Marco.h" -#include "glTFMaterial.h" #include "glTFBoundingBox.h" +#include "glTFMaterial.h" #include - GLTFLOADER_NAMESPACE_BEGIN class GLTFLOADER_API glTFPrimitive { public: - glTFPrimitive(uint32_t firstIndex, uint32_t indexCount, uint32_t vertexCount, glTFMaterial& material); - - ~glTFPrimitive(); + glTFPrimitive(uint32_t firstIndex, uint32_t indexCount, uint32_t vertexCount, glTFMaterial &material); - void setBoundingBox(glm::vec3 min, glm::vec3 max); - glTFBoundingBox getBoundingBox(); + ~glTFPrimitive(); - void setIndexCount(unsigned int indexCount); - unsigned int getIndexCount(); + void setBoundingBox(glm::vec3 min, glm::vec3 max); + glTFBoundingBox getBoundingBox(); - void setFirstIndex(unsigned int firstIndex); - unsigned int getFirstIndex(); + void setIndexCount(unsigned int indexCount); + unsigned int getIndexCount(); + + void setFirstIndex(unsigned int firstIndex); + unsigned int getFirstIndex(); + + void setGltfMaterial(glTFMaterial &material); + glTFMaterial &getGltfMaterial(); + + void setHasIndices(bool hasIndices); + bool getHasIndices(); + + void setVertexCount(unsigned int vertexCount); + unsigned int getVertexCount(); private: - - unsigned int m_firstIndex; - unsigned int m_indexCount; - unsigned int m_vertexCount; - glTFMaterial& m_material; - bool m_hasIndices; - glTFBoundingBox m_boundingBox; - - + unsigned int m_firstIndex; + unsigned int m_indexCount; + unsigned int m_vertexCount; + glTFMaterial &m_material; + bool m_hasIndices; + glTFBoundingBox m_boundingBox; }; - - GLTFLOADER_NAMESPACE_END #endif // !GLTFPRIMITIVE_H diff --git a/src/gltf/glTFTexture.h b/src/gltf/glTFTexture.h index 29aa06c..ae93278 100644 --- a/src/gltf/glTFTexture.h +++ b/src/gltf/glTFTexture.h @@ -8,43 +8,34 @@ #include - GLTFLOADER_NAMESPACE_BEGIN /// @brief gltf模型的贴图 class GLTFLOADER_API glTFTexture { public: - glTFTexture(); - ~glTFTexture(); + glTFTexture(); + ~glTFTexture(); - void updateDescriptor(); + void updateDescriptor(); - void destroy(); + void destroy(); - void fromglTfImage(tinygltf::Image& gltfimage, VulkanBase::VulkanTextureSampler textureSampler, VulkanBase::VulkanDevice* device, VkQueue copyQueue); + void fromglTfImage(tinygltf::Image &gltfimage, VulkanBase::VulkanTextureSampler textureSampler, VulkanBase::VulkanDevice *device, VkQueue copyQueue); private: - - VulkanBase::VulkanDevice* m_device; - VkImage m_image; - VkImageLayout m_imageLayout; - VkDeviceMemory m_deviceMemory; - VkImageView m_view; - unsigned int m_width, m_height; - unsigned int m_mipLevels; - unsigned int m_layerCount; - VkDescriptorImageInfo m_descriptor; - VkSampler m_sampler; + VulkanBase::VulkanDevice *m_device; + VkImage m_image; + VkImageLayout m_imageLayout; + VkDeviceMemory m_deviceMemory; + VkImageView m_view; + unsigned int m_width, m_height; + unsigned int m_mipLevels; + unsigned int m_layerCount; + VkDescriptorImageInfo m_descriptor; + VkSampler m_sampler; }; - - - - GLTFLOADER_NAMESPACE_END - - - #endif // !GLTFTEXTURE_H diff --git a/src/pbr/PbrBaseTexture.cpp b/src/pbr/PbrBaseTexture.cpp index 0ecc393..489a65b 100644 --- a/src/pbr/PbrBaseTexture.cpp +++ b/src/pbr/PbrBaseTexture.cpp @@ -1,6 +1,6 @@ #include "PbrBaseTexture.h" -GLTFLOADER_NAMESPACE_BEGIN +PBR_NAMESPACE_BEGIN PbrBaseTexture::PbrBaseTexture() { @@ -10,95 +10,94 @@ PbrBaseTexture::~PbrBaseTexture() { } -void PbrBaseTexture::setBaseColorTexture(glTFTexture* value) +void PbrBaseTexture::setBaseColorTexture(glTFLoader::glTFTexture *value) { - m_baseColorTexture = value; + m_baseColorTexture = value; } -glTFTexture* PbrBaseTexture::getBaseColorTexture() +glTFLoader::glTFTexture *PbrBaseTexture::getBaseColorTexture() { - return m_baseColorTexture; + return m_baseColorTexture; } void PbrBaseTexture::setBaseColorFactor(glm::vec4 value) { - m_baseColorFactor = value; + m_baseColorFactor = value; } glm::vec4 PbrBaseTexture::getBaseColorFactor() { - return m_baseColorFactor; + return m_baseColorFactor; } -void PbrBaseTexture::setNormalTexture(glTFTexture* value) +void PbrBaseTexture::setNormalTexture(glTFLoader::glTFTexture *value) { - m_normalTexture = value; + m_normalTexture = value; } -glTFTexture* PbrBaseTexture::getNormalTexture() +glTFLoader::glTFTexture *PbrBaseTexture::getNormalTexture() { - return m_normalTexture; + return m_normalTexture; } -void PbrBaseTexture::setMetallicRoughnessTexture(glTFTexture* value) +void PbrBaseTexture::setMetallicRoughnessTexture(glTFLoader::glTFTexture *value) { - m_metallicRoughnessTexture = value; + m_metallicRoughnessTexture = value; } -glTFTexture* PbrBaseTexture::getMetalicRoughnessTexture() +glTFLoader::glTFTexture *PbrBaseTexture::getMetalicRoughnessTexture() { - return m_metallicRoughnessTexture; + return m_metallicRoughnessTexture; } void PbrBaseTexture::setMetallicFactor(float value) { - m_metallicFactor = value; + m_metallicFactor = value; } float PbrBaseTexture::getMetallicFactor() { - return m_metallicFactor; + return m_metallicFactor; } void PbrBaseTexture::setRoughnessFactor(float value) { - m_roughnessFactor = value; + m_roughnessFactor = value; } float PbrBaseTexture::getRoughnessFactor() { - return m_roughnessFactor; + return m_roughnessFactor; } -void PbrBaseTexture::setEmissiveTexture(glTFTexture* value) +void PbrBaseTexture::setEmissiveTexture(glTFLoader::glTFTexture *value) { - m_emissiveTexture = value; + m_emissiveTexture = value; } -glTFTexture* PbrBaseTexture::getEmissiveTexture() +glTFLoader::glTFTexture *PbrBaseTexture::getEmissiveTexture() { - return m_emissiveTexture; + return m_emissiveTexture; } - void PbrBaseTexture::setEmissiveFactor(glm::vec4 value) { - m_emissiveFactor = value; + m_emissiveFactor = value; } glm::vec4 PbrBaseTexture::getEmissiveFactor() { - return m_emissiveFactor; + return m_emissiveFactor; } -void PbrBaseTexture::setOcclusionTexture(glTFTexture* value) +void PbrBaseTexture::setOcclusionTexture(glTFLoader::glTFTexture *value) { - m_occlusionTexture = value; + m_occlusionTexture = value; } -glTFTexture* PbrBaseTexture::getOcclusionTexture() +glTFLoader::glTFTexture *PbrBaseTexture::getOcclusionTexture() { - return m_occlusionTexture; + return m_occlusionTexture; } -GLTFLOADER_NAMESPACE_END \ No newline at end of file +PBR_NAMESPACE_END \ No newline at end of file diff --git a/src/pbr/PbrBaseTexture.h b/src/pbr/PbrBaseTexture.h index b6e8b9d..87e2e05 100644 --- a/src/pbr/PbrBaseTexture.h +++ b/src/pbr/PbrBaseTexture.h @@ -1,69 +1,64 @@ #ifndef PBRBASETEXTURE_H #define PBRBASETEXTURE_H -/// todo: 分离为单独的PBR命名空间 -#include "glTFModel_Marco.h" +#include "PbrMarco.h" #include "glTFTexture.h" #include -GLTFLOADER_NAMESPACE_BEGIN +PBR_NAMESPACE_BEGIN /// @brief todo:拆分texture基类摆脱gltf限制 class PbrBaseTexture { public: - PbrBaseTexture(); - ~PbrBaseTexture(); + PbrBaseTexture(); + ~PbrBaseTexture(); - void setBaseColorTexture(glTFTexture* value); - glTFTexture* getBaseColorTexture(); + void setBaseColorTexture(glTFLoader::glTFTexture *value); + glTFLoader::glTFTexture *getBaseColorTexture(); - void setBaseColorFactor(glm::vec4 value); - glm::vec4 getBaseColorFactor(); + void setBaseColorFactor(glm::vec4 value); + glm::vec4 getBaseColorFactor(); - void setNormalTexture(glTFTexture* value); - glTFTexture* getNormalTexture(); + void setNormalTexture(glTFLoader::glTFTexture *value); + glTFLoader::glTFTexture *getNormalTexture(); - void setMetallicRoughnessTexture(glTFTexture* value); - glTFTexture* getMetalicRoughnessTexture(); + void setMetallicRoughnessTexture(glTFLoader::glTFTexture *value); + glTFLoader::glTFTexture *getMetalicRoughnessTexture(); - void setMetallicFactor(float value); - float getMetallicFactor(); + void setMetallicFactor(float value); + float getMetallicFactor(); - void setRoughnessFactor(float value); - float getRoughnessFactor(); + void setRoughnessFactor(float value); + float getRoughnessFactor(); - void setEmissiveTexture(glTFTexture* value); - glTFTexture* getEmissiveTexture(); + void setEmissiveTexture(glTFLoader::glTFTexture *value); + glTFLoader::glTFTexture *getEmissiveTexture(); - void setEmissiveFactor(glm::vec4 value); - glm::vec4 getEmissiveFactor(); + void setEmissiveFactor(glm::vec4 value); + glm::vec4 getEmissiveFactor(); - void setOcclusionTexture(glTFTexture* value); - glTFTexture* getOcclusionTexture(); + void setOcclusionTexture(glTFLoader::glTFTexture *value); + glTFLoader::glTFTexture *getOcclusionTexture(); private: + glTFLoader::glTFTexture *m_baseColorTexture = nullptr; + glm::vec4 m_baseColorFactor = glm::vec4(1.0f); - glTFTexture* m_baseColorTexture = nullptr; - glm::vec4 m_baseColorFactor = glm::vec4(1.0f); + glTFLoader::glTFTexture *m_normalTexture = nullptr; - glTFTexture* m_normalTexture = nullptr; + glTFLoader::glTFTexture *m_metallicRoughnessTexture = nullptr; + float m_metallicFactor = 1.0f; + float m_roughnessFactor = 1.0f; - glTFTexture* m_metallicRoughnessTexture = nullptr; - float m_metallicFactor = 1.0f; - float m_roughnessFactor = 1.0f; - - glTFTexture* m_emissiveTexture = nullptr; - glm::vec4 m_emissiveFactor = glm::vec4(1.0f); - - glTFTexture* m_occlusionTexture = nullptr; + glTFLoader::glTFTexture *m_emissiveTexture = nullptr; + glm::vec4 m_emissiveFactor = glm::vec4(1.0f); + glTFLoader::glTFTexture *m_occlusionTexture = nullptr; }; - - -GLTFLOADER_NAMESPACE_END +PBR_NAMESPACE_END #endif // !PBRBASETEXTURE_H diff --git a/src/pbr/PbrMarco.h b/src/pbr/PbrMarco.h new file mode 100644 index 0000000..7fa2114 --- /dev/null +++ b/src/pbr/PbrMarco.h @@ -0,0 +1,34 @@ +#pragma once + +#define MAX_NUM_JOINTS 128u + +/// 命名空间宏 +#define PBR_NAMESPACE_BEGIN \ + namespace PBR \ + { +#define PBR_NAMESPACE_END } + +/// windows 导入导出宏,PBR_EXPORTS将在cmake中定义 +/// unix-like 下提供符号可见性控制 +#ifdef PBR_STATIC_BUILD +#define PBR_API +#define PBR_LOCAL +#else +#ifdef _WIN32 +#ifdef PBR_EXPORTS +#define PBR_API __declspec(dllexport) +#define PBR_LOCAL +#else +#define PBR_API __declspec(dllimport) +#define PBR_LOCAL +#endif +#else +#if __GNUC__ >= 4 || defined(__clang__) +#define PBR_API __attribute__((visibility("default"))) +#define PBR_LOCAL __attribute__((visibility("hidden"))) +#else +#define PBR_API +#define PBR_LOCAL +#endif +#endif +#endif \ No newline at end of file diff --git a/src/pbr/PbrTextureExtension.cpp b/src/pbr/PbrTextureExtension.cpp index bdadd25..4874c5f 100644 --- a/src/pbr/PbrTextureExtension.cpp +++ b/src/pbr/PbrTextureExtension.cpp @@ -1,6 +1,6 @@ #include "PbrTextureExtension.h" -GLTFLOADER_NAMESPACE_BEGIN +PBR_NAMESPACE_BEGIN PbrTextureExtension::PbrTextureExtension() { @@ -10,46 +10,44 @@ PbrTextureExtension::~PbrTextureExtension() { } -void PbrTextureExtension::setSpecularGlossinessTexture(glTFTexture* _texture) +void PbrTextureExtension::setSpecularGlossinessTexture(glTFLoader::glTFTexture *_texture) { - m_specularGlossinessTexture = _texture; + m_specularGlossinessTexture = _texture; } -glTFTexture* PbrTextureExtension::getSpecularGlossinessTexture() +glTFLoader::glTFTexture *PbrTextureExtension::getSpecularGlossinessTexture() { - return m_specularGlossinessTexture; + return m_specularGlossinessTexture; } -void PbrTextureExtension::setDiffuseTexture(glTFTexture* _texture) +void PbrTextureExtension::setDiffuseTexture(glTFLoader::glTFTexture *_texture) { - m_diffuseTexture = _texture; + m_diffuseTexture = _texture; } -glTFTexture* PbrTextureExtension::getDiffuseTexture() +glTFLoader::glTFTexture *PbrTextureExtension::getDiffuseTexture() { - return m_diffuseTexture; + return m_diffuseTexture; } void PbrTextureExtension::setDiffuseFactor(glm::vec4 value) { - m_diffuseFactor = value; + m_diffuseFactor = value; } glm::vec4 PbrTextureExtension::getDiffuseFactor() { - return m_diffuseFactor; + return m_diffuseFactor; } void PbrTextureExtension::setSpecularFactor(glm::vec3 value) { - m_specularFactor = value; + m_specularFactor = value; } glm::vec3 PbrTextureExtension::getSpecularFactor() { - return m_specularFactor; + return m_specularFactor; } - - -GLTFLOADER_NAMESPACE_END \ No newline at end of file +PBR_NAMESPACE_END \ No newline at end of file diff --git a/src/pbr/PbrTextureExtension.h b/src/pbr/PbrTextureExtension.h index 05eaf5c..4ce751a 100644 --- a/src/pbr/PbrTextureExtension.h +++ b/src/pbr/PbrTextureExtension.h @@ -1,51 +1,39 @@ #ifndef PBRTEXTUREEXTENSION_H #define PBRTEXTUREEXTENSION_H -#include "glTFModel_Marco.h" +#include "PbrMarco.h" #include "glTFTexture.h" #include +PBR_NAMESPACE_BEGIN -GLTFLOADER_NAMESPACE_BEGIN - -class PbrTextureExtension +class PbrTextureExtension { public: - PbrTextureExtension(); - ~PbrTextureExtension(); + PbrTextureExtension(); + ~PbrTextureExtension(); - void setSpecularGlossinessTexture(glTFTexture* _texture); - glTFTexture* getSpecularGlossinessTexture(); + void setSpecularGlossinessTexture(glTFLoader::glTFTexture *_texture); + glTFLoader::glTFTexture *getSpecularGlossinessTexture(); - void setDiffuseTexture(glTFTexture* _texture); - glTFTexture* getDiffuseTexture(); + void setDiffuseTexture(glTFLoader::glTFTexture *_texture); + glTFLoader::glTFTexture *getDiffuseTexture(); - void setDiffuseFactor(glm::vec4 value); - glm::vec4 getDiffuseFactor(); - - void setSpecularFactor(glm::vec3 value); - glm::vec3 getSpecularFactor(); + void setDiffuseFactor(glm::vec4 value); + glm::vec4 getDiffuseFactor(); + void setSpecularFactor(glm::vec3 value); + glm::vec3 getSpecularFactor(); private: - - glTFTexture* m_specularGlossinessTexture = nullptr; - glTFTexture* m_diffuseTexture = nullptr; - glm::vec4 m_diffuseFactor = glm::vec4(1.0f); - glm::vec3 m_specularFactor = glm::vec3(0.0f); - + glTFLoader::glTFTexture *m_specularGlossinessTexture = nullptr; + glTFLoader::glTFTexture *m_diffuseTexture = nullptr; + glm::vec4 m_diffuseFactor = glm::vec4(1.0f); + glm::vec3 m_specularFactor = glm::vec3(0.0f); }; - - - - -GLTFLOADER_NAMESPACE_END - - - +PBR_NAMESPACE_END #endif // !PbrTextureExtension_h - diff --git a/src/pbr/PbrWorkFlow.cpp b/src/pbr/PbrWorkFlow.cpp index 2852124..e129805 100644 --- a/src/pbr/PbrWorkFlow.cpp +++ b/src/pbr/PbrWorkFlow.cpp @@ -1,6 +1,6 @@ #include "PbrWorkFlow.h" -GLTFLOADER_NAMESPACE_BEGIN +PBR_NAMESPACE_BEGIN PbrWorkFlow::PbrWorkFlow() { @@ -32,4 +32,4 @@ bool PbrWorkFlow::getSpecularGlossiness() -GLTFLOADER_NAMESPACE_END \ No newline at end of file +PBR_NAMESPACE_END \ No newline at end of file diff --git a/src/pbr/PbrWorkFlow.h b/src/pbr/PbrWorkFlow.h index 8594466..aff7b16 100644 --- a/src/pbr/PbrWorkFlow.h +++ b/src/pbr/PbrWorkFlow.h @@ -1,33 +1,27 @@ #ifndef PBRWORKFLOW_H #define PBRWORKFLOW_H -#include "glTFModel_Marco.h" +#include "PbrMarco.h" -GLTFLOADER_NAMESPACE_BEGIN +PBR_NAMESPACE_BEGIN class PbrWorkFlow { public: - PbrWorkFlow(); - ~PbrWorkFlow(); + PbrWorkFlow(); + ~PbrWorkFlow(); - void setMetallicRoughness(bool value); - bool getMetallicRoughness(); + void setMetallicRoughness(bool value); + bool getMetallicRoughness(); - void setSpecularGlossiness(bool value); - bool getSpecularGlossiness(); + void setSpecularGlossiness(bool value); + bool getSpecularGlossiness(); private: - - bool m_metallicRoughness = true; - bool m_specularGlossiness = false; - + bool m_metallicRoughness = true; + bool m_specularGlossiness = false; }; - - - -GLTFLOADER_NAMESPACE_END - +PBR_NAMESPACE_END #endif // !PBRWORKFLOW_H diff --git a/src/render/IrradiancePushBlock.cpp b/src/render/IrradiancePushBlock.cpp index f5716ab..7a5ed8a 100644 --- a/src/render/IrradiancePushBlock.cpp +++ b/src/render/IrradiancePushBlock.cpp @@ -1,5 +1,12 @@ #include "IrradiancePushBlock.h" +IrradiancePushBlock::IrradiancePushBlock() +{ +} +IrradiancePushBlock::~IrradiancePushBlock() +{ +} + // Getter method definitions float IrradiancePushBlock::getDeltaPhi() const { diff --git a/src/render/RenderSceneTextures.cpp b/src/render/RenderSceneTextures.cpp index 7cf6015..fe09b9c 100644 --- a/src/render/RenderSceneTextures.cpp +++ b/src/render/RenderSceneTextures.cpp @@ -1,45 +1,45 @@ #include "RenderSceneTextures.h" // Getters -vks::TextureCubeMap &RenderSceneTextures::getEnvironmentCube() +VulkanBase::TextureCubeMap &RenderSceneTextures::getEnvironmentCube() { return m_environmentCube; } -vks::Texture2D &RenderSceneTextures::getEmpty() +VulkanBase::Texture2D &RenderSceneTextures::getEmpty() { return m_empty; } -vks::Texture2D &RenderSceneTextures::getLutBrdf() +VulkanBase::Texture2D &RenderSceneTextures::getLutBrdf() { return m_lutBrdf; } -vks::TextureCubeMap &RenderSceneTextures::getIrradianceCube() +VulkanBase::TextureCubeMap &RenderSceneTextures::getIrradianceCube() { return m_irradianceCube; } -vks::TextureCubeMap &RenderSceneTextures::getPrefilteredCube() +VulkanBase::TextureCubeMap &RenderSceneTextures::getPrefilteredCube() { return m_prefilteredCube; } // Setters -void RenderSceneTextures::setEnvironmentCube(const vks::TextureCubeMap &texture) +void RenderSceneTextures::setEnvironmentCube(const VulkanBase::TextureCubeMap &texture) { m_environmentCube = texture; } -void RenderSceneTextures::setEmpty(const vks::Texture2D &texture) +void RenderSceneTextures::setEmpty(const VulkanBase::Texture2D &texture) { m_empty = texture; } -void RenderSceneTextures::setLutBrdf(const vks::Texture2D &texture) +void RenderSceneTextures::setLutBrdf(const VulkanBase::Texture2D &texture) { m_lutBrdf = texture; } -void RenderSceneTextures::setIrradianceCube(const vks::TextureCubeMap &texture) +void RenderSceneTextures::setIrradianceCube(const VulkanBase::TextureCubeMap &texture) { m_irradianceCube = texture; } -void RenderSceneTextures::setPrefilteredCube(const vks::TextureCubeMap &texture) +void RenderSceneTextures::setPrefilteredCube(const VulkanBase::TextureCubeMap &texture) { m_prefilteredCube = texture; } diff --git a/src/render/RenderSceneTextures.h b/src/render/RenderSceneTextures.h index 063a542..4fedc1c 100644 --- a/src/render/RenderSceneTextures.h +++ b/src/render/RenderSceneTextures.h @@ -1,6 +1,6 @@ #pragma once -#include +#include class RenderSceneTextures { @@ -9,18 +9,18 @@ public: ~RenderSceneTextures(); // Getters - vks::TextureCubeMap &getEnvironmentCube(); - vks::Texture2D &getEmpty(); - vks::Texture2D &getLutBrdf(); - vks::TextureCubeMap &getIrradianceCube(); - vks::TextureCubeMap &getPrefilteredCube(); + VulkanBase::TextureCubeMap &getEnvironmentCube(); + VulkanBase::Texture2D &getEmpty(); + VulkanBase::Texture2D &getLutBrdf(); + VulkanBase::TextureCubeMap &getIrradianceCube(); + VulkanBase::TextureCubeMap &getPrefilteredCube(); // Setters - void setEnvironmentCube(const vks::TextureCubeMap &texture); - void setEmpty(const vks::Texture2D &texture); - void setLutBrdf(const vks::Texture2D &texture); - void setIrradianceCube(const vks::TextureCubeMap &texture); - void setPrefilteredCube(const vks::TextureCubeMap &texture); + void setEnvironmentCube(const VulkanBase::TextureCubeMap &texture); + void setEmpty(const VulkanBase::Texture2D &texture); + void setLutBrdf(const VulkanBase::Texture2D &texture); + void setIrradianceCube(const VulkanBase::TextureCubeMap &texture); + void setPrefilteredCube(const VulkanBase::TextureCubeMap &texture); // destroy void destroyEnvironmentCube(); @@ -30,9 +30,9 @@ public: void destroyPrefilteredCube(); private: - vks::TextureCubeMap m_environmentCube; - vks::Texture2D m_empty; - vks::Texture2D m_lutBrdf; - vks::TextureCubeMap m_irradianceCube; - vks::TextureCubeMap m_prefilteredCube; + VulkanBase::TextureCubeMap m_environmentCube; + VulkanBase::Texture2D m_empty; + VulkanBase::Texture2D m_lutBrdf; + VulkanBase::TextureCubeMap m_irradianceCube; + VulkanBase::TextureCubeMap m_prefilteredCube; }; diff --git a/src/render/ShaderLoader.cpp b/src/render/ShaderLoader.cpp new file mode 100644 index 0000000..5025513 --- /dev/null +++ b/src/render/ShaderLoader.cpp @@ -0,0 +1,147 @@ +#include "ShaderLoader.h" + +#include +#include +#include + +ShaderLoader::ShaderLoader() +{ +} + +ShaderLoader::~ShaderLoader() +{ +} + +VkPipelineShaderStageCreateInfo ShaderLoader::loadShader(VkDevice device, std::string filename, VkShaderStageFlagBits stage) +{ + VkPipelineShaderStageCreateInfo shaderStage{}; + shaderStage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shaderStage.stage = stage; + shaderStage.pName = "main"; +#if defined(VK_USE_PLATFORM_ANDROID_KHR) + std::string assetpath = "shaders/" + filename; + AAsset *asset = AAssetManager_open(androidApp->activity->assetManager, assetpath.c_str(), AASSET_MODE_STREAMING); + assert(asset); + size_t size = AAsset_getLength(asset); + assert(size > 0); + char *shaderCode = new char[size]; + AAsset_read(asset, shaderCode, size); + AAsset_close(asset); + VkShaderModule shaderModule; + VkShaderModuleCreateInfo moduleCreateInfo; + moduleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + moduleCreateInfo.pNext = NULL; + moduleCreateInfo.codeSize = size; + moduleCreateInfo.pCode = (uint32_t *)shaderCode; + moduleCreateInfo.flags = 0; + VK_CHECK_RESULT(vkCreateShaderModule(device, &moduleCreateInfo, NULL, &shaderStage.module)); + delete[] shaderCode; +#else + std::ifstream is(filename, std::ios::binary | std::ios::in | std::ios::ate); + + if (is.is_open()) + { + size_t size = is.tellg(); + is.seekg(0, std::ios::beg); + char *shaderCode = new char[size]; + is.read(shaderCode, size); + is.close(); + assert(size > 0); + VkShaderModuleCreateInfo moduleCreateInfo{}; + moduleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + moduleCreateInfo.codeSize = size; + moduleCreateInfo.pCode = (uint32_t *)shaderCode; + vkCreateShaderModule(device, &moduleCreateInfo, NULL, &shaderStage.module); + delete[] shaderCode; + } + else + { + std::cerr << "Error: Could not open shader file \"" << filename << "\"" << std::endl; + shaderStage.module = VK_NULL_HANDLE; + } +#endif + + return shaderStage; +} + +void ShaderLoader::readDirectory(const std::string &directory, const std::string &pattern, std::map &filelist, bool recursive) +{ +#if defined(VK_USE_PLATFORM_ANDROID_KHR) + AAssetDir *assetDir = AAssetManager_openDir(androidApp->activity->assetManager, directory.c_str()); + AAssetDir_rewind(assetDir); + const char *assetName; + while ((assetName = AAssetDir_getNextFileName(assetDir)) != 0) + { + std::string filename(assetName); + filename.erase(filename.find_last_of("."), std::string::npos); + filelist[filename] = directory + "/" + assetName; + } + AAssetDir_close(assetDir); +#elif defined(VK_USE_PLATFORM_WIN32_KHR) + std::string searchpattern(directory + "/" + pattern); + WIN32_FIND_DATA data; + HANDLE hFind; + if ((hFind = FindFirstFile(searchpattern.c_str(), &data)) != INVALID_HANDLE_VALUE) + { + do + { + std::string filename(data.cFileName); + filename.erase(filename.find_last_of("."), std::string::npos); + filelist[filename] = directory + "/" + data.cFileName; + } while (FindNextFile(hFind, &data) != 0); + FindClose(hFind); + } + if (recursive) + { + std::string dirpattern = directory + "/*"; + if ((hFind = FindFirstFile(dirpattern.c_str(), &data)) != INVALID_HANDLE_VALUE) + { + do + { + if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + char subdir[MAX_PATH]; + strcpy(subdir, directory.c_str()); + strcat(subdir, "/"); + strcat(subdir, data.cFileName); + if ((strcmp(data.cFileName, ".") != 0) && (strcmp(data.cFileName, "..") != 0)) + { + readDirectory(subdir, pattern, filelist, recursive); + } + } + } while (FindNextFile(hFind, &data) != 0); + FindClose(hFind); + } + } +#elif defined(__linux__) + std::string patternExt = pattern; + patternExt.erase(0, pattern.find_last_of(".")); + struct dirent *entry; + DIR *dir = opendir(directory.c_str()); + if (dir == NULL) + { + return; + } + while ((entry = readdir(dir)) != NULL) + { + if (entry->d_type == DT_REG) + { + std::string filename(entry->d_name); + if (filename.find(patternExt) != std::string::npos) + { + filename.erase(filename.find_last_of("."), std::string::npos); + filelist[filename] = directory + "/" + entry->d_name; + } + } + if (recursive && (entry->d_type == DT_DIR)) + { + std::string subdir = directory + "/" + entry->d_name; + if ((strcmp(entry->d_name, ".") != 0) && (strcmp(entry->d_name, "..") != 0)) + { + readDirectory(subdir, pattern, filelist, recursive); + } + } + } + closedir(dir); +#endif +} \ No newline at end of file diff --git a/src/render/ShaderLoader.h b/src/render/ShaderLoader.h new file mode 100644 index 0000000..f8f062d --- /dev/null +++ b/src/render/ShaderLoader.h @@ -0,0 +1,17 @@ +#ifndef SHADERLOADER_H +#define SHADERLOADER_H + +#include +#include +#include + +class ShaderLoader +{ +public: + ShaderLoader(); + ~ShaderLoader(); + static VkPipelineShaderStageCreateInfo loadShader(VkDevice device, std::string filename, VkShaderStageFlagBits stage); + static void readDirectory(const std::string &directory, const std::string &pattern, std::map &filelist, bool recursive); +}; + +#endif \ No newline at end of file diff --git a/src/render/VulkanDevice.cpp b/src/render/VulkanDevice.cpp deleted file mode 100644 index f79837f..0000000 --- a/src/render/VulkanDevice.cpp +++ /dev/null @@ -1,213 +0,0 @@ -#include "VulkanDevice.h" -#include -#include -#include - -VULKANBASE_NAMESPACE_BEGIN - -VulkanDevice::VulkanDevice() - :m_commandPool(VK_NULL_HANDLE) -{ - -} - -VulkanDevice::VulkanDevice(VkPhysicalDevice physicalDevice) -{ - /// 检查物理设备是否存在,后续使用log方式记录 - assert(physicalDevice); - m_physicalDevice = physicalDevice; - - /// 获取设备信息 - vkGetPhysicalDeviceProperties(physicalDevice, &m_properties); - /// 获取设备支持的功能 - vkGetPhysicalDeviceFeatures(physicalDevice, &m_features); - /// 获取设备内存信息,用于创建内存 - vkGetPhysicalDeviceMemoryProperties(physicalDevice, &m_memoryProperties); - /// 队列族信息,用于设备创建时获取设置获取的队列 - uint32_t queueFamilyCount; - vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr); - /// 检查设备队列族数量,必须大于0 - assert(queueFamilyCount > 0); - m_queueFamilyProperties.resize(queueFamilyCount); - vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, m_queueFamilyProperties.data()); -} - - -VulkanDevice::~VulkanDevice() -{ - if (m_commandPool) { - vkDestroyCommandPool(m_logicalDevice, m_commandPool, nullptr); - } - if (m_logicalDevice) { - vkDestroyDevice(m_logicalDevice, nullptr); - } -} - -VkDevice VulkanDevice::getLogicalDevice() -{ - return m_logicalDevice; -} - -VkPhysicalDevice VulkanDevice::getPhysicalDevice() -{ - return m_physicalDevice; -} - -uint32_t VulkanDevice::getMemoryType(uint32_t typeBits, VkMemoryPropertyFlags properties, VkBool32* memTypeFound) -{ - for (uint32_t i = 0; i < m_memoryProperties.memoryTypeCount; i++) { - if ((typeBits & 1) == 1) { - if ((m_memoryProperties.memoryTypes[i].propertyFlags & properties) == properties) { - if (memTypeFound) { - *memTypeFound = true; - } - return i; - } - } - typeBits >>= 1; - } - - if (memTypeFound) { - *memTypeFound = false; - return 0; - } - else { - throw std::runtime_error("Could not find a matching memory type"); - } -} - -uint32_t VulkanDevice::getQueueFamilyIndex(VkQueueFlagBits queueFlags) -{ - /// 获取支持计算的队列组索引 - if (queueFlags & VK_QUEUE_COMPUTE_BIT) - { - for (uint32_t i = 0; i < static_cast(m_queueFamilyProperties.size()); i++) { - if ((m_queueFamilyProperties[i].queueFlags & queueFlags) && ((m_queueFamilyProperties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) == 0)) { - return i; - break; - } - } - } - - /// 对于其他的队列类型,如果当前没有单独的计算队列,返回支持标志类型的第一个队列 - for (uint32_t i = 0; i < static_cast(m_queueFamilyProperties.size()); i++) { - if (m_queueFamilyProperties[i].queueFlags & queueFlags) { - return i; - break; - } - } - - throw std::runtime_error("Could not find a matching queue family index"); -} - -VkResult VulkanDevice::createBuffer(VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, VkDeviceSize size, VkBuffer* buffer, VkDeviceMemory* memory, void* data) -{ - // 创建buffer句柄 - VkBufferCreateInfo bufferCreateInfo{}; - bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; - bufferCreateInfo.usage = usageFlags; - bufferCreateInfo.size = size; - bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - VK_CHECK_RESULT(vkCreateBuffer(m_logicalDevice, &bufferCreateInfo, nullptr, buffer)); - - // 创建buffer的设备内存分配信息 - VkMemoryRequirements memReqs; - VkMemoryAllocateInfo memAlloc{}; - memAlloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - vkGetBufferMemoryRequirements(m_logicalDevice, *buffer, &memReqs); - memAlloc.allocationSize = memReqs.size; - /// 获取符合buffer的设备内存类型索引 - memAlloc.memoryTypeIndex = getMemoryType(memReqs.memoryTypeBits, memoryPropertyFlags); - VK_CHECK_RESULT(vkAllocateMemory(m_logicalDevice, &memAlloc, nullptr, memory)); - - // 如何buffer指针已经存在,复制或者映射该buffer - if (data != nullptr) - { - void* mapped; - VK_CHECK_RESULT(vkMapMemory(m_logicalDevice, *memory, 0, size, 0, &mapped)); - memcpy(mapped, data, size); - // 如果host coherency 未设置, 对buffer进行flush,让设备侧可见 - if ((memoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) == 0) - { - VkMappedMemoryRange mappedRange{}; - mappedRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; - mappedRange.memory = *memory; - mappedRange.offset = 0; - mappedRange.size = size; - vkFlushMappedMemoryRanges(m_logicalDevice, 1, &mappedRange); - } - vkUnmapMemory(m_logicalDevice, *memory); - } - - // 绑定设备内存到buffer object - VK_CHECK_RESULT(vkBindBufferMemory(m_logicalDevice, *buffer, *memory, 0)); - - return VK_SUCCESS; -} - -VkCommandPool VulkanDevice::createCommandPool(uint32_t queueFamilyIndex, VkCommandPoolCreateFlags createFlags) -{ - /// 创建命令缓冲池 - VkCommandPoolCreateInfo cmdPoolInfo = {}; - cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - cmdPoolInfo.queueFamilyIndex = queueFamilyIndex; - cmdPoolInfo.flags = createFlags; - VkCommandPool cmdPool; - VK_CHECK_RESULT(vkCreateCommandPool(m_logicalDevice, &cmdPoolInfo, nullptr, &cmdPool)); - return cmdPool; -} - -VkCommandBuffer VulkanDevice::createCommandBuffer(VkCommandBufferLevel level, bool begin) -{ - VkCommandBufferAllocateInfo cmdBufAllocateInfo{}; - cmdBufAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - cmdBufAllocateInfo.commandPool = m_commandPool; - cmdBufAllocateInfo.level = level; - cmdBufAllocateInfo.commandBufferCount = 1; - - VkCommandBuffer cmdBuffer; - VK_CHECK_RESULT(vkAllocateCommandBuffers(m_logicalDevice, &cmdBufAllocateInfo, &cmdBuffer)); - - // 开始记录指令buffer - if (begin) { - beginCommandBuffer(cmdBuffer); - } - - return cmdBuffer; -} - -void VulkanDevice::beginCommandBuffer(VkCommandBuffer commandBuffer) -{ - VkCommandBufferBeginInfo commandBufferBI{}; - commandBufferBI.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - VK_CHECK_RESULT(vkBeginCommandBuffer(commandBuffer, &commandBufferBI)); -} - -void VulkanDevice::flushCommandBuffer(VkCommandBuffer commandBuffer, VkQueue queue, bool free) -{ - VK_CHECK_RESULT(vkEndCommandBuffer(commandBuffer)); - - VkSubmitInfo submitInfo{}; - submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &commandBuffer; - - //创建同步栅栏,确保命令buffer执行完毕 - VkFenceCreateInfo fenceInfo{}; - fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; - VkFence fence; - VK_CHECK_RESULT(vkCreateFence(m_logicalDevice, &fenceInfo, nullptr, &fence)); - - // 提交队列 - VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, fence)); - // 等待栅栏发出信号说明命令buffer执行完毕 - VK_CHECK_RESULT(vkWaitForFences(m_logicalDevice, 1, &fence, VK_TRUE, 100000000000)); - // 同步栅栏使命结束,销毁 - vkDestroyFence(m_logicalDevice, fence, nullptr); - // 需要的时候,销毁命令buffer - if (free) { - vkFreeCommandBuffers(m_logicalDevice, m_commandPool, 1, &commandBuffer); - } -} - -VULKANBASE_NAMESPACE_END \ No newline at end of file diff --git a/src/render/VulkanDevice.h b/src/render/VulkanDevice.h deleted file mode 100644 index 68a0c88..0000000 --- a/src/render/VulkanDevice.h +++ /dev/null @@ -1,97 +0,0 @@ -#ifndef VULKANDEVICE_H -#define VULKANDEVICE_H - -#include "VulkanBase_Marco.h" - -#include -#include - -VULKANBASE_NAMESPACE_BEGIN - -/// @brief vulkan的设备类 -class VulkanDevice -{ -/// @brief 构造和setter/getter -public: - VulkanDevice(); - VulkanDevice(VkPhysicalDevice physicalDevice); - ~VulkanDevice(); - - VkDevice getLogicalDevice(); - VkPhysicalDevice getPhysicalDevice(); - -public: - /// @brief 获取已设置所有请求属性位的设备内存类型的索引 - /// @param typeBits 请求的每种设备内存类型设置的位掩码,通常通过VkMemoryRequirements获取 - /// @param properties 要请求的设备内存类型的属性位掩码 - /// @param memTypeFound 如果符合的设备内存类型存在,则该指针的布尔值为true - /// @return 请求的设备内存类型的索引 - /// @throw 如果 memTypeFound 为空且找不到支持需要的属性的设备内存类型,则抛出异常 - uint32_t getMemoryType(uint32_t typeBits, VkMemoryPropertyFlags properties, VkBool32* memTypeFound = nullptr); - /// @brief 获取支持所请求队列标志的队列族的索引 - /// @param queueFlags 用于查找队列族索引的队列标志 - /// @return 与标志匹配的队列族索引 - /// @throw 如果找不到支持所请求标志的队列族索引,则抛出异常 - uint32_t getQueueFamilyIndex(VkQueueFlagBits queueFlags); - /// @brief 据分配的物理设备创建逻辑设备,同时获取默认队列族索引 - /// @param enabledFeatures 在创建设备时启用某些功能 - /// @param enabledExtensions 在创建设备时启用某些扩展 - /// @param requestedQueueTypes 指定要从设备请求的队列类型 - /// @return 逻辑设备是否成功创建 - VkResult createLogicalDevice(VkPhysicalDeviceFeatures enabledFeatures, std::vector enabledExtensions, VkQueueFlags requestedQueueTypes = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT); - /// @brief 在设备上创建缓冲区 - /// @param usageFlags 缓冲区的使用标志位掩码(即索引、顶点、统一缓冲区) - /// @param memoryPropertyFlags 此缓冲区的内存属性(即设备本地、主机可见、一致) - /// @param size 缓冲区的大小(以字节为单位) - /// @param buffer 指向函数获取的缓冲区句柄的指针 - /// @param memory 指向函数获取的设备内存句柄的指针 - /// @param data 指向创建后应复制到缓冲区的数据的指针(可选,如果未设置,则不会复制任何数据) - /// @return 如果已创建缓冲区句柄和设备内存并且已复制数据(可选传递),则返回 VK_SUCCESS - VkResult createBuffer(VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, VkDeviceSize size, VkBuffer* buffer, VkDeviceMemory* memory, void* data = nullptr); - /// @brief 创建命令池以分配命令缓冲区 - /// @param queueFamilyIndex 要为其创建命令池的队列的系列索引 - /// @param createFlags 可选)命令池创建标志(默认为 VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT) - /// @return 创建的命令缓冲区的句柄 - VkCommandPool createCommandPool(uint32_t queueFamilyIndex, VkCommandPoolCreateFlags createFlags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT); - /// @brief 从命令池中分配命令缓冲区 - /// @param level 新命令缓冲区的级别(主或次) - /// @param begin 为 true时开始在新命令缓冲区上进行记录 - /// @return 分配的命令缓冲区的句柄 - VkCommandBuffer createCommandBuffer(VkCommandBufferLevel level, bool begin = false); - /// @brief 开始在指定命令缓冲区记录 - /// @param commandBuffer - void beginCommandBuffer(VkCommandBuffer commandBuffer); - /// @brief 停止指定命令缓冲区记录并将其提交到队列,使用栅栏来确保命令缓冲区已完成执行 - /// @param commandBuffer 要刷新的命令缓冲区 - /// @param queue 要将命令缓冲区提交到的队列 - /// @param free 提交后释放命令缓冲区(默认为 true) - void flushCommandBuffer(VkCommandBuffer commandBuffer, VkQueue queue, bool free = true); - -private: - - VkPhysicalDevice m_physicalDevice; - VkDevice m_logicalDevice; - VkPhysicalDeviceProperties m_properties; - VkPhysicalDeviceFeatures m_features; - VkPhysicalDeviceFeatures m_enabledFeatures; - VkPhysicalDeviceMemoryProperties m_memoryProperties; - std::vector m_queueFamilyProperties; - VkCommandPool m_commandPool; - - struct { - uint32_t graphics; - uint32_t compute; - } m_queueFamilyIndices; -}; - - - - - -VULKANBASE_NAMESPACE_END - - - - - -#endif // !VULKANDEVICE_H diff --git a/src/render/render.cpp b/src/render/render.cpp index 4b424f7..9267a79 100644 --- a/src/render/render.cpp +++ b/src/render/render.cpp @@ -1,18 +1,26 @@ - +#pragma once -#ifndef TINYGLTF_IMPLEMENTATION -#define TINYGLTF_IMPLEMENTATION +#include "render.h" + +#include "PbrBaseTexture.h" +#include "PbrTextureCoordSet.h" +#include "glTFMainModel.h" #include "PushConstBlockMaterial.h" -#include "VulkanTexture.hpp" -#include "glTFModel.h" +#include "ShaderLoader.h" +#include "VulkanTexture.h" + +#include "glTFMaterial.h" +#include "glTFPrimitive.h" +#include "glTFSkin.h" +#include "glTFTexture.h" #include "glm/gtc/matrix_transform.hpp" #include "renderUniformBufferSet.h" -#include "vulkan/vulkan_core.h" + #include #include +#include -#endif #ifndef STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_IMPLEMENTATION #endif @@ -21,7 +29,6 @@ #define TINYGLTF_NO_STB_IMAGE_WRITE #endif -#include "render.h" // #include "VulkanUtils.hpp" // #include "assetLoader.h" @@ -30,21 +37,21 @@ PlumageRender::PlumageRender() title = "plumage render"; } -void PlumageRender::renderNode(glTFModel::Node *node, uint32_t cbIndex, glTFModel::Material::AlphaMode alphaMode) +void PlumageRender::renderNode(glTFLoader::glTFNode *node, uint32_t cbIndex, glTFLoader::AlphaMode alphaMode) { - if (node->mesh) + if (node->getMesh()) { // Render mesh primitives - for (glTFModel::Primitive *primitive : node->mesh->primitives) + for (glTFLoader::glTFPrimitive *primitive : node->getMesh()->getPrimitives()) { - if (primitive->material.alphaMode == alphaMode) + if (primitive->getGltfMaterial().getAlphaMode() == alphaMode) { VkPipeline pipeline = VK_NULL_HANDLE; switch (alphaMode) { - case glTFModel::Material::ALPHAMODE_OPAQUE: - case glTFModel::Material::ALPHAMODE_MASK: - if (primitive->material.doubleSided) + case glTFLoader::ALPHAMODE_OPAQUE: + case glTFLoader::ALPHAMODE_MASK: + if (primitive->getGltfMaterial().getDoublesided()) { pipeline = m_pipelineList.getPbrDoubleSided(); } @@ -53,7 +60,7 @@ void PlumageRender::renderNode(glTFModel::Node *node, uint32_t cbIndex, glTFMode pipeline = m_pipelineList.getPbr(); } break; - case glTFModel::Material::ALPHAMODE_BLEND: + case glTFLoader::ALPHAMODE_BLEND: pipeline = m_pipelineList.getPbrAlphaBlend(); break; } @@ -66,78 +73,81 @@ void PlumageRender::renderNode(glTFModel::Node *node, uint32_t cbIndex, glTFMode const std::vector descriptorsets = { descriptorSets[cbIndex].getScene(), - primitive->material.descriptorSet, - node->mesh->uniformBuffer.descriptorSet, + primitive->getGltfMaterial().getDescriptorSet(), + node->getMesh()->getUniformBuffer().descriptorSet, }; vkCmdBindDescriptorSets(commandBuffers[cbIndex], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, static_cast(descriptorsets.size()), descriptorsets.data(), 0, NULL); // Pass material parameters as push constants PushConstBlockMaterial m_pushConstBlockMaterial; - m_pushConstBlockMaterial.setEmissiveFactor(primitive->material.emissiveFactor); + PBR::PbrBaseTexture *pbrBaseTexture = primitive->getGltfMaterial().getPbrBaseTexture(); + glTFLoader::TextureCoordSet *texCoordSet = primitive->getGltfMaterial().getTextureCoordSet(); + m_pushConstBlockMaterial.setEmissiveFactor(pbrBaseTexture->getEmissiveFactor()); // To save push constant space, availabilty and texture coordiante set are combined // -1 = texture not used for this material, >= 0 texture used and index of texture coordinate set - if (primitive->material.baseColorTexture != nullptr) + if (pbrBaseTexture->getBaseColorTexture() != nullptr) { - m_pushConstBlockMaterial.setColorTextureSet(primitive->material.texCoordSets.baseColor); + m_pushConstBlockMaterial.setColorTextureSet(texCoordSet->getBaseColor()); } else { m_pushConstBlockMaterial.setColorTextureSet(-1); } - if (primitive->material.normalTexture != nullptr) + if (pbrBaseTexture->getNormalTexture() != nullptr) { - m_pushConstBlockMaterial.setNormalTextureSet(primitive->material.texCoordSets.normal); + m_pushConstBlockMaterial.setNormalTextureSet(texCoordSet->getNormal()); } else { m_pushConstBlockMaterial.setNormalTextureSet(-1); } - if (primitive->material.occlusionTexture != nullptr) + if (pbrBaseTexture->getOcclusionTexture() != nullptr) { - m_pushConstBlockMaterial.setOcclusionTextureSet(primitive->material.texCoordSets.occlusion); + m_pushConstBlockMaterial.setOcclusionTextureSet(texCoordSet->getOcclusion()); } else { m_pushConstBlockMaterial.setOcclusionTextureSet(-1); } - if (primitive->material.emissiveTexture != nullptr) + if (pbrBaseTexture->getEmissiveTexture() != nullptr) { - m_pushConstBlockMaterial.setEmissiveTextureSet(primitive->material.texCoordSets.emissive); + m_pushConstBlockMaterial.setEmissiveTextureSet(texCoordSet->getEmissive()); } else { m_pushConstBlockMaterial.setEmissiveTextureSet(-1); } - if (primitive->material.alphaMode == glTFModel::Material::ALPHAMODE_MASK) + if (primitive->getGltfMaterial().getAlphaMode() == glTFLoader::ALPHAMODE_MASK) { - m_pushConstBlockMaterial.setAlphaMask(static_cast(primitive->material.alphaMode)); + m_pushConstBlockMaterial.setAlphaMask(static_cast(primitive->getGltfMaterial().getAlphaMode())); } - m_pushConstBlockMaterial.setAlphaMaskCutoff(primitive->material.alphaCutoff); + m_pushConstBlockMaterial.setAlphaMaskCutoff(primitive->getGltfMaterial().getAlphaCutOff()); // TODO: glTF specs states that metallic roughness should be preferred, even if specular glosiness is present - if (primitive->material.pbrWorkflows.metallicRoughness) + glTFLoader::glTFMaterial *material = &primitive->getGltfMaterial(); + if (material->getPbrWorkFlow()->getMetallicRoughness()) { // Metallic roughness workflow m_pushConstBlockMaterial.setWorkflow(static_cast(PBR_WORKFLOW_METALLIC_ROUGHNESS)); - m_pushConstBlockMaterial.setBaseColorFactor(primitive->material.baseColorFactor); - m_pushConstBlockMaterial.setMetallicFactor(primitive->material.metallicFactor); - m_pushConstBlockMaterial.setRoughnessFactor(primitive->material.roughnessFactor); - if (primitive->material.metallicRoughnessTexture != nullptr) + m_pushConstBlockMaterial.setBaseColorFactor(pbrBaseTexture->getBaseColorFactor()); + m_pushConstBlockMaterial.setMetallicFactor(pbrBaseTexture->getMetallicFactor()); + m_pushConstBlockMaterial.setRoughnessFactor(pbrBaseTexture->getRoughnessFactor()); + if (pbrBaseTexture->getMetalicRoughnessTexture() != nullptr) { - m_pushConstBlockMaterial.setPhysicalDescriptorTextureSet(primitive->material.texCoordSets.metallicRoughness); + m_pushConstBlockMaterial.setPhysicalDescriptorTextureSet(texCoordSet->getMetallicRoughness()); } else { m_pushConstBlockMaterial.setPhysicalDescriptorTextureSet(-1); } - if (primitive->material.baseColorTexture != nullptr) + if (pbrBaseTexture->getBaseColorTexture() != nullptr) { - m_pushConstBlockMaterial.setColorTextureSet(primitive->material.texCoordSets.baseColor); + m_pushConstBlockMaterial.setColorTextureSet(texCoordSet->getBaseColor()); } else { @@ -145,47 +155,49 @@ void PlumageRender::renderNode(glTFModel::Node *node, uint32_t cbIndex, glTFMode } } - if (primitive->material.pbrWorkflows.specularGlossiness) + if (material->getPbrWorkFlow()->getSpecularGlossiness()) { // Specular glossiness workflow m_pushConstBlockMaterial.setWorkflow(static_cast(PBR_WORKFLOW_SPECULAR_GLOSINESS)); - if (primitive->material.extension.specularGlossinessTexture != nullptr) + if (material->getTextureExtension()->getSpecularGlossinessTexture() != nullptr) { - m_pushConstBlockMaterial.setPhysicalDescriptorTextureSet(primitive->material.texCoordSets.specularGlossiness); + m_pushConstBlockMaterial.setPhysicalDescriptorTextureSet(texCoordSet->getSpecularGlossiness()); } else { m_pushConstBlockMaterial.setPhysicalDescriptorTextureSet(-1); } - if (primitive->material.extension.diffuseTexture != nullptr) + if (material->getTextureExtension()->getDiffuseTexture() != nullptr) { - m_pushConstBlockMaterial.setColorTextureSet(primitive->material.texCoordSets.baseColor); + m_pushConstBlockMaterial.setColorTextureSet(texCoordSet->getBaseColor()); } else { m_pushConstBlockMaterial.setColorTextureSet(-1); } - m_pushConstBlockMaterial.setDiffuseFactor(primitive->material.extension.diffuseFactor); - m_pushConstBlockMaterial.setSpecularFactor(glm::vec4(primitive->material.extension.specularFactor, 1.0f)); + m_pushConstBlockMaterial.setDiffuseFactor(material->getTextureExtension()->getDiffuseFactor()); + m_pushConstBlockMaterial.setSpecularFactor(glm::vec4(material->getTextureExtension()->getSpecularFactor(), 1.0f)); } vkCmdPushConstants(commandBuffers[cbIndex], pipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PushConstBlockMaterial), &m_pushConstBlockMaterial); - if (primitive->hasIndices) + if (primitive->getHasIndices()) { - vkCmdDrawIndexed(commandBuffers[cbIndex], primitive->indexCount, 1, primitive->firstIndex, 0, 0); + vkCmdDrawIndexed(commandBuffers[cbIndex], primitive->getIndexCount(), 1, primitive->getFirstIndex(), 0, 0); } else { - vkCmdDraw(commandBuffers[cbIndex], primitive->vertexCount, 1, 0, 0); + vkCmdDraw(commandBuffers[cbIndex], primitive->getVertexCount(), 1, 0, 0); } } } }; - for (auto child : node->children) + + std::vector nodeChildren = node->getChildren(); + for (auto child : nodeChildren) { renderNode(child, cbIndex, alphaMode); } @@ -249,12 +261,12 @@ void PlumageRender::buildCommandBuffers() m_sceneModel.getSkyBox().draw(currentCB); } - glTFModel::Model &model = m_sceneModel.getScene(); + glTFLoader::glTFMainModel &model = m_sceneModel.getScene(); - vkCmdBindVertexBuffers(currentCB, 0, 1, &model.vertices.buffer, offsets); - if (model.indices.buffer != VK_NULL_HANDLE) + vkCmdBindVertexBuffers(currentCB, 0, 1, &model.getModelVertex().buffer, offsets); + if (model.getModelIndex().buffer != VK_NULL_HANDLE) { - vkCmdBindIndexBuffer(currentCB, model.indices.buffer, 0, VK_INDEX_TYPE_UINT32); + vkCmdBindIndexBuffer(currentCB, model.getModelIndex().buffer, 0, VK_INDEX_TYPE_UINT32); } boundPipeline = VK_NULL_HANDLE; @@ -329,7 +341,7 @@ void PlumageRender::loadAssets() std::cout << msg << std::endl; } - readDirectory(assetpath + "environments", "*.ktx", environments, false); + ShaderLoader::readDirectory(assetpath + "environments", "*.ktx", environments, false); m_sceneTextures.getEmpty().loadFromFile(PlumageRender::m_configFilePath.getEmptyEnvmapFilePath(), VK_FORMAT_R8G8B8A8_UNORM, vulkanDevice, queue); @@ -459,7 +471,7 @@ void PlumageRender::setupDescriptors() VkDescriptorSetLayout sceneDescriptorSetLayout = m_descriptorSetLayoutList.getScene(); VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorSetLayoutCI, nullptr, &sceneDescriptorSetLayout)); - vks::TextureCubeMap refIrradianceCube = m_sceneTextures.getIrradianceCube(); + VulkanBase::TextureCubeMap refIrradianceCube = m_sceneTextures.getIrradianceCube(); for (auto i = 0; i < descriptorSets.size(); i++) { @@ -605,7 +617,7 @@ void PlumageRender::setupDescriptors() } } - vks::TextureCubeMap refPrefilterCube = m_sceneTextures.getPrefilteredCube(); + VulkanBase::TextureCubeMap refPrefilterCube = m_sceneTextures.getPrefilteredCube(); // Skybox (fixed set) for (auto i = 0; i < uniformBuffers.size(); i++) { @@ -752,8 +764,8 @@ void PlumageRender::preparePipelines() // Skybox pipeline (background cube) shaderStages = { - loadShader(device, m_configFilePath.getSkyboxVertShaderPath(), VK_SHADER_STAGE_VERTEX_BIT), - loadShader(device, m_configFilePath.getSkyboxFragShaderPath(), VK_SHADER_STAGE_FRAGMENT_BIT)}; + ShaderLoader::loadShader(device, m_configFilePath.getSkyboxVertShaderPath(), VK_SHADER_STAGE_VERTEX_BIT), + ShaderLoader::loadShader(device, m_configFilePath.getSkyboxFragShaderPath(), VK_SHADER_STAGE_FRAGMENT_BIT)}; VkPipeline skyboxPipeline = m_pipelineList.getSkybox(); VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &skyboxPipeline)); for (auto shaderStage : shaderStages) @@ -763,8 +775,8 @@ void PlumageRender::preparePipelines() // PBR pipeline shaderStages = { - loadShader(device, m_configFilePath.getPbrVertShaderPath(), VK_SHADER_STAGE_VERTEX_BIT), - loadShader(device, m_configFilePath.getPbrFragShaderPath(), VK_SHADER_STAGE_FRAGMENT_BIT)}; + ShaderLoader::loadShader(device, m_configFilePath.getPbrVertShaderPath(), VK_SHADER_STAGE_VERTEX_BIT), + ShaderLoader::loadShader(device, m_configFilePath.getPbrFragShaderPath(), VK_SHADER_STAGE_FRAGMENT_BIT)}; depthStencilStateCI.depthWriteEnable = VK_TRUE; depthStencilStateCI.depthTestEnable = VK_TRUE; VkPipeline pbrPipeline = m_pipelineList.getPbr(); @@ -807,7 +819,7 @@ void PlumageRender::generateCubemaps() for (uint32_t target = 0; target < PREFILTEREDENV + 1; target++) { - vks::TextureCubeMap cubemap; + VulkanBase::TextureCubeMap cubemap; auto tStart = std::chrono::high_resolution_clock::now(); @@ -1132,14 +1144,14 @@ void PlumageRender::generateCubemaps() pipelineCI.pStages = shaderStages.data(); pipelineCI.renderPass = renderpass; - shaderStages[0] = loadShader(device, m_configFilePath.getFilterVertShaderPath(), VK_SHADER_STAGE_VERTEX_BIT); + shaderStages[0] = ShaderLoader::loadShader(device, m_configFilePath.getFilterVertShaderPath(), VK_SHADER_STAGE_VERTEX_BIT); switch (target) { case IRRADIANCE: - shaderStages[1] = loadShader(device, m_configFilePath.getIrradianceFragShaderPath(), VK_SHADER_STAGE_FRAGMENT_BIT); + shaderStages[1] = ShaderLoader::loadShader(device, m_configFilePath.getIrradianceFragShaderPath(), VK_SHADER_STAGE_FRAGMENT_BIT); break; case PREFILTEREDENV: - shaderStages[1] = loadShader(device, m_configFilePath.getPrefilterEnvmapFragShaderPath(), VK_SHADER_STAGE_FRAGMENT_BIT); + shaderStages[1] = ShaderLoader::loadShader(device, m_configFilePath.getPrefilterEnvmapFragShaderPath(), VK_SHADER_STAGE_FRAGMENT_BIT); break; }; VkPipeline pipeline; @@ -1360,7 +1372,7 @@ void PlumageRender::generateBRDFLUT() const VkFormat format = VK_FORMAT_R16G16_SFLOAT; const int32_t dim = 2048; - vks::Texture2D RefLutBrdfTex = m_sceneTextures.getLutBrdf(); + VulkanBase::Texture2D RefLutBrdfTex = m_sceneTextures.getLutBrdf(); // Image VkImageCreateInfo imageCI{}; @@ -1553,8 +1565,8 @@ void PlumageRender::generateBRDFLUT() // Look-up-table (from BRDF) pipeline shaderStages = { - loadShader(device, m_configFilePath.getBrdfVertShaderPath(), VK_SHADER_STAGE_VERTEX_BIT), - loadShader(device, m_configFilePath.getBrdfFragShaderPath(), VK_SHADER_STAGE_FRAGMENT_BIT)}; + ShaderLoader::loadShader(device, m_configFilePath.getBrdfVertShaderPath(), VK_SHADER_STAGE_VERTEX_BIT), + ShaderLoader::loadShader(device, m_configFilePath.getBrdfFragShaderPath(), VK_SHADER_STAGE_FRAGMENT_BIT)}; VkPipeline pipeline; VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipeline)); for (auto shaderStage : shaderStages) @@ -2264,9 +2276,8 @@ void PlumageRender::updateUIOverlay() if (!filename.empty()) { vkDeviceWaitIdle(device); - std::wstring_convert> converter; + std::string stringFilename = std::filesystem::path(filename).string(); - std::string stringFilename = converter.to_bytes(filename); loadScene(stringFilename); setupDescriptors(); updateCBs = true; diff --git a/src/render/render.h b/src/render/render.h index 22c99b9..930be68 100644 --- a/src/render/render.h +++ b/src/render/render.h @@ -5,6 +5,8 @@ #include "RenderOffScreen.h" #include "RenderPipelineList.h" #include "RenderSceneTextures.h" +#include "glTFMaterial.h" +#include "glTFSkin.h" #include "renderShaderData.h" #if defined(_WIN32) #include @@ -36,8 +38,7 @@ #include "VulkanExampleBase.h" #include "glTFModel.h" #include "ui.hpp" -#include -#include +#include #include #define ENABLE_VALIDATION false @@ -55,6 +56,7 @@ #include "RenderPipelineList.h" #include "RenderSceneTextures.h" #include "SceneUBOMatrices.h" +#include "ShaderLoader.h" #include "SkyboxUBOMatrices.h" #include "VertexStagingBuffer.h" #include "renderEffectState.h" @@ -184,7 +186,7 @@ public: delete gui; } - void renderNode(glTFModel::Node *node, uint32_t cbIndex, glTFModel::Material::AlphaMode alphaMode); + void renderNode(glTFLoader::glTFNode *node, uint32_t cbIndex, glTFLoader::AlphaMode alphaMode); void loadScene(std::string filename); void loadEnvironment(std::string filename); void buildCommandBuffers(); diff --git a/src/render/renderSceneModel.cpp b/src/render/renderSceneModel.cpp index 765f5df..95bc742 100644 --- a/src/render/renderSceneModel.cpp +++ b/src/render/renderSceneModel.cpp @@ -9,22 +9,22 @@ RenderSceneModel::~RenderSceneModel() { } -void RenderSceneModel::setScene(glTFModel::Model value) +void RenderSceneModel::setScene(glTFLoader::glTFMainModel value) { m_scene = value; } -glTFModel::Model &RenderSceneModel::getScene() +glTFLoader::glTFMainModel &RenderSceneModel::getScene() { return m_scene; } -void RenderSceneModel::setSkyBox(glTFModel::Model value) +void RenderSceneModel::setSkyBox(glTFLoader::glTFMainModel value) { m_skybox = value; } -glTFModel::Model &RenderSceneModel::getSkyBox() +glTFLoader::glTFMainModel &RenderSceneModel::getSkyBox() { return m_skybox; } diff --git a/src/render/renderSceneModel.h b/src/render/renderSceneModel.h index 6ca2e71..33b444a 100644 --- a/src/render/renderSceneModel.h +++ b/src/render/renderSceneModel.h @@ -1,7 +1,7 @@ #pragma once -#include "glTFModel.h" -#include "vulkan/vulkan.h" +#include "glTFMainModel.h" +#include class RenderSceneModel { @@ -9,16 +9,16 @@ public: RenderSceneModel(); ~RenderSceneModel(); - void setScene(glTFModel::Model value); - glTFModel::Model &getScene(); + void setScene(glTFLoader::glTFMainModel value); + glTFLoader::glTFMainModel &getScene(); - void setSkyBox(glTFModel::Model value); - glTFModel::Model &getSkyBox(); + void setSkyBox(glTFLoader::glTFMainModel value); + glTFLoader::glTFMainModel &getSkyBox(); void destroyScene(VkDevice device); void destroySkyBox(VkDevice device); private: - glTFModel::Model m_scene; - glTFModel::Model m_skybox; + glTFLoader::glTFMainModel m_scene; + glTFLoader::glTFMainModel m_skybox; }; diff --git a/src/render/renderUniformBufferSet.cpp b/src/render/renderUniformBufferSet.cpp index 25797a1..138404b 100644 --- a/src/render/renderUniformBufferSet.cpp +++ b/src/render/renderUniformBufferSet.cpp @@ -9,33 +9,33 @@ RenderUniformBufferSet::~RenderUniformBufferSet() } // Getter method definitions -Buffer &RenderUniformBufferSet::getScene() +VulkanBase::Buffer &RenderUniformBufferSet::getScene() { return scene; } -Buffer &RenderUniformBufferSet::getSkybox() +VulkanBase::Buffer &RenderUniformBufferSet::getSkybox() { return skybox; } -Buffer &RenderUniformBufferSet::getParams() +VulkanBase::Buffer &RenderUniformBufferSet::getParams() { return params; } // Setter method definitions -void RenderUniformBufferSet::setScene(Buffer &buffer) +void RenderUniformBufferSet::setScene(VulkanBase::Buffer &buffer) { scene = buffer; } -void RenderUniformBufferSet::setSkybox(Buffer &buffer) +void RenderUniformBufferSet::setSkybox(VulkanBase::Buffer &buffer) { skybox = buffer; } -void RenderUniformBufferSet::setParams(Buffer &buffer) +void RenderUniformBufferSet::setParams(VulkanBase::Buffer &buffer) { params = buffer; } \ No newline at end of file diff --git a/src/render/renderUniformBufferSet.h b/src/render/renderUniformBufferSet.h index b0b0030..d618b10 100644 --- a/src/render/renderUniformBufferSet.h +++ b/src/render/renderUniformBufferSet.h @@ -1,6 +1,7 @@ -#pragma once +#ifndef RENDER_UNIFORM_BUFFER_SET_H +#define RENDER_UNIFORM_BUFFER_SET_H -#include "VulkanUtils.hpp" +#include "VulkanBuffer.h" class RenderUniformBufferSet { @@ -9,17 +10,19 @@ public: ~RenderUniformBufferSet(); // Getter methods - Buffer &getScene(); - Buffer &getSkybox(); - Buffer &getParams(); + VulkanBase::Buffer &getScene(); + VulkanBase::Buffer &getSkybox(); + VulkanBase::Buffer &getParams(); // Setter methods - void setScene(Buffer &buffer); - void setSkybox(Buffer &buffer); - void setParams(Buffer &buffer); + void setScene(VulkanBase::Buffer &buffer); + void setSkybox(VulkanBase::Buffer &buffer); + void setParams(VulkanBase::Buffer &buffer); private: - Buffer scene; - Buffer skybox; - Buffer params; -}; \ No newline at end of file + VulkanBase::Buffer scene; + VulkanBase::Buffer skybox; + VulkanBase::Buffer params; +}; + +#endif \ No newline at end of file diff --git a/src/render/ui.hpp b/src/render/ui.hpp new file mode 100644 index 0000000..9b69b92 --- /dev/null +++ b/src/render/ui.hpp @@ -0,0 +1,404 @@ + + +#include +#if defined(__ANDROID__) +#include +#endif + +#include + +#include +#include +#include + +#define GLM_FORCE_RADIANS +#define GLM_FORCE_DEPTH_ZERO_TO_ONE +#include +#include + +#include +#include +#include + +#include "ShaderLoader.h" +#include "VulkanBuffer.h" +#include "VulkanDevice.h" +#include "VulkanTexture.h" +#include "VulkanTools.h" + +struct UI +{ +private: + VkDevice device; + + std::string getAssetPath() const + { + if (_access("./../data/", 0) != -1) + { + + return "./../data/"; + } + else if (_access("./data/", 0) != -1) + { + + return "./data/"; + } + else if (_access("./../../data/", 0) != -1) + { + + return "../../data/"; + } + else + { + + return VK_EXAMPLE_DATA_DIR; + } + } + +public: + VulkanBase::Buffer vertexBuffer, indexBuffer; + VulkanBase::Texture2D fontTexture; + VkPipelineLayout pipelineLayout; + VkPipeline pipeline; + VkDescriptorPool descriptorPool; + VkDescriptorSetLayout descriptorSetLayout; + VkDescriptorSet descriptorSet; + + struct PushConstBlock + { + glm::vec2 scale; + glm::vec2 translate; + } pushConstBlock; + + UI(VulkanBase::VulkanDevice *vulkanDevice, VkRenderPass renderPass, VkQueue queue, VkPipelineCache pipelineCache, VkSampleCountFlagBits multiSampleCount) + { + + this->device = vulkanDevice->getLogicalDevice(); + + ImGui::CreateContext(); + + /* + Font texture loading + */ + ImGuiIO &io = ImGui::GetIO(); + + /// enable keyborad and gamepad input + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; + + unsigned char *fontData; + int texWidth, texHeight; + std::string ttfFilePath = getAssetPath() + "/STXINWEI.TTF"; + io.Fonts->AddFontFromFileTTF(ttfFilePath.data(), 16.0f, NULL, io.Fonts->GetGlyphRangesChineseSimplifiedCommon()); + io.Fonts->GetTexDataAsRGBA32(&fontData, &texWidth, &texHeight); + fontTexture.loadFromBuffer(fontData, texWidth * texHeight * 4 * sizeof(char), VK_FORMAT_R8G8B8A8_UNORM, texWidth, texHeight, vulkanDevice, queue); + + /* + Setup + */ + ImGuiStyle &style = ImGui::GetStyle(); + style.FrameBorderSize = 0.0f; + style.WindowBorderSize = 0.0f; + + /* + Descriptor pool + */ + std::vector poolSizes = { + {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1}}; + VkDescriptorPoolCreateInfo descriptorPoolCI{}; + descriptorPoolCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + descriptorPoolCI.poolSizeCount = 1; + descriptorPoolCI.pPoolSizes = poolSizes.data(); + descriptorPoolCI.maxSets = 1; + VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolCI, nullptr, &descriptorPool)); + + /* + Descriptor set layout + */ + VkDescriptorSetLayoutBinding setLayoutBinding{0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr}; + VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCI{}; + descriptorSetLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + descriptorSetLayoutCI.pBindings = &setLayoutBinding; + descriptorSetLayoutCI.bindingCount = 1; + VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorSetLayoutCI, nullptr, &descriptorSetLayout)); + + /* + Descriptor set + */ + VkDescriptorSetAllocateInfo descriptorSetAllocInfo{}; + descriptorSetAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + descriptorSetAllocInfo.descriptorPool = descriptorPool; + descriptorSetAllocInfo.pSetLayouts = &descriptorSetLayout; + descriptorSetAllocInfo.descriptorSetCount = 1; + VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorSetAllocInfo, &descriptorSet)); + VkWriteDescriptorSet writeDescriptorSet{}; + writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + writeDescriptorSet.descriptorCount = 1; + writeDescriptorSet.dstSet = descriptorSet; + writeDescriptorSet.dstBinding = 0; + writeDescriptorSet.pImageInfo = &fontTexture.descriptor; + vkUpdateDescriptorSets(device, 1, &writeDescriptorSet, 0, nullptr); + + /* + Pipeline layout + */ + VkPushConstantRange pushConstantRange{VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(PushConstBlock)}; + + VkPipelineLayoutCreateInfo pipelineLayoutCI{}; + pipelineLayoutCI.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutCI.pushConstantRangeCount = 1; + pipelineLayoutCI.pPushConstantRanges = &pushConstantRange; + pipelineLayoutCI.setLayoutCount = 1; + pipelineLayoutCI.pSetLayouts = &descriptorSetLayout; + pipelineLayoutCI.pushConstantRangeCount = 1; + pipelineLayoutCI.pPushConstantRanges = &pushConstantRange; + VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCI, nullptr, &pipelineLayout)); + + /* + Pipeline + */ + VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCI{}; + inputAssemblyStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssemblyStateCI.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + + VkPipelineRasterizationStateCreateInfo rasterizationStateCI{}; + rasterizationStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizationStateCI.polygonMode = VK_POLYGON_MODE_FILL; + rasterizationStateCI.cullMode = VK_CULL_MODE_NONE; + rasterizationStateCI.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + rasterizationStateCI.lineWidth = 1.0f; + + VkPipelineColorBlendAttachmentState blendAttachmentState{}; + blendAttachmentState.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + blendAttachmentState.blendEnable = VK_TRUE; + blendAttachmentState.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + blendAttachmentState.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + blendAttachmentState.colorBlendOp = VK_BLEND_OP_ADD; + blendAttachmentState.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + blendAttachmentState.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; + blendAttachmentState.alphaBlendOp = VK_BLEND_OP_ADD; + + VkPipelineColorBlendStateCreateInfo colorBlendStateCI{}; + colorBlendStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlendStateCI.attachmentCount = 1; + colorBlendStateCI.pAttachments = &blendAttachmentState; + + VkPipelineDepthStencilStateCreateInfo depthStencilStateCI{}; + depthStencilStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + depthStencilStateCI.depthTestEnable = VK_FALSE; + depthStencilStateCI.depthWriteEnable = VK_FALSE; + depthStencilStateCI.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; + depthStencilStateCI.front = depthStencilStateCI.back; + depthStencilStateCI.back.compareOp = VK_COMPARE_OP_ALWAYS; + + VkPipelineViewportStateCreateInfo viewportStateCI{}; + viewportStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportStateCI.viewportCount = 1; + viewportStateCI.scissorCount = 1; + + VkPipelineMultisampleStateCreateInfo multisampleStateCI{}; + multisampleStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + + if (multiSampleCount > VK_SAMPLE_COUNT_1_BIT) + { + multisampleStateCI.rasterizationSamples = multiSampleCount; + } + + std::vector dynamicStateEnables = { + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR}; + VkPipelineDynamicStateCreateInfo dynamicStateCI{}; + dynamicStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamicStateCI.pDynamicStates = dynamicStateEnables.data(); + dynamicStateCI.dynamicStateCount = static_cast(dynamicStateEnables.size()); + + VkVertexInputBindingDescription vertexInputBinding = {0, 20, VK_VERTEX_INPUT_RATE_VERTEX}; + std::vector vertexInputAttributes = { + {0, 0, VK_FORMAT_R32G32_SFLOAT, 0}, + {1, 0, VK_FORMAT_R32G32_SFLOAT, sizeof(float) * 2}, + {2, 0, VK_FORMAT_R8G8B8A8_UNORM, sizeof(float) * 4}, + }; + VkPipelineVertexInputStateCreateInfo vertexInputStateCI{}; + vertexInputStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertexInputStateCI.vertexBindingDescriptionCount = 1; + vertexInputStateCI.pVertexBindingDescriptions = &vertexInputBinding; + vertexInputStateCI.vertexAttributeDescriptionCount = static_cast(vertexInputAttributes.size()); + vertexInputStateCI.pVertexAttributeDescriptions = vertexInputAttributes.data(); + + std::array shaderStages; + + VkGraphicsPipelineCreateInfo pipelineCI{}; + pipelineCI.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineCI.layout = pipelineLayout; + pipelineCI.renderPass = renderPass; + pipelineCI.pInputAssemblyState = &inputAssemblyStateCI; + pipelineCI.pVertexInputState = &vertexInputStateCI; + 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.layout = pipelineLayout; + std::string uiVertShaderPath = getAssetPath() + "shaders/ui.vert.spv"; + std::string uiFragShaderPath = getAssetPath() + "shaders/ui.frag.spv"; + shaderStages = { + + ShaderLoader::loadShader(device, uiVertShaderPath.data(), VK_SHADER_STAGE_VERTEX_BIT), + ShaderLoader::loadShader(device, uiFragShaderPath.data(), VK_SHADER_STAGE_FRAGMENT_BIT)}; + VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipeline)); + + for (auto shaderStage : shaderStages) + { + vkDestroyShaderModule(device, shaderStage.module, nullptr); + } + } + + ~UI() + { + ImGui::DestroyContext(); + vertexBuffer.destroy(); + indexBuffer.destroy(); + vkDestroyPipeline(device, pipeline, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); + vkDestroyDescriptorPool(device, descriptorPool, nullptr); + } + + void draw(VkCommandBuffer cmdBuffer) + { + vkCmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + vkCmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr); + + const VkDeviceSize offsets[1] = {0}; + vkCmdBindVertexBuffers(cmdBuffer, 0, 1, &vertexBuffer.buffer, offsets); + vkCmdBindIndexBuffer(cmdBuffer, indexBuffer.buffer, 0, VK_INDEX_TYPE_UINT16); + + vkCmdPushConstants(cmdBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(UI::PushConstBlock), &pushConstBlock); + + ImDrawData *imDrawData = ImGui::GetDrawData(); + int32_t vertexOffset = 0; + int32_t indexOffset = 0; + for (int32_t j = 0; j < imDrawData->CmdListsCount; j++) + { + const ImDrawList *cmd_list = imDrawData->CmdLists[j]; + for (int32_t k = 0; k < cmd_list->CmdBuffer.Size; k++) + { + const ImDrawCmd *pcmd = &cmd_list->CmdBuffer[k]; + VkRect2D scissorRect; + scissorRect.offset.x = std::max((int32_t)(pcmd->ClipRect.x), 0); + scissorRect.offset.y = std::max((int32_t)(pcmd->ClipRect.y), 0); + scissorRect.extent.width = (uint32_t)(pcmd->ClipRect.z - pcmd->ClipRect.x); + scissorRect.extent.height = (uint32_t)(pcmd->ClipRect.w - pcmd->ClipRect.y); + vkCmdSetScissor(cmdBuffer, 0, 1, &scissorRect); + vkCmdDrawIndexed(cmdBuffer, pcmd->ElemCount, 1, indexOffset, vertexOffset, 0); + indexOffset += pcmd->ElemCount; + } + vertexOffset += cmd_list->VtxBuffer.Size; + } + } + + template + bool checkbox(const char *caption, T *value) + { + bool val = (*value == 1); + bool res = ImGui::Checkbox(caption, &val); + *value = val; + return res; + } + bool header(const char *caption) + { + return ImGui::CollapsingHeader(caption, ImGuiTreeNodeFlags_DefaultOpen); + } + bool slider(const char *caption, float *value, float min, float max) + { + return ImGui::SliderFloat(caption, value, min, max); + } + bool combo(const char *caption, int32_t *itemindex, std::vector items) + { + if (items.empty()) + { + return false; + } + std::vector charitems; + charitems.reserve(items.size()); + for (size_t i = 0; i < items.size(); i++) + { + charitems.push_back(items[i].c_str()); + } + uint32_t itemCount = static_cast(charitems.size()); + return ImGui::Combo(caption, itemindex, &charitems[0], itemCount, itemCount); + } + bool combo(const char *caption, std::string &selectedkey, std::map items) + { + bool selectionChanged = false; + if (ImGui::BeginCombo(caption, selectedkey.c_str())) + { + for (auto it = items.begin(); it != items.end(); ++it) + { + const bool isSelected = it->first == selectedkey; + if (ImGui::Selectable(it->first.c_str(), isSelected)) + { + selectionChanged = it->first != selectedkey; + selectedkey = it->first; + } + if (isSelected) + { + ImGui::SetItemDefaultFocus(); + } + } + ImGui::EndCombo(); + } + return selectionChanged; + } + bool button(const char *caption) + { + return ImGui::Button(caption); + } + bool beginChild(const char *caption, ImVec2 size, bool border) + { + return ImGui::BeginChild(caption, size, border); + } + + void endChild() + { + return ImGui::EndChild(); + } + + // menu GUI + bool beginMainMenuBar() + { + return ImGui::BeginMainMenuBar(); + } + + bool beginMenu(const char *caption) + { + return ImGui::BeginMenu(caption); + } + + bool menuItem(const char *caption) + { + return ImGui::MenuItem(caption); + } + void endMenu() + { + return ImGui::EndMenu(); + } + + void endMainMenuBar() + { + return ImGui::EndMainMenuBar(); + } + + void text(const char *formatstr, ...) + { + va_list args; + va_start(args, formatstr); + ImGui::TextV(formatstr, args); + va_end(args); + } +}; \ No newline at end of file