Add microbenchmarks for vkCmdClear*Image

vkCmdClearColorImage() and vkCmdClearDepthStencilImage() are currently
implemented using Blitter::clear(), which delegates to fastClear() for
certain formats, or falls back to using a generic Reactor routine.

This change benchmarks the performance for:
 - VK_FORMAT_R8G8B8A8_UNORM (fast path)
 - VK_FORMAT_R32_SFLOAT (slow path)
 - VK_FORMAT_D32_SFLOAT (slow path)

Bug: b/158231104
Change-Id: I5383b322e3ea5de6b21113843fb665a22a92b9bf
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/45470
Presubmit-Ready: Nicolas Capens <nicolascapens@google.com>
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Tested-by: Nicolas Capens <nicolascapens@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Reviewed-by: Alexis Hétu <sugoi@google.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7a2d5a6..0e06e4e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -747,6 +747,7 @@
 
     add_subdirectory(${TESTS_DIR}/ReactorBenchmarks) # Add ReactorBenchmarks target
     add_subdirectory(${TESTS_DIR}/SystemBenchmarks) # Add system-benchmarks target
+    add_subdirectory(${TESTS_DIR}/VulkanBenchmarks) # Add VulkanBenchmarks target
 endif()
 
 if(SWIFTSHADER_BUILD_TESTS AND SWIFTSHADER_BUILD_VULKAN)
