VulkanBenchmarks: add texture sampling to Triangle benchmarks

This change was originally started by capn@, which added texture
sampling to the Triangle benchmarks. I finished up this change with the
following changes and fixes:

* Implement a correct fill texture to white using copyBufferToImage.
* Add Buffer class to wrap vk::Buffer and vk::DeviceMemory.
* Add support for using a VK_EXT_headless_surface Window, which is the
default on non-Windows platforms for now. On Windows, can be forced to
use this by defining USE_HEADLESS_SURFACE = 1.
* Fix out of bounds access because renderPassBeginInfo.clearValueCount
was set to 2 instead of 1.
* Fix illegal access to destructed ArrayRef in call to
commandBuffers[i].bindVertexBuffers.
* Make it build and run on Linux (fix compilation errors and use
aforementioned headless surface mode)

Bug: b/176981107
Change-Id: If82496315e77a062c024766863a1ca01e6b424f9
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/51668
Kokoro-Result: Antonio Maiorano <amaiorano@google.com>
Tested-by: Antonio Maiorano <amaiorano@google.com>
Commit-Queue: Antonio Maiorano <amaiorano@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/tests/VulkanBenchmarks/CMakeLists.txt b/tests/VulkanBenchmarks/CMakeLists.txt
index 851ac91..3585067 100644
--- a/tests/VulkanBenchmarks/CMakeLists.txt
+++ b/tests/VulkanBenchmarks/CMakeLists.txt
@@ -13,7 +13,6 @@
 # limitations under the License.
 
 set(ROOT_PROJECT_COMPILE_OPTIONS
-    ${SWIFTSHADER_COMPILE_OPTIONS}
     ${WARNINGS_AS_ERRORS}
 )
 
diff --git a/tests/VulkanBenchmarks/VulkanBenchmarks.cpp b/tests/VulkanBenchmarks/VulkanBenchmarks.cpp
index 6b47b77..eb68aab 100644
--- a/tests/VulkanBenchmarks/VulkanBenchmarks.cpp
+++ b/tests/VulkanBenchmarks/VulkanBenchmarks.cpp
@@ -14,7 +14,19 @@
 
 #include "benchmark/benchmark.h"
 
-#define VK_USE_PLATFORM_WIN32_KHR
+#if !defined(USE_HEADLESS_SURFACE)
+#	define USE_HEADLESS_SURFACE 0
+#endif
+
+#if !defined(_WIN32)
+// @TODO: implement native Window support for current platform. For now, always use HeadlessSurface.
+#	undef USE_HEADLESS_SURFACE
+#	define USE_HEADLESS_SURFACE 1
+#endif
+
+#if defined(_WIN32)
+#	define VK_USE_PLATFORM_WIN32_KHR
+#endif
 #define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1
 #include <vulkan/vulkan.hpp>
 VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
@@ -23,19 +35,148 @@
 #include "StandAlone/ResourceLimits.h"
 #include "glslang/Public/ShaderLang.h"
 
