Allow the Blitter to clear and blit vk::Image objects directly

The sw::Surface object was the intermediate representation of an
image used between the vk::Image and the Blitter. This cl removes
the need for an intermediate representation by having the Blitter
use the vk::Image directly.

There should be no regression for the clear and blit tests.

Bug b/126883332

Change-Id: Icbc15470e3ad112ed78f4f62d6d82c66e3e37a20
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/25928
Tested-by: Alexis Hétu <sugoi@google.com>
Presubmit-Ready: Alexis Hétu <sugoi@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Reviewed-by: Chris Forbes <chrisforbes@google.com>
diff --git a/src/Device/Blitter.cpp b/src/Device/Blitter.cpp
index b86e4f9..4e42bd3 100644
--- a/src/Device/Blitter.cpp
+++ b/src/Device/Blitter.cpp
@@ -18,6 +18,7 @@
 #include "Reactor/Reactor.hpp"
 #include "System/Memory.hpp"
 #include "Vulkan/VkDebug.hpp"
+#include "Vulkan/VkImage.hpp"
 
 namespace sw
 {
@@ -44,6 +45,203 @@
 		delete color;
 	}
 
+	void Blitter::clear(void *pixel, VkFormat format, vk::Image *dest, const VkImageSubresourceRange& subresourceRange, const VkRect2D* renderArea)
+	{
+		VkImageAspectFlagBits aspect = static_cast<VkImageAspectFlagBits>(subresourceRange.aspectMask);
+		if(dest->getFormat(aspect) == VK_FORMAT_UNDEFINED)
+		{
+			return;
+		}
+
+		if(fastClear(pixel, format, dest, subresourceRange, renderArea))
+		{
+			return;
+		}
+
+		State state(format, dest->getFormat(aspect), dest->getSampleCountFlagBits(), { 0xF });
+		Routine *blitRoutine = getRoutine(state);
+		if(!blitRoutine)
+		{
+			return;
+		}
+
+		void(*blitFunction)(const BlitData *data) = (void(*)(const BlitData*))blitRoutine->getEntry();
+
+		VkImageSubresourceLayers subresLayers =
+		{
+			subresourceRange.aspectMask,
+			subresourceRange.baseMipLevel,
+			subresourceRange.baseArrayLayer,
+			1
+		};
+
+		uint32_t lastMipLevel = dest->getLastMipLevel(subresourceRange);
+		uint32_t lastLayer = dest->getLastLayerIndex(subresourceRange);
+
+		VkRect2D area = { { 0, 0 }, { 0, 0 } };
+		if(renderArea)
+		{
+			ASSERT(subresourceRange.levelCount == 1);
+			area = *renderArea;
+		}
+
+		for(; subresLayers.mipLevel <= lastMipLevel; subresLayers.mipLevel++)
+		{
+			VkExtent3D extent = dest->getMipLevelExtent(subresLayers.mipLevel);
+			if(!renderArea)
+			{
+				area.extent.width = extent.width;
+				area.extent.height = extent.height;
+			}
+
+			BlitData data =
+			{
+				pixel, nullptr, // source, dest
+
+				sw::Surface::bytes(format),                           // sPitchB
+				dest->rowPitchBytes(aspect, subresLayers.mipLevel),   // dPitchB
+				dest->slicePitchBytes(aspect, subresLayers.mipLevel), // dSliceB
+
+				0.5f, 0.5f, 0.0f, 0.0f, // x0, y0, w, h
+
+				area.offset.y, static_cast<int>(area.offset.y + area.extent.height), // y0d, y1d
+				area.offset.x, static_cast<int>(area.offset.x + area.extent.width),  // x0d, x1d
+
+				0, 0, // sWidth, sHeight
+			};
+
+			for(subresLayers.baseArrayLayer = subresourceRange.baseArrayLayer; subresLayers.baseArrayLayer <= lastLayer; subresLayers.baseArrayLayer++)
+			{
+				for(uint32_t depth = 0; depth < extent.depth; depth++)
+				{
+					data.dest = dest->getTexelPointer({ 0, 0, static_cast<int32_t>(depth) }, subresLayers);
+
+					blitFunction(&data);
+				}
+			}
+		}
+	}
+
+	bool Blitter::fastClear(void *pixel, VkFormat format, vk::Image *dest, const VkImageSubresourceRange& subresourceRange, const VkRect2D* renderArea)
+	{
+		if(format != VK_FORMAT_R32G32B32A32_SFLOAT)
+		{
+			return false;
+		}
+
+		float *color = (float*)pixel;
+		float r = color[0];
+		float g = color[1];
+		float b = color[2];
+		float a = color[3];
+
+		uint32_t packed;
+
+		VkImageAspectFlagBits aspect = static_cast<VkImageAspectFlagBits>(subresourceRange.aspectMask);
+		switch(dest->getFormat(aspect))
+		{
+		case VK_FORMAT_R5G6B5_UNORM_PACK16:
+			packed = ((uint16_t)(31 * b + 0.5f) << 0) |
+			         ((uint16_t)(63 * g + 0.5f) << 5) |
+			         ((uint16_t)(31 * r + 0.5f) << 11);
+			break;
+		case VK_FORMAT_B5G6R5_UNORM_PACK16:
+			packed = ((uint16_t)(31 * r + 0.5f) << 0) |
+			         ((uint16_t)(63 * g + 0.5f) << 5) |
+			         ((uint16_t)(31 * b + 0.5f) << 11);
+			break;
+		case VK_FORMAT_A8B8G8R8_UINT_PACK32:
+		case VK_FORMAT_A8B8G8R8_UNORM_PACK32:
+		case VK_FORMAT_R8G8B8A8_UNORM:
+			packed = ((uint32_t)(255 * a + 0.5f) << 24) |
+			         ((uint32_t)(255 * b + 0.5f) << 16) |
+			         ((uint32_t)(255 * g + 0.5f) << 8) |
+			         ((uint32_t)(255 * r + 0.5f) << 0);
+			break;
+		case VK_FORMAT_B8G8R8A8_UNORM:
+			packed = ((uint32_t)(255 * a + 0.5f) << 24) |
+			         ((uint32_t)(255 * r + 0.5f) << 16) |
+			         ((uint32_t)(255 * g + 0.5f) << 8) |
+			         ((uint32_t)(255 * b + 0.5f) << 0);
+			break;
+		case VK_FORMAT_B10G11R11_UFLOAT_PACK32:
+			packed = R11G11B10F(color);
+			break;
+		case VK_FORMAT_E5B9G9R9_UFLOAT_PACK32:
+			packed = RGB9E5(color);
+			break;
+		default:
+			return false;
+		}
+
+		VkImageSubresourceLayers subresLayers = 
+		{
+			subresourceRange.aspectMask,
+			subresourceRange.baseMipLevel,
+			subresourceRange.baseArrayLayer,
+			1
+		};
+		uint32_t lastMipLevel = dest->getLastMipLevel(subresourceRange);
+		uint32_t lastLayer = dest->getLastLayerIndex(subresourceRange);
+
+		VkRect2D area = { { 0, 0 }, { 0, 0 } };
+		if(renderArea)
+		{
+			ASSERT(subresourceRange.levelCount == 1);
+			area = *renderArea;
+		}
+
+		for(; subresLayers.mipLevel <= lastMipLevel; subresLayers.mipLevel++)
+		{
+			int rowPitchBytes = dest->rowPitchBytes(aspect, subresLayers.mipLevel);
+			int slicePitchBytes = dest->slicePitchBytes(aspect, subresLayers.mipLevel);
+			VkExtent3D extent = dest->getMipLevelExtent(subresLayers.mipLevel);
+			if(!renderArea)
+			{
+				area.extent.width = extent.width;
+				area.extent.height = extent.height;
+			}
+
+			for(subresLayers.baseArrayLayer = subresourceRange.baseArrayLayer; subresLayers.baseArrayLayer <= lastLayer; subresLayers.baseArrayLayer++)
+			{
+				for(uint32_t depth = 0; depth < extent.depth; depth++)
+				{
+					uint8_t *slice = (uint8_t*)dest->getTexelPointer(
+						{ area.offset.x, area.offset.y, static_cast<int32_t>(depth) }, subresLayers);
+
+					for(int j = 0; j < dest->getSampleCountFlagBits(); j++)
+					{
+						uint8_t *d = slice;
+
+						switch(Surface::bytes(dest->getFormat(aspect)))
+						{
+						case 2:
+							for(uint32_t i = 0; i < area.extent.height; i++)
+							{
+								sw::clear((uint16_t*)d, packed, area.extent.width);
+								d += rowPitchBytes;
+							}
+							break;
+						case 4:
+							for(uint32_t i = 0; i < area.extent.height; i++)
+							{
+								sw::clear((uint32_t*)d, packed, area.extent.width);
+								d += rowPitchBytes;
+							}
+							break;
+						default:
+							assert(false);
+						}
+
+						slice += slicePitchBytes;
+					}
+				}
+			}
+		}
+
+		return true;
+	}
+
 	bool Blitter::fastClear(void *pixel, VkFormat format, Surface *dest, const SliceRect &dRect, unsigned int rgbaMask)
 	{
 		if(format != VK_FORMAT_R32G32B32A32_SFLOAT)
@@ -1628,6 +1826,30 @@
 		return function("BlitRoutine");
 	}
 
