Browse Source

Feature: Texture copies can now use arbitrary ranges for source/destination, instead of forcing a copy of the entire texture face

BearishSun 8 years ago
parent
commit
3c1b9baaac

+ 69 - 20
Source/BansheeCore/Image/BsTexture.cpp

@@ -12,6 +12,8 @@
 
 
 namespace bs 
 namespace bs 
 {
 {
+	TEXTURE_COPY_DESC TEXTURE_COPY_DESC::DEFAULT = TEXTURE_COPY_DESC();
+
 	TextureProperties::TextureProperties()
 	TextureProperties::TextureProperties()
 	{ }
 	{ }
 
 
@@ -402,14 +404,13 @@ namespace bs
 		unlockImpl();
 		unlockImpl();
 	}
 	}
 
 
-	void Texture::copy(const SPtr<Texture>& target, UINT32 srcFace, UINT32 srcMipLevel, UINT32 dstFace,
-						   UINT32 dstMipLevel, const SPtr<CommandBuffer>& commandBuffer)
+	void Texture::copy(const SPtr<Texture>& target, const TEXTURE_COPY_DESC& desc, const SPtr<CommandBuffer>& commandBuffer)
 	{
 	{
 		THROW_IF_NOT_CORE_THREAD;
 		THROW_IF_NOT_CORE_THREAD;
 
 
 		if (target->mProperties.getTextureType() != mProperties.getTextureType())
 		if (target->mProperties.getTextureType() != mProperties.getTextureType())
 		{
 		{
-			LOGERR("Source and destination textures must be of same type and must have the same usage and type.");
+			LOGERR("Source and destination textures must be of same type.");
 			return;
 			return;
 		}
 		}
 
 
@@ -425,45 +426,93 @@ namespace bs
 			return;
 			return;
 		}
 		}
 
 
-		if (dstFace >= mProperties.getNumFaces())
+		if (desc.dstFace >= mProperties.getNumFaces())
+		{
+			LOGERR("Invalid destination face index.");
+			return;
+		}
+
+		if (desc.srcFace >= target->mProperties.getNumFaces())
 		{
 		{
-			LOGERR("Invalid destination face index");
+			LOGERR("Invalid destination face index.");
 			return;
 			return;
 		}
 		}
 
 
-		if (srcFace >= target->mProperties.getNumFaces())
+		if (desc.srcMip > mProperties.getNumMipmaps())
 		{
 		{
-			LOGERR("Invalid destination face index");
+			LOGERR("Source mip level out of range. Valid range is [0, " + toString(mProperties.getNumMipmaps()) + "].");
 			return;
 			return;
 		}
 		}
 
 
-		if (srcMipLevel > mProperties.getNumMipmaps())
+		if (desc.dstMip > target->mProperties.getNumMipmaps())
 		{
 		{
-			LOGERR("Source mip level out of range. Valid range is [0, " + toString(mProperties.getNumMipmaps()) + "]");
+			LOGERR("Destination mip level out of range. Valid range is [0, " + toString(target->mProperties.getNumMipmaps()) + "].");
 			return;
 			return;
 		}
 		}
 
 
-		if (dstMipLevel > target->mProperties.getNumMipmaps())
+		UINT32 srcWidth, srcHeight, srcDepth;
+		PixelUtil::getSizeForMipLevel(
+			mProperties.getWidth(),
+			mProperties.getHeight(),
+			mProperties.getDepth(),
+			desc.srcMip,
+			srcWidth,
+			srcHeight,
+			srcDepth);
+
+		UINT32 dstWidth, dstHeight, dstDepth;
+		PixelUtil::getSizeForMipLevel(
+			target->mProperties.getWidth(),
+			target->mProperties.getHeight(),
+			target->mProperties.getDepth(),
+			desc.dstMip,
+			dstWidth,
+			dstHeight,
+			dstDepth);
+
+		if(desc.dstPosition.x < 0 || desc.dstPosition.x >= (INT32)dstWidth || 
+			desc.dstPosition.y < 0 || desc.dstPosition.y >= (INT32)dstHeight ||
+			desc.dstPosition.z < 0 || desc.dstPosition.z >= (INT32)dstDepth)
 		{
 		{
-			LOGERR("Destination mip level out of range. Valid range is [0, " + toString(target->mProperties.getNumMipmaps()) + "]");
+			LOGERR("Destination position falls outside the destination texture.");
 			return;
 			return;
 		}
 		}
 
 