-#define WIN32_LEAN_AND_MEAN
-#include <Windows.h>
+#if defined(_WIN32)
+#	define WIN32_LEAN_AND_MEAN
+#	include <Windows.h>
+#endif
 
 #include <cassert>
 #include <vector>
 
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
+
+namespace {
+uint32_t getMemoryTypeIndex(vk::PhysicalDevice physicalDevice, uint32_t typeBits, vk::MemoryPropertyFlags properties)
+{
+	vk::PhysicalDeviceMemoryProperties deviceMemoryProperties = physicalDevice.getMemoryProperties();
+	for(uint32_t i = 0; i < deviceMemoryProperties.memoryTypeCount; i++)
+	{
+		if((typeBits & 1) == 1)
+		{
+			if((deviceMemoryProperties.memoryTypes[i].propertyFlags & properties) == properties)
+			{
+				return i;
+			}
+		}
+		typeBits >>= 1;
+	}
+
+	assert(false);
+	return -1;
+}
+
+vk::CommandBuffer beginSingleTimeCommands(vk::Device device, vk::CommandPool commandPool)
+{
+	vk::CommandBufferAllocateInfo allocInfo{};
+	allocInfo.level = vk::CommandBufferLevel::ePrimary;
+	allocInfo.commandPool = commandPool;
+	allocInfo.commandBufferCount = 1;
+
+	auto commandBuffer = device.allocateCommandBuffers(allocInfo);
+
+	vk::CommandBufferBeginInfo beginInfo{};
+	beginInfo.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit;
+
+	commandBuffer[0].begin(beginInfo);
+
+	return commandBuffer[0];
+}
+
+void endSingleTimeCommands(vk::Device device, vk::CommandPool commandPool, vk::Queue queue, vk::CommandBuffer commandBuffer)
+{
+	commandBuffer.end();
+
+	vk::SubmitInfo submitInfo{};
+	submitInfo.commandBufferCount = 1;
+	submitInfo.pCommandBuffers = &commandBuffer;
+
+	vk::Fence fence = {};  // TODO: pass in fence?
+	queue.submit(1, &submitInfo, fence);
+	queue.waitIdle();
+
+	device.freeCommandBuffers(commandPool, 1, &commandBuffer);
+}
+
+void transitionImageLayout(vk::Device device, vk::CommandPool commandPool, vk::Queue queue, vk::Image image, vk::Format format, vk::ImageLayout oldLayout, vk::ImageLayout newLayout)
+{
+	vk::CommandBuffer commandBuffer = beginSingleTimeCommands(device, commandPool);
+
+	vk::ImageMemoryBarrier barrier{};
+	barrier.oldLayout = oldLayout;
+	barrier.newLayout = newLayout;
+	barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+	barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+	barrier.image = image;
+	barrier.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor;
+	barrier.subresourceRange.baseMipLevel = 0;
+	barrier.subresourceRange.levelCount = 1;
+	barrier.subresourceRange.baseArrayLayer = 0;
+	barrier.subresourceRange.layerCount = 1;
+
+	vk::PipelineStageFlags sourceStage;
+	vk::PipelineStageFlags destinationStage;
+
+	if(oldLayout == vk::ImageLayout::eUndefined && newLayout == vk::ImageLayout::eTransferDstOptimal)
+	{
+		barrier.srcAccessMask = {};
+		barrier.dstAccessMask = vk::AccessFlagBits::eTransferWrite;
+
+		sourceStage = vk::PipelineStageFlagBits::eTopOfPipe;
+		destinationStage = vk::PipelineStageFlagBits::eTransfer;
+	}
+	else if(oldLayout == vk::ImageLayout::eTransferDstOptimal && newLayout == vk::ImageLayout::eShaderReadOnlyOptimal)
+	{
+		barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
+		barrier.dstAccessMask = vk::AccessFlagBits::eTransferRead;
+
+		sourceStage = vk::PipelineStageFlagBits::eTransfer;
+		destinationStage = vk::PipelineStageFlagBits::eFragmentShader;
+	}
+	else
+	{
+		assert(!"unsupported layout transition!");
+	}
+
+	commandBuffer.pipelineBarrier(sourceStage, destinationStage, vk::DependencyFlags{}, 0, nullptr, 0, nullptr, 1, &barrier);
+
+	endSingleTimeCommands(device, commandPool, queue, commandBuffer);
+}
+
+void copyBufferToImage(vk::Device device, vk::CommandPool commandPool, vk::Queue queue, vk::Buffer buffer, vk::Image image, uint32_t width, uint32_t height)
+{
+	vk::CommandBuffer commandBuffer = beginSingleTimeCommands(device, commandPool);
+
+	vk::BufferImageCopy region{};
+	region.bufferOffset = 0;
+	region.bufferRowLength = 0;
+	region.bufferImageHeight = 0;
+	region.imageSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
+	region.imageSubresource.mipLevel = 0;
+	region.imageSubresource.baseArrayLayer = 0;
+	region.imageSubresource.layerCount = 1;
+	region.imageOffset = vk::Offset3D{ 0, 0, 0 };
+	region.imageExtent = vk::Extent3D{ width, height, 1 };
+
+	commandBuffer.copyBufferToImage(buffer, image, vk::ImageLayout::eTransferDstOptimal, 1, &region);
+
+	endSingleTimeCommands(device, commandPool, queue, commandBuffer);
+}
+
+}  // namespace
+
 class VulkanBenchmark
 {
 public:
 	VulkanBenchmark()
 	{
 		// TODO(b/158231104): Other platforms
+#if defined(_WIN32)
 		dl = std::make_unique<vk::DynamicLoader>("./vk_swiftshader.dll");
+#elif defined(__linux__)
+		dl = std::make_unique<vk::DynamicLoader>("./libvk_swiftshader.so");
+#else
+#	error Unimplemented platform
+#endif
 		assert(dl->success());
 
 		PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = dl->getProcAddress<PFN_vkGetInstanceProcAddr>("vkGetInstanceProcAddr");
@@ -71,25 +212,6 @@
 	}
 
 protected:
-	uint32_t getMemoryTypeIndex(uint32_t typeBits, vk::MemoryPropertyFlags properties)
-	{
-		vk::PhysicalDeviceMemoryProperties deviceMemoryProperties = physicalDevice.getMemoryProperties();
-		for(uint32_t i = 0; i < deviceMemoryProperties.memoryTypeCount; i++)
-		{
-			if((typeBits & 1) == 1)
-			{
-				if((deviceMemoryProperties.memoryTypes[i].propertyFlags & properties) == properties)
-				{
-					return i;
-				}
-			}
-			typeBits >>= 1;
-		}
-
-		assert(false);
-		return -1;
-	}
-
 	const uint32_t queueFamilyIndex = 0;
 
 	vk::Instance instance;
@@ -178,7 +300,7 @@
 
 	~ClearImageBenchmark()
 	{
-		device.freeCommandBuffers(commandPool, { commandBuffer });
+		device.freeCommandBuffers(commandPool, 1, &commandBuffer);
 		device.destroyCommandPool(commandPool, nullptr);
 		device.freeMemory(memory, nullptr);
 		device.destroyImage(image, nullptr);
@@ -214,17 +336,42 @@
 		benchmark.clear();
 	}
 }