+	Routine *Blitter::getRoutine(const State &state)
+	{
+		criticalSection.lock();
+		Routine *blitRoutine = blitCache->query(state);
+
+		if(!blitRoutine)
+		{
+			blitRoutine = generate(state);
+
+			if(!blitRoutine)
+			{
+				criticalSection.unlock();
+				UNIMPLEMENTED();
+				return nullptr;
+			}
+
+			blitCache->add(state, blitRoutine);
+		}
+
+		criticalSection.unlock();
+
+		return blitRoutine;
+	}
+
 	bool Blitter::blitReactor(Surface *source, const SliceRectF &sourceRect, Surface *dest, const SliceRect &destRect, const Blitter::Options &options)
 	{
 		ASSERT(!options.clearOperation || ((source->getWidth() == 1) && (source->getHeight() == 1) && (source->getDepth() == 1)));
@@ -1659,24 +1881,12 @@
 		state.destFormat = isStencil ? dest->getStencilFormat() : dest->getFormat(useDestInternal);
 		state.destSamples = dest->getSamples();
 
-		criticalSection.lock();
-		Routine *blitRoutine = blitCache->query(state);
-
+		Routine *blitRoutine = getRoutine(state);
 		if(!blitRoutine)
 		{
-			blitRoutine = generate(state);
-
-			if(!blitRoutine)
-			{
-				criticalSection.unlock();
-				return false;
-			}
-
-			blitCache->add(state, blitRoutine);
+			return false;
 		}
 
-		criticalSection.unlock();
-
 		void (*blitFunction)(const BlitData *data) = (void(*)(const BlitData*))blitRoutine->getEntry();
 
 		BlitData data;
@@ -1720,4 +1930,90 @@
 
 		return true;
 	}
