aboutsummaryrefslogtreecommitdiffstatsci
path: root/src/texture.cpp
diff options
context:
space:
mode:
authorJean-Philippe Bruyère <jp_bruyere@hotmail.com>2018-10-17 18:49:56 +0200
committerJean-Philippe Bruyère <jp_bruyere@hotmail.com>2018-10-17 18:49:56 +0200
commit328c11f4dfd2653f9b6d12633b38d7baa49958da (patch)
treea5483d189f3a46f5fbbb32e3c63a8cda9039dfee /src/texture.cpp
first commit, splitted from vkChess
Diffstat (limited to 'src/texture.cpp')
-rw-r--r--src/texture.cpp418
1 files changed, 418 insertions, 0 deletions
diff --git a/src/texture.cpp b/src/texture.cpp
new file mode 100644
index 0000000..68101fc
--- /dev/null
+++ b/src/texture.cpp
@@ -0,0 +1,418 @@
+#include "texture.hpp"
+#include "VulkanDevice.hpp"
+#include "VulkanBuffer.hpp"
+#include "stb_image.h"
+
+namespace vks
+{
+ void Texture::create (ptrVkDev _device,
+ VkImageType imageType, VkFormat format, uint32_t width, uint32_t height,
+ VkImageUsageFlags usage, VkMemoryPropertyFlags memProps,
+ VkImageTiling tiling,
+ uint32_t mipLevels,
+ uint32_t arrayLayers,
+ VkImageCreateFlags flags,
+ VkSampleCountFlagBits samples,
+ VkImageLayout initialLayout,
+ VkSharingMode sharingMode) {
+ device = _device;
+ infos.imageType = imageType;
+ infos.format = format;
+ infos.extent = {width, height, 1};
+ infos.mipLevels = mipLevels == 0 ?
+ static_cast<uint32_t>(floor(log2(std::max(infos.extent.width, infos.extent.height))) + 1.0) : mipLevels;
+ infos.arrayLayers = arrayLayers;
+ infos.samples = samples;
+ infos.tiling = tiling;
+ infos.usage = usage;
+ infos.flags = flags;
+ infos.sharingMode = sharingMode;
+ infos.initialLayout = initialLayout;
+
+ VkMemoryAllocateInfo memAllocInfo{};
+ memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+
+ VK_CHECK_RESULT(vkCreateImage(device->dev, &infos, nullptr, &image));
+ vkGetImageMemoryRequirements(device->dev, image, &memReqs);
+ memAllocInfo.allocationSize = memReqs.size;
+ memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, memProps);
+ VK_CHECK_RESULT(vkAllocateMemory(device->dev, &memAllocInfo, nullptr, &deviceMemory));
+ VK_CHECK_RESULT(vkBindImageMemory(device->dev, image, deviceMemory, 0));
+ }
+ Texture::Texture (){}
+ Texture::Texture (ptrVkDev _device, VkFormat _format, VkImage importedImg, uint32_t width, uint32_t height,
+ VkSampleCountFlagBits samples, uint32_t mipLevels, uint32_t arrayLayers){
+ device = _device;
+ image = importedImg;
+ infos.format = _format;
+ infos.extent = {width, height, 1};
+ infos.imageType = VK_IMAGE_TYPE_2D;
+ infos.samples = samples;
+ infos.mipLevels = mipLevels;
+ infos.arrayLayers = arrayLayers;
+ }
+ Texture::Texture (ptrVkDev _device,
+ VkImageType imageType, VkFormat format, uint32_t width, uint32_t height,
+ VkImageUsageFlags usage, VkMemoryPropertyFlags memProps, VkImageTiling tiling,
+ uint32_t mipLevels, uint32_t arrayLayers,
+ VkImageCreateFlags flags,
+ VkSampleCountFlagBits samples,
+ VkImageLayout initialLayout,
+ VkSharingMode sharingMode)
+ {
+ create(_device, imageType, format, width, height, usage, memProps, tiling, mipLevels, arrayLayers,
+ flags, samples, initialLayout, sharingMode);
+ }
+ Texture::Texture (ptrVkDev _device, VkQueue copyQueue, VkImageType imageType, VkFormat format,
+ std::vector<Texture> texArray, uint32_t width, uint32_t height,
+ VkImageUsageFlags _usage) {
+
+ uint32_t mipLevels = (uint32_t)floor(log2(std::max(width, height))) + 1;
+
+ create(_device, imageType, format, width, height,
+ VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | _usage,
+ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, VK_IMAGE_TILING_OPTIMAL, mipLevels, texArray.size());
+
+ VkCommandBuffer blitCmd = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
+
+ //imageLayout = _imageLayout;
+ for (uint l = 0; l < texArray.size(); l++) {
+ Texture* inTex = &texArray[l];
+
+ VkImageBlit firstMipBlit{};
+
+ firstMipBlit.srcSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1};
+ firstMipBlit.srcOffsets[1] = {(int32_t)inTex->infos.extent.width,(int32_t)inTex->infos.extent.height,1};
+
+ firstMipBlit.dstSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, l, 1};
+ firstMipBlit.dstOffsets[1] = {(int32_t)infos.extent.width,(int32_t)infos.extent.height,1};
+
+ VkImageSubresourceRange mipSubRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, l, 1};
+
+ inTex->setImageLayout(blitCmd, VK_IMAGE_LAYOUT_UNDEFINED,VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ {VK_IMAGE_ASPECT_COLOR_BIT,0,1,0,1},
+ VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
+ // Transiton current array level to transfer dest
+ setImageLayout(blitCmd, VK_IMAGE_LAYOUT_UNDEFINED,VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ mipSubRange,
+ VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
+
+ vkCmdBlitImage(blitCmd, inTex->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &firstMipBlit, VK_FILTER_LINEAR);
+
+ setImageLayout(blitCmd, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, mipSubRange,
+ VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
+
+ /*setImageLayout(blitCmd, VK_IMAGE_LAYOUT_UNDEFINED,VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ mipSubRange,
+ VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);*/
+ }
+ //device->flushCommandBuffer(blitCmd, copyQueue, true);
+ buildMipmaps(copyQueue, blitCmd);
+
+ device->flushCommandBuffer(blitCmd, copyQueue, true);
+
+ createView(VK_IMAGE_VIEW_TYPE_2D_ARRAY, VK_IMAGE_ASPECT_COLOR_BIT, infos.mipLevels, infos.arrayLayers);
+ createSampler(VK_FILTER_LINEAR,VK_SAMPLER_ADDRESS_MODE_REPEAT,VK_SAMPLER_MIPMAP_MODE_LINEAR,
+ VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE);
+
+ updateDescriptor();
+ }
+
+ /** @brief Update image descriptor from current sampler, view and image layout */
+ void Texture::updateDescriptor()
+ {
+ descriptor.sampler = sampler;
+ descriptor.imageView = view;
+ }
+
+ VkWriteDescriptorSet Texture::getWriteDescriptorSet (VkDescriptorSet ds, uint32_t binding, VkDescriptorType descriptorType) {
+ VkWriteDescriptorSet wds = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET};
+ wds.dstSet = ds;
+ wds.descriptorType = descriptorType;
+ wds.dstBinding = binding;
+ wds.descriptorCount = 1;
+ wds.pImageInfo = &descriptor;
+ return wds;
+ }
+
+ /** @brief Release all Vulkan resources held by this texture */
+ void Texture::destroy()
+ {
+ if (view)
+ vkDestroyImageView (device->dev, view, VK_NULL_HANDLE);
+ if (sampler)
+ vkDestroySampler (device->dev, sampler, VK_NULL_HANDLE);
+ if (image){
+ vkDestroyImage (device->dev, image, VK_NULL_HANDLE);
+ vkFreeMemory (device->dev, deviceMemory, VK_NULL_HANDLE);
+ }
+ }
+ void Texture::setImageLayout(
+ VkCommandBuffer cmdbuffer,
+ VkImageLayout oldImageLayout,
+ VkImageLayout newImageLayout,
+ VkImageSubresourceRange subresourceRange,
+ VkPipelineStageFlags srcStageMask,
+ VkPipelineStageFlags dstStageMask)
+ {
+ // Create an image barrier object
+ VkImageMemoryBarrier imageMemoryBarrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER};
+ imageMemoryBarrier.oldLayout = oldImageLayout;
+ imageMemoryBarrier.newLayout = newImageLayout;
+ imageMemoryBarrier.image = image;
+ imageMemoryBarrier.subresourceRange = subresourceRange;
+
+ // Source layouts (old)
+ // Source access mask controls actions that have to be finished on the old layout
+ // before it will be transitioned to the new layout
+ switch (oldImageLayout)
+ {
+ case VK_IMAGE_LAYOUT_UNDEFINED:
+ // Image layout is undefined (or does not matter)
+ // Only valid as initial layout
+ // No flags required, listed only for completeness
+ imageMemoryBarrier.srcAccessMask = 0;
+ break;
+
+ case VK_IMAGE_LAYOUT_PREINITIALIZED:
+ // Image is preinitialized
+ // Only valid as initial layout for linear images, preserves memory contents
+ // Make sure host writes have been finished
+ imageMemoryBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
+ break;
+
+ case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
+ // Image is a color attachment
+ // Make sure any writes to the color buffer have been finished
+ imageMemoryBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+ break;
+
+ case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
+ // Image is a depth/stencil attachment
+ // Make sure any writes to the depth/stencil buffer have been finished
+ imageMemoryBarrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
+ break;
+
+ case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
+ // Image is a transfer source
+ // Make sure any reads from the image have been finished
+ imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
+ break;
+
+ case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
+ // Image is a transfer destination
+ // Make sure any writes to the image have been finished
+ imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+ break;
+
+ case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
+ // Image is read by a shader
+ // Make sure any shader reads from the image have been finished
+ imageMemoryBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
+ break;
+ default:
+ // Other source layouts aren't handled (yet)
+ break;
+ }
+
+ // Target layouts (new)
+ // Destination access mask controls the dependency for the new image layout
+ switch (newImageLayout)
+ {
+ case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
+ // Image will be used as a transfer destination
+ // Make sure any writes to the image have been finished
+ imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+ break;
+
+ case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
+ // Image will be used as a transfer source
+ // Make sure any reads from the image have been finished
+ imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
+ break;
+
+ case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
+ // Image will be used as a color attachment
+ // Make sure any writes to the color buffer have been finished
+ imageMemoryBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+ break;
+
+ case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
+ // Image layout will be used as a depth/stencil attachment
+ // Make sure any writes to depth/stencil buffer have been finished
+ imageMemoryBarrier.dstAccessMask = imageMemoryBarrier.dstAccessMask | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
+ break;
+
+ case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
+ // Image will be read in a shader (sampler, input attachment)
+ // Make sure any writes to the image have been finished
+ if (imageMemoryBarrier.srcAccessMask == 0)
+ {
+ imageMemoryBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT;
+ }
+ imageMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
+ break;
+ default:
+ // Other source layouts aren't handled (yet)
+ break;
+ }
+
+ // Put barrier inside setup command buffer
+ vkCmdPipelineBarrier(
+ cmdbuffer,
+ srcStageMask,
+ dstStageMask,
+ 0,
+ 0, nullptr,
+ 0, nullptr,
+ 1, &imageMemoryBarrier);
+ }
+
+ // Fixed sub resource on first mip level and layer
+ void Texture::setImageLayout(
+ VkCommandBuffer cmdbuffer,
+ VkImageAspectFlags aspectMask,
+ VkImageLayout oldImageLayout,
+ VkImageLayout newImageLayout,
+ VkPipelineStageFlags srcStageMask,
+ VkPipelineStageFlags dstStageMask)
+ {
+ setImageLayout(cmdbuffer, oldImageLayout, newImageLayout, {aspectMask,0,1,0,1}, srcStageMask, dstStageMask);
+ }
+
+ void Texture::copyTo (VkQueue copyQueue, unsigned char* buffer, VkDeviceSize bufferSize) {
+ Buffer stagingBuffer;
+ stagingBuffer.create (device, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+ bufferSize, buffer);
+
+ descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+
+ VkCommandBuffer copyCmd = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
+
+ setImageLayout (copyCmd, VK_IMAGE_ASPECT_COLOR_BIT,
+ VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
+
+ VkBufferImageCopy bufferCopyRegion = {};
+ bufferCopyRegion.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, infos.arrayLayers};
+ bufferCopyRegion.imageExtent = infos.extent;
+
+ vkCmdCopyBufferToImage(copyCmd, stagingBuffer.buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &bufferCopyRegion);
+
+ setImageLayout(copyCmd, VK_IMAGE_ASPECT_COLOR_BIT,
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
+
+ device->flushCommandBuffer(copyCmd, copyQueue, true);
+
+ stagingBuffer.destroy();
+ }
+ void Texture::createView (VkImageViewType viewType, VkImageAspectFlags aspectMask,
+ uint32_t levelCount, uint32_t layerCount, VkComponentMapping components) {
+
+ VkImageViewCreateInfo viewInfo{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};
+ viewInfo.image = image;
+ viewInfo.viewType = viewType;
+ viewInfo.format = infos.format;
+ viewInfo.components = components;
+ viewInfo.subresourceRange.aspectMask = aspectMask;
+ viewInfo.subresourceRange.levelCount = levelCount;
+ viewInfo.subresourceRange.layerCount = layerCount;
+ VK_CHECK_RESULT(vkCreateImageView(device->dev, &viewInfo, nullptr, &view));
+ }
+
+ void Texture::createSampler (VkFilter filter, VkSamplerAddressMode addressMode,
+ VkSamplerMipmapMode mipmapMode,
+ VkBorderColor borderColor,
+ VkCompareOp compareOp) {
+
+ VkSamplerCreateInfo samplerInfo = {VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO};
+ samplerInfo.magFilter = filter;
+ samplerInfo.minFilter = filter;
+ samplerInfo.mipmapMode = mipmapMode;
+ samplerInfo.addressModeU = addressMode;
+ samplerInfo.addressModeV = addressMode;
+ samplerInfo.addressModeW = addressMode;
+ samplerInfo.compareOp = compareOp;
+ samplerInfo.borderColor = borderColor;
+ samplerInfo.maxAnisotropy = device->enabledFeatures.samplerAnisotropy ? device->properties.limits.maxSamplerAnisotropy : 1.0f;
+ samplerInfo.anisotropyEnable = device->enabledFeatures.samplerAnisotropy;
+ samplerInfo.minLod = 0;
+ samplerInfo.maxLod = infos.mipLevels;
+ VK_CHECK_RESULT(vkCreateSampler(device->dev, &samplerInfo, nullptr, &sampler));
+ }
+ void Texture::buildMipmaps (VkQueue copyQueue, VkCommandBuffer _blitCmd) {
+ // Generate the mip chain (glTF uses jpg and png, so we need to create this manually)
+
+ VkCommandBuffer blitCmd = VK_NULL_HANDLE;
+ if (_blitCmd)
+ blitCmd = _blitCmd;
+ else
+ blitCmd = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
+
+ for (uint32_t i = 1; i < infos.mipLevels; i++) {
+ VkImageBlit imageBlit{};
+
+ imageBlit.srcSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, i - 1, 0, infos.arrayLayers};
+ imageBlit.srcOffsets[1] = {int32_t(infos.extent.width >> (i - 1)),int32_t(infos.extent.height >> (i - 1)),1};
+
+ imageBlit.dstSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, i, 0, infos.arrayLayers};
+ imageBlit.dstOffsets[1] = {int32_t(infos.extent.width >> i),int32_t(infos.extent.height >> i),1};
+
+ VkImageSubresourceRange mipSubRange = {VK_IMAGE_ASPECT_COLOR_BIT, i, 1, 0, infos.arrayLayers};
+
+ setImageLayout(blitCmd, VK_IMAGE_LAYOUT_UNDEFINED,VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, mipSubRange,
+ VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
+
+ vkCmdBlitImage(blitCmd, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &imageBlit, VK_FILTER_LINEAR);
+
+ setImageLayout(blitCmd, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, mipSubRange,
+ VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
+ }
+
+ setImageLayout(blitCmd, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
+ {VK_IMAGE_ASPECT_COLOR_BIT, 0, infos.mipLevels, 0, infos.arrayLayers},
+ VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
+
+
+ if (_blitCmd)//if continuing an already existing cmd buff, cancel flush here
+ return;
+ device->flushCommandBuffer(blitCmd, copyQueue, true);
+ }
+ void Texture::loadStbLinearNoSampling (std::string filename,
+ ptrVkDev device,
+ VkImageUsageFlags imageUsageFlags, bool flipY)
+ {
+ device = device;
+
+ stbi_set_flip_vertically_on_load(flipY);
+
+ int w=0,h=0,channels=0;
+ unsigned char *img = stbi_load(filename.c_str(),&w,&h,&channels,4);
+
+ if (img == NULL){
+ std::cerr << "unable to load image " << std::string(stbi_failure_reason());
+ exit(-1);
+ }
+
+ create(device, VK_IMAGE_TYPE_2D, VK_FORMAT_R8G8B8A8_UNORM, static_cast<uint32_t>(w), static_cast<uint32_t>(h),
+ imageUsageFlags, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, VK_IMAGE_TILING_LINEAR,
+ 1,1,0,VK_SAMPLE_COUNT_1_BIT,VK_IMAGE_LAYOUT_PREINITIALIZED);
+ uint32_t imgSize = infos.extent.width * infos.extent.height * 4;
+
+ VkImageSubresource subRes = {};
+ subRes.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ VkSubresourceLayout subResLayout;
+
+ void *data;
+ vkGetImageSubresourceLayout(device->dev, image, &subRes, &subResLayout);
+ VK_CHECK_RESULT(vkMapMemory(device->dev, deviceMemory, 0, imgSize, 0, &data));
+ memcpy(data, img, imgSize); // Copy image data into memory
+ vkUnmapMemory(device->dev, deviceMemory);
+
+ stbi_image_free(img);
+ stbi_set_flip_vertically_on_load(false);
+ }
+}
+