From 72feb044a3af7679012ad7683b7bba55c4c03c09 Mon Sep 17 00:00:00 2001 From: Philip Rideout Date: Tue, 25 Jan 2022 13:11:31 -0800 Subject: [PATCH] Vulkan now supports offsets when uploading texture data. This allows `MorphStressTest` to work on Vulkan. However, `Horse` is still broken because it provides positions but not tangents. Separate fix for that is coming. Partial fix for #5109. --- filament/backend/src/vulkan/VulkanContext.cpp | 2 +- filament/backend/src/vulkan/VulkanDriver.cpp | 8 +- filament/backend/src/vulkan/VulkanTexture.cpp | 121 +++++++++--------- filament/backend/src/vulkan/VulkanTexture.h | 21 +-- 4 files changed, 73 insertions(+), 79 deletions(-) diff --git a/filament/backend/src/vulkan/VulkanContext.cpp b/filament/backend/src/vulkan/VulkanContext.cpp index 296a4a79f0..f5d8b73a14 100644 --- a/filament/backend/src/vulkan/VulkanContext.cpp +++ b/filament/backend/src/vulkan/VulkanContext.cpp @@ -340,7 +340,7 @@ void VulkanContext::createEmptyTexture(VulkanStagePool& stagePool) { TextureUsage::SUBPASS_INPUT, stagePool); uint32_t black = 0; PixelBufferDescriptor pbd(&black, 4, PixelDataFormat::RGBA, PixelDataType::UBYTE); - emptyTexture->update2DImage(pbd, 1, 1, 0); + emptyTexture->updateImage(pbd, 1, 1, 1, 0, 0, 0, 0); } } // namespace filament diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index 11b6aa6737..90836c008b 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -865,8 +865,8 @@ void VulkanDriver::updateBufferObject(Handle boh, BufferDescript void VulkanDriver::update2DImage(Handle th, uint32_t level, uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height, PixelBufferDescriptor&& data) { - assert_invariant(xoffset == 0 && yoffset == 0 && "Offsets not yet supported."); - handle_cast(th)->update2DImage(data, width, height, level); + handle_cast(th)->updateImage(data, width, height, 1, + xoffset, yoffset, 0, level); scheduleDestroy(std::move(data)); } @@ -879,8 +879,8 @@ void VulkanDriver::update3DImage( uint32_t level, uint32_t xoffset, uint32_t yoffset, uint32_t zoffset, uint32_t width, uint32_t height, uint32_t depth, PixelBufferDescriptor&& data) { - assert_invariant(xoffset == 0 && yoffset == 0 && zoffset == 0 && "Offsets not yet supported."); - handle_cast(th)->update3DImage(data, width, height, depth, level); + handle_cast(th)->updateImage(data, width, height, depth, + xoffset, yoffset, zoffset, level); scheduleDestroy(std::move(data)); } diff --git a/filament/backend/src/vulkan/VulkanTexture.cpp b/filament/backend/src/vulkan/VulkanTexture.cpp index 41537481c8..e119a9e00e 100644 --- a/filament/backend/src/vulkan/VulkanTexture.cpp +++ b/filament/backend/src/vulkan/VulkanTexture.cpp @@ -195,13 +195,8 @@ VulkanTexture::~VulkanTexture() { } } -void VulkanTexture::update2DImage(const PixelBufferDescriptor& data, uint32_t width, - uint32_t height, int miplevel) { - update3DImage(std::move(data), width, height, 1, miplevel); -} - -void VulkanTexture::update3DImage(const PixelBufferDescriptor& data, uint32_t width, uint32_t height, - uint32_t depth, int miplevel) { +void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t width, uint32_t height, + uint32_t depth, uint32_t xoffset, uint32_t yoffset, uint32_t zoffset, uint32_t miplevel) { assert_invariant(width <= this->width && height <= this->height && depth <= this->depth); const PixelBufferDescriptor* hostData = &data; PixelBufferDescriptor reshapedData; @@ -213,41 +208,67 @@ void VulkanTexture::update3DImage(const PixelBufferDescriptor& data, uint32_t wi hostData = &reshapedData; } - // If format conversion is both required and supported, use vkCmdBlitImage. Otherwise, use - // vkCmdCopyBufferToImage. - + // If format conversion is both required and supported, use vkCmdBlitImage. const VkFormat hostFormat = backend::getVkFormat(hostData->format, hostData->type); const VkFormat deviceFormat = getVkFormatLinear(mVkFormat); - if (hostFormat != deviceFormat && hostFormat != VK_FORMAT_UNDEFINED) { - updateWithBlitImage(*hostData, width, height, depth, miplevel); - } else { - updateWithCopyBuffer(*hostData, width, height, depth, miplevel); + assert_invariant(xoffset == 0 && yoffset == 0 && zoffset == 0 && + "Offsets not yet supported when format conversion is required."); + updateImageWithBlit(*hostData, width, height, depth, miplevel); + return; } -} -void VulkanTexture::updateWithCopyBuffer(const PixelBufferDescriptor& hostData, uint32_t width, - uint32_t height, uint32_t depth, uint32_t miplevel) { + // Otherwise, use vkCmdCopyBufferToImage. void* mapped = nullptr; - VulkanStage const* stage = mStagePool.acquireStage(hostData.size); + VulkanStage const* stage = mStagePool.acquireStage(hostData->size); vmaMapMemory(mContext.allocator, stage->memory, &mapped); - memcpy(mapped, hostData.buffer, hostData.size); + memcpy(mapped, hostData->buffer, hostData->size); vmaUnmapMemory(mContext.allocator, stage->memory); - vmaFlushAllocation(mContext.allocator, stage->memory, 0, hostData.size); + vmaFlushAllocation(mContext.allocator, stage->memory, 0, hostData->size); const VkCommandBuffer cmdbuffer = mContext.commands->get().cmdbuffer; - const VkImageSubresourceRange range = { mAspect, miplevel, 1, 0, 1 }; - const VkImageLayout textureLayout = getDefaultImageLayout(usage); - transitionLayout(cmdbuffer, range, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + VkBufferImageCopy copyRegion = { + .bufferOffset = {}, + .bufferRowLength = {}, + .bufferImageHeight = {}, + .imageSubresource = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .mipLevel = miplevel, + .baseArrayLayer = 0, + .layerCount = 1 + }, + .imageOffset = { int32_t(xoffset), int32_t(yoffset), int32_t(zoffset) }, + .imageExtent = { width, height, depth } + }; - copyBufferToImage(cmdbuffer, stage->buffer, mTextureImage, width, height, depth, - nullptr, miplevel); + VkImageSubresourceRange transitionRange = { + .aspectMask = mAspect, + .baseMipLevel = miplevel, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1 + }; - transitionLayout(cmdbuffer, range, textureLayout); + // Vulkan specifies subregions for 3D textures differently than from 2D arrays. + if (target == SamplerType::SAMPLER_2D_ARRAY) { + copyRegion.imageOffset.z = 0; + copyRegion.imageExtent.depth = 1; + copyRegion.imageSubresource.baseArrayLayer = zoffset; + copyRegion.imageSubresource.layerCount = depth; + transitionRange.baseArrayLayer = zoffset; + transitionRange.layerCount = depth; + } + + transitionLayout(cmdbuffer, transitionRange, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + + vkCmdCopyBufferToImage(cmdbuffer, stage->buffer, mTextureImage, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©Region); + + transitionLayout(cmdbuffer, transitionRange, getDefaultImageLayout(usage)); } -void VulkanTexture::updateWithBlitImage(const PixelBufferDescriptor& hostData, uint32_t width, +void VulkanTexture::updateImageWithBlit(const PixelBufferDescriptor& hostData, uint32_t width, uint32_t height, uint32_t depth, uint32_t miplevel) { void* mapped = nullptr; VulkanStageImage const* stage = mStagePool.acquireImage( @@ -310,8 +331,19 @@ void VulkanTexture::updateCubeImage(const PixelBufferDescriptor& data, transitionLayout(cmdbuffer, range, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - copyBufferToImage(cmdbuffer, stage->buffer, mTextureImage, width, height, 1, - &faceOffsets, miplevel); + VkBufferImageCopy regions[6] = {{}}; + VkExtent3D extent { width, height, 1 }; + for (size_t face = 0; face < 6; face++) { + auto& region = regions[face]; + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageSubresource.baseArrayLayer = face; + region.imageSubresource.layerCount = 1; + region.imageSubresource.mipLevel = miplevel; + region.imageExtent = extent; + region.bufferOffset = faceOffsets.offsets[face]; + } + vkCmdCopyBufferToImage(cmdbuffer, stage->buffer, mTextureImage, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 6, regions); transitionLayout(cmdbuffer, range, textureLayout); } @@ -373,37 +405,6 @@ VkImageView VulkanTexture::getImageView(VkImageSubresourceRange range) { return imageView; } -void VulkanTexture::copyBufferToImage(VkCommandBuffer cmd, VkBuffer buffer, VkImage image, - uint32_t width, uint32_t height, uint32_t depth, FaceOffsets const* faceOffsets, uint32_t miplevel) { - VkExtent3D extent { width, height, depth }; - if (target == SamplerType::SAMPLER_CUBEMAP) { - assert_invariant(faceOffsets); - VkBufferImageCopy regions[6] = {{}}; - for (size_t face = 0; face < 6; face++) { - auto& region = regions[face]; - region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - region.imageSubresource.baseArrayLayer = face; - region.imageSubresource.layerCount = 1; - region.imageSubresource.mipLevel = miplevel; - region.imageExtent = extent; - region.bufferOffset = faceOffsets->offsets[face]; - } - vkCmdCopyBufferToImage(cmd, buffer, image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 6, regions); - return; - } - VkBufferImageCopy region = {}; - region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - region.imageSubresource.mipLevel = miplevel; - region.imageSubresource.layerCount = 1; - region.imageExtent = extent; - if (target == SamplerType::SAMPLER_2D_ARRAY) { - region.imageExtent.depth = 1; - region.imageSubresource.layerCount = depth; - } - vkCmdCopyBufferToImage(cmd, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); -} - void VulkanTexture::transitionLayout(VkCommandBuffer commands, const VkImageSubresourceRange& range, VkImageLayout newLayout) { // In debug builds, ensure that all subresources in the given range have the same layout. diff --git a/filament/backend/src/vulkan/VulkanTexture.h b/filament/backend/src/vulkan/VulkanTexture.h index 29ca732d18..623dd6212e 100644 --- a/filament/backend/src/vulkan/VulkanTexture.h +++ b/filament/backend/src/vulkan/VulkanTexture.h @@ -31,10 +31,12 @@ struct VulkanTexture : public HwTexture { TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth, TextureUsage usage, VulkanStagePool& stagePool, VkComponentMapping swizzle = {}); ~VulkanTexture(); - void update2DImage(const PixelBufferDescriptor& data, uint32_t width, uint32_t height, - int miplevel); - void update3DImage(const PixelBufferDescriptor& data, uint32_t width, uint32_t height, - uint32_t depth, int miplevel); + + // Uploads data into a subregion of a 2D or 3D texture. + void updateImage(const PixelBufferDescriptor& data, uint32_t width, uint32_t height, + uint32_t depth, uint32_t xoffset, uint32_t yoffset, uint32_t zoffset, uint32_t miplevel); + + // Uploads data into all 6 faces of a cubemap for a given miplevel. void updateCubeImage(const PixelBufferDescriptor& data, const FaceOffsets& faceOffsets, uint32_t miplevel); @@ -63,16 +65,7 @@ private: // Gets or creates a cached VkImageView for a range of miplevels and array layers. VkImageView getImageView(VkImageSubresourceRange range); - // Issues a copy from a VkBuffer to a specified miplevel in a VkImage. The given width and - // height define a subregion within the miplevel. - void copyBufferToImage(VkCommandBuffer cmdbuffer, VkBuffer buffer, VkImage image, - uint32_t width, uint32_t height, uint32_t depth, - FaceOffsets const* faceOffsets, uint32_t miplevel); - - void updateWithCopyBuffer(const PixelBufferDescriptor& hostData, uint32_t width, - uint32_t height, uint32_t depth, uint32_t miplevel); - - void updateWithBlitImage(const PixelBufferDescriptor& hostData, uint32_t width, + void updateImageWithBlit(const PixelBufferDescriptor& hostData, uint32_t width, uint32_t height, uint32_t depth, uint32_t miplevel); VulkanTexture* mSidecarMSAA = nullptr;