-BENCHMARK_CAPTURE(ClearImage, VK_FORMAT_R8G8B8A8_UNORM, vk::Format::eR8G8B8A8Unorm, vk::ImageAspectFlagBits::eColor)->Unit(benchmark::kMillisecond);
-BENCHMARK_CAPTURE(ClearImage, VK_FORMAT_R32_SFLOAT, vk::Format::eR32Sfloat, vk::ImageAspectFlagBits::eColor)->Unit(benchmark::kMillisecond);
-BENCHMARK_CAPTURE(ClearImage, VK_FORMAT_D32_SFLOAT, vk::Format::eD32Sfloat, vk::ImageAspectFlagBits::eDepth)->Unit(benchmark::kMillisecond);
 
+#if USE_HEADLESS_SURFACE
 class Window
 {
 public:
 	Window(vk::Instance instance, vk::Extent2D windowSize)
 	{
-		moduleInstance = GetModuleHandle(NULL);
+		vk::HeadlessSurfaceCreateInfoEXT surfaceCreateInfo;
+		surface = instance.createHeadlessSurfaceEXT(surfaceCreateInfo);
+		assert(surface);
+	}
 
+	~Window()
+	{
+		instance.destroySurfaceKHR(surface, nullptr);
+	}
+
+	vk::SurfaceKHR getSurface()
+	{
+		return surface;
+	}
+
+	void show()
+	{
+	}
+
+private:
+	const vk::Instance instance;
+	vk::SurfaceKHR surface;
+};
+#elif defined(_WIN32)
+class Window
+{
+public:
+	Window(vk::Instance instance, vk::Extent2D windowSize)
+	{
 		windowClass.cbSize = sizeof(WNDCLASSEX);
 		windowClass.style = CS_HREDRAW | CS_VREDRAW;
 		windowClass.lpfnWndProc = DefWindowProc;
@@ -274,7 +421,6 @@
 	~Window()
 	{
 		instance.destroySurfaceKHR(surface, nullptr);
-
 		DestroyWindow(window);
 		UnregisterClass("Window", moduleInstance);
 	}
@@ -293,10 +439,12 @@
 	HWND window;
 	HINSTANCE moduleInstance;
 	WNDCLASSEX windowClass;
-
 	const vk::Instance instance;
 	vk::SurfaceKHR surface;
 };
+#else
+#	error Window class unimplemented for this platform
+#endif
 
 class Swapchain
 {
@@ -308,13 +456,14 @@
 
 		// Create the swapchain
 		vk::SurfaceCapabilitiesKHR surfaceCapabilities = physicalDevice.getSurfaceCapabilitiesKHR(surface);
+		extent = surfaceCapabilities.currentExtent;
 
 		vk::SwapchainCreateInfoKHR swapchainCreateInfo;
 		swapchainCreateInfo.surface = surface;
 		swapchainCreateInfo.minImageCount = 2;  // double-buffered
 		swapchainCreateInfo.imageFormat = colorFormat;
 		swapchainCreateInfo.imageColorSpace = vk::ColorSpaceKHR::eSrgbNonlinear;
-		swapchainCreateInfo.imageExtent = surfaceCapabilities.currentExtent;
+		swapchainCreateInfo.imageExtent = extent;
 		swapchainCreateInfo.imageUsage = vk::ImageUsageFlagBits::eColorAttachment;
 		swapchainCreateInfo.preTransform = vk::SurfaceTransformFlagBitsKHR::eIdentity;
 		swapchainCreateInfo.imageArrayLayers = 1;
@@ -383,17 +532,75 @@
 		return imageViews[i];
 	}
 
+	vk::Extent2D getExtent() const
+	{
+		return extent;
+	}
+
 	const vk::Format colorFormat = vk::Format::eB8G8R8A8Unorm;
 
 private:
 	const vk::Device device;
 
 	vk::SwapchainKHR swapchain;
+	vk::Extent2D extent;
 
 	std::vector<vk::Image> images;  // Weak pointers. Presentable images owned by swapchain object.
 	std::vector<vk::ImageView> imageViews;
 };
 