+
+	void Blitter::blit(vk::Image *src, vk::Image *dst, VkImageBlit region, VkFilter filter)
+	{
+		if(dst->getFormat() == VK_FORMAT_UNDEFINED)
+		{
+			return;
+		}
+
+		if((region.srcSubresource.baseArrayLayer != 0) ||
+		   (region.dstSubresource.baseArrayLayer != 0) ||
+		   (region.srcSubresource.layerCount != 1) ||
+		   (region.dstSubresource.layerCount != 1) ||
+		   (region.srcSubresource.aspectMask != region.dstSubresource.aspectMask))
+		{
+			UNIMPLEMENTED();
+		}
+
+		if(region.dstOffsets[0].x > region.dstOffsets[1].x)
+		{
+			swap(region.srcOffsets[0].x, region.srcOffsets[1].x);
+			swap(region.dstOffsets[0].x, region.dstOffsets[1].x);
+		}
+
+		if(region.dstOffsets[0].y > region.dstOffsets[1].y)
+		{
+			swap(region.srcOffsets[0].y, region.srcOffsets[1].y);
+			swap(region.dstOffsets[0].y, region.dstOffsets[1].y);
+		}
+
+		VkExtent3D srcExtent = src->getMipLevelExtent(region.srcSubresource.mipLevel);
+		VkExtent3D dstExtent = dst->getMipLevelExtent(region.dstSubresource.mipLevel);
+
+		int32_t numSlices = (region.srcOffsets[1].z - region.srcOffsets[0].z);
+		ASSERT(numSlices == (region.dstOffsets[1].z - region.dstOffsets[0].z));
+
+		VkImageAspectFlagBits srcAspect = static_cast<VkImageAspectFlagBits>(region.srcSubresource.aspectMask);
+		VkImageAspectFlagBits dstAspect = static_cast<VkImageAspectFlagBits>(region.dstSubresource.aspectMask);
+
+		State state(src->getFormat(srcAspect), dst->getFormat(dstAspect), dst->getSampleCountFlagBits(),
+		            { filter != VK_FILTER_NEAREST, region.srcSubresource.aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT, false });
+		state.clampToEdge = (region.srcOffsets[0].x < 0) ||
+		                    (region.srcOffsets[0].y < 0) ||
+		                    (static_cast<uint32_t>(region.srcOffsets[1].x) > srcExtent.width) ||
+		                    (static_cast<uint32_t>(region.srcOffsets[1].y) > srcExtent.height);
+
+		Routine *blitRoutine = getRoutine(state);
+		if(!blitRoutine)
+		{
+			return;
+		}
+
+		void(*blitFunction)(const BlitData *data) = (void(*)(const BlitData*))blitRoutine->getEntry();
+
+		BlitData data;
+
+		data.sPitchB = src->rowPitchBytes(srcAspect, region.srcSubresource.mipLevel);
+		data.dPitchB = dst->rowPitchBytes(dstAspect, region.dstSubresource.mipLevel);
+		data.dSliceB = dst->slicePitchBytes(dstAspect, region.dstSubresource.mipLevel);
+
+		data.w = static_cast<float>(region.srcOffsets[1].x - region.srcOffsets[0].x) /
+		         static_cast<float>(region.dstOffsets[1].x - region.dstOffsets[0].x);
+		data.h = static_cast<float>(region.srcOffsets[1].y - region.srcOffsets[0].y) /
+		         static_cast<float>(region.dstOffsets[1].y - region.dstOffsets[0].y);
+		data.x0 = region.srcOffsets[0].x + (0.5f - region.dstOffsets[0].x) * data.w;
+		data.y0 = region.srcOffsets[0].y + (0.5f - region.dstOffsets[0].y) * data.h;
+
+		data.x0d = region.dstOffsets[0].x;
+		data.x1d = region.dstOffsets[1].x;
+		data.y0d = region.dstOffsets[0].y;
+		data.y1d = region.dstOffsets[1].y;
+
+		data.sWidth = srcExtent.width;
+		data.sHeight = srcExtent.height;
+
+		VkOffset3D srcOffset = { 0, 0, region.srcOffsets[0].z };
+		VkOffset3D dstOffset = { 0, 0, region.dstOffsets[0].z };
+
+		for(int i = 0; i < numSlices; i++)
+		{
+			data.source = src->getTexelPointer(srcOffset, region.srcSubresource);
+			data.dest = dst->getTexelPointer(dstOffset, region.dstSubresource);
+			blitFunction(&data);
+			srcOffset.z++;
+			dstOffset.z++;
+		}
+	}
 }
