VulkanBenchmarks: split out benchmark classes into separate files
* Move out VulkanBenchmark and DrawBenchmark classes to their own
hpp/cpps.
* Move out ClearImageBenchmark and TriangleBenchmark classes and tests
to their own cpps.
* Add non-template DrawBenchmark::addVertexBuffer that takes the size of
the vertex type. This allows for the implementation to be moved to the
cpp file, keeping the header light and easy to understand.
Bug: b/176981107
Change-Id: I657bd112c5523aa56dc5617082d6744b9edac03a
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/52130
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Tested-by: Antonio Maiorano <amaiorano@google.com>
Commit-Queue: Antonio Maiorano <amaiorano@google.com>
diff --git a/tests/VulkanBenchmarks/CMakeLists.txt b/tests/VulkanBenchmarks/CMakeLists.txt
index 9186bd6..cbcdaed 100644
--- a/tests/VulkanBenchmarks/CMakeLists.txt
+++ b/tests/VulkanBenchmarks/CMakeLists.txt
@@ -22,8 +22,13 @@
)
set(VULKAN_BENCHMARKS_SRC_FILES
+ ClearImageBenchmarks.cpp
+ DrawBenchmark.cpp
+ DrawBenchmark.hpp
main.cpp
- VulkanBenchmarks.cpp
+ TriangleBenchmarks.cpp
+ VulkanBenchmark.cpp
+ VulkanBenchmark.hpp
)
add_executable(VulkanBenchmarks
diff --git a/tests/VulkanBenchmarks/ClearImageBenchmarks.cpp b/tests/VulkanBenchmarks/ClearImageBenchmarks.cpp
new file mode 100644
index 0000000..0cc64d5
--- /dev/null
+++ b/tests/VulkanBenchmarks/ClearImageBenchmarks.cpp
@@ -0,0 +1,138 @@
+// Copyright 2021 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 "VulkanBenchmark.hpp"
+#include "benchmark/benchmark.h"
+
+#include <cassert>
+
+class ClearImageBenchmark : public VulkanBenchmark
+{
+public:
+ void initialize(vk::Format clearFormat, vk::ImageAspectFlagBits clearAspect)
+ {
+ VulkanBenchmark::initialize();
+
+ vk::ImageCreateInfo imageInfo;
+ imageInfo.imageType = vk::ImageType::e2D;
+ imageInfo.format = clearFormat;
+ imageInfo.tiling = vk::ImageTiling::eOptimal;
+ imageInfo.initialLayout = vk::ImageLayout::eGeneral;
+ imageInfo.usage = vk::ImageUsageFlagBits::eTransferDst;
+ imageInfo.samples = vk::SampleCountFlagBits::e4;
+ imageInfo.extent = vk::Extent3D(1024, 1024, 1);
+ imageInfo.mipLevels = 1;
+ imageInfo.arrayLayers = 1;
+
+ image = device.createImage(imageInfo);
+
+ vk::MemoryRequirements memoryRequirements = device.getImageMemoryRequirements(image);
+
+ vk::MemoryAllocateInfo allocateInfo;
+ allocateInfo.allocationSize = memoryRequirements.size;
+ allocateInfo.memoryTypeIndex = 0;
+
+ memory = device.allocateMemory(allocateInfo);
+
+ device.bindImageMemory(image, memory, 0);
+
+ vk::CommandPoolCreateInfo commandPoolCreateInfo;
+ commandPoolCreateInfo.queueFamilyIndex = queueFamilyIndex;
+
+ commandPool = device.createCommandPool(commandPoolCreateInfo);
+
+ vk::CommandBufferAllocateInfo commandBufferAllocateInfo;
+ commandBufferAllocateInfo.commandPool = commandPool;
+ commandBufferAllocateInfo.commandBufferCount = 1;
+
+ commandBuffer = device.allocateCommandBuffers(commandBufferAllocateInfo)[0];
+
+ vk::CommandBufferBeginInfo commandBufferBeginInfo;
+ commandBufferBeginInfo.flags = {};
+
+ commandBuffer.begin(commandBufferBeginInfo);
+
+ vk::ImageSubresourceRange range;
+ range.aspectMask = clearAspect;
+ range.baseMipLevel = 0;
+ range.levelCount = 1;
+ range.baseArrayLayer = 0;
+ range.layerCount = 1;
+
+ if(clearAspect == 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(clearAspect == 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();
+ }
+
+ ~ClearImageBenchmark()
+ {
+ device.freeCommandBuffers(commandPool, 1, &commandBuffer);
+ device.destroyCommandPool(commandPool, nullptr);
+ device.freeMemory(memory, nullptr);
+ device.destroyImage(image, nullptr);
+ }
+
+ void clear()
+ {
+ vk::SubmitInfo submitInfo;
+ submitInfo.commandBufferCount = 1;
+ submitInfo.pCommandBuffers = &commandBuffer;
+
+ queue.submit(1, &submitInfo, nullptr);
+ queue.waitIdle();
+ }
+
+private:
+ vk::Image image; // Owning handle
+ vk::DeviceMemory memory; // Owning handle
+ vk::CommandPool commandPool; // Owning handle
+ vk::CommandBuffer commandBuffer; // Owning handle
+};
+
+static void ClearImage(benchmark::State &state, vk::Format clearFormat, vk::ImageAspectFlagBits clearAspect)
+{
+ ClearImageBenchmark benchmark;
+ benchmark.initialize(clearFormat, clearAspect);
+
+ // Execute once to have the Reactor routine generated.
+ benchmark.clear();
+
+ for(auto _ : state)
+ {
+ 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);
diff --git a/tests/VulkanBenchmarks/DrawBenchmark.cpp b/tests/VulkanBenchmarks/DrawBenchmark.cpp
new file mode 100644
index 0000000..567e542
--- /dev/null
+++ b/tests/VulkanBenchmarks/DrawBenchmark.cpp
@@ -0,0 +1,449 @@
+// Copyright 2021 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 "DrawBenchmark.hpp"
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
+
+DrawBenchmark::DrawBenchmark(Multisample multisample)
+ : multisample(multisample == Multisample::True)
+{
+}
+
+DrawBenchmark::~DrawBenchmark()
+{
+ device.freeCommandBuffers(commandPool, commandBuffers);
+
+ device.destroyDescriptorPool(descriptorPool);
+ for(auto &sampler : samplers)
+ {
+ device.destroySampler(sampler, nullptr);
+ }
+ images.clear();
+ device.destroyCommandPool(commandPool, nullptr);
+
+ for(auto &fence : waitFences)
+ {
+ device.destroyFence(fence, nullptr);
+ }
+
+ device.destroySemaphore(renderCompleteSemaphore, nullptr);
+ device.destroySemaphore(presentCompleteSemaphore, nullptr);
+
+ device.destroyPipeline(pipeline);
+ device.destroyPipelineLayout(pipelineLayout, nullptr);
+ device.destroyDescriptorSetLayout(descriptorSetLayout);
+
+ device.freeMemory(vertices.memory, nullptr);
+ device.destroyBuffer(vertices.buffer, nullptr);
+
+ for(auto &framebuffer : framebuffers)
+ {
+ framebuffer.reset();
+ }
+
+ device.destroyRenderPass(renderPass, nullptr);
+
+ swapchain.reset();
+ window.reset();
+}
+
+void DrawBenchmark::initialize()
+{
+ VulkanBenchmark::initialize();
+
+ window.reset(new Window(instance, windowSize));
+ swapchain.reset(new Swapchain(physicalDevice, device, *window));
+
+ renderPass = createRenderPass(swapchain->colorFormat);
+ createFramebuffers(renderPass);
+
+ prepareVertices();
+
+ pipeline = createGraphicsPipeline(renderPass);
+
+ createSynchronizationPrimitives();
+
+ createCommandBuffers(renderPass);
+}
+
+void DrawBenchmark::renderFrame()
+{
+ swapchain->acquireNextImage(presentCompleteSemaphore, currentFrameBuffer);
+
+ device.waitForFences(1, &waitFences[currentFrameBuffer], VK_TRUE, UINT64_MAX);
+ device.resetFences(1, &waitFences[currentFrameBuffer]);
+
+ vk::PipelineStageFlags waitStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput;
+
+ vk::SubmitInfo submitInfo;
+ submitInfo.pWaitDstStageMask = &waitStageMask;
+ submitInfo.pWaitSemaphores = &presentCompleteSemaphore;
+ submitInfo.waitSemaphoreCount = 1;
+ submitInfo.pSignalSemaphores = &renderCompleteSemaphore;
+ submitInfo.signalSemaphoreCount = 1;
+ submitInfo.pCommandBuffers = &commandBuffers[currentFrameBuffer];
+ submitInfo.commandBufferCount = 1;
+
+ queue.submit(1, &submitInfo, waitFences[currentFrameBuffer]);
+
+ swapchain->queuePresent(queue, currentFrameBuffer, renderCompleteSemaphore);
+}
+
+void DrawBenchmark::show()
+{
+ window->show();
+}
+
+vk::RenderPass DrawBenchmark::createRenderPass(vk::Format colorFormat)
+{
+ std::vector<vk::AttachmentDescription> attachments(multisample ? 2 : 1);
+
+ if(multisample)
+ {
+ // Color attachment
+ attachments[0].format = colorFormat;
+ attachments[0].samples = vk::SampleCountFlagBits::e4;
+ attachments[0].loadOp = vk::AttachmentLoadOp::eClear;
+ attachments[0].storeOp = vk::AttachmentStoreOp::eStore;
+ attachments[0].stencilLoadOp = vk::AttachmentLoadOp::eDontCare;
+ attachments[0].stencilStoreOp = vk::AttachmentStoreOp::eDontCare;
+ attachments[0].initialLayout = vk::ImageLayout::eUndefined;
+ attachments[0].finalLayout = vk::ImageLayout::eColorAttachmentOptimal;
+
+ // Resolve attachment
+ attachments[1].format = colorFormat;
+ attachments[1].samples = vk::SampleCountFlagBits::e1;
+ attachments[1].loadOp = vk::AttachmentLoadOp::eDontCare;
+ attachments[1].storeOp = vk::AttachmentStoreOp::eStore;
+ attachments[1].stencilLoadOp = vk::AttachmentLoadOp::eDontCare;
+ attachments[1].stencilStoreOp = vk::AttachmentStoreOp::eDontCare;
+ attachments[1].initialLayout = vk::ImageLayout::eUndefined;
+ attachments[1].finalLayout = vk::ImageLayout::ePresentSrcKHR;
+ }
+ else
+ {
+ attachments[0].format = colorFormat;
+ attachments[0].samples = vk::SampleCountFlagBits::e1;
+ attachments[0].loadOp = vk::AttachmentLoadOp::eDontCare;
+ attachments[0].storeOp = vk::AttachmentStoreOp::eStore;
+ attachments[0].stencilLoadOp = vk::AttachmentLoadOp::eDontCare;
+ attachments[0].stencilStoreOp = vk::AttachmentStoreOp::eDontCare;
+ attachments[0].initialLayout = vk::ImageLayout::eUndefined;
+ attachments[0].finalLayout = vk::ImageLayout::ePresentSrcKHR;
+ }
+
+ vk::AttachmentReference attachment0;
+ attachment0.attachment = 0;
+ attachment0.layout = vk::ImageLayout::eColorAttachmentOptimal;
+
+ vk::AttachmentReference attachment1;
+ attachment1.attachment = 1;
+ attachment1.layout = vk::ImageLayout::eColorAttachmentOptimal;
+
+ vk::SubpassDescription subpassDescription;
+ subpassDescription.pipelineBindPoint = vk::PipelineBindPoint::eGraphics;
+ subpassDescription.colorAttachmentCount = 1;
+ subpassDescription.pResolveAttachments = multisample ? &attachment1 : nullptr;
+ subpassDescription.pColorAttachments = &attachment0;
+
+ std::array<vk::SubpassDependency, 2> dependencies;
+
+ dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
+ dependencies[0].dstSubpass = 0;
+ dependencies[0].srcStageMask = vk::PipelineStageFlagBits::eBottomOfPipe;
+ dependencies[0].dstStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput;
+ dependencies[0].srcAccessMask = vk::AccessFlagBits::eMemoryRead;
+ dependencies[0].dstAccessMask = vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite;
+ dependencies[0].dependencyFlags = vk::DependencyFlagBits::eByRegion;
+
+ dependencies[1].srcSubpass = 0;
+ dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL;
+ dependencies[1].srcStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput;
+ dependencies[1].dstStageMask = vk::PipelineStageFlagBits::eBottomOfPipe;
+ dependencies[1].srcAccessMask = vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite;
+ dependencies[1].dstAccessMask = vk::AccessFlagBits::eMemoryRead;
+ dependencies[1].dependencyFlags = vk::DependencyFlagBits::eByRegion;
+
+ vk::RenderPassCreateInfo renderPassInfo;
+ renderPassInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
+ renderPassInfo.pAttachments = attachments.data();
+ renderPassInfo.subpassCount = 1;
+ renderPassInfo.pSubpasses = &subpassDescription;
+ renderPassInfo.dependencyCount = static_cast<uint32_t>(dependencies.size());
+ renderPassInfo.pDependencies = dependencies.data();
+
+ return device.createRenderPass(renderPassInfo);
+}
+
+void DrawBenchmark::createFramebuffers(vk::RenderPass renderPass)
+{
+ framebuffers.resize(swapchain->imageCount());
+
+ for(size_t i = 0; i < framebuffers.size(); i++)
+ {
+ framebuffers[i].reset(new Framebuffer(device, swapchain->getImageView(i), swapchain->colorFormat, renderPass, swapchain->getExtent(), multisample));
+ }
+}
+
+void DrawBenchmark::prepareVertices()
+{
+ doCreateVertexBuffers();
+}
+
+vk::Pipeline DrawBenchmark::createGraphicsPipeline(vk::RenderPass renderPass)
+{
+ auto setLayoutBindings = doCreateDescriptorSetLayouts();
+
+ std::vector<vk::DescriptorSetLayout> setLayouts;
+ if(!setLayoutBindings.empty())
+ {
+ vk::DescriptorSetLayoutCreateInfo layoutInfo;
+ layoutInfo.bindingCount = static_cast<uint32_t>(setLayoutBindings.size());
+ layoutInfo.pBindings = setLayoutBindings.data();
+ descriptorSetLayout = device.createDescriptorSetLayout(layoutInfo);
+
+ setLayouts.push_back(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;
+ pipelineCreateInfo.renderPass = renderPass;
+
+ vk::PipelineInputAssemblyStateCreateInfo inputAssemblyState;
+ inputAssemblyState.topology = vk::PrimitiveTopology::eTriangleList;
+
+ vk::PipelineRasterizationStateCreateInfo rasterizationState;
+ rasterizationState.depthClampEnable = VK_FALSE;
+ rasterizationState.rasterizerDiscardEnable = VK_FALSE;
+ rasterizationState.polygonMode = vk::PolygonMode::eFill;
+ rasterizationState.cullMode = vk::CullModeFlagBits::eNone;
+ rasterizationState.frontFace = vk::FrontFace::eCounterClockwise;
+ rasterizationState.depthBiasEnable = VK_FALSE;
+ rasterizationState.lineWidth = 1.0f;
+
+ vk::PipelineColorBlendAttachmentState blendAttachmentState;
+ blendAttachmentState.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA;
+ blendAttachmentState.blendEnable = VK_FALSE;
+ vk::PipelineColorBlendStateCreateInfo colorBlendState;
+ colorBlendState.attachmentCount = 1;
+ colorBlendState.pAttachments = &blendAttachmentState;
+
+ vk::PipelineViewportStateCreateInfo viewportState;
+ viewportState.viewportCount = 1;
+ viewportState.scissorCount = 1;
+
+ std::vector<vk::DynamicState> dynamicStateEnables;
+ dynamicStateEnables.push_back(vk::DynamicState::eViewport);
+ dynamicStateEnables.push_back(vk::DynamicState::eScissor);
+ vk::PipelineDynamicStateCreateInfo dynamicState = {};
+ dynamicState.pDynamicStates = dynamicStateEnables.data();
+ dynamicState.dynamicStateCount = static_cast<uint32_t>(dynamicStateEnables.size());
+
+ vk::PipelineDepthStencilStateCreateInfo depthStencilState;
+ depthStencilState.depthTestEnable = VK_FALSE;
+ depthStencilState.depthWriteEnable = VK_FALSE;
+ depthStencilState.depthCompareOp = vk::CompareOp::eLessOrEqual;
+ depthStencilState.depthBoundsTestEnable = VK_FALSE;
+ depthStencilState.back.failOp = vk::StencilOp::eKeep;
+ depthStencilState.back.passOp = vk::StencilOp::eKeep;
+ depthStencilState.back.compareOp = vk::CompareOp::eAlways;
+ depthStencilState.stencilTestEnable = VK_FALSE;
+ depthStencilState.front = depthStencilState.back;
+
+ vk::PipelineMultisampleStateCreateInfo multisampleState;
+ multisampleState.rasterizationSamples = multisample ? vk::SampleCountFlagBits::e4 : vk::SampleCountFlagBits::e1;
+ multisampleState.pSampleMask = nullptr;
+
+ vk::ShaderModule vertexModule = doCreateVertexShader();
+ vk::ShaderModule fragmentModule = doCreateFragmentShader();
+
+ assert(vertexModule); // TODO: if nullptr, use a default
+ assert(fragmentModule); // TODO: if nullptr, use a default
+
+ std::array<vk::PipelineShaderStageCreateInfo, 2> shaderStages;
+
+ shaderStages[0].module = vertexModule;
+ shaderStages[0].stage = vk::ShaderStageFlagBits::eVertex;
+ shaderStages[0].pName = "main";
+
+ shaderStages[1].module = fragmentModule;
+ shaderStages[1].stage = vk::ShaderStageFlagBits::eFragment;
+ shaderStages[1].pName = "main";
+
+ pipelineCreateInfo.stageCount = static_cast<uint32_t>(shaderStages.size());
+ pipelineCreateInfo.pStages = shaderStages.data();
+ pipelineCreateInfo.pVertexInputState = &vertices.inputState;
+ pipelineCreateInfo.pInputAssemblyState = &inputAssemblyState;
+ pipelineCreateInfo.pRasterizationState = &rasterizationState;
+ pipelineCreateInfo.pColorBlendState = &colorBlendState;
+ pipelineCreateInfo.pMultisampleState = &multisampleState;
+ pipelineCreateInfo.pViewportState = &viewportState;
+ pipelineCreateInfo.pDepthStencilState = &depthStencilState;
+ pipelineCreateInfo.renderPass = renderPass;
+ pipelineCreateInfo.pDynamicState = &dynamicState;
+
+ auto pipeline = device.createGraphicsPipeline(nullptr, pipelineCreateInfo).value;
+
+ device.destroyShaderModule(fragmentModule);
+ device.destroyShaderModule(vertexModule);
+
+ return pipeline;
+}
+
+void DrawBenchmark::createSynchronizationPrimitives()
+{
+ vk::SemaphoreCreateInfo semaphoreCreateInfo;
+ presentCompleteSemaphore = device.createSemaphore(semaphoreCreateInfo);
+ renderCompleteSemaphore = device.createSemaphore(semaphoreCreateInfo);
+
+ vk::FenceCreateInfo fenceCreateInfo;
+ fenceCreateInfo.flags = vk::FenceCreateFlagBits::eSignaled;
+ waitFences.resize(swapchain->imageCount());
+ for(auto &fence : waitFences)
+ {
+ fence = device.createFence(fenceCreateInfo);
+ }
+}
+
+void DrawBenchmark::createCommandBuffers(vk::RenderPass renderPass)
+{
+ vk::CommandPoolCreateInfo commandPoolCreateInfo;
+ commandPoolCreateInfo.queueFamilyIndex = queueFamilyIndex;
+ commandPoolCreateInfo.flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer;
+ commandPool = device.createCommandPool(commandPoolCreateInfo);
+
+ std::vector<vk::DescriptorSet> descriptorSets;
+ if(descriptorSetLayout)
+ {
+ 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();
+
+ descriptorSets = device.allocateDescriptorSets(allocInfo);
+
+ doUpdateDescriptorSet(commandPool, descriptorSets[0]);
+ }
+
+ vk::CommandBufferAllocateInfo commandBufferAllocateInfo;
+ commandBufferAllocateInfo.commandPool = commandPool;
+ commandBufferAllocateInfo.commandBufferCount = static_cast<uint32_t>(swapchain->imageCount());
+ commandBufferAllocateInfo.level = vk::CommandBufferLevel::ePrimary;
+
+ commandBuffers = device.allocateCommandBuffers(commandBufferAllocateInfo);
+
+ for(size_t i = 0; i < commandBuffers.size(); i++)
+ {
+ vk::CommandBufferBeginInfo commandBufferBeginInfo;
+ commandBuffers[i].begin(commandBufferBeginInfo);
+
+ vk::ClearValue clearValues[1];
+ clearValues[0].color = vk::ClearColorValue(std::array<float, 4>{ 0.5f, 0.5f, 0.5f, 1.0f });
+
+ vk::RenderPassBeginInfo renderPassBeginInfo;
+ renderPassBeginInfo.framebuffer = framebuffers[i]->getFramebuffer();
+ renderPassBeginInfo.renderPass = renderPass;
+ renderPassBeginInfo.renderArea.offset.x = 0;
+ renderPassBeginInfo.renderArea.offset.y = 0;
+ renderPassBeginInfo.renderArea.extent = windowSize;
+ 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, 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);
+
+ if(!descriptorSets.empty())
+ {
+ commandBuffers[i].bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipelineLayout, 0, 1, &descriptorSets[0], 0, nullptr);
+ }
+
+ // Draw
+ commandBuffers[i].bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline);
+ VULKAN_HPP_NAMESPACE::DeviceSize offset = 0;
+ commandBuffers[i].bindVertexBuffers(0, 1, &vertices.buffer, &offset);
+ commandBuffers[i].draw(vertices.numVertices, 1, 0, 0);
+
+ commandBuffers[i].endRenderPass();
+ commandBuffers[i].end();
+ }
+}
+
+void DrawBenchmark::addVertexBuffer(void *vertexBufferData, size_t vertexBufferDataSize, size_t vertexSize, std::vector<vk::VertexInputAttributeDescription> inputAttributes)
+{
+ assert(!vertices.buffer); // For now, only support adding once
+
+ vk::BufferCreateInfo vertexBufferInfo;
+ vertexBufferInfo.size = vertexBufferDataSize;
+ vertexBufferInfo.usage = vk::BufferUsageFlagBits::eVertexBuffer;
+ vertices.buffer = device.createBuffer(vertexBufferInfo);
+
+ vk::MemoryAllocateInfo memoryAllocateInfo;
+ vk::MemoryRequirements memoryRequirements = device.getBufferMemoryRequirements(vertices.buffer);
+ memoryAllocateInfo.allocationSize = memoryRequirements.size;
+ memoryAllocateInfo.memoryTypeIndex = Util::getMemoryTypeIndex(physicalDevice, memoryRequirements.memoryTypeBits, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent);
+ vertices.memory = device.allocateMemory(memoryAllocateInfo);
+
+ void *data = device.mapMemory(vertices.memory, 0, VK_WHOLE_SIZE);
+ memcpy(data, vertexBufferData, vertexBufferDataSize);
+ device.unmapMemory(vertices.memory);
+ device.bindBufferMemory(vertices.buffer, vertices.memory, 0);
+
+ vertices.inputBinding.binding = 0;
+ vertices.inputBinding.stride = static_cast<uint32_t>(vertexSize);
+ vertices.inputBinding.inputRate = vk::VertexInputRate::eVertex;
+
+ vertices.inputAttributes = std::move(inputAttributes);
+
+ vertices.inputState.vertexBindingDescriptionCount = 1;
+ vertices.inputState.pVertexBindingDescriptions = &vertices.inputBinding;
+ vertices.inputState.vertexAttributeDescriptionCount = static_cast<uint32_t>(vertices.inputAttributes.size());
+ vertices.inputState.pVertexAttributeDescriptions = vertices.inputAttributes.data();
+
+ // Note that we assume data is tightly packed
+ vertices.numVertices = static_cast<uint32_t>(vertexBufferDataSize / vertexSize);
+}
+
+vk::ShaderModule DrawBenchmark::createShaderModule(const char *glslSource, EShLanguage glslLanguage)
+{
+ auto spirv = Util::compileGLSLtoSPIRV(glslSource, glslLanguage);
+
+ vk::ShaderModuleCreateInfo moduleCreateInfo;
+ moduleCreateInfo.codeSize = spirv.size() * sizeof(uint32_t);
+ moduleCreateInfo.pCode = (uint32_t *)spirv.data();
+
+ return device.createShaderModule(moduleCreateInfo);
+}
diff --git a/tests/VulkanBenchmarks/DrawBenchmark.hpp b/tests/VulkanBenchmarks/DrawBenchmark.hpp
new file mode 100644
index 0000000..2c30609
--- /dev/null
+++ b/tests/VulkanBenchmarks/DrawBenchmark.hpp
@@ -0,0 +1,174 @@
+// Copyright 2021 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.
+
+#ifndef DRAW_BENCHMARK_HPP_
+#define DRAW_BENCHMARK_HPP_
+
+#include "Framebuffer.hpp"
+#include "Image.hpp"
+#include "Swapchain.hpp"
+#include "Util.hpp"
+#include "VulkanBenchmark.hpp"
+#include "Window.hpp"
+
+enum class Multisample
+{
+ False,
+ True
+};
+
+class DrawBenchmark : public VulkanBenchmark
+{
+public:
+ DrawBenchmark(Multisample multisample);
+ ~DrawBenchmark();
+
+ void initialize();
+ void renderFrame();
+ void show();
+
+private:
+ void createSynchronizationPrimitives();
+ void createCommandBuffers(vk::RenderPass renderPass);
+ void prepareVertices();
+ void createFramebuffers(vk::RenderPass renderPass);
+ vk::RenderPass createRenderPass(vk::Format colorFormat);
+ vk::Pipeline createGraphicsPipeline(vk::RenderPass renderPass);
+ void addVertexBuffer(void *vertexBufferData, size_t vertexBufferDataSize, size_t vertexSize, std::vector<vk::VertexInputAttributeDescription> inputAttributes);
+
+protected:
+ /////////////////////////
+ // Hooks
+ /////////////////////////
+
+ // Called from prepareVertices.
+ // Child type may call addVertexBuffer() from this function.
+ virtual void doCreateVertexBuffers() {}
+
+ // Called from createGraphicsPipeline.
+ // Child type may return vector of DescriptorSetLayoutBindings for which a DescriptorSetLayout
+ // will be created and stored in this->descriptorSetLayout.
+ virtual std::vector<vk::DescriptorSetLayoutBinding> doCreateDescriptorSetLayouts()
+ {
+ return {};
+ }
+
+ // Called from createGraphicsPipeline.
+ // Child type may call createShaderModule() and return the result.
+ virtual vk::ShaderModule doCreateVertexShader()
+ {
+ return nullptr;
+ }
+
+ // Called from createGraphicsPipeline.
+ // Child type may call createShaderModule() and return the result.
+ virtual vk::ShaderModule doCreateFragmentShader()
+ {
+ return nullptr;
+ }
+
+ // Called from createCommandBuffers.
+ // Child type may create resources (addImage, addSampler, etc.), and make sure to
+ // call device.updateDescriptorSets.
+ virtual void doUpdateDescriptorSet(vk::CommandPool &commandPool, vk::DescriptorSet &descriptorSet)
+ {
+ }
+
+ /////////////////////////
+ // Resource Management
+ /////////////////////////
+
+ // Call from doCreateFragmentShader()
+ vk::ShaderModule createShaderModule(const char *glslSource, EShLanguage glslLanguage);
+
+ // Call from doCreateVertexBuffers()
+ template<typename VertexType>
+ void addVertexBuffer(VertexType *vertexBufferData, size_t vertexBufferDataSize, std::vector<vk::VertexInputAttributeDescription> inputAttributes)
+ {
+ addVertexBuffer(vertexBufferData, vertexBufferDataSize, sizeof(VertexType), std::move(inputAttributes));
+ }
+
+ template<typename T>
+ struct Resource
+ {
+ size_t id;
+ T &obj;
+ };
+
+ template<typename... Args>
+ Resource<Image> addImage(Args &&... args)
+ {
+ images.emplace_back(std::make_unique<Image>(std::forward<Args>(args)...));
+ return { images.size() - 1, *images.back() };
+ }
+
+ Image &getImageById(size_t id)
+ {
+ return *images[id].get();
+ }
+
+ Resource<vk::Sampler> addSampler(const vk::SamplerCreateInfo &samplerCreateInfo)
+ {
+ auto sampler = device.createSampler(samplerCreateInfo);
+ samplers.push_back(sampler);
+ return { samplers.size() - 1, sampler };
+ }
+
+ vk::Sampler &getSamplerById(size_t id)
+ {
+ return samplers[id];
+ }
+
+private:
+ const vk::Extent2D windowSize = { 1280, 720 };
+ const bool multisample;
+
+ std::unique_ptr<Window> window;
+ std::unique_ptr<Swapchain> swapchain;
+
+ vk::RenderPass renderPass; // Owning handle
+ std::vector<std::unique_ptr<Framebuffer>> framebuffers;
+ uint32_t currentFrameBuffer = 0;
+
+ struct VertexBuffer
+ {
+ vk::Buffer buffer; // Owning handle
+ vk::DeviceMemory memory; // Owning handle
+
+ vk::VertexInputBindingDescription inputBinding;
+ std::vector<vk::VertexInputAttributeDescription> inputAttributes;
+ vk::PipelineVertexInputStateCreateInfo inputState;
+
+ uint32_t numVertices;
+ } vertices;
+
+ vk::DescriptorSetLayout descriptorSetLayout; // Owning handle
+ vk::PipelineLayout pipelineLayout; // Owning handle
+ vk::Pipeline pipeline; // Owning handle
+
+ vk::Semaphore presentCompleteSemaphore; // Owning handle
+ vk::Semaphore renderCompleteSemaphore; // Owning handle
+ std::vector<vk::Fence> waitFences; // Owning handles
+
+ vk::CommandPool commandPool; // Owning handle
+ vk::DescriptorPool descriptorPool; // Owning handle
+
+ // Resources
+ std::vector<std::unique_ptr<Image>> images;
+ std::vector<vk::Sampler> samplers; // Owning handles
+
+ std::vector<vk::CommandBuffer> commandBuffers; // Owning handles
+};
+
+#endif // DRAW_BENCHMARK_HPP_
diff --git a/tests/VulkanBenchmarks/TriangleBenchmarks.cpp b/tests/VulkanBenchmarks/TriangleBenchmarks.cpp
new file mode 100644
index 0000000..832aa69
--- /dev/null
+++ b/tests/VulkanBenchmarks/TriangleBenchmarks.cpp
@@ -0,0 +1,311 @@
+// Copyright 2021 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 "Buffer.hpp"
+#include "DrawBenchmark.hpp"
+#include "benchmark/benchmark.h"
+
+#include <cassert>
+#include <vector>
+
+class TriangleSolidColorBenchmark : public DrawBenchmark
+{
+public:
+ TriangleSolidColorBenchmark(Multisample multisample)
+ : DrawBenchmark(multisample)
+ {}
+
+protected:
+ void doCreateVertexBuffers() override
+ {
+ struct Vertex
+ {
+ float position[3];
+ };
+
+ Vertex vertexBufferData[] = {
+ { { 1.0f, 1.0f, 0.5f } },
+ { { -1.0f, 1.0f, 0.5f } },
+ { { 0.0f, -1.0f, 0.5f } }
+ };
+
+ std::vector<vk::VertexInputAttributeDescription> inputAttributes;
+ inputAttributes.push_back(vk::VertexInputAttributeDescription(0, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, position)));
+
+ addVertexBuffer(vertexBufferData, sizeof(vertexBufferData), std::move(inputAttributes));
+ }
+
+ vk::ShaderModule doCreateVertexShader() override
+ {
+ const char *vertexShader = R"(#version 310 es
+ layout(location = 0) in vec3 inPos;
+
+ void main()
+ {
+ gl_Position = vec4(inPos.xyz, 1.0);
+ })";
+
+ return createShaderModule(vertexShader, EShLanguage::EShLangVertex);
+ }
+
+ vk::ShaderModule doCreateFragmentShader() override
+ {
+ const char *fragmentShader = R"(#version 310 es
+ precision highp float;
+
+ layout(location = 0) out vec4 outColor;
+
+ void main()
+ {
+ outColor = vec4(1.0, 1.0, 1.0, 1.0);
+ })";
+
+ return createShaderModule(fragmentShader, EShLanguage::EShLangFragment);
+ }
+};
+
+class TriangleInterpolateColorBenchmark : public DrawBenchmark
+{
+public:
+ TriangleInterpolateColorBenchmark(Multisample multisample)
+ : DrawBenchmark(multisample)
+ {}
+
+protected:
+ void doCreateVertexBuffers() override
+ {
+ struct Vertex
+ {
+ float position[3];
+ float color[3];
+ };
+
+ 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 } }
+ };
+
+ std::vector<vk::VertexInputAttributeDescription> inputAttributes;
+ inputAttributes.push_back(vk::VertexInputAttributeDescription(0, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, position)));
+ inputAttributes.push_back(vk::VertexInputAttributeDescription(1, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, color)));
+
+ addVertexBuffer(vertexBufferData, sizeof(vertexBufferData), std::move(inputAttributes));
+ }
+
+ vk::ShaderModule doCreateVertexShader() override
+ {
+ 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;
+
+ void main()
+ {
+ outColor = inColor;
+ gl_Position = vec4(inPos.xyz, 1.0);
+ })";
+
+ return createShaderModule(vertexShader, EShLanguage::EShLangVertex);
+ }
+
+ vk::ShaderModule doCreateFragmentShader() override
+ {
+ const char *fragmentShader = R"(#version 310 es
+ precision highp float;
+
+ layout(location = 0) in vec3 inColor;
+
+ layout(location = 0) out vec4 outColor;
+
+ void main()
+ {
+ outColor = vec4(inColor, 1.0);
+ })";
+
+ return createShaderModule(fragmentShader, EShLanguage::EShLangFragment);
+ }
+};
+
+class TriangleSampleTextureBenchmark : public DrawBenchmark
+{
+public:
+ TriangleSampleTextureBenchmark(Multisample multisample)
+ : DrawBenchmark(multisample)
+ {}
+
+protected:
+ void doCreateVertexBuffers() override
+ {
+ struct Vertex
+ {
+ float position[3];
+ float color[3];
+ float texCoord[2];
+ };
+
+ Vertex vertexBufferData[] = {
+ { { 1.0f, 1.0f, 0.5f }, { 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 } }
+ };
+
+ std::vector<vk::VertexInputAttributeDescription> inputAttributes;
+ inputAttributes.push_back(vk::VertexInputAttributeDescription(0, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, position)));
+ inputAttributes.push_back(vk::VertexInputAttributeDescription(1, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, color)));
+ inputAttributes.push_back(vk::VertexInputAttributeDescription(2, 0, vk::Format::eR32G32Sfloat, offsetof(Vertex, texCoord)));
+
+ addVertexBuffer(vertexBufferData, sizeof(vertexBufferData), std::move(inputAttributes));
+ }
+
+ vk::ShaderModule doCreateVertexShader() override
+ {
+ 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;
+ })";
+
+ return createShaderModule(vertexShader, EShLanguage::EShLangVertex);
+ }
+
+ vk::ShaderModule doCreateFragmentShader() override
+ {
+ const char *fragmentShader = R"(#version 310 es
+ 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 = texture(texSampler, fragTexCoord) * vec4(inColor, 1.0);
+ })";
+
+ return createShaderModule(fragmentShader, EShLanguage::EShLangFragment);
+ }
+
+ std::vector<vk::DescriptorSetLayoutBinding> doCreateDescriptorSetLayouts() override
+ {
+ vk::DescriptorSetLayoutBinding samplerLayoutBinding;
+ samplerLayoutBinding.binding = 1;
+ samplerLayoutBinding.descriptorCount = 1;
+ samplerLayoutBinding.descriptorType = vk::DescriptorType::eCombinedImageSampler;
+ samplerLayoutBinding.pImmutableSamplers = nullptr;
+ samplerLayoutBinding.stageFlags = vk::ShaderStageFlagBits::eFragment;
+
+ return { samplerLayoutBinding };
+ }
+
+ void doUpdateDescriptorSet(vk::CommandPool &commandPool, vk::DescriptorSet &descriptorSet) override
+ {
+ auto &texture = addImage(device, 16, 16, vk::Format::eR8G8B8A8Unorm).obj;
+
+ // 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();
+
+ Util::transitionImageLayout(device, commandPool, queue, texture.getImage(), vk::Format::eR8G8B8A8Unorm, vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal);
+ Util::copyBufferToImage(device, commandPool, queue, buffer.getBuffer(), texture.getImage(), 16, 16);
+ Util::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;
+
+ auto sampler = addSampler(samplerInfo);
+
+ vk::DescriptorImageInfo imageInfo;
+ imageInfo.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
+ imageInfo.imageView = texture.getImageView();
+ imageInfo.sampler = sampler.obj;
+
+ std::array<vk::WriteDescriptorSet, 1> descriptorWrites = {};
+
+ descriptorWrites[0].dstSet = descriptorSet;
+ 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);
+ }
+};
+
+template<typename T>
+static void RunBenchmark(benchmark::State &state, T &benchmark)
+{
+ benchmark.initialize();
+
+ if(false) benchmark.show(); // Enable for visual verification.
+
+ // Warmup
+ benchmark.renderFrame();
+
+ for(auto _ : state)
+ {
+ benchmark.renderFrame();
+ }
+}
+
+static void TriangleSolidColor(benchmark::State &state, Multisample multisample)
+{
+ TriangleSolidColorBenchmark benchmark(multisample);
+ RunBenchmark(state, benchmark);
+}
+
+static void TriangleInterpolateColor(benchmark::State &state, Multisample multisample)
+{
+ TriangleInterpolateColorBenchmark benchmark(multisample);
+ RunBenchmark(state, benchmark);
+}
+
+static void TriangleSampleTexture(benchmark::State &state, Multisample multisample)
+{
+ TriangleSampleTextureBenchmark benchmark(multisample);
+ RunBenchmark(state, benchmark);
+}
+
+BENCHMARK_CAPTURE(TriangleSolidColor, TriangleSolidColor, Multisample::False)->Unit(benchmark::kMillisecond);
+BENCHMARK_CAPTURE(TriangleInterpolateColor, TriangleInterpolateColor, Multisample::False)->Unit(benchmark::kMillisecond);
+BENCHMARK_CAPTURE(TriangleSampleTexture, TriangleSampleTexture, Multisample::False)->Unit(benchmark::kMillisecond);
+BENCHMARK_CAPTURE(TriangleSolidColor, TriangleSolidColor_Multisample, Multisample::True)->Unit(benchmark::kMillisecond);
+BENCHMARK_CAPTURE(TriangleInterpolateColor, TriangleInterpolateColor_Multisample, Multisample::True)->Unit(benchmark::kMillisecond);
+BENCHMARK_CAPTURE(TriangleSampleTexture, TriangleSampleTexture_Multisample, Multisample::True)->Unit(benchmark::kMillisecond);
diff --git a/tests/VulkanBenchmarks/VulkanBenchmark.cpp b/tests/VulkanBenchmarks/VulkanBenchmark.cpp
new file mode 100644
index 0000000..41dda63
--- /dev/null
+++ b/tests/VulkanBenchmarks/VulkanBenchmark.cpp
@@ -0,0 +1,59 @@
+// Copyright 2021 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 "VulkanBenchmark.hpp"
+
+VulkanBenchmark::~VulkanBenchmark()
+{
+ device.waitIdle();
+ device.destroy(nullptr);
+ instance.destroy(nullptr);
+}
+
+void VulkanBenchmark::initialize()
+{
+ // 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");
+ 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());
+ physicalDevice = physicalDevices[0];
+
+ 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 = physicalDevice.createDevice(deviceCreateInfo, nullptr);
+
+ queue = device.getQueue(queueFamilyIndex, 0);
+}
diff --git a/tests/VulkanBenchmarks/VulkanBenchmark.hpp b/tests/VulkanBenchmarks/VulkanBenchmark.hpp
new file mode 100644
index 0000000..f724bcc
--- /dev/null
+++ b/tests/VulkanBenchmarks/VulkanBenchmark.hpp
@@ -0,0 +1,41 @@
+// Copyright 2021 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.
+
+#ifndef VULKAN_BENCHMARK_HPP_
+#define VULKAN_BENCHMARK_HPP_
+
+#include "VulkanHeaders.hpp"
+
+class VulkanBenchmark
+{
+public:
+ VulkanBenchmark() = default;
+ virtual ~VulkanBenchmark();
+
+ // Call once after construction so that virtual functions may be called during init
+ void initialize();
+
+private:
+ std::unique_ptr<vk::DynamicLoader> dl;
+
+protected:
+ const uint32_t queueFamilyIndex = 0;
+
+ vk::Instance instance; // Owning handle
+ vk::PhysicalDevice physicalDevice;
+ vk::Device device; // Owning handle
+ vk::Queue queue;
+};
+
+#endif // VULKAN_BENCHMARK_HPP_
diff --git a/tests/VulkanBenchmarks/VulkanBenchmarks.cpp b/tests/VulkanBenchmarks/VulkanBenchmarks.cpp
deleted file mode 100644
index 2647640..0000000
--- a/tests/VulkanBenchmarks/VulkanBenchmarks.cpp
+++ /dev/null
@@ -1,1062 +0,0 @@
-// 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"
-
-#include "Buffer.hpp"
-#include "Framebuffer.hpp"
-#include "Image.hpp"
-#include "Swapchain.hpp"
-#include "Util.hpp"
-#include "VulkanHeaders.hpp"
-#include "Window.hpp"
-
-#include <cassert>
-#include <vector>
-
-#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
-
-class VulkanBenchmark
-{
-public:
- VulkanBenchmark()
- {
- }
-
- virtual ~VulkanBenchmark()
- {
- device.waitIdle();
- device.destroy(nullptr);
- instance.destroy(nullptr);
- }
-
- // Call once after construction so that virtual functions may be called during init
- void initialize()
- {
- // 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");
- 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());
- physicalDevice = physicalDevices[0];
-
- 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 = physicalDevice.createDevice(deviceCreateInfo, nullptr);
-
- queue = device.getQueue(queueFamilyIndex, 0);
- }
-
-private:
- std::unique_ptr<vk::DynamicLoader> dl;
-
-protected:
- const uint32_t queueFamilyIndex = 0;
-
- vk::Instance instance; // Owning handle
- vk::PhysicalDevice physicalDevice;
- vk::Device device; // Owning handle
- vk::Queue queue;
-};
-
-class ClearImageBenchmark : public VulkanBenchmark
-{
-public:
- void initialize(vk::Format clearFormat, vk::ImageAspectFlagBits clearAspect)
- {
- VulkanBenchmark::initialize();
-
- vk::ImageCreateInfo imageInfo;
- imageInfo.imageType = vk::ImageType::e2D;
- imageInfo.format = clearFormat;
- imageInfo.tiling = vk::ImageTiling::eOptimal;
- imageInfo.initialLayout = vk::ImageLayout::eGeneral;
- imageInfo.usage = vk::ImageUsageFlagBits::eTransferDst;
- imageInfo.samples = vk::SampleCountFlagBits::e4;
- imageInfo.extent = vk::Extent3D(1024, 1024, 1);
- imageInfo.mipLevels = 1;
- imageInfo.arrayLayers = 1;
-
- image = device.createImage(imageInfo);
-
- vk::MemoryRequirements memoryRequirements = device.getImageMemoryRequirements(image);
-
- vk::MemoryAllocateInfo allocateInfo;
- allocateInfo.allocationSize = memoryRequirements.size;
- allocateInfo.memoryTypeIndex = 0;
-
- memory = device.allocateMemory(allocateInfo);
-
- device.bindImageMemory(image, memory, 0);
-
- vk::CommandPoolCreateInfo commandPoolCreateInfo;
- commandPoolCreateInfo.queueFamilyIndex = queueFamilyIndex;
-
- commandPool = device.createCommandPool(commandPoolCreateInfo);
-
- vk::CommandBufferAllocateInfo commandBufferAllocateInfo;
- commandBufferAllocateInfo.commandPool = commandPool;
- commandBufferAllocateInfo.commandBufferCount = 1;
-
- commandBuffer = device.allocateCommandBuffers(commandBufferAllocateInfo)[0];
-
- vk::CommandBufferBeginInfo commandBufferBeginInfo;
- commandBufferBeginInfo.flags = {};
-
- commandBuffer.begin(commandBufferBeginInfo);
-
- vk::ImageSubresourceRange range;
- range.aspectMask = clearAspect;
- range.baseMipLevel = 0;
- range.levelCount = 1;
- range.baseArrayLayer = 0;
- range.layerCount = 1;
-
- if(clearAspect == 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(clearAspect == 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();
- }
-
- ~ClearImageBenchmark()
- {
- device.freeCommandBuffers(commandPool, 1, &commandBuffer);
- device.destroyCommandPool(commandPool, nullptr);
- device.freeMemory(memory, nullptr);
- device.destroyImage(image, nullptr);
- }
-
- void clear()
- {
- vk::SubmitInfo submitInfo;
- submitInfo.commandBufferCount = 1;
- submitInfo.pCommandBuffers = &commandBuffer;
-
- queue.submit(1, &submitInfo, nullptr);
- queue.waitIdle();
- }
-
-private:
- vk::Image image; // Owning handle
- vk::DeviceMemory memory; // Owning handle
- vk::CommandPool commandPool; // Owning handle
- vk::CommandBuffer commandBuffer; // Owning handle
-};
-
-static void ClearImage(benchmark::State &state, vk::Format clearFormat, vk::ImageAspectFlagBits clearAspect)
-{
- ClearImageBenchmark benchmark;
- benchmark.initialize(clearFormat, clearAspect);
-
- // Execute once to have the Reactor routine generated.
- benchmark.clear();
-
- for(auto _ : state)
- {
- benchmark.clear();
- }
-}
-
-enum class Multisample
-{
- False,
- True
-};
-
-class DrawBenchmark : public VulkanBenchmark
-{
-public:
- DrawBenchmark(Multisample multisample)
- : multisample(multisample == Multisample::True)
- {
- }
-
- void initialize()
- {
- VulkanBenchmark::initialize();
-
- window.reset(new Window(instance, windowSize));
- swapchain.reset(new Swapchain(physicalDevice, device, *window));
-
- renderPass = createRenderPass(swapchain->colorFormat);
- createFramebuffers(renderPass);
-
- prepareVertices();
-
- pipeline = createGraphicsPipeline(renderPass);
-
- createSynchronizationPrimitives();
-
- createCommandBuffers(renderPass);
- }
-
- ~DrawBenchmark()
- {
- device.freeCommandBuffers(commandPool, commandBuffers);
-
- device.destroyDescriptorPool(descriptorPool);
- for(auto &sampler : samplers)
- {
- device.destroySampler(sampler, nullptr);
- }
- images.clear();
- device.destroyCommandPool(commandPool, nullptr);
-
- for(auto &fence : waitFences)
- {
- device.destroyFence(fence, nullptr);
- }
-
- device.destroySemaphore(renderCompleteSemaphore, nullptr);
- device.destroySemaphore(presentCompleteSemaphore, nullptr);
-
- device.destroyPipeline(pipeline);
- device.destroyPipelineLayout(pipelineLayout, nullptr);
- device.destroyDescriptorSetLayout(descriptorSetLayout);
-
- device.freeMemory(vertices.memory, nullptr);
- device.destroyBuffer(vertices.buffer, nullptr);
-
- for(auto &framebuffer : framebuffers)
- {
- framebuffer.reset();
- }
-
- device.destroyRenderPass(renderPass, nullptr);
-
- swapchain.reset();
- window.reset();
- }
-
- void renderFrame()
- {
- swapchain->acquireNextImage(presentCompleteSemaphore, currentFrameBuffer);
-
- device.waitForFences(1, &waitFences[currentFrameBuffer], VK_TRUE, UINT64_MAX);
- device.resetFences(1, &waitFences[currentFrameBuffer]);
-
- vk::PipelineStageFlags waitStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput;
-
- vk::SubmitInfo submitInfo;
- submitInfo.pWaitDstStageMask = &waitStageMask;
- submitInfo.pWaitSemaphores = &presentCompleteSemaphore;
- submitInfo.waitSemaphoreCount = 1;
- submitInfo.pSignalSemaphores = &renderCompleteSemaphore;
- submitInfo.signalSemaphoreCount = 1;
- submitInfo.pCommandBuffers = &commandBuffers[currentFrameBuffer];
- submitInfo.commandBufferCount = 1;
-
- queue.submit(1, &submitInfo, waitFences[currentFrameBuffer]);
-
- swapchain->queuePresent(queue, currentFrameBuffer, renderCompleteSemaphore);
- }
-
- void show()
- {
- window->show();
- }
-
-private:
- void createSynchronizationPrimitives()
- {
- vk::SemaphoreCreateInfo semaphoreCreateInfo;
- presentCompleteSemaphore = device.createSemaphore(semaphoreCreateInfo);
- renderCompleteSemaphore = device.createSemaphore(semaphoreCreateInfo);
-
- vk::FenceCreateInfo fenceCreateInfo;
- fenceCreateInfo.flags = vk::FenceCreateFlagBits::eSignaled;
- waitFences.resize(swapchain->imageCount());
- for(auto &fence : waitFences)
- {
- fence = device.createFence(fenceCreateInfo);
- }
- }
-
- void createCommandBuffers(vk::RenderPass renderPass)
- {
- vk::CommandPoolCreateInfo commandPoolCreateInfo;
- commandPoolCreateInfo.queueFamilyIndex = queueFamilyIndex;
- commandPoolCreateInfo.flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer;
- commandPool = device.createCommandPool(commandPoolCreateInfo);
-
- std::vector<vk::DescriptorSet> descriptorSets;
- if(descriptorSetLayout)
- {
- 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();
-
- descriptorSets = device.allocateDescriptorSets(allocInfo);
-
- doUpdateDescriptorSet(commandPool, descriptorSets[0]);
- }
-
- vk::CommandBufferAllocateInfo commandBufferAllocateInfo;
- commandBufferAllocateInfo.commandPool = commandPool;
- commandBufferAllocateInfo.commandBufferCount = static_cast<uint32_t>(swapchain->imageCount());
- commandBufferAllocateInfo.level = vk::CommandBufferLevel::ePrimary;
-
- commandBuffers = device.allocateCommandBuffers(commandBufferAllocateInfo);
-
- for(size_t i = 0; i < commandBuffers.size(); i++)
- {
- vk::CommandBufferBeginInfo commandBufferBeginInfo;
- commandBuffers[i].begin(commandBufferBeginInfo);
-
- vk::ClearValue clearValues[1];
- clearValues[0].color = vk::ClearColorValue(std::array<float, 4>{ 0.5f, 0.5f, 0.5f, 1.0f });
-
- vk::RenderPassBeginInfo renderPassBeginInfo;
- renderPassBeginInfo.framebuffer = framebuffers[i]->getFramebuffer();
- renderPassBeginInfo.renderPass = renderPass;
- renderPassBeginInfo.renderArea.offset.x = 0;
- renderPassBeginInfo.renderArea.offset.y = 0;
- renderPassBeginInfo.renderArea.extent = windowSize;
- 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, 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);
-
- if(!descriptorSets.empty())
- {
- commandBuffers[i].bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipelineLayout, 0, 1, &descriptorSets[0], 0, nullptr);
- }
-
- // Draw
- commandBuffers[i].bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline);
- VULKAN_HPP_NAMESPACE::DeviceSize offset = 0;
- commandBuffers[i].bindVertexBuffers(0, 1, &vertices.buffer, &offset);
- commandBuffers[i].draw(vertices.numVertices, 1, 0, 0);
-
- commandBuffers[i].endRenderPass();
- commandBuffers[i].end();
- }
- }
-
- void prepareVertices()
- {
- doCreateVertexBuffers();
- }
-
- void createFramebuffers(vk::RenderPass renderPass)
- {
- framebuffers.resize(swapchain->imageCount());
-
- for(size_t i = 0; i < framebuffers.size(); i++)
- {
- framebuffers[i].reset(new Framebuffer(device, swapchain->getImageView(i), swapchain->colorFormat, renderPass, swapchain->getExtent(), multisample));
- }
- }
-
- vk::RenderPass createRenderPass(vk::Format colorFormat)
- {
- std::vector<vk::AttachmentDescription> attachments(multisample ? 2 : 1);
-
- if(multisample)
- {
- // Color attachment
- attachments[0].format = colorFormat;
- attachments[0].samples = vk::SampleCountFlagBits::e4;
- attachments[0].loadOp = vk::AttachmentLoadOp::eClear;
- attachments[0].storeOp = vk::AttachmentStoreOp::eStore;
- attachments[0].stencilLoadOp = vk::AttachmentLoadOp::eDontCare;
- attachments[0].stencilStoreOp = vk::AttachmentStoreOp::eDontCare;
- attachments[0].initialLayout = vk::ImageLayout::eUndefined;
- attachments[0].finalLayout = vk::ImageLayout::eColorAttachmentOptimal;
-
- // Resolve attachment
- attachments[1].format = colorFormat;
- attachments[1].samples = vk::SampleCountFlagBits::e1;
- attachments[1].loadOp = vk::AttachmentLoadOp::eDontCare;
- attachments[1].storeOp = vk::AttachmentStoreOp::eStore;
- attachments[1].stencilLoadOp = vk::AttachmentLoadOp::eDontCare;
- attachments[1].stencilStoreOp = vk::AttachmentStoreOp::eDontCare;
- attachments[1].initialLayout = vk::ImageLayout::eUndefined;
- attachments[1].finalLayout = vk::ImageLayout::ePresentSrcKHR;
- }
- else
- {
- attachments[0].format = colorFormat;
- attachments[0].samples = vk::SampleCountFlagBits::e1;
- attachments[0].loadOp = vk::AttachmentLoadOp::eDontCare;
- attachments[0].storeOp = vk::AttachmentStoreOp::eStore;
- attachments[0].stencilLoadOp = vk::AttachmentLoadOp::eDontCare;
- attachments[0].stencilStoreOp = vk::AttachmentStoreOp::eDontCare;
- attachments[0].initialLayout = vk::ImageLayout::eUndefined;
- attachments[0].finalLayout = vk::ImageLayout::ePresentSrcKHR;
- }
-
- vk::AttachmentReference attachment0;
- attachment0.attachment = 0;
- attachment0.layout = vk::ImageLayout::eColorAttachmentOptimal;
-
- vk::AttachmentReference attachment1;
- attachment1.attachment = 1;
- attachment1.layout = vk::ImageLayout::eColorAttachmentOptimal;
-
- vk::SubpassDescription subpassDescription;
- subpassDescription.pipelineBindPoint = vk::PipelineBindPoint::eGraphics;
- subpassDescription.colorAttachmentCount = 1;
- subpassDescription.pResolveAttachments = multisample ? &attachment1 : nullptr;
- subpassDescription.pColorAttachments = &attachment0;
-
- std::array<vk::SubpassDependency, 2> dependencies;
-
- dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
- dependencies[0].dstSubpass = 0;
- dependencies[0].srcStageMask = vk::PipelineStageFlagBits::eBottomOfPipe;
- dependencies[0].dstStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput;
- dependencies[0].srcAccessMask = vk::AccessFlagBits::eMemoryRead;
- dependencies[0].dstAccessMask = vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite;
- dependencies[0].dependencyFlags = vk::DependencyFlagBits::eByRegion;
-
- dependencies[1].srcSubpass = 0;
- dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL;
- dependencies[1].srcStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput;
- dependencies[1].dstStageMask = vk::PipelineStageFlagBits::eBottomOfPipe;
- dependencies[1].srcAccessMask = vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite;
- dependencies[1].dstAccessMask = vk::AccessFlagBits::eMemoryRead;
- dependencies[1].dependencyFlags = vk::DependencyFlagBits::eByRegion;
-
- vk::RenderPassCreateInfo renderPassInfo;
- renderPassInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
- renderPassInfo.pAttachments = attachments.data();
- renderPassInfo.subpassCount = 1;
- renderPassInfo.pSubpasses = &subpassDescription;
- renderPassInfo.dependencyCount = static_cast<uint32_t>(dependencies.size());
- renderPassInfo.pDependencies = dependencies.data();
-
- return device.createRenderPass(renderPassInfo);
- }
-
- vk::Pipeline createGraphicsPipeline(vk::RenderPass renderPass)
- {
- auto setLayoutBindings = doCreateDescriptorSetLayouts();
-
- std::vector<vk::DescriptorSetLayout> setLayouts;
- if(!setLayoutBindings.empty())
- {
- vk::DescriptorSetLayoutCreateInfo layoutInfo;
- layoutInfo.bindingCount = static_cast<uint32_t>(setLayoutBindings.size());
- layoutInfo.pBindings = setLayoutBindings.data();
- descriptorSetLayout = device.createDescriptorSetLayout(layoutInfo);
-
- setLayouts.push_back(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;
- pipelineCreateInfo.renderPass = renderPass;
-
- vk::PipelineInputAssemblyStateCreateInfo inputAssemblyState;
- inputAssemblyState.topology = vk::PrimitiveTopology::eTriangleList;
-
- vk::PipelineRasterizationStateCreateInfo rasterizationState;
- rasterizationState.depthClampEnable = VK_FALSE;
- rasterizationState.rasterizerDiscardEnable = VK_FALSE;
- rasterizationState.polygonMode = vk::PolygonMode::eFill;
- rasterizationState.cullMode = vk::CullModeFlagBits::eNone;
- rasterizationState.frontFace = vk::FrontFace::eCounterClockwise;
- rasterizationState.depthBiasEnable = VK_FALSE;
- rasterizationState.lineWidth = 1.0f;
-
- vk::PipelineColorBlendAttachmentState blendAttachmentState;
- blendAttachmentState.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA;
- blendAttachmentState.blendEnable = VK_FALSE;
- vk::PipelineColorBlendStateCreateInfo colorBlendState;
- colorBlendState.attachmentCount = 1;
- colorBlendState.pAttachments = &blendAttachmentState;
-
- vk::PipelineViewportStateCreateInfo viewportState;
- viewportState.viewportCount = 1;
- viewportState.scissorCount = 1;
-
- std::vector<vk::DynamicState> dynamicStateEnables;
- dynamicStateEnables.push_back(vk::DynamicState::eViewport);
- dynamicStateEnables.push_back(vk::DynamicState::eScissor);
- vk::PipelineDynamicStateCreateInfo dynamicState = {};
- dynamicState.pDynamicStates = dynamicStateEnables.data();
- dynamicState.dynamicStateCount = static_cast<uint32_t>(dynamicStateEnables.size());
-
- vk::PipelineDepthStencilStateCreateInfo depthStencilState;
- depthStencilState.depthTestEnable = VK_FALSE;
- depthStencilState.depthWriteEnable = VK_FALSE;
- depthStencilState.depthCompareOp = vk::CompareOp::eLessOrEqual;
- depthStencilState.depthBoundsTestEnable = VK_FALSE;
- depthStencilState.back.failOp = vk::StencilOp::eKeep;
- depthStencilState.back.passOp = vk::StencilOp::eKeep;
- depthStencilState.back.compareOp = vk::CompareOp::eAlways;
- depthStencilState.stencilTestEnable = VK_FALSE;
- depthStencilState.front = depthStencilState.back;
-
- vk::PipelineMultisampleStateCreateInfo multisampleState;
- multisampleState.rasterizationSamples = multisample ? vk::SampleCountFlagBits::e4 : vk::SampleCountFlagBits::e1;
- multisampleState.pSampleMask = nullptr;
-
- vk::ShaderModule vertexModule = doCreateVertexShader();
- vk::ShaderModule fragmentModule = doCreateFragmentShader();
-
- assert(vertexModule); // TODO: if nullptr, use a default
- assert(fragmentModule); // TODO: if nullptr, use a default
-
- std::array<vk::PipelineShaderStageCreateInfo, 2> shaderStages;
-
- shaderStages[0].module = vertexModule;
- shaderStages[0].stage = vk::ShaderStageFlagBits::eVertex;
- shaderStages[0].pName = "main";
-
- shaderStages[1].module = fragmentModule;
- shaderStages[1].stage = vk::ShaderStageFlagBits::eFragment;
- shaderStages[1].pName = "main";
-
- pipelineCreateInfo.stageCount = static_cast<uint32_t>(shaderStages.size());
- pipelineCreateInfo.pStages = shaderStages.data();
- pipelineCreateInfo.pVertexInputState = &vertices.inputState;
- pipelineCreateInfo.pInputAssemblyState = &inputAssemblyState;
- pipelineCreateInfo.pRasterizationState = &rasterizationState;
- pipelineCreateInfo.pColorBlendState = &colorBlendState;
- pipelineCreateInfo.pMultisampleState = &multisampleState;
- pipelineCreateInfo.pViewportState = &viewportState;
- pipelineCreateInfo.pDepthStencilState = &depthStencilState;
- pipelineCreateInfo.renderPass = renderPass;
- pipelineCreateInfo.pDynamicState = &dynamicState;
-
- auto pipeline = device.createGraphicsPipeline(nullptr, pipelineCreateInfo).value;
-
- device.destroyShaderModule(fragmentModule);
- device.destroyShaderModule(vertexModule);
-
- return pipeline;
- }
-
-protected:
- /////////////////////////
- // Hooks
- /////////////////////////
-
- // Called from prepareVertices.
- // Child type may call addVertexBuffer() from this function.
- virtual void doCreateVertexBuffers() {}
-
- // Called from createGraphicsPipeline.
- // Child type may return vector of DescriptorSetLayoutBindings for which a DescriptorSetLayout
- // will be created and stored in this->descriptorSetLayout.
- virtual std::vector<vk::DescriptorSetLayoutBinding> doCreateDescriptorSetLayouts()
- {
- return {};
- }
-
- // Called from createGraphicsPipeline.
- // Child type may call createShaderModule() and return the result.
- virtual vk::ShaderModule doCreateVertexShader()
- {
- return nullptr;
- }
-
- // Called from createGraphicsPipeline.
- // Child type may call createShaderModule() and return the result.
- virtual vk::ShaderModule doCreateFragmentShader()
- {
- return nullptr;
- }
-
- // Called from createCommandBuffers.
- // Child type may create resources (addImage, addSampler, etc.), and make sure to
- // call device.updateDescriptorSets.
- virtual void doUpdateDescriptorSet(vk::CommandPool &commandPool, vk::DescriptorSet &descriptorSet)
- {
- }
-
- /////////////////////////
- // Resource Management
- /////////////////////////
-
- // Call from doCreateFragmentShader()
- vk::ShaderModule createShaderModule(const char *glslSource, EShLanguage glslLanguage)
- {
- auto spirv = Util::compileGLSLtoSPIRV(glslSource, glslLanguage);
-
- vk::ShaderModuleCreateInfo moduleCreateInfo;
- moduleCreateInfo.codeSize = spirv.size() * sizeof(uint32_t);
- moduleCreateInfo.pCode = (uint32_t *)spirv.data();
-
- return device.createShaderModule(moduleCreateInfo);
- }
-
- // Call from doCreateVertexBuffers()
- template<typename VertexType>
- void addVertexBuffer(VertexType *vertexBufferData, size_t vertexBufferDataSize, std::vector<vk::VertexInputAttributeDescription> inputAttributes)
- {
- assert(!vertices.buffer); // For now, only support adding once
-
- vk::BufferCreateInfo vertexBufferInfo;
- vertexBufferInfo.size = vertexBufferDataSize;
- vertexBufferInfo.usage = vk::BufferUsageFlagBits::eVertexBuffer;
- vertices.buffer = device.createBuffer(vertexBufferInfo);
-
- vk::MemoryAllocateInfo memoryAllocateInfo;
- vk::MemoryRequirements memoryRequirements = device.getBufferMemoryRequirements(vertices.buffer);
- memoryAllocateInfo.allocationSize = memoryRequirements.size;
- memoryAllocateInfo.memoryTypeIndex = Util::getMemoryTypeIndex(physicalDevice, memoryRequirements.memoryTypeBits, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent);
- vertices.memory = device.allocateMemory(memoryAllocateInfo);
-
- void *data = device.mapMemory(vertices.memory, 0, VK_WHOLE_SIZE);
- memcpy(data, vertexBufferData, vertexBufferDataSize);
- device.unmapMemory(vertices.memory);
- device.bindBufferMemory(vertices.buffer, vertices.memory, 0);
-
- vertices.inputBinding.binding = 0;
- vertices.inputBinding.stride = sizeof(VertexType);
- vertices.inputBinding.inputRate = vk::VertexInputRate::eVertex;
-
- vertices.inputAttributes = std::move(inputAttributes);
-
- vertices.inputState.vertexBindingDescriptionCount = 1;
- vertices.inputState.pVertexBindingDescriptions = &vertices.inputBinding;
- vertices.inputState.vertexAttributeDescriptionCount = static_cast<uint32_t>(vertices.inputAttributes.size());
- vertices.inputState.pVertexAttributeDescriptions = vertices.inputAttributes.data();
-
- // Note that we assume data is tightly packed
- vertices.numVertices = static_cast<uint32_t>(vertexBufferDataSize / sizeof(VertexType));
- }
-
- template<typename T>
- struct Resource
- {
- size_t id;
- T &obj;
- };
-
- template<typename... Args>
- Resource<Image> addImage(Args &&... args)
- {
- images.emplace_back(std::make_unique<Image>(std::forward<Args>(args)...));
- return { images.size() - 1, *images.back() };
- }
-
- Image &getImageById(size_t id)
- {
- return *images[id].get();
- }
-
- Resource<vk::Sampler> addSampler(const vk::SamplerCreateInfo &samplerCreateInfo)
- {
- auto sampler = device.createSampler(samplerCreateInfo);
- samplers.push_back(sampler);
- return { samplers.size() - 1, sampler };
- }
-
- vk::Sampler &getSamplerById(size_t id)
- {
- return samplers[id];
- }
-
-private:
- const vk::Extent2D windowSize = { 1280, 720 };
- const bool multisample;
-
- std::unique_ptr<Window> window;
- std::unique_ptr<Swapchain> swapchain;
-
- vk::RenderPass renderPass; // Owning handle
- std::vector<std::unique_ptr<Framebuffer>> framebuffers;
- uint32_t currentFrameBuffer = 0;
-
- struct VertexBuffer
- {
- vk::Buffer buffer; // Owning handle
- vk::DeviceMemory memory; // Owning handle
-
- vk::VertexInputBindingDescription inputBinding;
- std::vector<vk::VertexInputAttributeDescription> inputAttributes;
- vk::PipelineVertexInputStateCreateInfo inputState;
-
- uint32_t numVertices;
- } vertices;
-
- vk::DescriptorSetLayout descriptorSetLayout; // Owning handle
- vk::PipelineLayout pipelineLayout; // Owning handle
- vk::Pipeline pipeline; // Owning handle
-
- vk::Semaphore presentCompleteSemaphore; // Owning handle
- vk::Semaphore renderCompleteSemaphore; // Owning handle
- std::vector<vk::Fence> waitFences; // Owning handles
-
- vk::CommandPool commandPool; // Owning handle
- vk::DescriptorPool descriptorPool; // Owning handle
-
- // Resources
- std::vector<std::unique_ptr<Image>> images;
- std::vector<vk::Sampler> samplers; // Owning handles
-
- std::vector<vk::CommandBuffer> commandBuffers; // Owning handles
-};
-
-class TriangleSolidColorBenchmark : public DrawBenchmark
-{
-public:
- TriangleSolidColorBenchmark(Multisample multisample)
- : DrawBenchmark(multisample)
- {}
-
-protected:
- void doCreateVertexBuffers() override
- {
- struct Vertex
- {
- float position[3];
- };
-
- Vertex vertexBufferData[] = {
- { { 1.0f, 1.0f, 0.5f } },
- { { -1.0f, 1.0f, 0.5f } },
- { { 0.0f, -1.0f, 0.5f } }
- };
-
- std::vector<vk::VertexInputAttributeDescription> inputAttributes;
- inputAttributes.push_back(vk::VertexInputAttributeDescription(0, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, position)));
-
- addVertexBuffer(vertexBufferData, sizeof(vertexBufferData), std::move(inputAttributes));
- }
-
- vk::ShaderModule doCreateVertexShader() override
- {
- const char *vertexShader = R"(#version 310 es
- layout(location = 0) in vec3 inPos;
-
- void main()
- {
- gl_Position = vec4(inPos.xyz, 1.0);
- })";
-
- return createShaderModule(vertexShader, EShLanguage::EShLangVertex);
- }
-
- vk::ShaderModule doCreateFragmentShader() override
- {
- const char *fragmentShader = R"(#version 310 es
- precision highp float;
-
- layout(location = 0) out vec4 outColor;
-
- void main()
- {
- outColor = vec4(1.0, 1.0, 1.0, 1.0);
- })";
-
- return createShaderModule(fragmentShader, EShLanguage::EShLangFragment);
- }
-};
-
-class TriangleInterpolateColorBenchmark : public DrawBenchmark
-{
-public:
- TriangleInterpolateColorBenchmark(Multisample multisample)
- : DrawBenchmark(multisample)
- {}
-
-protected:
- void doCreateVertexBuffers() override
- {
- struct Vertex
- {
- float position[3];
- float color[3];
- };
-
- 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 } }
- };
-
- std::vector<vk::VertexInputAttributeDescription> inputAttributes;
- inputAttributes.push_back(vk::VertexInputAttributeDescription(0, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, position)));
- inputAttributes.push_back(vk::VertexInputAttributeDescription(1, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, color)));
-
- addVertexBuffer(vertexBufferData, sizeof(vertexBufferData), std::move(inputAttributes));
- }
-
- vk::ShaderModule doCreateVertexShader() override
- {
- 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;
-
- void main()
- {
- outColor = inColor;
- gl_Position = vec4(inPos.xyz, 1.0);
- })";
-
- return createShaderModule(vertexShader, EShLanguage::EShLangVertex);
- }
-
- vk::ShaderModule doCreateFragmentShader() override
- {
- const char *fragmentShader = R"(#version 310 es
- precision highp float;
-
- layout(location = 0) in vec3 inColor;
-
- layout(location = 0) out vec4 outColor;
-
- void main()
- {
- outColor = vec4(inColor, 1.0);
- })";
-
- return createShaderModule(fragmentShader, EShLanguage::EShLangFragment);
- }
-};
-
-class TriangleSampleTextureBenchmark : public DrawBenchmark
-{
-public:
- TriangleSampleTextureBenchmark(Multisample multisample)
- : DrawBenchmark(multisample)
- {}
-
-protected:
- void doCreateVertexBuffers() override
- {
- struct Vertex
- {
- float position[3];
- float color[3];
- float texCoord[2];
- };
-
- Vertex vertexBufferData[] = {
- { { 1.0f, 1.0f, 0.5f }, { 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 } }
- };
-
- std::vector<vk::VertexInputAttributeDescription> inputAttributes;
- inputAttributes.push_back(vk::VertexInputAttributeDescription(0, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, position)));
- inputAttributes.push_back(vk::VertexInputAttributeDescription(1, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, color)));
- inputAttributes.push_back(vk::VertexInputAttributeDescription(2, 0, vk::Format::eR32G32Sfloat, offsetof(Vertex, texCoord)));
-
- addVertexBuffer(vertexBufferData, sizeof(vertexBufferData), std::move(inputAttributes));
- }
-
- vk::ShaderModule doCreateVertexShader() override
- {
- 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;
- })";
-
- return createShaderModule(vertexShader, EShLanguage::EShLangVertex);
- }
-
- vk::ShaderModule doCreateFragmentShader() override
- {
- const char *fragmentShader = R"(#version 310 es
- 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 = texture(texSampler, fragTexCoord) * vec4(inColor, 1.0);
- })";
-
- return createShaderModule(fragmentShader, EShLanguage::EShLangFragment);
- }
-
- std::vector<vk::DescriptorSetLayoutBinding> doCreateDescriptorSetLayouts() override
- {
- vk::DescriptorSetLayoutBinding samplerLayoutBinding;
- samplerLayoutBinding.binding = 1;
- samplerLayoutBinding.descriptorCount = 1;
- samplerLayoutBinding.descriptorType = vk::DescriptorType::eCombinedImageSampler;
- samplerLayoutBinding.pImmutableSamplers = nullptr;
- samplerLayoutBinding.stageFlags = vk::ShaderStageFlagBits::eFragment;
-
- return { samplerLayoutBinding };
- }
-
- void doUpdateDescriptorSet(vk::CommandPool &commandPool, vk::DescriptorSet &descriptorSet) override
- {
- auto &texture = addImage(device, 16, 16, vk::Format::eR8G8B8A8Unorm).obj;
-
- // 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();
-
- Util::transitionImageLayout(device, commandPool, queue, texture.getImage(), vk::Format::eR8G8B8A8Unorm, vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal);
- Util::copyBufferToImage(device, commandPool, queue, buffer.getBuffer(), texture.getImage(), 16, 16);
- Util::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;
-
- auto sampler = addSampler(samplerInfo);
-
- vk::DescriptorImageInfo imageInfo;
- imageInfo.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
- imageInfo.imageView = texture.getImageView();
- imageInfo.sampler = sampler.obj;
-
- std::array<vk::WriteDescriptorSet, 1> descriptorWrites = {};
-
- descriptorWrites[0].dstSet = descriptorSet;
- 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);
- }
-};
-
-template<typename T>
-static void RunBenchmark(benchmark::State &state, T &benchmark)
-{
- benchmark.initialize();
-
- if(false) benchmark.show(); // Enable for visual verification.
-
- // Warmup
- benchmark.renderFrame();
-
- for(auto _ : state)
- {
- benchmark.renderFrame();
- }
-}
-
-static void TriangleSolidColor(benchmark::State &state, Multisample multisample)
-{
- TriangleSolidColorBenchmark benchmark(multisample);
- RunBenchmark(state, benchmark);
-}
-
-static void TriangleInterpolateColor(benchmark::State &state, Multisample multisample)
-{
- TriangleInterpolateColorBenchmark benchmark(multisample);
- RunBenchmark(state, benchmark);
-}
-
-static void TriangleSampleTexture(benchmark::State &state, Multisample multisample)
-{
- TriangleSampleTextureBenchmark benchmark(multisample);
- RunBenchmark(state, benchmark);
-}
-
-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(TriangleSolidColor, TriangleSolidColor, Multisample::False)->Unit(benchmark::kMillisecond);
-BENCHMARK_CAPTURE(TriangleInterpolateColor, TriangleInterpolateColor, Multisample::False)->Unit(benchmark::kMillisecond);
-BENCHMARK_CAPTURE(TriangleSampleTexture, TriangleSampleTexture, Multisample::False)->Unit(benchmark::kMillisecond);
-BENCHMARK_CAPTURE(TriangleSolidColor, TriangleSolidColor_Multisample, Multisample::True)->Unit(benchmark::kMillisecond);
-BENCHMARK_CAPTURE(TriangleInterpolateColor, TriangleInterpolateColor_Multisample, Multisample::True)->Unit(benchmark::kMillisecond);
-BENCHMARK_CAPTURE(TriangleSampleTexture, TriangleSampleTexture_Multisample, Multisample::True)->Unit(benchmark::kMillisecond);