+class Buffer
+{
+public:
+	Buffer(vk::Device device, vk::DeviceSize size, vk::BufferUsageFlags usage)
+	    : device(device)
+	    , size(size)
+	{
+		vk::BufferCreateInfo bufferInfo{};
+		bufferInfo.size = size;
+		bufferInfo.usage = usage;
+		bufferInfo.sharingMode = vk::SharingMode::eExclusive;
+
+		buffer = device.createBuffer(bufferInfo);
+
+		auto memRequirements = device.getBufferMemoryRequirements(buffer);
+
+		vk::MemoryAllocateInfo allocInfo{};
+		allocInfo.allocationSize = memRequirements.size;
+		allocInfo.memoryTypeIndex = 0;  //TODO: getMemoryTypeIndex(memRequirements.memoryTypeBits, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent);
+
+		bufferMemory = device.allocateMemory(allocInfo);
+		device.bindBufferMemory(buffer, bufferMemory, 0);
+	}
+
+	~Buffer()
+	{
+		device.freeMemory(bufferMemory);
+		device.destroyBuffer(buffer);
+	}
+
+	vk::Buffer getBuffer()
+	{
+		return buffer;
+	}
+
+	void *mapMemory()
+	{
+		return device.mapMemory(bufferMemory, 0, size);
+	}
+
+	void unmapMemory()
+	{
+		device.unmapMemory(bufferMemory);
+	}
+
+private:
+	const vk::Device device;
+	vk::DeviceSize size;
+	vk::Buffer buffer;
+	vk::DeviceMemory bufferMemory;
+};
+
 class Image
 {
 public:
@@ -443,6 +650,11 @@
 		device.freeMemory(imageMemory);
 	}
 