diff --git a/src/Device/Blitter.hpp b/src/Device/Blitter.hpp
index 637a5f8..6717fb1 100644
--- a/src/Device/Blitter.hpp
+++ b/src/Device/Blitter.hpp
@@ -21,6 +21,11 @@
 
 #include <string.h>
 
+namespace vk
+{
+	class Image;
+}
+
 namespace sw
 {
 	class Blitter
@@ -57,6 +62,8 @@
 		{
 			State() = default;
 			State(const Options &options) : Options(options) {}
+			State(VkFormat sourceFormat, VkFormat destFormat, int destSamples, const Options &options) :
+				Options(options), sourceFormat(sourceFormat), destFormat(destFormat), destSamples(destSamples) {}
 
 			bool operator==(const State &state) const
 			{
@@ -95,11 +102,15 @@
 		virtual ~Blitter();
 
 		void clear(void *pixel, VkFormat format, Surface *dest, const SliceRect &dRect, unsigned int rgbaMask);
+		void clear(void *pixel, VkFormat format, vk::Image *dest, const VkImageSubresourceRange& subresourceRange, const VkRect2D* renderArea = nullptr);
+
 		void blit(Surface *source, const SliceRectF &sRect, Surface *dest, const SliceRect &dRect, const Options &options);
+		void blit(vk::Image *src, vk::Image *dst, VkImageBlit region, VkFilter filter);
 		void blit3D(Surface *source, Surface *dest);
 
 	private:
 		bool fastClear(void *pixel, VkFormat format, Surface *dest, const SliceRect &dRect, unsigned int rgbaMask);
+		bool fastClear(void *pixel, VkFormat format, vk::Image *dest, const VkImageSubresourceRange& subresourceRange, const VkRect2D* renderArea);
 
 		bool read(Float4 &color, Pointer<Byte> element, const State &state);
 		bool write(Float4 &color, Pointer<Byte> element, const State &state);
@@ -111,6 +122,7 @@
 		static Float4 LinearToSRGB(Float4 &color);
 		static Float4 sRGBtoLinear(Float4 &color);
 		bool blitReactor(Surface *source, const SliceRectF &sRect, Surface *dest, const SliceRect &dRect, const Options &options);
+		Routine *getRoutine(const State &state);
 		Routine *generate(const State &state);
 
 		RoutineCache<State> *blitCache;
diff --git a/src/Vulkan/VkImage.cpp b/src/Vulkan/VkImage.cpp
index 22819f4..4d2d71a 100644
--- a/src/Vulkan/VkImage.cpp
+++ b/src/Vulkan/VkImage.cpp
@@ -458,50 +458,9 @@
 	return arrayLayers * getLayerSize(static_cast<VkImageAspectFlagBits>(aspectMask));
 }
 
-sw::Surface* Image::asSurface(VkImageAspectFlagBits aspect, uint32_t mipLevel, uint32_t layer) const
-{
-	VkExtent3D mipLevelExtent = getMipLevelExtent(mipLevel);
-	return sw::Surface::create(mipLevelExtent.width, mipLevelExtent.height, mipLevelExtent.depth, getFormat(aspect),
-	                           deviceMemory->getOffsetPointer(getMemoryOffset(aspect, mipLevel, layer)),
-	                           rowPitchBytes(aspect, mipLevel), slicePitchBytes(aspect, mipLevel));
-}
-
 void Image::blit(VkImage dstImage, const VkImageBlit& region, VkFilter filter)
 {
-	VkImageAspectFlagBits srcAspect = static_cast<VkImageAspectFlagBits>(region.srcSubresource.aspectMask);
-	VkImageAspectFlagBits dstAspect = static_cast<VkImageAspectFlagBits>(region.dstSubresource.aspectMask);
-	if((region.srcSubresource.baseArrayLayer != 0) ||
-	   (region.dstSubresource.baseArrayLayer != 0) ||
-	   (region.srcSubresource.layerCount != 1) ||
-	   (region.dstSubresource.layerCount != 1) ||
-	   (srcAspect != dstAspect))
-	{
-		UNIMPLEMENTED();
-	}
-
-	int32_t numSlices = (region.srcOffsets[1].z - region.srcOffsets[0].z);
-	ASSERT(numSlices == (region.dstOffsets[1].z - region.dstOffsets[0].z));
-
-	sw::Surface* srcSurface = asSurface(srcAspect, region.srcSubresource.mipLevel, 0);
-	sw::Surface* dstSurface = Cast(dstImage)->asSurface(dstAspect, region.dstSubresource.mipLevel, 0);
-
-	sw::SliceRectF sRect(static_cast<float>(region.srcOffsets[0].x), static_cast<float>(region.srcOffsets[0].y),
-	                     static_cast<float>(region.srcOffsets[1].x), static_cast<float>(region.srcOffsets[1].y),
-	                     region.srcOffsets[0].z);
-
-	sw::SliceRect dRect(region.dstOffsets[0].x, region.dstOffsets[0].y,
-	                    region.dstOffsets[1].x, region.dstOffsets[1].y, region.dstOffsets[0].z);
-
-	for(int i = 0; i < numSlices; i++)
-	{
-		device->getBlitter()->blit(srcSurface, sRect, dstSurface, dRect,
-		              {filter != VK_FILTER_NEAREST, srcAspect == VK_IMAGE_ASPECT_STENCIL_BIT, false});
-		sRect.slice++;
-		dRect.slice++;
-	}
-
-	delete srcSurface;
-	delete dstSurface;
+	device->getBlitter()->blit(this, Cast(dstImage), region, filter);
 }
 
 VkFormat Image::getClearFormat() const
@@ -532,28 +491,7 @@
 	        mipLevels : (subresourceRange.baseMipLevel + subresourceRange.levelCount)) - 1;
 }
 
