vulkan/src/vulkan_helper.cpp

483 lines
18 KiB
C++

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#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<VkBufferImageCopy>(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;
}