-		UINT32 srcMipWidth = mProperties.getWidth() >> srcMipLevel;
-		UINT32 srcMipHeight = mProperties.getHeight() >> srcMipLevel;
-		UINT32 srcMipDepth = mProperties.getDepth() >> srcMipLevel;
+		bool entireSurface = desc.srcVolume.getWidth() == 0 || 
+			desc.srcVolume.getHeight() == 0 || 
+			desc.srcVolume.getDepth() == 0;
+
+		UINT32 dstRight = (UINT32)desc.dstPosition.x;
+		UINT32 dstBottom = (UINT32)desc.dstPosition.y;
+		UINT32 dstBack = (UINT32)desc.dstPosition.z;
+		if(!entireSurface)
+		{
+			if(desc.srcVolume.left >= srcWidth || desc.srcVolume.right > srcWidth ||
+				desc.srcVolume.top >= srcHeight || desc.srcVolume.bottom > srcHeight ||
+				desc.srcVolume.front >= srcDepth || desc.srcVolume.back > srcDepth)
+			{
+				LOGERR("Source volume falls outside the source texture.");
+				return;
+			}
 
 
-		UINT32 dstMipWidth = target->mProperties.getWidth() >> dstMipLevel;
-		UINT32 dstMipHeight = target->mProperties.getHeight() >> dstMipLevel;
-		UINT32 dstMipDepth = target->mProperties.getDepth() >> dstMipLevel;
+			dstRight += desc.srcVolume.getWidth();
+			dstBottom += desc.srcVolume.getHeight();
+			dstBack += desc.srcVolume.getDepth();
+		}
+		else
+		{
+			dstRight += srcWidth;
+			dstBottom += srcHeight;
+			dstBack += srcDepth;
+		}
 
 
-		if (srcMipWidth != dstMipWidth || srcMipHeight != dstMipHeight || srcMipDepth != dstMipDepth)
+		if(dstRight > dstWidth || dstBottom > dstHeight || dstBack > dstDepth)
 		{
 		{
-			LOGERR("Source and destination sizes must match");
+			LOGERR("Destination volume falls outside the destination texture.");
 			return;
 			return;
 		}
 		}
 
 
-		copyImpl(srcFace, srcMipLevel, dstFace, dstMipLevel, target, commandBuffer);
+		copyImpl(target, desc, commandBuffer);
 	}
 	}
 
 
 	/************************************************************************/
 	/************************************************************************/

+ 39 - 8
Source/BansheeCore/Image/BsTexture.h

@@ -7,6 +7,7 @@
 #include "RenderAPI/BsHardwareBuffer.h"
 #include "RenderAPI/BsHardwareBuffer.h"
 #include "Image/BsPixelUtil.h"
 #include "Image/BsPixelUtil.h"
 #include "RenderAPI/BsTextureView.h"
 #include "RenderAPI/BsTextureView.h"
+#include "Math/BsVector3I.h"
 
 
 namespace bs 
 namespace bs 
 {
 {
@@ -78,6 +79,39 @@ namespace bs
 		UINT32 numArraySlices = 1;
 		UINT32 numArraySlices = 1;
 	};
 	};
 
 