+	vk::Image getImage()
+	{
+		return image;
+	}
+
 	vk::ImageView getImageView()
 	{
 		return imageView;
@@ -459,14 +671,14 @@
 class Framebuffer
 {
 public:
-	Framebuffer(vk::Device device, vk::ImageView attachment, vk::Format colorFormat, vk::RenderPass renderPass, uint32_t width, uint32_t height, bool multisample)
+	Framebuffer(vk::Device device, vk::ImageView attachment, vk::Format colorFormat, vk::RenderPass renderPass, vk::Extent2D extent, bool multisample)
 	    : device(device)
 	{
 		std::vector<vk::ImageView> attachments(multisample ? 2 : 1);
 
 		if(multisample)
 		{
-			multisampleImage = new Image(device, width, height, colorFormat, vk::SampleCountFlagBits::e4);
+			multisampleImage = new Image(device, extent.width, extent.height, colorFormat, vk::SampleCountFlagBits::e4);
 
 			// We'll be rendering to attachment location 0
 			attachments[0] = multisampleImage->getImageView();
@@ -482,8 +694,8 @@
 		framebufferCreateInfo.renderPass = renderPass;
 		framebufferCreateInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
 		framebufferCreateInfo.pAttachments = attachments.data();
-		framebufferCreateInfo.width = width;
-		framebufferCreateInfo.height = height;
+		framebufferCreateInfo.width = extent.width;
+		framebufferCreateInfo.height = extent.height;
 		framebufferCreateInfo.layers = 1;
 
 		framebuffer = device.createFramebuffer(framebufferCreateInfo);
@@ -569,6 +781,12 @@
 
 	~TriangleBenchmark()
 	{
+		delete texture;
+
+		device.destroyDescriptorPool(descriptorPool);
+
+		device.destroySampler(sampler, nullptr);
+
 		device.destroyPipelineLayout(pipelineLayout, nullptr);
 		device.destroyPipelineCache(pipelineCache, nullptr);
 
@@ -648,9 +866,72 @@
 		commandPoolCreateInfo.flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer;
 		commandPool = device.createCommandPool(commandPoolCreateInfo);
 
+		texture = new Image(device, 16, 16, vk::Format::eR8G8B8A8Unorm);
+
+		// Fill texture with white
+		vk::DeviceSize bufferSize = 16 * 16 * 4;
+		Buffer buffer(device, bufferSize, vk::BufferUsageFlagBits::eTransferSrc);
+		void *data = buffer.mapMemory();
+		memset(data, 255, bufferSize);
+		buffer.unmapMemory();
+
+		transitionImageLayout(device, commandPool, queue, texture->getImage(), vk::Format::eR8G8B8A8Unorm, vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal);
+		copyBufferToImage(device, commandPool, queue, buffer.getBuffer(), texture->getImage(), 16, 16);
+		transitionImageLayout(device, commandPool, queue, texture->getImage(), vk::Format::eR8G8B8A8Unorm, vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eShaderReadOnlyOptimal);
+
+		vk::SamplerCreateInfo samplerInfo;
+		samplerInfo.magFilter = vk::Filter::eLinear;
+		samplerInfo.minFilter = vk::Filter::eLinear;
+		samplerInfo.addressModeU = vk::SamplerAddressMode::eRepeat;
+		samplerInfo.addressModeV = vk::SamplerAddressMode::eRepeat;
+		samplerInfo.addressModeW = vk::SamplerAddressMode::eRepeat;
+		samplerInfo.anisotropyEnable = VK_FALSE;
+		samplerInfo.unnormalizedCoordinates = VK_FALSE;
+		samplerInfo.mipmapMode = vk::SamplerMipmapMode::eLinear;
+		samplerInfo.mipLodBias = 0.0f;
+		samplerInfo.minLod = 0.0f;
+		samplerInfo.maxLod = 0.0f;
+
+		sampler = device.createSampler(samplerInfo);
+
+		std::array<vk::DescriptorPoolSize, 1> poolSizes = {};
+		poolSizes[0].type = vk::DescriptorType::eCombinedImageSampler;
+		poolSizes[0].descriptorCount = 1;
+
+		vk::DescriptorPoolCreateInfo poolInfo;
+		poolInfo.poolSizeCount = static_cast<uint32_t>(poolSizes.size());
+		poolInfo.pPoolSizes = poolSizes.data();
+		poolInfo.maxSets = 1;
+
+		descriptorPool = device.createDescriptorPool(poolInfo);
+
+		std::vector<vk::DescriptorSetLayout> layouts(1, descriptorSetLayout);
+		vk::DescriptorSetAllocateInfo allocInfo;
+		allocInfo.descriptorPool = descriptorPool;
+		allocInfo.descriptorSetCount = 1;
+		allocInfo.pSetLayouts = layouts.data();
+
+		std::vector<vk::DescriptorSet> descriptorSets = device.allocateDescriptorSets(allocInfo);
+
+		vk::DescriptorImageInfo imageInfo;
+		imageInfo.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
+		imageInfo.imageView = texture->getImageView();
+		imageInfo.sampler = sampler;
+
+		std::array<vk::WriteDescriptorSet, 1> descriptorWrites = {};
+
+		descriptorWrites[0].dstSet = descriptorSets[0];
+		descriptorWrites[0].dstBinding = 1;
+		descriptorWrites[0].dstArrayElement = 0;
+		descriptorWrites[0].descriptorType = vk::DescriptorType::eCombinedImageSampler;
+		descriptorWrites[0].descriptorCount = 1;
+		descriptorWrites[0].pImageInfo = &imageInfo;
+
+		device.updateDescriptorSets(static_cast<uint32_t>(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr);
+
 		vk::CommandBufferAllocateInfo commandBufferAllocateInfo;
 		commandBufferAllocateInfo.commandPool = commandPool;
-		commandBufferAllocateInfo.commandBufferCount = swapchain->imageCount();
+		commandBufferAllocateInfo.commandBufferCount = static_cast<uint32_t>(swapchain->imageCount());
 		commandBufferAllocateInfo.level = vk::CommandBufferLevel::ePrimary;
 
 		commandBuffers = device.allocateCommandBuffers(commandBufferAllocateInfo);
@@ -669,20 +950,23 @@
 			renderPassBeginInfo.renderArea.offset.x = 0;
 			renderPassBeginInfo.renderArea.offset.y = 0;
 			renderPassBeginInfo.renderArea.extent = windowSize;
-			renderPassBeginInfo.clearValueCount = 2;
+			renderPassBeginInfo.clearValueCount = ARRAY_SIZE(clearValues);
 			renderPassBeginInfo.pClearValues = clearValues;
 			commandBuffers[i].beginRenderPass(renderPassBeginInfo, vk::SubpassContents::eInline);
 
 			// Set dynamic state
-			vk::Viewport viewport(0.0f, 0.0f, windowSize.width, windowSize.height, 0.0f, 1.0f);
+			vk::Viewport viewport(0.0f, 0.0f, static_cast<float>(windowSize.width), static_cast<float>(windowSize.height), 0.0f, 1.0f);
 			commandBuffers[i].setViewport(0, 1, &viewport);
 
 			vk::Rect2D scissor(vk::Offset2D(0, 0), windowSize);
 			commandBuffers[i].setScissor(0, 1, &scissor);
 
+			commandBuffers[i].bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipelineLayout, 0, 1, &descriptorSets[0], 0, nullptr);
+
 			// Draw a triangle
 			commandBuffers[i].bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.get());
-			commandBuffers[i].bindVertexBuffers(0, { vertices.buffer }, { 0 });
+			VULKAN_HPP_NAMESPACE::DeviceSize offset = 0;
+			commandBuffers[i].bindVertexBuffers(0, 1, &vertices.buffer, &offset);
 			commandBuffers[i].draw(3, 1, 0, 0);
 
 			commandBuffers[i].endRenderPass();
@@ -696,12 +980,13 @@
 		{
 			float position[3];
 			float color[3];
+			float texCoord[2];
 		};
 
 		Vertex vertexBufferData[] = {
-			{ { 1.0f, 1.0f, 0.05f }, { 1.0f, 0.0f, 0.0f } },
-			{ { -1.0f, 1.0f, 0.5f }, { 0.0f, 1.0f, 0.0f } },
-			{ { 0.0f, -1.0f, 0.5f }, { 0.0f, 0.0f, 1.0f } }
+			{ { 1.0f, 1.0f, 0.05f }, { 1.0f, 0.0f, 0.0f }, { 1.0f, 0.0f } },
+			{ { -1.0f, 1.0f, 0.5f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 1.0f } },
+			{ { 0.0f, -1.0f, 0.5f }, { 0.0f, 0.0f, 1.0f }, { 0.0f, 0.0f } }
 		};
 
 		vk::BufferCreateInfo vertexBufferInfo;
@@ -712,7 +997,7 @@
 		vk::MemoryAllocateInfo memoryAllocateInfo;
 		vk::MemoryRequirements memoryRequirements = device.getBufferMemoryRequirements(vertices.buffer);
 		memoryAllocateInfo.allocationSize = memoryRequirements.size;
-		memoryAllocateInfo.memoryTypeIndex = getMemoryTypeIndex(memoryRequirements.memoryTypeBits, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent);
+		memoryAllocateInfo.memoryTypeIndex = getMemoryTypeIndex(physicalDevice, memoryRequirements.memoryTypeBits, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent);
 		vk::DeviceMemory vertexBufferMemory = device.allocateMemory(memoryAllocateInfo);
 
 		void *data = device.mapMemory(vertexBufferMemory, 0, VK_WHOLE_SIZE);
@@ -726,6 +1011,7 @@
 
 		vertices.inputAttributes.push_back(vk::VertexInputAttributeDescription(0, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, position)));
 		vertices.inputAttributes.push_back(vk::VertexInputAttributeDescription(1, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, color)));
+		vertices.inputAttributes.push_back(vk::VertexInputAttributeDescription(2, 0, vk::Format::eR32G32Sfloat, offsetof(Vertex, texCoord)));
 
 		vertices.inputState.vertexBindingDescriptionCount = 1;
 		vertices.inputState.pVertexBindingDescriptions = &vertices.inputBinding;
@@ -739,7 +1025,7 @@
 
 		for(size_t i = 0; i < framebuffers.size(); i++)
 		{
-			framebuffers[i] = new Framebuffer(device, swapchain->getImageView(i), swapchain->colorFormat, renderPass, windowSize.width, windowSize.height, multisample);
+			framebuffers[i] = new Framebuffer(device, swapchain->getImageView(i), swapchain->colorFormat, renderPass, swapchain->getExtent(), multisample);
 		}
 	}
 
@@ -837,9 +1123,26 @@
 
 	vk::UniquePipeline createGraphicsPipeline(vk::RenderPass renderPass)
 	{
-		vk::PipelineLayoutCreateInfo pPipelineLayoutCreateInfo;
-		pPipelineLayoutCreateInfo.setLayoutCount = 0;
-		pipelineLayout = device.createPipelineLayout(pPipelineLayoutCreateInfo);
+		vk::DescriptorSetLayoutBinding samplerLayoutBinding;
+		samplerLayoutBinding.binding = 1;
+		samplerLayoutBinding.descriptorCount = 1;
+		samplerLayoutBinding.descriptorType = vk::DescriptorType::eCombinedImageSampler;
+		samplerLayoutBinding.pImmutableSamplers = nullptr;
+		samplerLayoutBinding.stageFlags = vk::ShaderStageFlagBits::eFragment;
+
+		std::array<vk::DescriptorSetLayoutBinding, 1> bindings = { samplerLayoutBinding };
+		vk::DescriptorSetLayoutCreateInfo layoutInfo;
+		layoutInfo.bindingCount = static_cast<uint32_t>(bindings.size());
+		layoutInfo.pBindings = bindings.data();
+
+		descriptorSetLayout = device.createDescriptorSetLayout(layoutInfo);
+
+		std::vector<vk::DescriptorSetLayout> setLayouts(1, descriptorSetLayout);
+
+		vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo;
+		pipelineLayoutCreateInfo.setLayoutCount = static_cast<uint32_t>(setLayouts.size());
+		pipelineLayoutCreateInfo.pSetLayouts = setLayouts.data();
+		pipelineLayout = device.createPipelineLayout(pipelineLayoutCreateInfo);
 
 		vk::GraphicsPipelineCreateInfo pipelineCreateInfo;
 		pipelineCreateInfo.layout = pipelineLayout;
@@ -893,12 +1196,15 @@
 		const char *vertexShader = R"(#version 310 es
 			layout(location = 0) in vec3 inPos;
 			layout(location = 1) in vec3 inColor;
+
 			layout(location = 0) out vec3 outColor;
+			layout(location = 1) out vec2 fragTexCoord;
 
 			void main()
 			{
 				outColor = inColor;
 				gl_Position = vec4(inPos.xyz, 1.0);
+				fragTexCoord = inPos.xy;
 			}
 		)";
 
@@ -906,11 +1212,15 @@
 			precision highp float;
 
 			layout(location = 0) in vec3 inColor;
+			layout(location = 1) in vec2 fragTexCoord;
+
 			layout(location = 0) out vec4 outColor;
 
+			layout(binding = 0) uniform sampler2D texSampler;
+
 			void main()
 			{
-				outColor = vec4(inColor, 1.0);
+				outColor = texture(texSampler, fragTexCoord) * vec4(inColor, 1.0);
 			}
 		)";
 
