#include #include #include #include #include "volk/volk.h" #include "vulkan/vk_enum_string_helper.h" #include "minmax.h" #include "new.h" #include "file.h" #include "check.h" #include "dds/validate.h" #include "dds/vulkan.h" #include "tga/tga.h" #include "vulkan_helper.h" inline static uint32_t findMemoryTypeIndex(VkPhysicalDeviceMemoryProperties const & memoryProperties, uint32_t memoryTypeBits, VkMemoryPropertyFlags propertyFlags) { // find an exact match for (uint32_t i = 0; i < memoryProperties.memoryTypeCount; i++) { if (!(memoryTypeBits & (1u << i))) continue; if (memoryProperties.memoryTypes[i].propertyFlags == propertyFlags) { return i; } } // find a partial match for (uint32_t i = 0; i < memoryProperties.memoryTypeCount; i++) { if (!(memoryTypeBits & (1u << i))) continue; if ((memoryProperties.memoryTypes[i].propertyFlags & propertyFlags) == propertyFlags) { return i; } } ASSERT(false, "no memory type index matching memoryTypeBits and propertyFlags"); UNREACHABLE(); } VkDeviceSize allocateFromMemoryRequirements(VkDevice device, VkDeviceSize nonCoherentAtomSize, VkPhysicalDeviceMemoryProperties const & physicalDeviceMemoryProperties, VkMemoryRequirements const & memoryRequirements, VkMemoryPropertyFlags memoryPropertyFlags, VkMemoryAllocateFlags memoryAllocateFlags, uint32_t count, VkDeviceMemory * memory, VkDeviceSize * outStride) { uint32_t memoryTypeIndex = findMemoryTypeIndex(physicalDeviceMemoryProperties, memoryRequirements.memoryTypeBits, memoryPropertyFlags); VkDeviceSize alignedSize = roundAlignment(memoryRequirements.size, memoryRequirements.alignment); VkDeviceSize stride = (count == 1) ? memoryRequirements.size : alignedSize; VkMemoryAllocateFlagsInfo memoryAllocateFlagsInfo{ .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO, .flags = memoryAllocateFlags, }; VkMemoryAllocateInfo memoryAllocateInfo{ .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .pNext = &memoryAllocateFlagsInfo, .allocationSize = roundAlignment(stride * count, nonCoherentAtomSize), .memoryTypeIndex = memoryTypeIndex, }; VK_CHECK(vkAllocateMemory(device, &memoryAllocateInfo, nullptr, memory)); *outStride = stride; return memoryAllocateInfo.allocationSize; } VkDeviceSize allocateFromMemoryRequirements2(VkDevice device, VkDeviceSize nonCoherentAtomSize, VkPhysicalDeviceMemoryProperties const & physicalDeviceMemoryProperties, VkMemoryPropertyFlags memoryPropertyFlags, VkMemoryAllocateFlags memoryAllocateFlags, uint32_t memoryRequirementsCount, VkMemoryRequirements const * memoryRequirements, VkDeviceMemory * memory, VkDeviceSize * offsets) { assert(memoryRequirementsCount > 0); uint32_t memoryTypeBits = memoryRequirements[0].memoryTypeBits; for (uint32_t i = 1; i < memoryRequirementsCount; i++) { assert(memoryRequirements[i].memoryTypeBits == memoryTypeBits); } uint32_t memoryTypeIndex = findMemoryTypeIndex(physicalDeviceMemoryProperties, memoryTypeBits, memoryPropertyFlags); VkDeviceSize offset = 0; for (uint32_t i = 0; i < memoryRequirementsCount; i++) { offset = roundAlignment(offset, memoryRequirements[i].alignment); offsets[i] = offset; offset += memoryRequirements[i].size; } VkMemoryAllocateFlagsInfo memoryAllocateFlagsInfo{ .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO, .flags = memoryAllocateFlags, }; VkMemoryAllocateInfo memoryAllocateInfo{ .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .pNext = &memoryAllocateFlagsInfo, .allocationSize = roundAlignment(offset, nonCoherentAtomSize), .memoryTypeIndex = memoryTypeIndex, }; VK_CHECK(vkAllocateMemory(device, &memoryAllocateInfo, nullptr, memory)); return memoryAllocateInfo.allocationSize; } // ddsFile->header.dwWidth // ddsFile->header.dwHeight // ddsFile->header.dwMipMapCount // uint32_t * mipOffsets; void textureTransfer(VkDevice device, VkQueue queue, VkCommandBuffer commandBuffer, VkFence fence, VkDeviceSize nonCoherentAtomSize, VkPhysicalDeviceMemoryProperties const & physicalDeviceMemoryProperties, uint32_t imageDataSize, void * imageData, VkImage image, uint32_t width, uint32_t height, uint32_t levelCount, uint32_t * levelOffsets) { VkBuffer sourceBuffer{}; VkBufferCreateInfo sourceBufferCreateInfo{ .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .size = imageDataSize, .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT }; VK_CHECK(vkCreateBuffer(device, &sourceBufferCreateInfo, nullptr, &sourceBuffer)); VkMemoryRequirements sourceBufferMemoryRequirements; vkGetBufferMemoryRequirements(device, sourceBuffer, &sourceBufferMemoryRequirements); VkMemoryPropertyFlags sourceBufferMemoryPropertyFlags{ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT }; VkMemoryAllocateFlags sourceBufferMemoryAllocateFlags{ }; VkDeviceMemory sourceBufferMemory; VkDeviceSize sourceBufferStride; allocateFromMemoryRequirements(device, nonCoherentAtomSize, physicalDeviceMemoryProperties, sourceBufferMemoryRequirements, sourceBufferMemoryPropertyFlags, sourceBufferMemoryAllocateFlags, 1, &sourceBufferMemory, &sourceBufferStride); VK_CHECK(vkBindBufferMemory(device, sourceBuffer, sourceBufferMemory, 0)); void * sourceMappedData; VK_CHECK(vkMapMemory(device, sourceBufferMemory, 0, sourceBufferCreateInfo.size, 0, &sourceMappedData)); memcpy((void *)(((ptrdiff_t)sourceMappedData) + 0), imageData, imageDataSize); vkUnmapMemory(device, sourceBufferMemory); // transfer: command buffer VkCommandBufferBeginInfo commandBufferBeginInfo{ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT }; VK_CHECK(vkBeginCommandBuffer(commandBuffer, &commandBufferBeginInfo)); VkImageMemoryBarrier2 imageBarrier{ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, .srcStageMask = VK_PIPELINE_STAGE_2_NONE, .srcAccessMask = VK_ACCESS_2_NONE, .dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, .dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, .image = image, .subresourceRange = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .levelCount = levelCount, .layerCount = 1 } }; VkDependencyInfo imageDependencyInfo{ .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, .imageMemoryBarrierCount = 1, .pImageMemoryBarriers = &imageBarrier }; vkCmdPipelineBarrier2(commandBuffer, &imageDependencyInfo); VkBufferImageCopy * copyRegions = NewM(levelCount); for (uint32_t level = 0; level < levelCount; level++) { copyRegions[level] = { .bufferOffset = levelOffsets[level], .imageSubresource{ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .mipLevel = level, .layerCount = 1 }, .imageExtent{ .width = max(1u, width >> level), .height = max(1u, height >> level), .depth = 1 }, }; } vkCmdCopyBufferToImage(commandBuffer, sourceBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, levelCount, copyRegions); free(copyRegions); VkImageMemoryBarrier2 readBarrier{ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, .srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT, .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, .dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, .newLayout = VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL, .image = image, .subresourceRange = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .levelCount = levelCount, .layerCount = 1 } }; VkDependencyInfo readDependencyInfo{ .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, .imageMemoryBarrierCount = 1, .pImageMemoryBarriers = &readBarrier }; vkCmdPipelineBarrier2(commandBuffer, &readDependencyInfo); VK_CHECK(vkEndCommandBuffer(commandBuffer)); vkResetFences(device, 1, &fence); VkSubmitInfo submitInfo{ .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .commandBufferCount = 1, .pCommandBuffers = &commandBuffer }; VK_CHECK(vkQueueSubmit(queue, 1, &submitInfo, fence)); VK_CHECK(vkWaitForFences(device, 1, &fence, VK_TRUE, UINT64_MAX)); vkDestroyBuffer(device, sourceBuffer, nullptr); vkFreeMemory(device, sourceBufferMemory, nullptr); } void createImage(VkDevice device, VkDeviceSize nonCoherentAtomSize, VkPhysicalDeviceMemoryProperties const & physicalDeviceMemoryProperties, VkFormat format, uint32_t width, uint32_t height, uint32_t levelCount, VkImage * outImage, VkDeviceMemory * outMemory, VkImageView * outImageView) { // image VkImage image; VkImageCreateInfo imageCreateInfo{ .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .imageType = VK_IMAGE_TYPE_2D, .format = format, .extent = { .width = width, .height = height, .depth = 1 }, .mipLevels = levelCount, .arrayLayers = 1, .samples = VK_SAMPLE_COUNT_1_BIT, .tiling = VK_IMAGE_TILING_OPTIMAL, .usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED }; VK_CHECK(vkCreateImage(device, &imageCreateInfo, nullptr, &image)); *outImage = image; // image view VkDeviceMemory imageMemory; VkMemoryRequirements imageMemoryRequirements; vkGetImageMemoryRequirements(device, image, &imageMemoryRequirements); VkMemoryPropertyFlags imageMemoryPropertyFlags{ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT }; VkMemoryAllocateFlags imageMemoryAllocateFlags{ }; VkDeviceSize stride; allocateFromMemoryRequirements(device, nonCoherentAtomSize, physicalDeviceMemoryProperties, imageMemoryRequirements, imageMemoryPropertyFlags, imageMemoryAllocateFlags, 1, &imageMemory, &stride); *outMemory = imageMemory; VK_CHECK(vkBindImageMemory(device, image, imageMemory, 0)); VkImageView imageView; VkImageViewCreateInfo textureViewCreateInfo{ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .image = image, .viewType = VK_IMAGE_VIEW_TYPE_2D, .format = format, .subresourceRange{ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .levelCount = levelCount, .layerCount = 1 } }; VK_CHECK(vkCreateImageView(device, &textureViewCreateInfo, nullptr, &imageView)); *outImageView = imageView; } void createImageFromFilenameDDS(VkDevice device, VkQueue queue, VkCommandBuffer commandBuffer, VkFence fence, VkDeviceSize nonCoherentAtomSize, VkPhysicalDeviceMemoryProperties const & physicalDeviceMemoryProperties, char const * const filename, VkImage * outImage, VkDeviceMemory * outMemory, VkImageView * outImageView) { uint32_t imageSize; void const * imageStart = file::open(filename, &imageSize); void * imageData; uint32_t * levelOffsets; uint32_t imageDataSize; DDS_FILE const * ddsFile = dds::validate(imageStart, imageSize, &levelOffsets, &imageData, &imageDataSize); VkFormat format = dds::dxgi_to_vulkan(ddsFile->header10.dxgiFormat); uint32_t width = ddsFile->header.dwWidth; uint32_t height = ddsFile->header.dwHeight; uint32_t levelCount = ddsFile->header.dwMipMapCount; createImage(device, nonCoherentAtomSize, physicalDeviceMemoryProperties, format, width, height, levelCount, outImage, outMemory, outImageView); textureTransfer(device, queue, commandBuffer, fence, nonCoherentAtomSize, physicalDeviceMemoryProperties, imageDataSize, imageData, *outImage, width, height, levelCount, levelOffsets); free(levelOffsets); // imageData is not malloc'ed, it is a pointer to file:: data, which is also not malloc'ed } void createImageFromFilenameTGA(VkDevice device, VkQueue queue, VkCommandBuffer commandBuffer, VkFence fence, VkDeviceSize nonCoherentAtomSize, VkPhysicalDeviceMemoryProperties const & physicalDeviceMemoryProperties, char const * const filename, VkImage * outImage, VkDeviceMemory * outMemory, VkImageView * outImageView) { uint32_t imageSize; //void const * imageStart = file::open(filename, &imageSize); void * imageStart = file::openRelative(filename, &imageSize); void * imageData; uint32_t imageDataSize; tga::header const * tga = tga::validate(imageStart, imageSize, &imageData, &imageDataSize); VkFormat format = VK_FORMAT_B8G8R8A8_UNORM; uint32_t width = tga->image.width; uint32_t height = tga->image.height; uint32_t levelCount = 1; uint32_t levelOffset = 0; createImage(device, nonCoherentAtomSize, physicalDeviceMemoryProperties, format, width, height, levelCount, outImage, outMemory, outImageView); textureTransfer(device, queue, commandBuffer, fence, nonCoherentAtomSize, physicalDeviceMemoryProperties, imageDataSize, imageData, *outImage, width, height, levelCount, &levelOffset); free(imageStart); } VertexIndex createVertexIndexBuffer(VkDevice device, VkPhysicalDeviceProperties const& physicalDeviceProperties, VkPhysicalDeviceMemoryProperties const& physicalDeviceMemoryProperties, void const * vertexStart, uint32_t vertexSize, void const * indexStart, uint32_t indexSize) { VertexIndex vertexIndex{}; vertexIndex.indexOffset = vertexSize; // create buffer VkDeviceSize bufferSize{ vertexSize + indexSize }; VkBufferCreateInfo vertexIndexBufferCreateInfo{ .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .size = bufferSize, .usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, .sharingMode = VK_SHARING_MODE_EXCLUSIVE }; VK_CHECK(vkCreateBuffer(device, &vertexIndexBufferCreateInfo, nullptr, &vertexIndex.buffer)); // allocate memory VkMemoryRequirements memoryRequirements; vkGetBufferMemoryRequirements(device, vertexIndex.buffer, &memoryRequirements); VkMemoryPropertyFlags memoryPropertyFlags{ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT }; VkMemoryAllocateFlags memoryAllocateFlags{}; VkDeviceSize stride; allocateFromMemoryRequirements(device, physicalDeviceProperties.limits.nonCoherentAtomSize, physicalDeviceMemoryProperties, memoryRequirements, memoryPropertyFlags, memoryAllocateFlags, 1, &vertexIndex.memory, &stride); VK_CHECK(vkBindBufferMemory(device, vertexIndex.buffer, vertexIndex.memory, 0)); // copy data void * vertexIndexMappedData; VK_CHECK(vkMapMemory(device, vertexIndex.memory, 0, VK_WHOLE_SIZE, 0, &vertexIndexMappedData)); memcpy((void *)(((ptrdiff_t)vertexIndexMappedData) + 0), vertexStart, vertexSize); memcpy((void *)(((ptrdiff_t)vertexIndexMappedData) + vertexSize), indexStart, indexSize); VkMappedMemoryRange mappedMemoryRange{ .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, .memory = vertexIndex.memory, .offset = 0, .size = VK_WHOLE_SIZE, }; vkFlushMappedMemoryRanges(device, 1, &mappedMemoryRange); vkUnmapMemory(device, vertexIndex.memory); return vertexIndex; }