+	/** Structure used for specifying information about a texture copy operation. */
+	struct TEXTURE_COPY_DESC
+	{
+		/** 
+		 * Face from which to copy. This can be an entry in an array of textures, or a single face of a cube map. If cubemap
+		 * array, then each array entry takes up six faces.
+		 */
+		UINT32 srcFace = 0;
+
+		/** Mip level from which to copy. */
+		UINT32 srcMip = 0;
+
+		/** Pixel volume from which to copy from. This defaults to all pixels of the face. */
+		PixelVolume srcVolume = PixelVolume(0, 0, 0, 0, 0, 0);
+
+		/** 
+		 * Face to which to copy. This can be an entry in an array of textures, or a single face of a cube map. If cubemap
+		 * array, then each array entry takes up six faces.
+		 */
+		UINT32 dstFace = 0;
+
+		/** Mip level to which to copy. */
+		UINT32 dstMip = 0;
+
+		/** 
+		 * Coordinates to write the source pixels to. The destination texture must have enough pixels to fit the entire
+		 * source volume.
+		 */
+		Vector3I dstPosition;
+
+		BS_CORE_EXPORT static TEXTURE_COPY_DESC DEFAULT;
+	};
+
 	/** Properties of a Texture. Shared between sim and core thread versions of a Texture. */
 	/** Properties of a Texture. Shared between sim and core thread versions of a Texture. */
 	class BS_CORE_EXPORT TextureProperties
 	class BS_CORE_EXPORT TextureProperties
 	{
 	{
@@ -359,15 +393,12 @@ namespace bs
 		 * surface before copying.
 		 * surface before copying.
 		 *
 		 *
 		 * @param[in]	target				Texture that contains the destination subresource.
 		 * @param[in]	target				Texture that contains the destination subresource.
-		 * @param[in]	srcFace				Face to copy from.
-		 * @param[in]	srcMipLevel			Mip level to copy from.
-		 * @param[in]	dstFace				Face to copy to.
-		 * @param[in]	dstMipLevel			Mip level to copy to.
+		 * @param[in]	desc				Structure used for customizing the copy operation.
 		 * @param[in]	commandBuffer		Command buffer to queue the copy operation on. If null, main command buffer is
 		 * @param[in]	commandBuffer		Command buffer to queue the copy operation on. If null, main command buffer is
 		 *									used.
 		 *									used.
 		 */
 		 */
-		void copy(const SPtr<Texture>& target, UINT32 srcFace = 0, UINT32 srcMipLevel = 0, UINT32 dstFace = 0,
-			UINT32 dstMipLevel = 0, const SPtr<CommandBuffer>& commandBuffer = nullptr);
+		void copy(const SPtr<Texture>& target, const TEXTURE_COPY_DESC& desc = TEXTURE_COPY_DESC::DEFAULT,
+			const SPtr<CommandBuffer>& commandBuffer = nullptr);
 
 
 		/**
 		/**
 		 * Reads data from the texture buffer into the provided buffer.
 		 * Reads data from the texture buffer into the provided buffer.
@@ -445,8 +476,8 @@ namespace bs
 		virtual void unlockImpl() = 0;
 		virtual void unlockImpl() = 0;
 
 
 		/** @copydoc copy */
 		/** @copydoc copy */
-		virtual void copyImpl(UINT32 srcFace, UINT32 srcMipLevel, UINT32 dstFace, UINT32 dstMipLevel, 
-			const SPtr<Texture>& target, const SPtr<CommandBuffer>& commandBuffer) = 0;
+		virtual void copyImpl(const SPtr<Texture>& target, const TEXTURE_COPY_DESC& desc, 
+			const SPtr<CommandBuffer>& commandBuffer) = 0;
 
 
 		/** @copydoc readData */
 		/** @copydoc readData */
 		virtual void readDataImpl(PixelData& dest, UINT32 mipLevel = 0, UINT32 face = 0, UINT32 deviceIdx = 0,
 		virtual void readDataImpl(PixelData& dest, UINT32 mipLevel = 0, UINT32 face = 0, UINT32 deviceIdx = 0,

+ 54 - 17
Source/BansheeD3D11RenderAPI/BsD3D11Texture.cpp

@@ -61,16 +61,15 @@ namespace bs { namespace ct
 		Texture::initialize();
 		Texture::initialize();
 	}
 	}
 
 
-	void D3D11Texture::copyImpl(UINT32 srcFace, UINT32 srcMipLevel, UINT32 destFace, UINT32 destMipLevel,
-									const SPtr<Texture>& target, const SPtr<CommandBuffer>& commandBuffer)
+	void D3D11Texture::copyImpl(const SPtr<Texture>& target, const TEXTURE_COPY_DESC& desc, 
+			const SPtr<CommandBuffer>& commandBuffer)
 	{
 	{
-		auto executeRef = [this](UINT32 srcFace, UINT32 srcMipLevel, UINT32 destFace, UINT32 destMipLevel,
-			const SPtr<Texture>& target)
+		auto executeRef = [this](const SPtr<Texture>& target, const TEXTURE_COPY_DESC& desc)
 		{
 		{
 			D3D11Texture* other = static_cast<D3D11Texture*>(target.get());
 			D3D11Texture* other = static_cast<D3D11Texture*>(target.get());
 
 
-			UINT32 srcResIdx = D3D11CalcSubresource(srcMipLevel, srcFace, mProperties.getNumMipmaps() + 1);
-			UINT32 destResIdx = D3D11CalcSubresource(destMipLevel, destFace, target->getProperties().getNumMipmaps() + 1);
+			UINT32 srcResIdx = D3D11CalcSubresource(desc.srcMip, desc.srcFace, mProperties.getNumMipmaps() + 1);
+			UINT32 destResIdx = D3D11CalcSubresource(desc.dstMip, desc.dstFace, target->getProperties().getNumMipmaps() + 1);
 
 
 			D3D11RenderAPI* rs = static_cast<D3D11RenderAPI*>(RenderAPI::instancePtr());
 			D3D11RenderAPI* rs = static_cast<D3D11RenderAPI*>(RenderAPI::instancePtr());
 			D3D11Device& device = rs->getPrimaryDevice();
 			D3D11Device& device = rs->getPrimaryDevice();
@@ -78,19 +77,57 @@ namespace bs { namespace ct
 			bool srcHasMultisample = mProperties.getNumSamples() > 1;
 			bool srcHasMultisample = mProperties.getNumSamples() > 1;
 			bool destHasMultisample = target->getProperties().getNumSamples() > 1;
 			bool destHasMultisample = target->getProperties().getNumSamples() > 1;
 
 
+			bool copyEntireSurface = desc.srcVolume.getWidth() == 0 || 
+				desc.srcVolume.getHeight() == 0 || 
+				desc.srcVolume.getDepth() == 0;
+
 			if (srcHasMultisample && !destHasMultisample) // Resolving from MS to non-MS texture
 			if (srcHasMultisample && !destHasMultisample) // Resolving from MS to non-MS texture
 			{
 			{
-				device.getImmediateContext()->ResolveSubresource(other->getDX11Resource(), destResIdx, mTex, srcResIdx, mDXGIFormat);
+				if(copyEntireSurface)
+					device.getImmediateContext()->ResolveSubresource(other->getDX11Resource(), destResIdx, mTex, srcResIdx, mDXGIFormat);
+				else
+				{
+					// Need to first resolve to a temporary texture, then copy
+					TEXTURE_DESC tempDesc;
+					tempDesc.width = mProperties.getWidth();
+					tempDesc.height = mProperties.getHeight();
+					tempDesc.format = mProperties.getFormat();
+					tempDesc.hwGamma = mProperties.isHardwareGammaEnabled();
+
+					SPtr<D3D11Texture> temporary = std::static_pointer_cast<D3D11Texture>(Texture::create(tempDesc));
+					device.getImmediateContext()->ResolveSubresource(temporary->getDX11Resource(), 0, mTex, srcResIdx, mDXGIFormat);
+
+					TEXTURE_COPY_DESC tempCopyDesc;
+					tempCopyDesc.dstMip = desc.dstMip;
+					tempCopyDesc.dstFace = desc.dstFace;
+					tempCopyDesc.dstPosition = desc.dstPosition;
+
+					temporary->copy(target, tempCopyDesc);
+				}
 			}
 			}
 			else
 			else
 			{
 			{
-				if (mProperties.getNumSamples() != target->getProperties().getNumSamples())
-				{
-					LOGERR("When copying textures their multisample counts must match. Ignoring copy.");
-					return;
-				}
-
-				device.getImmediateContext()->CopySubresourceRegion(other->getDX11Resource(), destResIdx, 0, 0, 0, mTex, srcResIdx, nullptr);
+				D3D11_BOX srcRegion;
+				srcRegion.left = desc.srcVolume.left;
+				srcRegion.right = desc.srcVolume.right;
+				srcRegion.top = desc.srcVolume.top;
+				srcRegion.bottom = desc.srcVolume.bottom;
+				srcRegion.front = desc.srcVolume.front;
+				srcRegion.back = desc.srcVolume.back;
+
+				D3D11_BOX* srcRegionPtr = nullptr;
+				if(!copyEntireSurface)
+					srcRegionPtr = &srcRegion;
+
+				device.getImmediateContext()->CopySubresourceRegion(
+					other->getDX11Resource(),
+					destResIdx,
+					(UINT32)desc.dstPosition.x,
+					(UINT32)desc.dstPosition.y,
+					(UINT32)desc.dstPosition.z,
+					mTex,
+					srcResIdx,
+					srcRegionPtr);
 
 
 				if (device.hasError())
 				if (device.hasError())
 				{
 				{
@@ -101,10 +138,10 @@ namespace bs { namespace ct
 		};
 		};
 
 
 		if (commandBuffer == nullptr)
 		if (commandBuffer == nullptr)
-			executeRef(srcFace, srcMipLevel, destFace, destMipLevel, target);
+			executeRef(target, desc);
 		else
 		else
 		{
 		{
-			auto execute = [=]() { executeRef(srcFace, srcMipLevel, destFace, destMipLevel, target); };
+			auto execute = [=]() { executeRef(target, desc); };
 
 
 			SPtr<D3D11CommandBuffer> cb = std::static_pointer_cast<D3D11CommandBuffer>(commandBuffer);
 			SPtr<D3D11CommandBuffer> cb = std::static_pointer_cast<D3D11CommandBuffer>(commandBuffer);
 			cb->queueCommand(execute);
 			cb->queueCommand(execute);
@@ -442,7 +479,7 @@ namespace bs { namespace ct
 		desc.MipLevels = (numMips == MIP_UNLIMITED || (1U << numMips) > width) ? 0 : numMips + 1;
 		desc.MipLevels = (numMips == MIP_UNLIMITED || (1U << numMips) > width) ? 0 : numMips + 1;
 
 
 		if (texType == TEX_TYPE_CUBE_MAP)
 		if (texType == TEX_TYPE_CUBE_MAP)
-            desc.MiscFlags |= D3D11_RESOURCE_MISC_TEXTURECUBE;
+			desc.MiscFlags |= D3D11_RESOURCE_MISC_TEXTURECUBE;
 
 
 		if ((usage & TU_LOADSTORE) != 0)
 		if ((usage & TU_LOADSTORE) != 0)
 		{
 		{

+ 2 - 2
Source/BansheeD3D11RenderAPI/BsD3D11Texture.h

@@ -48,8 +48,8 @@ namespace bs { namespace ct
 		void unlockImpl() override;
 		void unlockImpl() override;
 
 
 		/** @copydoc Texture::copyImpl */
 		/** @copydoc Texture::copyImpl */
-		void copyImpl(UINT32 srcFace, UINT32 srcMipLevel, UINT32 dstFace, UINT32 dstMipLevel,
-					  const SPtr<Texture>& target, const SPtr<CommandBuffer>& commandBuffer) override;
+		void copyImpl(const SPtr<Texture>& target, const TEXTURE_COPY_DESC& desc, 
+			const SPtr<CommandBuffer>& commandBuffer) override;
 
 
 		/** @copydoc Texture::readData */
 		/** @copydoc Texture::readData */
 		void readDataImpl(PixelData& dest, UINT32 mipLevel = 0, UINT32 face = 0, UINT32 deviceIdx = 0,
 		void readDataImpl(PixelData& dest, UINT32 mipLevel = 0, UINT32 face = 0, UINT32 deviceIdx = 0,

+ 5 - 0
Source/BansheeGLRenderAPI/BsGLPixelBuffer.cpp

@@ -479,6 +479,11 @@ namespace bs { namespace ct
 		}
 		}
 	}
 	}
 
 
+	void GLTextureBuffer::blitFromTexture(GLTextureBuffer* src)
+	{
+		GLPixelBuffer::blitFromTexture(src);
+	}
+
 	void GLTextureBuffer::blitFromTexture(GLTextureBuffer* src, const PixelVolume& srcBox, const PixelVolume& dstBox)
 	void GLTextureBuffer::blitFromTexture(GLTextureBuffer* src, const PixelVolume& srcBox, const PixelVolume& dstBox)
 	{
 	{
 		// If supported, prefer direct image copy. If not supported, or if sample counts don't match, fall back to FB blit
 		// If supported, prefer direct image copy. If not supported, or if sample counts don't match, fall back to FB blit

+ 3 - 0
Source/BansheeGLRenderAPI/BsGLPixelBuffer.h

@@ -165,6 +165,9 @@ namespace bs { namespace ct
 
 
 		/** @copydoc GLPixelBuffer::download */
 		/** @copydoc GLPixelBuffer::download */
 		void download(const PixelData &data) override;
 		void download(const PixelData &data) override;
+
+		/** @copydoc GLPixelBuffer::blitFromTexture */
+		void blitFromTexture(GLTextureBuffer *src) override;
   
   
 		/** @copydoc GLPixelBuffer::blitFromTexture */
 		/** @copydoc GLPixelBuffer::blitFromTexture */
 		void blitFromTexture(GLTextureBuffer *src, const PixelVolume &srcBox, const PixelVolume &dstBox) override;
 		void blitFromTexture(GLTextureBuffer *src, const PixelVolume &srcBox, const PixelVolume &dstBox) override;

+ 25 - 8
Source/BansheeGLRenderAPI/BsGLTexture.cpp

@@ -373,23 +373,40 @@ namespace bs { namespace ct
 			getBuffer(face, mipLevel)->upload(src, src.getExtents());
 			getBuffer(face, mipLevel)->upload(src, src.getExtents());
 	}
 	}
 
 
-	void GLTexture::copyImpl(UINT32 srcFace, UINT32 srcMipLevel, UINT32 destFace, UINT32 destMipLevel,
-								 const SPtr<Texture>& target, const SPtr<CommandBuffer>& commandBuffer)
+	void GLTexture::copyImpl(const SPtr<Texture>& target, const TEXTURE_COPY_DESC& desc, 
+		const SPtr<CommandBuffer>& commandBuffer)
 	{
 	{
-		auto executeRef = [this](UINT32 srcFace, UINT32 srcMipLevel, UINT32 destFace, UINT32 destMipLevel, 
-			const SPtr<Texture>& target)
+		auto executeRef = [this](const SPtr<Texture>& target, const TEXTURE_COPY_DESC& desc)
 		{
 		{
 			GLTexture* destTex = static_cast<GLTexture*>(target.get());
 			GLTexture* destTex = static_cast<GLTexture*>(target.get());
-			GLTextureBuffer *src = static_cast<GLTextureBuffer*>(getBuffer(srcFace, srcMipLevel).get());
+			GLTextureBuffer* dest = static_cast<GLTextureBuffer*>(destTex->getBuffer(desc.dstFace, desc.dstMip).get());
+			GLTextureBuffer* src = static_cast<GLTextureBuffer*>(getBuffer(desc.srcFace, desc.srcMip).get());
 
 
-			destTex->getBuffer(destFace, destMipLevel)->blitFromTexture(src);
+			bool copyEntireSurface = desc.srcVolume.getWidth() == 0 || 
+				desc.srcVolume.getHeight() == 0 || 
+				desc.srcVolume.getDepth() == 0;
+
+			if(copyEntireSurface)
+				dest->blitFromTexture(src);
+			else
+			{
+				PixelVolume dstVolume;
+				dstVolume.left = (UINT32)desc.dstPosition.x;
+				dstVolume.top = (UINT32)desc.dstPosition.y;
+				dstVolume.front = (UINT32)desc.dstPosition.z;
+				dstVolume.right = dstVolume.left + desc.srcVolume.getWidth();
+				dstVolume.bottom = dstVolume.top + desc.srcVolume.getHeight();
+				dstVolume.back = dstVolume.front + desc.srcVolume.getDepth();
+
+				dest->blitFromTexture(src, desc.srcVolume, dstVolume);
+			}
 		};
 		};
 
 
 		if (commandBuffer == nullptr)
 		if (commandBuffer == nullptr)
-			executeRef(srcFace, srcMipLevel, destFace, destMipLevel, target);
+			executeRef(target, desc);
 		else
 		else
 		{
 		{
-			auto execute = [=]() { executeRef(srcFace, srcMipLevel, destFace, destMipLevel, target); };
+			auto execute = [=]() { executeRef(target, desc); };
 
 
 			SPtr<GLCommandBuffer> cb = std::static_pointer_cast<GLCommandBuffer>(commandBuffer);
 			SPtr<GLCommandBuffer> cb = std::static_pointer_cast<GLCommandBuffer>(commandBuffer);
 			cb->queueCommand(execute);
 			cb->queueCommand(execute);

+ 2 - 2
Source/BansheeGLRenderAPI/BsGLTexture.h

@@ -60,8 +60,8 @@ namespace bs { namespace ct
 		void unlockImpl() override;
 		void unlockImpl() override;
 
 
 		/** @copydoc Texture::copyImpl */
 		/** @copydoc Texture::copyImpl */
-		void copyImpl(UINT32 srcFace, UINT32 srcMipLevel, UINT32 dstFace, UINT32 dstMipLevel,
-					  const SPtr<Texture>& target, const SPtr<CommandBuffer>& commandBuffer) override;
+		void copyImpl(const SPtr<Texture>& target, const TEXTURE_COPY_DESC& desc, 
+			const SPtr<CommandBuffer>& commandBuffer) override;
 
 
 		/** @copydoc Texture::readData */
 		/** @copydoc Texture::readData */
 		void readDataImpl(PixelData& dest, UINT32 mipLevel = 0, UINT32 face = 0, UINT32 deviceIdx = 0,
 		void readDataImpl(PixelData& dest, UINT32 mipLevel = 0, UINT32 face = 0, UINT32 deviceIdx = 0,

+ 42 - 32
Source/BansheeVulkanRenderAPI/BsVulkanTexture.cpp

@@ -826,8 +826,8 @@ namespace bs { namespace ct
 		bs_stack_free(imageRegions);
 		bs_stack_free(imageRegions);
 	}
 	}
 
 
-	void VulkanTexture::copyImpl(UINT32 srcFace, UINT32 srcMipLevel, UINT32 destFace, UINT32 destMipLevel,
-									 const SPtr<Texture>& target, const SPtr<CommandBuffer>& commandBuffer)
+	void VulkanTexture::copyImpl(const SPtr<Texture>& target, const TEXTURE_COPY_DESC& desc, 
+			const SPtr<CommandBuffer>& commandBuffer)
 	{
 	{
 		VulkanTexture* other = static_cast<VulkanTexture*>(target.get());
 		VulkanTexture* other = static_cast<VulkanTexture*>(target.get());
 
 
@@ -858,47 +858,67 @@ namespace bs { namespace ct
 		VkImageLayout transferDstLayout = other->mDirectlyMappable ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
 		VkImageLayout transferDstLayout = other->mDirectlyMappable ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
 
 
 		UINT32 mipWidth, mipHeight, mipDepth;
 		UINT32 mipWidth, mipHeight, mipDepth;
-		PixelUtil::getSizeForMipLevel(srcProps.getWidth(), srcProps.getHeight(), srcProps.getDepth(), srcMipLevel,
-									  mipWidth, mipHeight, mipDepth);
+
+		bool copyEntireSurface = desc.srcVolume.getWidth() == 0 || 
+			desc.srcVolume.getHeight() == 0 || 
+			desc.srcVolume.getDepth() == 0;
+
+		if(copyEntireSurface)
+		{
+			PixelUtil::getSizeForMipLevel(
+				srcProps.getWidth(),
+				srcProps.getHeight(),
+				srcProps.getDepth(),
+				desc.srcMip,
+				mipWidth,
+				mipHeight,
+				mipDepth);
+		}
+		else
+		{
+			mipWidth = desc.srcVolume.getWidth();
+			mipHeight = desc.srcVolume.getHeight();
+			mipDepth = desc.srcVolume.getDepth();
+		}
 
 
 		VkImageResolve resolveRegion;
 		VkImageResolve resolveRegion;
-		resolveRegion.srcOffset = { 0, 0, 0 };
-		resolveRegion.dstOffset = { 0, 0, 0 };
+		resolveRegion.srcOffset = { (INT32)desc.srcVolume.left, (INT32)desc.srcVolume.top, (INT32)desc.srcVolume.front };
+		resolveRegion.dstOffset = { desc.dstPosition.x, desc.dstPosition.y, desc.dstPosition.z };
 		resolveRegion.extent = { mipWidth, mipHeight, mipDepth };
 		resolveRegion.extent = { mipWidth, mipHeight, mipDepth };
-		resolveRegion.srcSubresource.baseArrayLayer = srcFace;
+		resolveRegion.srcSubresource.baseArrayLayer = desc.srcFace;
 		resolveRegion.srcSubresource.layerCount = 1;
 		resolveRegion.srcSubresource.layerCount = 1;
-		resolveRegion.srcSubresource.mipLevel = srcMipLevel;
+		resolveRegion.srcSubresource.mipLevel = desc.srcMip;
 		resolveRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
 		resolveRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
-		resolveRegion.dstSubresource.baseArrayLayer = destFace;
+		resolveRegion.dstSubresource.baseArrayLayer = desc.dstFace;
 		resolveRegion.dstSubresource.layerCount = 1;
 		resolveRegion.dstSubresource.layerCount = 1;
-		resolveRegion.dstSubresource.mipLevel = destMipLevel;
+		resolveRegion.dstSubresource.mipLevel = desc.dstMip;
 		resolveRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
 		resolveRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
 
 
 		VkImageCopy imageRegion;
 		VkImageCopy imageRegion;
-		imageRegion.srcOffset = { 0, 0, 0 };
-		imageRegion.dstOffset = { 0, 0, 0 };
+		imageRegion.srcOffset = { (INT32)desc.srcVolume.left, (INT32)desc.srcVolume.top, (INT32)desc.srcVolume.front };
+		imageRegion.dstOffset = { desc.dstPosition.x, desc.dstPosition.y, desc.dstPosition.z };
 		imageRegion.extent = { mipWidth, mipHeight, mipDepth };
 		imageRegion.extent = { mipWidth, mipHeight, mipDepth };
-		imageRegion.srcSubresource.baseArrayLayer = srcFace;
+		imageRegion.srcSubresource.baseArrayLayer = desc.srcFace;
 		imageRegion.srcSubresource.layerCount = 1;
 		imageRegion.srcSubresource.layerCount = 1;
-		imageRegion.srcSubresource.mipLevel = srcMipLevel;
+		imageRegion.srcSubresource.mipLevel = desc.srcMip;
 		imageRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
 		imageRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
-		imageRegion.dstSubresource.baseArrayLayer = destFace;
+		imageRegion.dstSubresource.baseArrayLayer = desc.dstFace;
 		imageRegion.dstSubresource.layerCount = 1;
 		imageRegion.dstSubresource.layerCount = 1;
-		imageRegion.dstSubresource.mipLevel = destMipLevel;
+		imageRegion.dstSubresource.mipLevel = desc.dstMip;
 		imageRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
 		imageRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
 
 
 		VkImageSubresourceRange srcRange;
 		VkImageSubresourceRange srcRange;
 		srcRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
 		srcRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
-		srcRange.baseArrayLayer = srcFace;
+		srcRange.baseArrayLayer = desc.srcFace;
 		srcRange.layerCount = 1;
 		srcRange.layerCount = 1;
-		srcRange.baseMipLevel = srcMipLevel;
+		srcRange.baseMipLevel = desc.srcMip;
 		srcRange.levelCount = 1;
 		srcRange.levelCount = 1;
 
 
 		VkImageSubresourceRange dstRange;
 		VkImageSubresourceRange dstRange;
 		dstRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
 		dstRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
-		dstRange.baseArrayLayer = destFace;
+		dstRange.baseArrayLayer = desc.dstFace;
 		dstRange.layerCount = 1;
 		dstRange.layerCount = 1;
-		dstRange.baseMipLevel = destMipLevel;
+		dstRange.baseMipLevel = desc.dstMip;
 		dstRange.levelCount = 1;
 		dstRange.levelCount = 1;
 
 
 		VulkanRenderAPI& rapi = static_cast<VulkanRenderAPI&>(RenderAPI::instance());
 		VulkanRenderAPI& rapi = static_cast<VulkanRenderAPI&>(RenderAPI::instance());
@@ -949,19 +969,9 @@ namespace bs { namespace ct
 		// Transfer back to optimal layouts
 		// Transfer back to optimal layouts
 		srcLayout = srcImage->getOptimalLayout();
 		srcLayout = srcImage->getOptimalLayout();
 
 
-		srcAccessMask = srcImage->getAccessFlags(srcLayout);
-		vkCB->setLayout(srcImage->getHandle(), VK_ACCESS_TRANSFER_READ_BIT, srcAccessMask, transferSrcLayout, 
-			srcLayout, srcRange);
-
-		dstLayout = dstImage->getOptimalLayout();
-
-		dstAccessMask = dstImage->getAccessFlags(dstLayout);
-		vkCB->setLayout(dstImage->getHandle(), VK_ACCESS_TRANSFER_WRITE_BIT, dstAccessMask, transferDstLayout, 
-			dstLayout, dstRange);
-
 		// Notify the command buffer that these resources are being used on it
 		// Notify the command buffer that these resources are being used on it
-		vkCB->registerResource(srcImage, srcRange, VulkanUseFlag::Read, ResourceUsage::Transfer);
-		vkCB->registerResource(dstImage, dstRange, VulkanUseFlag::Write, ResourceUsage::Transfer);
+		vkCB->registerResource(srcImage, srcRange, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VulkanUseFlag::Read, ResourceUsage::Transfer);
+		vkCB->registerResource(dstImage, dstRange, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VulkanUseFlag::Write, ResourceUsage::Transfer);
 	}
 	}
 
 
 	PixelData VulkanTexture::lockImpl(GpuLockOptions options, UINT32 mipLevel, UINT32 face, UINT32 deviceIdx,
 	PixelData VulkanTexture::lockImpl(GpuLockOptions options, UINT32 mipLevel, UINT32 face, UINT32 deviceIdx,

+ 2 - 2
Source/BansheeVulkanRenderAPI/BsVulkanTexture.h

@@ -210,8 +210,8 @@ namespace bs { namespace ct
 		void unlockImpl() override;
 		void unlockImpl() override;
 
 
 		/** @copydoc Texture::copyImpl */
 		/** @copydoc Texture::copyImpl */
-		void copyImpl(UINT32 srcFace, UINT32 srcMipLevel, UINT32 dstFace, UINT32 dstMipLevel,
-					  const SPtr<Texture>& target, const SPtr<CommandBuffer>& commandBuffer) override;
+		void copyImpl(const SPtr<Texture>& target, const TEXTURE_COPY_DESC& desc, 
+			const SPtr<CommandBuffer>& commandBuffer) override;
 
 
 		/** @copydoc Texture::readData */
 		/** @copydoc Texture::readData */
 		void readDataImpl(PixelData& dest, UINT32 mipLevel = 0, UINT32 face = 0, UINT32 deviceIdx = 0,
 		void readDataImpl(PixelData& dest, UINT32 mipLevel = 0, UINT32 face = 0, UINT32 deviceIdx = 0,