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