-void Image::clear(void* pixelData, VkFormat format, const VkImageSubresourceRange& subresourceRange, VkImageAspectFlagBits aspect)
-{
-	uint32_t firstLayer = subresourceRange.baseArrayLayer;
-	uint32_t lastLayer = getLastLayerIndex(subresourceRange);
-	for(uint32_t layer = firstLayer; layer <= lastLayer; ++layer)
-	{
-		uint32_t lastLevel = getLastMipLevel(subresourceRange);
-		for(uint32_t mipLevel = subresourceRange.baseMipLevel; mipLevel <= lastLevel; ++mipLevel)
-		{
-			VkExtent3D mipLevelExtent = getMipLevelExtent(mipLevel);
-			for(uint32_t s = 0; s < mipLevelExtent.depth; ++s)
-			{
-				const sw::SliceRect dRect(0, 0, mipLevelExtent.width, mipLevelExtent.height, s);
-				sw::Surface* surface = asSurface(aspect, mipLevel, layer);
-				device->getBlitter()->clear(pixelData, format, surface, dRect, 0xF);
-				delete surface;
-			}
-		}
-	}
-}
-
-void Image::clear(void* pixelData, VkFormat format, const VkRect2D& renderArea, const VkImageSubresourceRange& subresourceRange, VkImageAspectFlagBits aspect)
+void Image::clear(void* pixelData, VkFormat format, const VkImageSubresourceRange& subresourceRange, const VkRect2D& renderArea)
 {
 	if((subresourceRange.baseMipLevel != 0) ||
 	   (subresourceRange.levelCount != 1))
@@ -561,22 +499,7 @@
 		UNIMPLEMENTED();
 	}
 