diff --git a/tests/VulkanBenchmarks/CMakeLists.txt b/tests/VulkanBenchmarks/CMakeLists.txt
new file mode 100644
index 0000000..783b1ea
--- /dev/null
+++ b/tests/VulkanBenchmarks/CMakeLists.txt
@@ -0,0 +1,66 @@
+# Copyright 2020 The SwiftShader Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set(ROOT_PROJECT_COMPILE_OPTIONS
+    ${SWIFTSHADER_COMPILE_OPTIONS}
+    ${WARNINGS_AS_ERRORS}
+)
+
+set(ROOT_PROJECT_LINK_LIBRARIES
+    ${OS_LIBS}
+    ${SWIFTSHADER_LIBS}
+)
+
+set(VULKAN_BENCHMARKS_SRC_FILES
+    VulkanBenchmarks.cpp
+    main.cpp
+)
+
+add_executable(VulkanBenchmarks
+    ${VULKAN_BENCHMARKS_SRC_FILES}
+)
+
+if (NOT TARGET benchmark::benchmark)
+    message(FATAL_ERROR "Missing required target: benchmark::benchmark")
+endif()
+
+add_dependencies(VulkanBenchmarks
+    vk_swiftshader
+)
+
+set_target_properties(VulkanBenchmarks PROPERTIES
+    FOLDER "Benchmarks"
+    RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}"
+)
+
+target_include_directories(VulkanBenchmarks
+    PRIVATE
+        "${SWIFTSHADER_DIR}/include"
+)
+
+target_compile_options(VulkanBenchmarks
+    PRIVATE
+        ${ROOT_PROJECT_COMPILE_OPTIONS}
+)
+
+target_link_options(VulkanBenchmarks
+    PRIVATE
+        ${SWIFTSHADER_LINK_FLAGS}
+)
+
+target_link_libraries(VulkanBenchmarks
+    PRIVATE
+        benchmark::benchmark
+        ${ROOT_PROJECT_LINK_LIBRARIES}
+)
diff --git a/tests/VulkanBenchmarks/VulkanBenchmarks.cpp b/tests/VulkanBenchmarks/VulkanBenchmarks.cpp
new file mode 100644
index 0000000..5f8fd37
--- /dev/null
+++ b/tests/VulkanBenchmarks/VulkanBenchmarks.cpp
@@ -0,0 +1,201 @@
+// Copyright 2020 The SwiftShader Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "benchmark/benchmark.h"
+
+#define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1
+#include <vulkan/vulkan.hpp>
+VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
+
+#include <cassert>
+#include <vector>
+
+class VulkanBenchmark : public benchmark::Fixture
+{
+public:
+	void SetUp(::benchmark::State &state) override
+	{
+		// TODO(b/158231104): Other platforms
+		dl = std::make_unique<vk::DynamicLoader>("./vk_swiftshader.dll");
+		assert(dl->success());
+
+		PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = dl->getProcAddress<PFN_vkGetInstanceProcAddr>("vkGetInstanceProcAddr");
+		VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr);
+
+		instance = vk::createInstance({}, nullptr);
+		VULKAN_HPP_DEFAULT_DISPATCHER.init(instance);
+
+		std::vector<vk::PhysicalDevice> physicalDevices = instance.enumeratePhysicalDevices();
+		assert(!physicalDevices.empty());
+
+		const float defaultQueuePriority = 0.0f;
+		vk::DeviceQueueCreateInfo queueCreatInfo;
+		queueCreatInfo.queueFamilyIndex = queueFamilyIndex;
+		queueCreatInfo.queueCount = 1;
+		queueCreatInfo.pQueuePriorities = &defaultQueuePriority;
+
+		vk::DeviceCreateInfo deviceCreateInfo;
+		deviceCreateInfo.queueCreateInfoCount = 1;
+		deviceCreateInfo.pQueueCreateInfos = &queueCreatInfo;
+
+		device = physicalDevices[0].createDevice(deviceCreateInfo, nullptr);
+
+		queue = device.getQueue(queueFamilyIndex, 0);
+	}
+
+	void TearDown(const ::benchmark::State &state) override
+	{
+		device.waitIdle();
+		device.destroy();
+		instance.destroy();
+	}
+
+protected:
+	const uint32_t queueFamilyIndex = 0;
+
+	vk::Instance instance;
+	vk::Device device;
+	vk::Queue queue;
+
+private:
+	std::unique_ptr<vk::DynamicLoader> dl;
+};
+
+struct ClearBenchmarkVariant
+{
+	vk::Format format;
+	const char *formatName;
+	vk::ImageAspectFlags aspect;
+};
+
+const std::vector<ClearBenchmarkVariant> clearBenchmarkVariants = {
+	{ vk::Format::eR8G8B8A8Unorm, "VK_FORMAT_R8G8B8A8_UNORM", vk::ImageAspectFlagBits::eColor },
+	{ vk::Format::eR32Sfloat, "VK_FORMAT_R32_SFLOAT", vk::ImageAspectFlagBits::eColor },
+	{ vk::Format::eD32Sfloat, "VK_FORMAT_D32_SFLOAT", vk::ImageAspectFlagBits::eDepth },
+};
+
+class ClearImageBenchmark : public VulkanBenchmark
+{
+public:
+	void SetUp(::benchmark::State &state) override
+	{
+		VulkanBenchmark::SetUp(state);
+
+		benchmark = clearBenchmarkVariants[state.range(0)];
+		state.counters[benchmark.formatName] = 1;  // Small hack to display the format's name.
+	}
+
+	void TearDown(const ::benchmark::State &state) override
+	{
+		VulkanBenchmark::TearDown(state);
+	}
+
+protected:
+	ClearBenchmarkVariant benchmark;
+};
+
+BENCHMARK_DEFINE_F(ClearImageBenchmark, Clear)
+(benchmark::State &state)
+{
+	vk::ImageCreateInfo imageInfo;
+	imageInfo.imageType = vk::ImageType::e2D;
+	imageInfo.format = benchmark.format;
+	imageInfo.tiling = vk::ImageTiling::eOptimal;
+	imageInfo.initialLayout = vk::ImageLayout::eGeneral;
+	imageInfo.usage = vk::ImageUsageFlagBits::eTransferDst;
+	imageInfo.samples = vk::SampleCountFlagBits::e4;
+	imageInfo.extent = { 1024, 1024, 1 };
+	imageInfo.mipLevels = 1;
+	imageInfo.arrayLayers = 1;
+
+	vk::Image image = device.createImage(imageInfo);
+
+	vk::MemoryRequirements memoryRequirements = device.getImageMemoryRequirements(image);
+
+	vk::MemoryAllocateInfo allocateInfo;
+	allocateInfo.allocationSize = memoryRequirements.size;
+	allocateInfo.memoryTypeIndex = 0;
+
+	vk::DeviceMemory memory = device.allocateMemory(allocateInfo);
+
+	device.bindImageMemory(image, memory, 0);
+
+	vk::CommandPoolCreateInfo commandPoolCreateInfo;
+	commandPoolCreateInfo.queueFamilyIndex = queueFamilyIndex;
+
+	vk::CommandPool commandPool = device.createCommandPool(commandPoolCreateInfo);
+
+	vk::CommandBufferAllocateInfo commandBufferAllocateInfo;
+	commandBufferAllocateInfo.commandPool = commandPool;
+	commandBufferAllocateInfo.commandBufferCount = 1;
+
+	vk::CommandBuffer commandBuffer = device.allocateCommandBuffers(commandBufferAllocateInfo)[0];
+
+	vk::CommandBufferBeginInfo commandBufferBeginInfo;
+	commandBufferBeginInfo.flags = {};
+
+	commandBuffer.begin(commandBufferBeginInfo);
+
+	vk::ImageSubresourceRange range;
+	range.aspectMask = benchmark.aspect;
+	range.baseMipLevel = 0;
+	range.levelCount = 1;
+	range.baseArrayLayer = 0;
+	range.layerCount = 1;
+
+	if(benchmark.aspect == vk::ImageAspectFlagBits::eColor)
+	{
+		vk::ClearColorValue clearColorValue;
+		clearColorValue.float32[0] = 0.0f;
+		clearColorValue.float32[1] = 1.0f;
+		clearColorValue.float32[2] = 0.0f;
+		clearColorValue.float32[3] = 1.0f;
+
+		commandBuffer.clearColorImage(image, vk::ImageLayout::eGeneral, &clearColorValue, 1, &range);
+	}
+	else if(benchmark.aspect == vk::ImageAspectFlagBits::eDepth)
+	{
+		vk::ClearDepthStencilValue clearDepthStencilValue;
+		clearDepthStencilValue.depth = 1.0f;
+		clearDepthStencilValue.stencil = 0xFF;
+
+		commandBuffer.clearDepthStencilImage(image, vk::ImageLayout::eGeneral, &clearDepthStencilValue, 1, &range);
+	}
+	else
+		assert(false);
+
+	commandBuffer.end();
+
+	vk::SubmitInfo submitInfo;
+	submitInfo.commandBufferCount = 1;
+	submitInfo.pCommandBuffers = &commandBuffer;
+
+	// Execute once to have the Reactor routine generated.
+	queue.submit(1, &submitInfo, nullptr);
+	queue.waitIdle();
+
+	for(auto _ : state)
+	{
+		queue.submit(1, &submitInfo, nullptr);
+		queue.waitIdle();
+	}
+
+	device.freeCommandBuffers(commandPool, { commandBuffer });
+	device.destroyCommandPool(commandPool);
+	device.freeMemory(memory);
+	device.destroyImage(image);
+}
+BENCHMARK_REGISTER_F(ClearImageBenchmark, Clear)
+    ->Unit(benchmark::kMillisecond)
+    ->DenseRange(0, clearBenchmarkVariants.size() - 1, 1);
diff --git a/tests/VulkanBenchmarks/main.cpp b/tests/VulkanBenchmarks/main.cpp
new file mode 100644
index 0000000..9f307ab
--- /dev/null
+++ b/tests/VulkanBenchmarks/main.cpp
@@ -0,0 +1,17 @@
+// Copyright 2020 The SwiftShader Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "benchmark/benchmark.h"
+
+BENCHMARK_MAIN();