@@ -962,10 +1272,14 @@
 		std::vector<vk::VertexInputAttributeDescription> inputAttributes;
 	} vertices;
 
+	vk::DescriptorPool descriptorPool;
+	vk::DescriptorSetLayout descriptorSetLayout;
 	vk::PipelineLayout pipelineLayout;
 	vk::PipelineCache pipelineCache;
 	vk::UniquePipeline pipeline;
 
+	vk::Sampler sampler;
+
 	vk::CommandPool commandPool;
 	std::vector<vk::CommandBuffer> commandBuffers;
 
@@ -973,6 +1287,8 @@
 	vk::Semaphore renderCompleteSemaphore;
 
 	std::vector<vk::Fence> waitFences;
+
+	Image *texture = nullptr;
 };
 
 static void Triangle(benchmark::State &state, bool multisample)
@@ -989,5 +1305,9 @@
 		benchmark.renderFrame();
 	}
 }
+
+BENCHMARK_CAPTURE(ClearImage, VK_FORMAT_R8G8B8A8_UNORM, vk::Format::eR8G8B8A8Unorm, vk::ImageAspectFlagBits::eColor)->Unit(benchmark::kMillisecond);
+BENCHMARK_CAPTURE(ClearImage, VK_FORMAT_R32_SFLOAT, vk::Format::eR32Sfloat, vk::ImageAspectFlagBits::eColor)->Unit(benchmark::kMillisecond);
+BENCHMARK_CAPTURE(ClearImage, VK_FORMAT_D32_SFLOAT, vk::Format::eD32Sfloat, vk::ImageAspectFlagBits::eDepth)->Unit(benchmark::kMillisecond);
 BENCHMARK_CAPTURE(Triangle, Hello, false)->Unit(benchmark::kMillisecond);
 BENCHMARK_CAPTURE(Triangle, Multisample, true)->Unit(benchmark::kMillisecond);