|
|
@@ -1,278 +0,0 @@
|
|
|
|
#include <array>
|
|
|
|
|
|
|
|
#include <chrono>
|
|
|
|
|
|
|
|
#include <cstddef>
|
|
|
|
|
|
|
|
#include <limits>
|
|
|
|
|
|
|
|
#include <sophia/render_triangle_test.hpp>
|
|
|
|
|
|
|
|
#include <sophia/util/logger.hpp>
|
|
|
|
|
|
|
|
#include <stdexcept>
|
|
|
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#include <vulkan/vulkan.hpp>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define GLM_FORCE_RADIANS
|
|
|
|
|
|
|
|
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
|
|
|
|
|
|
|
|
#include <glm/glm.hpp>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "buffer.hpp"
|
|
|
|
|
|
|
|
#include "device.hpp"
|
|
|
|
|
|
|
|
#include "image.hpp"
|
|
|
|
|
|
|
|
#include "pipelines/graphics_pipeline.hpp"
|
|
|
|
|
|
|
|
#include "shader.hpp"
|
|
|
|
|
|
|
|
#include "swapchain.hpp"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace sophia {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct Vertex {
|
|
|
|
|
|
|
|
glm::vec2 position;
|
|
|
|
|
|
|
|
glm::vec3 color;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct PushConstants {
|
|
|
|
|
|
|
|
glm::mat4 transform;
|
|
|
|
|
|
|
|
glm::vec4 color;
|
|
|
|
|
|
|
|
glm::vec4 data;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void runRenderTriangleTest(const std::string& vertShaderPath,
|
|
|
|
|
|
|
|
const std::string& fragShaderPath) {
|
|
|
|
|
|
|
|
std::unique_ptr<sophia::Window> window =
|
|
|
|
|
|
|
|
std::make_unique<Window>(750, 1000, "Render Triangle Test");
|
|
|
|
|
|
|
|
std::unique_ptr<sophia::Device> device =
|
|
|
|
|
|
|
|
Device::Builder().withWindow(*window).build();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
log::info("Running Render Triangle Test...");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::unique_ptr<Swapchain> swapchain =
|
|
|
|
|
|
|
|
std::make_unique<Swapchain>(*device, window->getExtent());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
auto vertexShader = Shader::Builder()
|
|
|
|
|
|
|
|
.fromGLSL(vertShaderPath)
|
|
|
|
|
|
|
|
.setStage(ShaderStage::VERTEX)
|
|
|
|
|
|
|
|
.build();
|
|
|
|
|
|
|
|
auto fragmentShader = Shader::Builder()
|
|
|
|
|
|
|
|
.fromGLSL(fragShaderPath)
|
|
|
|
|
|
|
|
.setStage(ShaderStage::FRAGMENT)
|
|
|
|
|
|
|
|
.build();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vk::PushConstantRange pushConstantRange{};
|
|
|
|
|
|
|
|
pushConstantRange.stageFlags =
|
|
|
|
|
|
|
|
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment;
|
|
|
|
|
|
|
|
pushConstantRange.offset = 0;
|
|
|
|
|
|
|
|
pushConstantRange.size = sizeof(PushConstants);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vk::PipelineLayoutCreateInfo pipelineLayoutInfo{};
|
|
|
|
|
|
|
|
pipelineLayoutInfo.pushConstantRangeCount = 1;
|
|
|
|
|
|
|
|
pipelineLayoutInfo.pPushConstantRanges = &pushConstantRange;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vk::UniquePipelineLayout pipelineLayout =
|
|
|
|
|
|
|
|
device->get()->createPipelineLayoutUnique(pipelineLayoutInfo);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::vector<vk::VertexInputBindingDescription> bindingDescriptions{
|
|
|
|
|
|
|
|
{0, sizeof(Vertex), vk::VertexInputRate::eVertex}};
|
|
|
|
|
|
|
|
std::vector<vk::VertexInputAttributeDescription> attributeDescriptions{
|
|
|
|
|
|
|
|
{0, 0, vk::Format::eR32G32Sfloat, offsetof(Vertex, position)},
|
|
|
|
|
|
|
|
{1, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, color)}};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::unique_ptr<sophia::GraphicsPipeline> graphicsPipeline =
|
|
|
|
|
|
|
|
GraphicsPipeline::Builder(*device)
|
|
|
|
|
|
|
|
.setVertexShader(*vertexShader)
|
|
|
|
|
|
|
|
.setFragmentShader(*fragmentShader)
|
|
|
|
|
|
|
|
.setPipelineLayout(pipelineLayout.get())
|
|
|
|
|
|
|
|
.setColorAttachmentFormats({swapchain->getImageFormat()})
|
|
|
|
|
|
|
|
.setVertexInput(bindingDescriptions, attributeDescriptions)
|
|
|
|
|
|
|
|
.build();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Oversized triangle covering the full screen so the fragment shader runs
|
|
|
|
|
|
|
|
// for every pixel.
|
|
|
|
|
|
|
|
std::vector<Vertex> vertices{
|
|
|
|
|
|
|
|
{{-1.0f, -1.0f}, {1.0f, 0.0f, 0.0f}},
|
|
|
|
|
|
|
|
{{3.0f, -1.0f}, {0.0f, 1.0f, 0.0f}},
|
|
|
|
|
|
|
|
{{-1.0f, 3.0f}, {0.0f, 0.0f, 1.0f}},
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Buffer vertexBuffer{*device, sizeof(Vertex),
|
|
|
|
|
|
|
|
static_cast<u32>(vertices.size()),
|
|
|
|
|
|
|
|
vk::BufferUsageFlagBits::eVertexBuffer,
|
|
|
|
|
|
|
|
vk::MemoryPropertyFlagBits::eHostVisible |
|
|
|
|
|
|
|
|
vk::MemoryPropertyFlagBits::eHostCoherent};
|
|
|
|
|
|
|
|
vertexBuffer.map();
|
|
|
|
|
|
|
|
vertexBuffer.writeToBuffer(vertices.data(), sizeof(Vertex) * vertices.size());
|
|
|
|
|
|
|
|
vertexBuffer.unmap();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vk::UniqueSemaphore imageAvailableSemaphore =
|
|
|
|
|
|
|
|
device->get()->createSemaphoreUnique({});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::vector<vk::UniqueSemaphore> renderFinishedSemaphores;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < swapchain->imageCount(); i++) {
|
|
|
|
|
|
|
|
renderFinishedSemaphores.push_back(
|
|
|
|
|
|
|
|
device->get()->createSemaphoreUnique({}));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
auto recreateSwapchain = [&]() {
|
|
|
|
|
|
|
|
vk::Extent2D extent = window->getExtent();
|
|
|
|
|
|
|
|
while (extent.width == 0 || extent.height == 0) {
|
|
|
|
|
|
|
|
extent = window->getExtent();
|
|
|
|
|
|
|
|
glfwWaitEvents();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
device->waitIdle();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::shared_ptr<Swapchain> oldSwapchain = std::move(swapchain);
|
|
|
|
|
|
|
|
swapchain = std::make_unique<Swapchain>(*device, extent, oldSwapchain);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!oldSwapchain->compareSwapchainFormats(*swapchain)) {
|
|
|
|
|
|
|
|
log::fatal("swapchain image format changed on recreation");
|
|
|
|
|
|
|
|
throw std::runtime_error("swapchain image format changed on recreation");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
renderFinishedSemaphores.clear();
|
|
|
|
|
|
|
|
for (size_t i = 0; i < swapchain->imageCount(); i++) {
|
|
|
|
|
|
|
|
renderFinishedSemaphores.push_back(
|
|
|
|
|
|
|
|
device->get()->createSemaphoreUnique({}));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
log::trace("recreated swapchain");
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vk::UniqueFence inFlightFence =
|
|
|
|
|
|
|
|
device->get()->createFenceUnique({vk::FenceCreateFlagBits::eSignaled});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vk::CommandBufferAllocateInfo commandBufferAllocInfo{
|
|
|
|
|
|
|
|
device->getCommandPool(), vk::CommandBufferLevel::ePrimary, 1};
|
|
|
|
|
|
|
|
vk::CommandBuffer commandBuffer =
|
|
|
|
|
|
|
|
device->get()->allocateCommandBuffers(commandBufferAllocInfo)[0];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
auto startTime = std::chrono::high_resolution_clock::now();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
while (!window->shouldClose()) {
|
|
|
|
|
|
|
|
glfwPollEvents();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
double elapsedTime =
|
|
|
|
|
|
|
|
std::chrono::duration<double>(
|
|
|
|
|
|
|
|
std::chrono::high_resolution_clock::now() - startTime)
|
|
|
|
|
|
|
|
.count();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vk::Fence inFlightFenceHandle = inFlightFence.get();
|
|
|
|
|
|
|
|
(void)device->get()->waitForFences(inFlightFenceHandle, vk::True,
|
|
|
|
|
|
|
|
std::numeric_limits<u64>::max());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vk::Semaphore imageAvailableSemaphoreHandle = imageAvailableSemaphore.get();
|
|
|
|
|
|
|
|
u32 imageIndex = 0;
|
|
|
|
|
|
|
|
vk::Result acquireResult =
|
|
|
|
|
|
|
|
swapchain->acquireNextImage(imageAvailableSemaphoreHandle, imageIndex);
|
|
|
|
|
|
|
|
if (acquireResult == vk::Result::eErrorOutOfDateKHR) {
|
|
|
|
|
|
|
|
recreateSwapchain();
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (acquireResult != vk::Result::eSuccess &&
|
|
|
|
|
|
|
|
acquireResult != vk::Result::eSuboptimalKHR) {
|
|
|
|
|
|
|
|
log::fatal("failed to acquire swapchain image");
|
|
|
|
|
|
|
|
throw std::runtime_error("failed to acquire swapchain image");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
device->get()->resetFences(inFlightFenceHandle);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
commandBuffer.reset();
|
|
|
|
|
|
|
|
commandBuffer.begin(vk::CommandBufferBeginInfo{});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Image& image = swapchain->getImage(static_cast<int>(imageIndex));
|
|
|
|
|
|
|
|
image.transitionLayout(commandBuffer,
|
|
|
|
|
|
|
|
vk::ImageLayout::eColorAttachmentOptimal);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vk::ClearValue clearValue{};
|
|
|
|
|
|
|
|
clearValue.color =
|
|
|
|
|
|
|
|
vk::ClearColorValue{std::array<float, 4>{0.01f, 0.01f, 0.02f, 1.0f}};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vk::RenderingAttachmentInfo colorAttachment{};
|
|
|
|
|
|
|
|
colorAttachment.imageView = image.getView();
|
|
|
|
|
|
|
|
colorAttachment.imageLayout = vk::ImageLayout::eColorAttachmentOptimal;
|
|
|
|
|
|
|
|
colorAttachment.loadOp = vk::AttachmentLoadOp::eClear;
|
|
|
|
|
|
|
|
colorAttachment.storeOp = vk::AttachmentStoreOp::eStore;
|
|
|
|
|
|
|
|
colorAttachment.clearValue = clearValue;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vk::RenderingInfo renderingInfo{};
|
|
|
|
|
|
|
|
renderingInfo.renderArea = vk::Rect2D{{0, 0}, swapchain->getExtent()};
|
|
|
|
|
|
|
|
renderingInfo.layerCount = 1;
|
|
|
|
|
|
|
|
renderingInfo.colorAttachmentCount = 1;
|
|
|
|
|
|
|
|
renderingInfo.pColorAttachments = &colorAttachment;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
commandBuffer.beginRendering(renderingInfo);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vk::Extent2D extent = swapchain->getExtent();
|
|
|
|
|
|
|
|
vk::Viewport viewport{0.0f,
|
|
|
|
|
|
|
|
0.0f,
|
|
|
|
|
|
|
|
static_cast<float>(extent.width),
|
|
|
|
|
|
|
|
static_cast<float>(extent.height),
|
|
|
|
|
|
|
|
0.0f,
|
|
|
|
|
|
|
|
1.0f};
|
|
|
|
|
|
|
|
vk::Rect2D scissor{{0, 0}, extent};
|
|
|
|
|
|
|
|
commandBuffer.setViewport(0, viewport);
|
|
|
|
|
|
|
|
commandBuffer.setScissor(0, scissor);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
graphicsPipeline->bind(commandBuffer);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vk::Buffer vertexBuffers[] = {vertexBuffer.getBuffer()};
|
|
|
|
|
|
|
|
vk::DeviceSize offsets[] = {0};
|
|
|
|
|
|
|
|
commandBuffer.bindVertexBuffers(0, vertexBuffers, offsets);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PushConstants push{};
|
|
|
|
|
|
|
|
push.transform = glm::mat4(1.0f);
|
|
|
|
|
|
|
|
push.color = glm::vec4(1.0f);
|
|
|
|
|
|
|
|
push.data = glm::vec4(static_cast<float>(extent.width),
|
|
|
|
|
|
|
|
static_cast<float>(extent.height),
|
|
|
|
|
|
|
|
static_cast<float>(elapsedTime), 0.0f);
|
|
|
|
|
|
|
|
commandBuffer.pushConstants(
|
|
|
|
|
|
|
|
pipelineLayout.get(),
|
|
|
|
|
|
|
|
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment,
|
|
|
|
|
|
|
|
0, sizeof(push), &push);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
commandBuffer.draw(static_cast<u32>(vertices.size()), 1, 0, 0);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
commandBuffer.endRendering();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
image.transitionLayout(commandBuffer, vk::ImageLayout::ePresentSrcKHR);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
commandBuffer.end();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vk::Semaphore renderFinishedSemaphoreHandle =
|
|
|
|
|
|
|
|
renderFinishedSemaphores.at(imageIndex).get();
|
|
|
|
|
|
|
|
vk::PipelineStageFlags waitStage =
|
|
|
|
|
|
|
|
vk::PipelineStageFlagBits::eColorAttachmentOutput;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vk::SubmitInfo submitInfo{};
|
|
|
|
|
|
|
|
submitInfo.waitSemaphoreCount = 1;
|
|
|
|
|
|
|
|
submitInfo.pWaitSemaphores = &imageAvailableSemaphoreHandle;
|
|
|
|
|
|
|
|
submitInfo.pWaitDstStageMask = &waitStage;
|
|
|
|
|
|
|
|
submitInfo.commandBufferCount = 1;
|
|
|
|
|
|
|
|
submitInfo.pCommandBuffers = &commandBuffer;
|
|
|
|
|
|
|
|
submitInfo.signalSemaphoreCount = 1;
|
|
|
|
|
|
|
|
submitInfo.pSignalSemaphores = &renderFinishedSemaphoreHandle;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
device->getGraphicsQueue().submit(submitInfo, inFlightFenceHandle);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vk::Result presentResult = swapchain->present(
|
|
|
|
|
|
|
|
device->getPresentQueue(), imageIndex, renderFinishedSemaphoreHandle);
|
|
|
|
|
|
|
|
if (presentResult == vk::Result::eErrorOutOfDateKHR ||
|
|
|
|
|
|
|
|
presentResult == vk::Result::eSuboptimalKHR || window->wasResized()) {
|
|
|
|
|
|
|
|
window->resetResizedFlag();
|
|
|
|
|
|
|
|
recreateSwapchain();
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (presentResult != vk::Result::eSuccess) {
|
|
|
|
|
|
|
|
log::fatal("failed to present swapchain image");
|
|
|
|
|
|
|
|
throw std::runtime_error("failed to present swapchain image");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
device->waitIdle();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
auto endTime = std::chrono::high_resolution_clock::now();
|
|
|
|
|
|
|
|
double totalRuntime =
|
|
|
|
|
|
|
|
std::chrono::duration<double>(endTime - startTime).count();
|
|
|
|
|
|
|
|
log::info("Application ran for", totalRuntime, "s");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace sophia
|
|
|
|
|