Image copy
Image objects now have the ability to copy their content to another
image object. They can also copy their content to or from Buffer
objects.
Bug b\119620767
Change-Id: I047e9ecdcc11e264589de1d9461ef448f22a4d9e
Reviewed-on: https://swiftshader-review.googlesource.com/c/23070
Tested-by: Alexis Hétu <sugoi@google.com>
Reviewed-by: Chris Forbes <chrisforbes@google.com>
diff --git a/src/Vulkan/VkImage.cpp b/src/Vulkan/VkImage.cpp
index e5856ac..04f87f4 100644
--- a/src/Vulkan/VkImage.cpp
+++ b/src/Vulkan/VkImage.cpp
@@ -12,12 +12,24 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include "VkDeviceMemory.hpp"
+#include "VkBuffer.hpp"
#include "VkImage.hpp"
+#include "Device/Surface.hpp"
+#include <cstring>
namespace vk
{
-Image::Image(const VkImageCreateInfo* pCreateInfo, void* mem)
+Image::Image(const VkImageCreateInfo* pCreateInfo, void* mem) :
+ flags(pCreateInfo->flags),
+ imageType(pCreateInfo->imageType),
+ format(pCreateInfo->format),
+ extent(pCreateInfo->extent),
+ mipLevels(pCreateInfo->mipLevels),
+ arrayLayers(pCreateInfo->arrayLayers),
+ samples(pCreateInfo->samples),
+ tiling(pCreateInfo->tiling)
{
}
@@ -30,4 +42,183 @@
return 0;
}
+const VkMemoryRequirements Image::getMemoryRequirements() const
+{
+ VkMemoryRequirements memoryRequirements;
+ memoryRequirements.alignment = vk::REQUIRED_MEMORY_ALIGNMENT;
+ memoryRequirements.memoryTypeBits = vk::MEMORY_TYPE_GENERIC_BIT;
+ memoryRequirements.size = getStorageSize();
+ return memoryRequirements;
+}
+
+void Image::bind(VkDeviceMemory pDeviceMemory, VkDeviceSize pMemoryOffset)
+{
+ deviceMemory = pDeviceMemory;
+ memoryOffset = pMemoryOffset;
+}
+
+void Image::copyTo(VkImage dstImage, const VkImageCopy& pRegion)
+{
+ // Image copy does not perform any conversion, it simply copies memory from
+ // an image to another image that has the same number of bytes per pixel.
+ Image* dst = Cast(dstImage);
+ int srcBytesPerTexel = bytesPerTexel();
+ ASSERT(srcBytesPerTexel == dst->bytesPerTexel());
+
+ if(!((pRegion.srcSubresource.aspectMask == VK_IMAGE_ASPECT_COLOR_BIT) ||
+ (pRegion.srcSubresource.aspectMask == VK_IMAGE_ASPECT_DEPTH_BIT) ||
+ (pRegion.srcSubresource.aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT)) ||
+ (pRegion.srcSubresource.baseArrayLayer != 0) ||
+ (pRegion.srcSubresource.layerCount != 1) ||
+ (pRegion.srcSubresource.mipLevel != 0))
+ {
+ UNIMPLEMENTED();
+ }
+
+ if(!((pRegion.dstSubresource.aspectMask == VK_IMAGE_ASPECT_COLOR_BIT) ||
+ (pRegion.dstSubresource.aspectMask == VK_IMAGE_ASPECT_DEPTH_BIT) ||
+ (pRegion.dstSubresource.aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT)) ||
+ (pRegion.dstSubresource.baseArrayLayer != 0) ||
+ (pRegion.dstSubresource.layerCount != 1) ||
+ (pRegion.dstSubresource.mipLevel != 0))
+ {
+ UNIMPLEMENTED();
+ }
+
+ const char* srcMem = static_cast<const char*>(getTexelPointer(pRegion.srcOffset));
+ char* dstMem = static_cast<char*>(dst->getTexelPointer(pRegion.dstOffset));
+
+ int srcRowPitchBytes = rowPitchBytes();
+ int srcSlicePitchBytes = slicePitchBytes();
+ int dstRowPitchBytes = dst->rowPitchBytes();
+ int dstSlicePitchBytes = dst->slicePitchBytes();
+
+ bool isSingleLine = (pRegion.extent.height == 1) && (pRegion.extent.depth == 1);
+ bool isEntireLine = (pRegion.extent.width == extent.width) && (pRegion.extent.width == dst->extent.width) && (srcRowPitchBytes == dstRowPitchBytes);
+ bool isSinglePlane = (pRegion.extent.depth == 1);
+ bool isEntirePlane = isEntireLine && (pRegion.extent.height == extent.height) && (pRegion.extent.height == dst->extent.height) && (srcSlicePitchBytes == dstSlicePitchBytes);
+
+ if(isSingleLine)
+ {
+ memcpy(dstMem, srcMem, pRegion.extent.width * srcBytesPerTexel); // Copy one line
+ }
+ else if(isEntireLine && isSinglePlane)
+ {
+ memcpy(dstMem, srcMem, pRegion.extent.height * srcRowPitchBytes); // Copy one plane
+ }
+ else if(isEntireLine && isEntirePlane)
+ {
+ memcpy(dstMem, srcMem, pRegion.extent.depth * srcSlicePitchBytes); // Copy multiple planes
+ }
+ else if(isEntireLine) // Copy plane by plane
+ {
+ for(uint32_t z = 0; z < pRegion.extent.depth; z++, dstMem += dstSlicePitchBytes, srcMem += srcSlicePitchBytes)
+ {
+ memcpy(dstMem, srcMem, pRegion.extent.height * srcRowPitchBytes);
+ }
+ }
+ else // Copy line by line
+ {
+ for(uint32_t z = 0; z < pRegion.extent.depth; z++)
+ {
+ for(uint32_t y = 0; y < pRegion.extent.height; y++, dstMem += dstRowPitchBytes, srcMem += srcRowPitchBytes)
+ {
+ memcpy(dstMem, srcMem, pRegion.extent.width * srcBytesPerTexel);
+ }
+ }
+ }
+}
+
+void Image::copyTo(VkBuffer dstBuffer, const VkBufferImageCopy& pRegion)
+{
+ if((pRegion.imageExtent.width != extent.width) ||
+ (pRegion.imageExtent.height != extent.height) ||
+ (pRegion.imageExtent.depth != extent.depth) ||
+ !((pRegion.imageSubresource.aspectMask == VK_IMAGE_ASPECT_COLOR_BIT) ||
+ (pRegion.imageSubresource.aspectMask == VK_IMAGE_ASPECT_DEPTH_BIT) ||
+ (pRegion.imageSubresource.aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT)) ||
+ (pRegion.imageSubresource.baseArrayLayer != 0) ||
+ (pRegion.imageSubresource.layerCount != 1) ||
+ (pRegion.imageSubresource.mipLevel != 0) ||
+ (pRegion.imageOffset.x != 0) ||
+ (pRegion.imageOffset.y != 0) ||
+ (pRegion.imageOffset.z != 0) ||
+ (pRegion.bufferRowLength != extent.width) ||
+ (pRegion.bufferImageHeight != extent.height))
+ {
+ UNIMPLEMENTED();
+ }
+
+ Cast(dstBuffer)->copyFrom(Cast(deviceMemory)->getOffsetPointer(memoryOffset),
+ sw::Surface::sliceB(pRegion.imageExtent.width, pRegion.imageExtent.height,
+ getBorder(), format, false) * pRegion.imageExtent.depth,
+ pRegion.bufferOffset);
+}
+
+void Image::copyFrom(VkBuffer srcBuffer, const VkBufferImageCopy& pRegion)
+{
+ if((pRegion.imageExtent.width != extent.width) ||
+ (pRegion.imageExtent.height != extent.height) ||
+ (pRegion.imageExtent.depth != extent.depth) ||
+ !((pRegion.imageSubresource.aspectMask == VK_IMAGE_ASPECT_COLOR_BIT) ||
+ (pRegion.imageSubresource.aspectMask == VK_IMAGE_ASPECT_DEPTH_BIT) ||
+ (pRegion.imageSubresource.aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT)) ||
+ (pRegion.imageSubresource.baseArrayLayer != 0) ||
+ (pRegion.imageSubresource.layerCount != 1) ||
+ (pRegion.imageSubresource.mipLevel != 0) ||
+ (pRegion.imageOffset.x != 0) ||
+ (pRegion.imageOffset.y != 0) ||
+ (pRegion.imageOffset.z != 0) ||
+ (pRegion.bufferRowLength != extent.width) ||
+ (pRegion.bufferImageHeight != extent.height))
+ {
+ UNIMPLEMENTED();
+ }
+
+ Cast(srcBuffer)->copyTo(Cast(deviceMemory)->getOffsetPointer(memoryOffset),
+ sw::Surface::sliceB(pRegion.imageExtent.width, pRegion.imageExtent.height,
+ getBorder(), format, false) * pRegion.imageExtent.depth,
+ pRegion.bufferOffset);
+}
+
+void* Image::getTexelPointer(const VkOffset3D& offset) const
+{
+ return Cast(deviceMemory)->getOffsetPointer(texelOffsetBytesInStorage(offset) + memoryOffset);
+}
+
+VkDeviceSize Image::texelOffsetBytesInStorage(const VkOffset3D& offset) const
+{
+ return offset.z * slicePitchBytes() + offset.y * rowPitchBytes() + offset.x * bytesPerTexel();
+}
+
+int Image::rowPitchBytes() const
+{
+ return sw::Surface::pitchB(extent.width, getBorder(), format, false);
+}
+
+int Image::slicePitchBytes() const
+{
+ return sw::Surface::sliceB(extent.width, extent.height, getBorder(), format, false);
+}
+
+int Image::bytesPerTexel() const
+{
+ return sw::Surface::bytes(format);
+}
+
+int Image::getBorder() const
+{
+ return ((flags & VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT) && (imageType == VK_IMAGE_TYPE_2D)) ? 1 : 0;
+}
+
+VkDeviceSize Image::getStorageSize() const
+{
+ if(mipLevels > 1)
+ {
+ UNIMPLEMENTED();
+ }
+
+ return extent.depth * slicePitchBytes();
+}
+
} // namespace vk
\ No newline at end of file
diff --git a/src/Vulkan/VkImage.hpp b/src/Vulkan/VkImage.hpp
index 9e4261f..6fea3c5 100644
--- a/src/Vulkan/VkImage.hpp
+++ b/src/Vulkan/VkImage.hpp
@@ -17,6 +17,11 @@
#include "VkObject.hpp"
+namespace sw
+{
+ class Surface;
+};
+
namespace vk
{
@@ -29,7 +34,31 @@
static size_t ComputeRequiredAllocationSize(const VkImageCreateInfo* pCreateInfo);
+ VkDeviceSize getStorageSize() const;
+ const VkMemoryRequirements getMemoryRequirements() const;
+ void bind(VkDeviceMemory pDeviceMemory, VkDeviceSize pMemoryOffset);
+ void copyTo(VkImage dstImage, const VkImageCopy& pRegion);
+ void copyTo(VkBuffer dstBuffer, const VkBufferImageCopy& pRegion);
+ void copyFrom(VkBuffer srcBuffer, const VkBufferImageCopy& pRegion);
+
private:
+ void* getTexelPointer(const VkOffset3D& offset) const;
+ VkDeviceSize texelOffsetBytesInStorage(const VkOffset3D& offset) const;
+ int rowPitchBytes() const;
+ int slicePitchBytes() const;
+ int bytesPerTexel() const;
+ int getBorder() const;
+
+ VkDeviceMemory deviceMemory = nullptr;
+ VkDeviceSize memoryOffset = 0;
+ VkImageCreateFlags flags = 0;
+ VkImageType imageType = VK_IMAGE_TYPE_2D;
+ VkFormat format = VK_FORMAT_UNDEFINED;
+ VkExtent3D extent = {0, 0, 0};
+ uint32_t mipLevels = 0;
+ uint32_t arrayLayers = 0;
+ VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT;
+ VkImageTiling tiling = VK_IMAGE_TILING_OPTIMAL;
};
static inline Image* Cast(VkImage object)
diff --git a/src/Vulkan/libVulkan.cpp b/src/Vulkan/libVulkan.cpp
index 5ce5b48..768f728 100644
--- a/src/Vulkan/libVulkan.cpp
+++ b/src/Vulkan/libVulkan.cpp
@@ -551,7 +551,7 @@
TRACE("(VkDevice device = 0x%X, VkImage image = 0x%X, VkDeviceMemory memory = 0x%X, VkDeviceSize memoryOffset = %d)",
device, image, memory, memoryOffset);
- UNIMPLEMENTED();
+ vk::Cast(image)->bind(memory, memoryOffset);
return VK_SUCCESS;
}
@@ -569,7 +569,7 @@
TRACE("(VkDevice device = 0x%X, VkImage image = 0x%X, VkMemoryRequirements* pMemoryRequirements = 0x%X)",
device, image, pMemoryRequirements);
- UNIMPLEMENTED();
+ *pMemoryRequirements = vk::Cast(image)->getMemoryRequirements();
}
VKAPI_ATTR void VKAPI_CALL vkGetImageSparseMemoryRequirements(VkDevice device, VkImage image, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements* pSparseMemoryRequirements)