-	sw::SliceRect dRect(renderArea.offset.x, renderArea.offset.y,
-			            renderArea.offset.x + renderArea.extent.width,
-			            renderArea.offset.y + renderArea.extent.height, 0);
-
-	uint32_t firstLayer = subresourceRange.baseArrayLayer;
-	uint32_t lastLayer = getLastLayerIndex(subresourceRange);
-	for(uint32_t layer = firstLayer; layer <= lastLayer; ++layer)
-	{
-		for(uint32_t s = 0; s < extent.depth; ++s)
-		{
-			dRect.slice = s;
-			sw::Surface* surface = asSurface(aspect, 0, layer);
-			device->getBlitter()->clear(pixelData, format, surface, dRect, 0xF);
-			delete surface;
-		}
-	}
+	device->getBlitter()->clear(pixelData, format, this, subresourceRange, &renderArea);
 }
 
 void Image::clear(const VkClearColorValue& color, const VkImageSubresourceRange& subresourceRange)
@@ -586,7 +509,7 @@
 		UNIMPLEMENTED();
 	}
 
-	clear((void*)color.float32, getClearFormat(), subresourceRange, VK_IMAGE_ASPECT_COLOR_BIT);
+	device->getBlitter()->clear((void*)color.float32, getClearFormat(), this, subresourceRange);
 }
 
 void Image::clear(const VkClearDepthStencilValue& color, const VkImageSubresourceRange& subresourceRange)
@@ -599,12 +522,16 @@
 
 	if(subresourceRange.aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT)
 	{
-		clear((void*)(&color.depth), VK_FORMAT_D32_SFLOAT, subresourceRange, VK_IMAGE_ASPECT_DEPTH_BIT);
+		VkImageSubresourceRange depthSubresourceRange = subresourceRange;
+		depthSubresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
+		device->getBlitter()->clear((void*)(&color.depth), VK_FORMAT_D32_SFLOAT, this, depthSubresourceRange);
 	}
 
 	if(subresourceRange.aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT)
 	{
-		clear((void*)(&color.stencil), VK_FORMAT_S8_UINT, subresourceRange, VK_IMAGE_ASPECT_STENCIL_BIT);
+		VkImageSubresourceRange stencilSubresourceRange = subresourceRange;
+		stencilSubresourceRange.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT;
+		device->getBlitter()->clear((void*)(&color.stencil), VK_FORMAT_S8_UINT, this, stencilSubresourceRange);
 	}
 }
 
