483 lines
18 KiB
C++
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;
|
|
}
|