Updates
This commit is contained in:
@@ -1,82 +0,0 @@
|
||||
#include "engine.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <sophia/frame_info.hpp>
|
||||
#include <sophia/util/logger.hpp>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
namespace sophia {
|
||||
|
||||
Engine::Engine(const Config& config) : config{config} {
|
||||
if (config.headless) {
|
||||
this->device = Device::Builder().headless().build();
|
||||
} else {
|
||||
this->window =
|
||||
std::make_unique<Window>(config.width, config.height, config.name);
|
||||
this->device = Device::Builder().withWindow(*this->window).build();
|
||||
}
|
||||
|
||||
// this->imguiDescriptorPool =
|
||||
// DescriptorPool::Builder(this->device)
|
||||
// .addPoolSize(vk::DescriptorType::eCombinedImageSampler,
|
||||
// IMGUI_IMPL_VULKAN_MINIMUM_IMAGE_SAMPLER_POOL_SIZE
|
||||
// + 5)
|
||||
// .setPoolFlags(vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet)
|
||||
// .setMaxSets(IMGUI_IMPL_VULKAN_MINIMUM_IMAGE_SAMPLER_POOL_SIZE
|
||||
// + 5)
|
||||
// .build();
|
||||
|
||||
// this->uiManager =
|
||||
// UIManager::Builder(this->window, this->device, this->renderer,
|
||||
// imguiDescriptorPool->get())
|
||||
// .darkTheme()
|
||||
// .setDocking(true)
|
||||
// .setKeyboard(true)
|
||||
// .build();
|
||||
};
|
||||
|
||||
Engine::~Engine() { this->device->waitIdle(); };
|
||||
|
||||
void Engine::run() {
|
||||
auto startTime = std::chrono::high_resolution_clock::now();
|
||||
auto currentTime = startTime;
|
||||
|
||||
// EventSystem::get().addListener<KeyReleasedEvent>(
|
||||
// std::bind(&Application::onEvent, this, std::placeholders::_1));
|
||||
|
||||
while (this->isRunning) {
|
||||
if (this->window) {
|
||||
glfwPollEvents();
|
||||
}
|
||||
|
||||
auto newTime = std::chrono::high_resolution_clock::now();
|
||||
double deltaTime =
|
||||
std::chrono::duration<double>(newTime - currentTime).count();
|
||||
currentTime = newTime;
|
||||
|
||||
// // Attempt to start a new frame
|
||||
// vk::CommandBuffer commandBuffer = this->renderer.beginFrame();
|
||||
// if (commandBuffer != nullptr) {
|
||||
// double elapsedTime =
|
||||
// std::chrono::duration<double>(currentTime - startTime).count();
|
||||
|
||||
// vk::Extent2D extent = this->renderer.getCurrentFramebufferExtent();
|
||||
// glm::vec2 extentVec2(static_cast<float>(extent.width),
|
||||
// static_cast<float>(extent.height));
|
||||
|
||||
// FrameInfo frameInfo{this->renderer.getFrameIndex(), elapsedTime,
|
||||
// deltaTime, extentVec2};
|
||||
// }
|
||||
|
||||
this->isRunning = false;
|
||||
}
|
||||
|
||||
this->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
|
||||
@@ -1,37 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "device.hpp"
|
||||
#include "window.hpp"
|
||||
|
||||
namespace sophia {
|
||||
|
||||
class Engine {
|
||||
public:
|
||||
struct Config {
|
||||
int width;
|
||||
int height;
|
||||
std::string name;
|
||||
bool headless = false;
|
||||
};
|
||||
|
||||
Engine(const Engine&) = delete;
|
||||
Engine& operator=(const Engine&) = delete;
|
||||
|
||||
Engine(const Config& config);
|
||||
~Engine();
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
Config config;
|
||||
|
||||
std::unique_ptr<Window> window;
|
||||
std::unique_ptr<Device> device;
|
||||
|
||||
bool isRunning = true;
|
||||
};
|
||||
|
||||
} // namespace sophia
|
||||
@@ -0,0 +1,124 @@
|
||||
#include "model.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <sophia/util/logger.hpp>
|
||||
|
||||
namespace sophia {
|
||||
|
||||
std::vector<vk::VertexInputBindingDescription>
|
||||
Model::Vertex::getBindingDescriptions() {
|
||||
std::vector<vk::VertexInputBindingDescription> bindingDescriptions(1);
|
||||
bindingDescriptions[0].binding = 0;
|
||||
bindingDescriptions[0].stride = sizeof(Vertex);
|
||||
bindingDescriptions[0].inputRate = vk::VertexInputRate::eVertex;
|
||||
|
||||
return bindingDescriptions;
|
||||
}
|
||||
|
||||
std::vector<vk::VertexInputAttributeDescription>
|
||||
Model::Vertex::getAttributeDescriptions() {
|
||||
std::vector<vk::VertexInputAttributeDescription> attributeDescriptions{};
|
||||
|
||||
attributeDescriptions.push_back(
|
||||
{0, 0, vk::Format::eR32G32Sfloat, offsetof(Vertex, position)});
|
||||
attributeDescriptions.push_back(
|
||||
{1, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, color)});
|
||||
// attributeDescriptions.push_back(
|
||||
// {2, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, normal)});
|
||||
// attributeDescriptions.push_back(
|
||||
// {3, 0, vk::Format::eR32G32Sfloat, offsetof(Vertex, uv)});
|
||||
|
||||
return attributeDescriptions;
|
||||
}
|
||||
|
||||
Model::Model(Device& device, const Builder& builder) : device{device} {
|
||||
createVertexBuffers(builder.vertices);
|
||||
createIndexBuffers(builder.indices);
|
||||
}
|
||||
|
||||
Model::~Model() {}
|
||||
|
||||
void Model::bind(vk::CommandBuffer commandBuffer) {
|
||||
vk::Buffer buffers[] = {this->vertexBuffer->getBuffer()};
|
||||
vk::DeviceSize offset[] = {0};
|
||||
commandBuffer.bindVertexBuffers(0, 1, buffers, offset);
|
||||
|
||||
if (this->hasIndexBuffer) {
|
||||
commandBuffer.bindIndexBuffer(this->indexBuffer->getBuffer(), 0,
|
||||
vk::IndexType::eUint32);
|
||||
}
|
||||
}
|
||||
|
||||
void Model::draw(vk::CommandBuffer commandBuffer) {
|
||||
if (this->hasIndexBuffer) {
|
||||
commandBuffer.drawIndexed(this->indexCount, 1, 0, 0, 0);
|
||||
} else {
|
||||
commandBuffer.draw(this->vertexCount, 1, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void Model::createVertexBuffers(const std::vector<Vertex>& vertices) {
|
||||
this->vertexCount = static_cast<u32>(vertices.size());
|
||||
assert(this->vertexCount >= 3 && "vertex count must be at least 3");
|
||||
|
||||
vk::DeviceSize bufferSize = sizeof(vertices[0]) * this->vertexCount;
|
||||
|
||||
u32 vertexSize = sizeof(vertices[0]);
|
||||
|
||||
Buffer stagingBuffer{
|
||||
this->device,
|
||||
vertexSize,
|
||||
vertexCount,
|
||||
vk::BufferUsageFlagBits::eTransferSrc,
|
||||
vk::MemoryPropertyFlagBits::eHostVisible |
|
||||
vk::MemoryPropertyFlagBits::eHostCoherent,
|
||||
};
|
||||
|
||||
stagingBuffer.map();
|
||||
stagingBuffer.writeToBuffer((void*)vertices.data());
|
||||
|
||||
vertexBuffer =
|
||||
std::make_unique<Buffer>(this->device, vertexSize, vertexCount,
|
||||
vk::BufferUsageFlagBits::eTransferDst |
|
||||
vk::BufferUsageFlagBits::eVertexBuffer,
|
||||
vk::MemoryPropertyFlagBits::eDeviceLocal);
|
||||
|
||||
this->device.copyBuffer(stagingBuffer.getBuffer(),
|
||||
this->vertexBuffer->getBuffer(), bufferSize);
|
||||
}
|
||||
|
||||
void Model::createIndexBuffers(const std::vector<u32>& indicies) {
|
||||
this->indexCount = static_cast<u32>(indicies.size());
|
||||
this->hasIndexBuffer = this->indexCount > 0;
|
||||
|
||||
if (!this->hasIndexBuffer) {
|
||||
return;
|
||||
}
|
||||
|
||||
vk::DeviceSize bufferSize = sizeof(indicies[0]) * this->indexCount;
|
||||
|
||||
u32 indexSize = sizeof(indicies[0]);
|
||||
|
||||
Buffer stagingBuffer{
|
||||
this->device,
|
||||
indexSize,
|
||||
indexCount,
|
||||
vk::BufferUsageFlagBits::eTransferSrc,
|
||||
vk::MemoryPropertyFlagBits::eHostVisible |
|
||||
vk::MemoryPropertyFlagBits::eHostCoherent,
|
||||
};
|
||||
|
||||
stagingBuffer.map();
|
||||
stagingBuffer.writeToBuffer((void*)indicies.data());
|
||||
|
||||
indexBuffer =
|
||||
std::make_unique<Buffer>(this->device, indexSize, indexCount,
|
||||
vk::BufferUsageFlagBits::eTransferDst |
|
||||
vk::BufferUsageFlagBits::eIndexBuffer,
|
||||
vk::MemoryPropertyFlagBits::eDeviceLocal);
|
||||
|
||||
this->device.copyBuffer(stagingBuffer.getBuffer(),
|
||||
this->indexBuffer->getBuffer(), bufferSize);
|
||||
}
|
||||
|
||||
} // namespace sophia
|
||||
@@ -0,0 +1,66 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#define GLM_FORCE_RADIANS
|
||||
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "buffer.hpp"
|
||||
#include "device.hpp"
|
||||
|
||||
namespace sophia {
|
||||
|
||||
class Model {
|
||||
public:
|
||||
struct Vertex {
|
||||
glm::vec2 position{};
|
||||
glm::vec3 color{};
|
||||
// glm::vec3 normal{};
|
||||
// glm::vec2 uv{};
|
||||
|
||||
static std::vector<vk::VertexInputBindingDescription>
|
||||
getBindingDescriptions();
|
||||
static std::vector<vk::VertexInputAttributeDescription>
|
||||
getAttributeDescriptions();
|
||||
|
||||
// bool operator==(const Vertex& other) const {
|
||||
// return position == other.position && color == other.color;
|
||||
// }
|
||||
};
|
||||
|
||||
struct Builder {
|
||||
std::vector<Vertex> vertices{};
|
||||
std::vector<uint32_t> indices{};
|
||||
|
||||
// void loadModel(const std::string& filepath);
|
||||
};
|
||||
|
||||
Model(Device& device, const Model::Builder& builder);
|
||||
~Model();
|
||||
|
||||
Model(const Model&) = delete;
|
||||
Model& operator=(const Model&) = delete;
|
||||
|
||||
// static std::unique_ptr<Model> createModelFromFile(
|
||||
// Device& device, const std::string& filepath);
|
||||
|
||||
void bind(vk::CommandBuffer commandBuffer);
|
||||
void draw(vk::CommandBuffer commandBuffer);
|
||||
|
||||
private:
|
||||
void createVertexBuffers(const std::vector<Vertex>& vertices);
|
||||
void createIndexBuffers(const std::vector<u32>& indices);
|
||||
|
||||
Device& device;
|
||||
|
||||
std::unique_ptr<Buffer> vertexBuffer;
|
||||
u32 vertexCount;
|
||||
|
||||
bool hasIndexBuffer = false;
|
||||
std::unique_ptr<Buffer> indexBuffer;
|
||||
u32 indexCount;
|
||||
};
|
||||
|
||||
} // namespace sophia
|
||||
@@ -0,0 +1,165 @@
|
||||
#include "render_context.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <limits>
|
||||
#include <sophia/util/logger.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace sophia {
|
||||
|
||||
RenderContext::RenderContext(Window& window, Device& device)
|
||||
: window{window}, device{device} {
|
||||
createFrames();
|
||||
recreateSwapchain();
|
||||
}
|
||||
|
||||
RenderContext::~RenderContext() { this->device.waitIdle(); }
|
||||
|
||||
vk::CommandBuffer RenderContext::beginFrame() {
|
||||
assert(!this->isFrameStarted &&
|
||||
"Can't call beginFrame while already in progress");
|
||||
|
||||
FrameData& frame = this->frames[this->currentFrameIndex];
|
||||
|
||||
vk::Fence inFlightFence = frame.inFlight.get();
|
||||
vk::Result fenceResult = this->device.get()->waitForFences(
|
||||
inFlightFence, vk::True, std::numeric_limits<u64>::max());
|
||||
|
||||
if (fenceResult != vk::Result::eSuccess) {
|
||||
log::error("failed to wait for fence: " + vk::to_string(fenceResult));
|
||||
}
|
||||
|
||||
vk::Semaphore imageAvailable = frame.imageAvailable.get();
|
||||
vk::Result result = this->swapchain->acquireNextImage(
|
||||
imageAvailable, this->currentImageIndex);
|
||||
if (result == vk::Result::eErrorOutOfDateKHR) {
|
||||
recreateSwapchain();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (result != vk::Result::eSuccess && result != vk::Result::eSuboptimalKHR) {
|
||||
log::fatal("failed to acquire swapchain image");
|
||||
throw std::runtime_error("failed to acquire swapchain image");
|
||||
}
|
||||
|
||||
this->isFrameStarted = true;
|
||||
|
||||
vk::Fence& imageInFlight = this->imagesInFlight.at(this->currentImageIndex);
|
||||
if (imageInFlight) {
|
||||
vk::Result fenceResult = this->device.get()->waitForFences(
|
||||
imageInFlight, vk::True, std::numeric_limits<u64>::max());
|
||||
|
||||
if (fenceResult != vk::Result::eSuccess) {
|
||||
log::error("failed to wait for fence: " + vk::to_string(fenceResult));
|
||||
}
|
||||
}
|
||||
|
||||
imageInFlight = inFlightFence;
|
||||
|
||||
this->device.get()->resetFences(inFlightFence);
|
||||
|
||||
frame.commandBuffer.reset();
|
||||
|
||||
try {
|
||||
frame.commandBuffer.begin(vk::CommandBufferBeginInfo{});
|
||||
} catch (const vk::SystemError& err) {
|
||||
log::fatal("failed to begin recording command buffer");
|
||||
throw std::runtime_error("failed to begin recording command buffer");
|
||||
}
|
||||
|
||||
return frame.commandBuffer;
|
||||
}
|
||||
|
||||
void RenderContext::endFrame() {
|
||||
assert(this->isFrameStarted &&
|
||||
"Can't call endFrame while no frame is in progress");
|
||||
|
||||
FrameData& frame = this->frames[this->currentFrameIndex];
|
||||
frame.commandBuffer.end();
|
||||
|
||||
vk::Semaphore waitSemaphore = frame.imageAvailable.get();
|
||||
vk::Semaphore signalSemaphore =
|
||||
this->renderFinishedSemaphores.at(this->currentImageIndex).get();
|
||||
vk::PipelineStageFlags waitStage =
|
||||
vk::PipelineStageFlagBits::eColorAttachmentOutput;
|
||||
vk::CommandBuffer commandBuffer = frame.commandBuffer;
|
||||
|
||||
vk::SubmitInfo submitInfo{};
|
||||
submitInfo.waitSemaphoreCount = 1;
|
||||
submitInfo.pWaitSemaphores = &waitSemaphore;
|
||||
submitInfo.pWaitDstStageMask = &waitStage;
|
||||
submitInfo.commandBufferCount = 1;
|
||||
submitInfo.pCommandBuffers = &commandBuffer;
|
||||
submitInfo.signalSemaphoreCount = 1;
|
||||
submitInfo.pSignalSemaphores = &signalSemaphore;
|
||||
|
||||
this->device.getGraphicsQueue().submit(submitInfo, frame.inFlight.get());
|
||||
|
||||
vk::Result result = this->swapchain->present(
|
||||
this->device.getPresentQueue(), this->currentImageIndex, signalSemaphore);
|
||||
if (result == vk::Result::eErrorOutOfDateKHR ||
|
||||
result == vk::Result::eSuboptimalKHR || this->window.wasResized()) {
|
||||
this->window.resetResizedFlag();
|
||||
recreateSwapchain();
|
||||
} else if (result != vk::Result::eSuccess) {
|
||||
log::fatal("failed to present swapchain image");
|
||||
throw std::runtime_error("failed to present swapchain image");
|
||||
}
|
||||
|
||||
this->isFrameStarted = false;
|
||||
this->currentFrameIndex =
|
||||
(this->currentFrameIndex + 1) % MAX_FRAMES_IN_FLIGHT;
|
||||
}
|
||||
|
||||
void RenderContext::recreateSwapchain() {
|
||||
vk::Extent2D extent = this->window.getExtent();
|
||||
while (extent.width == 0 || extent.height == 0) {
|
||||
extent = this->window.getExtent();
|
||||
glfwWaitEvents();
|
||||
}
|
||||
|
||||
this->device.waitIdle();
|
||||
|
||||
if (this->swapchain == nullptr) {
|
||||
this->swapchain = std::make_unique<Swapchain>(this->device, extent);
|
||||
} else {
|
||||
std::shared_ptr<Swapchain> oldSwapchain = std::move(this->swapchain);
|
||||
this->swapchain =
|
||||
std::make_unique<Swapchain>(this->device, extent, oldSwapchain);
|
||||
|
||||
if (!oldSwapchain->compareSwapchainFormats(*this->swapchain)) {
|
||||
log::fatal("swapchain image format changed on recreation");
|
||||
throw std::runtime_error("swapchain image format changed on recreation");
|
||||
}
|
||||
}
|
||||
|
||||
this->renderFinishedSemaphores.clear();
|
||||
for (size_t i = 0; i < this->swapchain->imageCount(); i++) {
|
||||
this->renderFinishedSemaphores.push_back(
|
||||
this->device.get()->createSemaphoreUnique({}));
|
||||
}
|
||||
|
||||
// Old fences referred to images owned by the now-replaced swapchain;
|
||||
// there's nothing valid left to wait on.
|
||||
this->imagesInFlight.assign(this->swapchain->imageCount(), vk::Fence{});
|
||||
|
||||
log::trace("recreated swapchain");
|
||||
}
|
||||
|
||||
void RenderContext::createFrames() {
|
||||
vk::CommandBufferAllocateInfo allocInfo{this->device.getCommandPool(),
|
||||
vk::CommandBufferLevel::ePrimary,
|
||||
MAX_FRAMES_IN_FLIGHT};
|
||||
std::vector<vk::CommandBuffer> commandBuffers =
|
||||
this->device.get()->allocateCommandBuffers(allocInfo);
|
||||
|
||||
for (u32 i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
|
||||
this->frames[i].commandBuffer = commandBuffers[i];
|
||||
this->frames[i].imageAvailable =
|
||||
this->device.get()->createSemaphoreUnique({});
|
||||
this->frames[i].inFlight = this->device.get()->createFenceUnique(
|
||||
{vk::FenceCreateFlagBits::eSignaled});
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace sophia
|
||||
@@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <sophia/types.hpp>
|
||||
#include <vector>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
#include "device.hpp"
|
||||
#include "swapchain.hpp"
|
||||
#include "window.hpp"
|
||||
|
||||
namespace sophia {
|
||||
|
||||
struct FrameData {
|
||||
vk::CommandBuffer commandBuffer;
|
||||
vk::UniqueSemaphore imageAvailable;
|
||||
vk::UniqueFence inFlight;
|
||||
};
|
||||
|
||||
class RenderContext {
|
||||
public:
|
||||
static constexpr u32 MAX_FRAMES_IN_FLIGHT = 2;
|
||||
|
||||
RenderContext(const RenderContext&) = delete;
|
||||
RenderContext& operator=(const RenderContext&) = delete;
|
||||
|
||||
RenderContext(Window& window, Device& device);
|
||||
~RenderContext();
|
||||
|
||||
vk::CommandBuffer beginFrame();
|
||||
void endFrame();
|
||||
|
||||
void transitionCurrentImage(vk::CommandBuffer commandBuffer,
|
||||
vk::ImageLayout newLayout) const {
|
||||
this->swapchain->getImage(static_cast<int>(this->currentImageIndex))
|
||||
.transitionLayout(commandBuffer, newLayout);
|
||||
}
|
||||
vk::ImageView getCurrentImageView() const {
|
||||
return this->swapchain->getImageView(
|
||||
static_cast<int>(this->currentImageIndex));
|
||||
}
|
||||
|
||||
vk::Extent2D getExtent() const { return this->swapchain->getExtent(); }
|
||||
vk::Format getImageFormat() const {
|
||||
return this->swapchain->getImageFormat();
|
||||
}
|
||||
|
||||
private:
|
||||
void recreateSwapchain();
|
||||
void createFrames();
|
||||
|
||||
Window& window;
|
||||
Device& device;
|
||||
|
||||
std::unique_ptr<Swapchain> swapchain;
|
||||
|
||||
std::array<FrameData, MAX_FRAMES_IN_FLIGHT> frames;
|
||||
|
||||
std::vector<vk::UniqueSemaphore> renderFinishedSemaphores;
|
||||
std::vector<vk::Fence> imagesInFlight;
|
||||
|
||||
u32 currentFrameIndex = 0;
|
||||
u32 currentImageIndex = 0;
|
||||
bool isFrameStarted = false;
|
||||
};
|
||||
|
||||
} // namespace sophia
|
||||
Reference in New Issue
Block a user