@@ -621,18 +548,22 @@
 
 	if(subresourceRange.aspectMask == VK_IMAGE_ASPECT_COLOR_BIT)
 	{
-		clear((void*)(clearValue.color.float32), getClearFormat(), renderArea, subresourceRange, VK_IMAGE_ASPECT_COLOR_BIT);
+		clear((void*)(clearValue.color.float32), getClearFormat(), subresourceRange, renderArea);
 	}
 	else
 	{
 		if(subresourceRange.aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT)
 		{
-			clear((void*)(&clearValue.depthStencil.depth), VK_FORMAT_D32_SFLOAT, renderArea, subresourceRange, VK_IMAGE_ASPECT_DEPTH_BIT);
+			VkImageSubresourceRange depthSubresourceRange = subresourceRange;
+			depthSubresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
+			clear((void*)(&clearValue.depthStencil.depth), VK_FORMAT_D32_SFLOAT, depthSubresourceRange, renderArea);
 		}
 
 		if(subresourceRange.aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT)
 		{
-			clear((void*)(&clearValue.depthStencil.stencil), VK_FORMAT_S8_UINT, renderArea, subresourceRange, VK_IMAGE_ASPECT_STENCIL_BIT);
+			VkImageSubresourceRange stencilSubresourceRange = subresourceRange;
+			stencilSubresourceRange.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT;
+			clear((void*)(&clearValue.depthStencil.stencil), VK_FORMAT_S8_UINT, stencilSubresourceRange, renderArea);
 		}
 	}
 }
diff --git a/src/Vulkan/VkImage.hpp b/src/Vulkan/VkImage.hpp
index 17a9ce8..2aec28a 100644
--- a/src/Vulkan/VkImage.hpp
+++ b/src/Vulkan/VkImage.hpp
@@ -17,11 +17,6 @@
 
 #include "VkObject.hpp"
 
-namespace sw
-{
-	class Surface;
-};
-
 namespace vk
 {
 
@@ -57,15 +52,19 @@
 
 	VkImageType              getImageType() const { return imageType; }
 	VkFormat                 getFormat() const { return format; }
+	VkFormat                 getFormat(VkImageAspectFlagBits aspect) const;
 	uint32_t                 getArrayLayers() const { return arrayLayers; }
+	uint32_t                 getMipLevels() const { return mipLevels; }
+	uint32_t                 getLastLayerIndex(const VkImageSubresourceRange& subresourceRange) const;
+	uint32_t                 getLastMipLevel(const VkImageSubresourceRange& subresourceRange) const;
 	VkSampleCountFlagBits    getSampleCountFlagBits() const { return samples; }
+	VkExtent3D               getMipLevelExtent(uint32_t mipLevel) const;
 	int                      rowPitchBytes(VkImageAspectFlagBits aspect, uint32_t mipLevel) const;
 	int                      slicePitchBytes(VkImageAspectFlagBits aspect, uint32_t mipLevel) const;
 	void*                    getTexelPointer(const VkOffset3D& offset, const VkImageSubresourceLayers& subresource) const;
 	bool                     isCube() const;
 
 private:
-	sw::Surface* asSurface(VkImageAspectFlagBits aspect, uint32_t mipLevel, uint32_t layer) const;
 	void copy(VkBuffer buffer, const VkBufferImageCopy& region, bool bufferIsSource);
 	VkDeviceSize getStorageSize(VkImageAspectFlags flags) const;
 	VkDeviceSize getMipLevelSize(VkImageAspectFlagBits aspect, uint32_t mipLevel) const;
@@ -75,13 +74,8 @@
 	VkDeviceSize texelOffsetBytesInStorage(const VkOffset3D& offset, const VkImageSubresourceLayers& subresource) const;
 	VkDeviceSize getMemoryOffset(VkImageAspectFlagBits aspect) const;
 	int bytesPerTexel(VkImageAspectFlagBits flags) const;
-	VkExtent3D getMipLevelExtent(uint32_t mipLevel) const;
-	VkFormat getFormat(VkImageAspectFlagBits flags) const;
-	uint32_t getLastLayerIndex(const VkImageSubresourceRange& subresourceRange) const;
-	uint32_t getLastMipLevel(const VkImageSubresourceRange& subresourceRange) const;
 	VkFormat getClearFormat() const;
-	void clear(void* pixelData, VkFormat format, const VkImageSubresourceRange& subresourceRange, VkImageAspectFlagBits aspect);
-	void clear(void* pixelData, VkFormat format, const VkRect2D& renderArea, const VkImageSubresourceRange& subresourceRange, VkImageAspectFlagBits aspect);
+	void clear(void* pixelData, VkFormat format, const VkImageSubresourceRange& subresourceRange, const VkRect2D& renderArea);
 
 	const Device *const      device = nullptr;
 	DeviceMemory*            deviceMemory = nullptr;