// 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 "DrawTester.hpp" #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) DrawTester::DrawTester(Multisample multisample) : multisample(multisample == Multisample::True) { } DrawTester::~DrawTester() { 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 DrawTester::initialize() { VulkanTester::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 DrawTester::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 DrawTester::show() { window->show(); } vk::RenderPass DrawTester::createRenderPass(vk::Format colorFormat) { std::vector 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 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(attachments.size()); renderPassInfo.pAttachments = attachments.data(); renderPassInfo.subpassCount = 1; renderPassInfo.pSubpasses = &subpassDescription; renderPassInfo.dependencyCount = static_cast(dependencies.size()); renderPassInfo.pDependencies = dependencies.data(); return device.createRenderPass(renderPassInfo); } void DrawTester::createFramebuffers(vk::RenderPass renderPass) { framebuffers.resize(swapchain->imageCount()); for(size_t i = 0; i < framebuffers.size(); i++) { framebuffers[i].reset(new Framebuffer(device, physicalDevice, swapchain->getImageView(i), swapchain->colorFormat, renderPass, swapchain->getExtent(), multisample)); } } void DrawTester::prepareVertices() { hooks.createVertexBuffers(*this); } vk::Pipeline DrawTester::createGraphicsPipeline(vk::RenderPass renderPass) { auto setLayoutBindings = hooks.createDescriptorSetLayout(*this); std::vector setLayouts; if(!setLayoutBindings.empty()) { vk::DescriptorSetLayoutCreateInfo layoutInfo; layoutInfo.bindingCount = static_cast(setLayoutBindings.size()); layoutInfo.pBindings = setLayoutBindings.data(); descriptorSetLayout = device.createDescriptorSetLayout(layoutInfo); setLayouts.push_back(descriptorSetLayout); } vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo; pipelineLayoutCreateInfo.setLayoutCount = static_cast(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 dynamicStateEnables; dynamicStateEnables.push_back(vk::DynamicState::eViewport); dynamicStateEnables.push_back(vk::DynamicState::eScissor); vk::PipelineDynamicStateCreateInfo dynamicState = {}; dynamicState.pDynamicStates = dynamicStateEnables.data(); dynamicState.dynamicStateCount = static_cast(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 = hooks.createVertexShader(*this); vk::ShaderModule fragmentModule = hooks.createFragmentShader(*this); assert(vertexModule); // TODO: if nullptr, use a default assert(fragmentModule); // TODO: if nullptr, use a default std::array 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(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 DrawTester::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 DrawTester::createCommandBuffers(vk::RenderPass renderPass) { vk::CommandPoolCreateInfo commandPoolCreateInfo; commandPoolCreateInfo.queueFamilyIndex = queueFamilyIndex; commandPoolCreateInfo.flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer; commandPool = device.createCommandPool(commandPoolCreateInfo); std::vector descriptorSets; if(descriptorSetLayout) { std::array poolSizes = {}; poolSizes[0].type = vk::DescriptorType::eCombinedImageSampler; poolSizes[0].descriptorCount = 1; vk::DescriptorPoolCreateInfo poolInfo; poolInfo.poolSizeCount = static_cast(poolSizes.size()); poolInfo.pPoolSizes = poolSizes.data(); poolInfo.maxSets = 1; descriptorPool = device.createDescriptorPool(poolInfo); std::vector layouts(1, descriptorSetLayout); vk::DescriptorSetAllocateInfo allocInfo; allocInfo.descriptorPool = descriptorPool; allocInfo.descriptorSetCount = 1; allocInfo.pSetLayouts = layouts.data(); descriptorSets = device.allocateDescriptorSets(allocInfo); hooks.updateDescriptorSet(*this, commandPool, descriptorSets[0]); } vk::CommandBufferAllocateInfo commandBufferAllocateInfo; commandBufferAllocateInfo.commandPool = commandPool; commandBufferAllocateInfo.commandBufferCount = static_cast(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{ 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(windowSize.width), static_cast(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 if(vertices.numVertices > 0) { 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 DrawTester::addVertexBuffer(void *vertexBufferData, size_t vertexBufferDataSize, size_t vertexSize, std::vector 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(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(vertices.inputAttributes.size()); vertices.inputState.pVertexAttributeDescriptions = vertices.inputAttributes.data(); // Note that we assume data is tightly packed vertices.numVertices = static_cast(vertexBufferDataSize / vertexSize); } vk::ShaderModule DrawTester::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); }