diff --git a/src/modules/graphics/vulkan/Graphics.cpp b/src/modules/graphics/vulkan/Graphics.cpp index c02158c3f..d1ea9f541 100644 --- a/src/modules/graphics/vulkan/Graphics.cpp +++ b/src/modules/graphics/vulkan/Graphics.cpp @@ -303,8 +303,8 @@ void Graphics::initCapabilities() { static_assert(LIMIT_MAX_ENUM == 13, "Graphics::initCapabilities must be updated when adding a new system limit!"); capabilities.textureTypes[TEXTURE_2D] = true; + capabilities.textureTypes[TEXTURE_2D_ARRAY] = true; capabilities.textureTypes[TEXTURE_VOLUME] = false; - capabilities.textureTypes[TEXTURE_2D_ARRAY] = false; capabilities.textureTypes[TEXTURE_CUBE] = false; } diff --git a/src/modules/graphics/vulkan/Texture.cpp b/src/modules/graphics/vulkan/Texture.cpp index 6bfafb31c..5389545a6 100644 --- a/src/modules/graphics/vulkan/Texture.cpp +++ b/src/modules/graphics/vulkan/Texture.cpp @@ -21,16 +21,29 @@ bool Texture::loadVolatile() { auto vulkanFormat = Vulkan::getTextureFormat(format); - VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + // fixme: can we cut down these flags? + VkImageUsageFlags usageFlags = + VK_IMAGE_USAGE_TRANSFER_SRC_BIT | + VK_IMAGE_USAGE_TRANSFER_DST_BIT | + VK_IMAGE_USAGE_SAMPLED_BIT | + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + + layerCount = 1; + if (texType == TEXTURE_VOLUME) + layerCount = getDepth(); + else if (texType == TEXTURE_2D_ARRAY) + layerCount = getLayerCount(); + else if (texType == TEXTURE_CUBE) + layerCount = 6; VkImageCreateInfo imageInfo{}; imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - imageInfo.imageType = VK_IMAGE_TYPE_2D; + imageInfo.imageType = Vulkan::getImageType(getTextureType()); imageInfo.extent.width = static_cast(width); imageInfo.extent.height = static_cast(height); imageInfo.extent.depth = 1; imageInfo.mipLevels = 1; - imageInfo.arrayLayers = 1; + imageInfo.arrayLayers = layerCount; imageInfo.format = vulkanFormat.internalFormat; imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; @@ -44,21 +57,23 @@ bool Texture::loadVolatile() { throw love::Exception("failed to create image"); } // fixme: we should use VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL as the default image layout instead of VK_IMAGE_LAYOUT_GENERAL. - vgfx->queueDatatransfer([textureImage = textureImage](VkCommandBuffer commandBuffer){ - Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL); + vgfx->queueDatatransfer([textureImage = textureImage, layerCount = layerCount](VkCommandBuffer commandBuffer){ + Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, 0, 1, 0, layerCount); }, nullptr); if (data) { - auto sliceData = data->get(0, 0); - auto size = sliceData->getSize(); - auto dataPtr = sliceData->getData(); - Rect rect{}; - rect.x = 0; - rect.y = 0; - rect.w = sliceData->getWidth(); - rect.h = sliceData->getHeight(); + for (int slice = 0; slice < layerCount; slice++) { + auto sliceData = data->get(slice, 0); + auto size = sliceData->getSize(); + auto dataPtr = sliceData->getData(); + Rect rect{}; + rect.x = 0; + rect.y = 0; + rect.w = sliceData->getWidth(); + rect.h = sliceData->getHeight(); - uploadByteData(format, dataPtr, size, 0, 0, rect); + uploadByteData(format, dataPtr, size, 0, slice, rect); + } } else { if (isRenderTarget()) { clear(false); @@ -102,13 +117,13 @@ void Texture::createTextureImageView() { VkImageViewCreateInfo viewInfo{}; viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; viewInfo.image = textureImage; - viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + viewInfo.viewType = Vulkan::getImageViewType(getTextureType()); viewInfo.format = vulkanFormat.internalFormat; viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; viewInfo.subresourceRange.baseMipLevel = 0; viewInfo.subresourceRange.levelCount = 1; viewInfo.subresourceRange.baseArrayLayer = 0; - viewInfo.subresourceRange.layerCount = 1; + viewInfo.subresourceRange.layerCount = layerCount; viewInfo.components.r = vulkanFormat.swizzleR; viewInfo.components.g = vulkanFormat.swizzleG; viewInfo.components.b = vulkanFormat.swizzleB; @@ -154,7 +169,7 @@ void Texture::clear(bool white) { VkImageSubresourceRange range{}; range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - range.layerCount = getMipmapCount(); + range.layerCount = layerCount; range.levelCount = 1; vkCmdClearColorImage(commandBuffer, textureImage, VK_IMAGE_LAYOUT_GENERAL, &clearColor, 1, &range); @@ -231,15 +246,15 @@ void Texture::uploadByteData(PixelFormat pixelformat, const void* data, size_t s memcpy(allocInfo.pMappedData, data, size); - auto command = [buffer = stagingBuffer, image = textureImage, r = r](VkCommandBuffer commandBuffer) { + auto command = [buffer = stagingBuffer, image = textureImage, r = r, level = level, slice = slice](VkCommandBuffer commandBuffer) { VkBufferImageCopy region{}; region.bufferOffset = 0; region.bufferRowLength = 0; region.bufferImageHeight = 0; region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - region.imageSubresource.mipLevel = 0; - region.imageSubresource.baseArrayLayer = 0; + region.imageSubresource.mipLevel = level; + region.imageSubresource.baseArrayLayer = slice; region.imageSubresource.layerCount = 1; region.imageOffset = { r.x, r.y, 0 }; @@ -248,7 +263,7 @@ void Texture::uploadByteData(PixelFormat pixelformat, const void* data, size_t s static_cast(r.h), 1 }; - Vulkan::cmdTransitionImageLayout(commandBuffer, image, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + Vulkan::cmdTransitionImageLayout(commandBuffer, image, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, level, 1, slice, 1); vkCmdCopyBufferToImage( commandBuffer, @@ -259,7 +274,7 @@ void Texture::uploadByteData(PixelFormat pixelformat, const void* data, size_t s ®ion ); - Vulkan::cmdTransitionImageLayout(commandBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL); + Vulkan::cmdTransitionImageLayout(commandBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL, level, 1, slice, 1); }; auto cleanUp = [allocator = allocator, stagingBuffer, vmaAllocation]() { diff --git a/src/modules/graphics/vulkan/Texture.h b/src/modules/graphics/vulkan/Texture.h index 6c3349a01..d25e46f92 100644 --- a/src/modules/graphics/vulkan/Texture.h +++ b/src/modules/graphics/vulkan/Texture.h @@ -50,6 +50,7 @@ private: VkImageView textureImageView; VkSampler textureSampler; const Slices* data; + int layerCount = 0; }; } // vulkan } // graphics diff --git a/src/modules/graphics/vulkan/Vulkan.cpp b/src/modules/graphics/vulkan/Vulkan.cpp index cda447bb4..2041a18ee 100644 --- a/src/modules/graphics/vulkan/Vulkan.cpp +++ b/src/modules/graphics/vulkan/Vulkan.cpp @@ -453,7 +453,36 @@ VkCullModeFlags Vulkan::getCullMode(CullMode cullmode) { } } -void Vulkan::cmdTransitionImageLayout(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout) { +VkImageType Vulkan::getImageType(TextureType textureType) { + switch (textureType) { + case TEXTURE_2D: + case TEXTURE_2D_ARRAY: + return VK_IMAGE_TYPE_2D; + case TEXTURE_VOLUME: + case TEXTURE_CUBE: + return VK_IMAGE_TYPE_3D; + default: + throw love::Exception("unknown texture type"); + } +} + +VkImageViewType Vulkan::getImageViewType(TextureType textureType) { + switch (textureType) { + case TEXTURE_2D: + return VK_IMAGE_VIEW_TYPE_2D; + case TEXTURE_2D_ARRAY: + return VK_IMAGE_VIEW_TYPE_2D_ARRAY; + case TEXTURE_CUBE: + return VK_IMAGE_VIEW_TYPE_CUBE; + case TEXTURE_VOLUME: + return VK_IMAGE_VIEW_TYPE_3D; + default: + throw love::Exception("unknown texture type"); + } +} + +void Vulkan::cmdTransitionImageLayout(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout, + uint32_t baseLevel, uint32_t levelCount, uint32_t baseLayer, uint32_t layerCount) { VkImageMemoryBarrier barrier{}; barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; barrier.oldLayout = oldLayout; @@ -462,10 +491,10 @@ void Vulkan::cmdTransitionImageLayout(VkCommandBuffer commandBuffer, VkImage ima barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.image = image; barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - barrier.subresourceRange.baseMipLevel = 0; - barrier.subresourceRange.levelCount = 1; - barrier.subresourceRange.baseArrayLayer = 0; - barrier.subresourceRange.layerCount = 1; + barrier.subresourceRange.baseMipLevel = baseLevel; + barrier.subresourceRange.levelCount = levelCount; + barrier.subresourceRange.baseArrayLayer = baseLayer; + barrier.subresourceRange.layerCount = layerCount; VkPipelineStageFlags sourceStage; VkPipelineStageFlags destinationStage; diff --git a/src/modules/graphics/vulkan/Vulkan.h b/src/modules/graphics/vulkan/Vulkan.h index e45d609ff..efe64bb29 100644 --- a/src/modules/graphics/vulkan/Vulkan.h +++ b/src/modules/graphics/vulkan/Vulkan.h @@ -41,8 +41,12 @@ public: static VkColorComponentFlags getColorMask(ColorChannelMask); static VkFrontFace getFrontFace(Winding); static VkCullModeFlags getCullMode(CullMode); + static VkImageType getImageType(TextureType); + static VkImageViewType getImageViewType(TextureType); - static void cmdTransitionImageLayout(VkCommandBuffer, VkImage, VkImageLayout oldLayout, VkImageLayout newLayout); + static void cmdTransitionImageLayout( + VkCommandBuffer, VkImage, VkImageLayout oldLayout, VkImageLayout newLayout, + uint32_t baseLevel = 0, uint32_t levelCount = 1, uint32_t baseLayer = 0, uint32_t layerCount = 1); }; } // vulkan } // graphics