Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • 119-graphicspipeline-refactoring
  • 129-projekte-und-assets-auslagern
  • 132-denoising-module
  • 143-ar-vr-support-via-openxr
  • 43-multi-threading
  • 91-compute-first-network
  • 95-arm64-raspberry-pi-4-support
  • develop
  • master
  • optimizations
  • 0.1.0
  • 0.2.0
12 results

Target

Select target project
  • vulkan2021/vkcv-framework
1 result
Select Git revision
  • 119-graphicspipeline-refactoring
  • 129-projekte-und-assets-auslagern
  • 132-denoising-module
  • 143-ar-vr-support-via-openxr
  • 43-multi-threading
  • 91-compute-first-network
  • 95-arm64-raspberry-pi-4-support
  • develop
  • master
  • optimizations
  • 0.1.0
  • 0.2.0
12 results
Show changes
Commits on Source (12)
......@@ -51,4 +51,6 @@ set(vkcv_sources
${vkcv_source}/vkcv/Framebuffer.hpp
${vkcv_source}/vkcv/Framebuffer.cpp
${vkcv_include}/vkcv/Event.hpp
)
#pragma once
#include <vulkan/vulkan.hpp>
#include <unordered_set>
#include "QueueManager.hpp"
namespace vkcv {
struct CommandResources {
vk::CommandPool commandPool;
vk::CommandBuffer commandBuffer;
std::vector<vk::CommandPool> cmdPoolPerQueueFamily;
};
CommandResources createDefaultCommandResources(const vk::Device& device, const int graphicFamilyIndex);
void destroyCommandResources(const vk::Device& device, const CommandResources& resources);
std::unordered_set<int> generateQueueFamilyIndexSet(const QueueManager& queueManager);
CommandResources createCommandResources(const vk::Device& device, const std::unordered_set<int> &familyIndexSet);
void destroyCommandResources(const vk::Device& device, const CommandResources& resources);
vk::CommandBuffer allocateCommandBuffer(const vk::Device& device, const vk::CommandPool cmdPool);
vk::CommandPool chooseCmdPool(const Queue &queue, const CommandResources &cmdResources);
Queue getQueueForSubmit(const QueueType type, const QueueManager &queueManager);
void beginCommandBuffer(const vk::CommandBuffer cmdBuffer, const vk::CommandBufferUsageFlags flags);
void submitCommandBufferToQueue(
const vk::Queue queue,
const vk::CommandBuffer cmdBuffer,
const vk::Fence fence,
const std::vector<vk::Semaphore>& waitSemaphores,
const std::vector<vk::Semaphore>& signalSemaphores);
}
\ No newline at end of file
......@@ -24,6 +24,15 @@ namespace vkcv
class PassManager;
class PipelineManager;
struct SubmitInfo {
QueueType queueType;
std::vector<vk::Semaphore> waitSemaphores;
std::vector<vk::Semaphore> signalSemaphores;
};
typedef std::function<void(const vk::CommandBuffer& cmdBuffer)> RecordCommandFunction;
typedef std::function<void(void)> FinishCommandFunction;
class Core final
{
private:
......@@ -33,7 +42,7 @@ namespace vkcv
*
* @param context encapsulates various Vulkan objects
*/
Core(Context &&context, const Window &window, SwapChain swapChain, std::vector<vk::ImageView> imageViews,
Core(Context &&context, Window &window, SwapChain swapChain, std::vector<vk::ImageView> imageViews,
const CommandResources& commandResources, const SyncResources& syncResources) noexcept;
// explicit destruction of default constructor
Core() = delete;
......@@ -53,6 +62,14 @@ namespace vkcv
SyncResources m_SyncResources;
uint32_t m_currentSwapchainImageIndex;
std::vector<vk::Framebuffer> m_TemporaryFramebuffers;
/**
* recreates the swapchain
* @param[in] width new window width
* @param[in] height new window hight
*/
static void recreateSwapchain(int width, int height);
public:
/**
* Destructor of #Core destroys the Vulkan objects contained in the core's context.
......@@ -107,7 +124,7 @@ namespace vkcv
* @param[in] deviceExtensions (optional) Requested device extensions
* @return New instance of #Context
*/
static Core create(const Window &window,
static Core create(Window &window,
const char *applicationName,
uint32_t applicationVersion,
std::vector<vk::QueueFlagBits> queueFlags = {},
......@@ -164,5 +181,16 @@ namespace vkcv
void endFrame();
vk::Format getSwapchainImageFormat();
/**
* Submit a command buffer to any queue of selected type. The recording can be customized by a
* custom record-command-function. If the command submission has finished, an optional finish-function
* will be called.
*
* @param submitInfo Submit information
* @param record Record-command-function
* @param finish Finish-command-function or nullptr
*/
void submitCommands(const SubmitInfo &submitInfo, const RecordCommandFunction& record, const FinishCommandFunction& finish);
};
}
#pragma once
#include <functional>
namespace vkcv {
template<typename... T>
struct event_function {
typedef std::function<void(T...)> type;
};
/**
* template for event handling
* @tparam T parameter list
*/
template<typename... T>
struct event {
private:
std::vector<typename event_function<T...>::type> m_handles;
public:
/**
* calls all function handles with the given arguments
* @param arguments of the given function
*/
void operator()(T... arguments) {
for (auto &handle : this->m_handles) {
handle(arguments...);
}
}
/**
* adds a function handle to the event to be called
* @param handle of the function
*/
void add(typename event_function<T...>::type handle) {
this->m_handles.push_back(handle);
}
/**
* removes a function handle of the event
* @param handle of the function
*/
void remove(typename event_function<T...>::type handle) {
this->m_handles.erase(
remove(this->m_handles.begin(), this->m_handles.end(), handle),
this->m_handles.end()
);
}
event() = default;
event(const event &other) = delete;
event(event &&other) = delete;
~event() = default;
event &operator=(const event &other) = delete;
event &operator=(event &&other) = delete;
};
}
......@@ -2,7 +2,9 @@
#include <vulkan/vulkan.hpp>
namespace vkcv {
enum class QueueType { Compute, Transfer, Graphics, Present };
struct Queue {
int familyIndex;
int queueIndex;
......
......@@ -3,11 +3,13 @@
namespace vkcv {
struct SyncResources {
vk::Semaphore renderFinished;
vk::Fence swapchainImageAcquired;
vk::Fence presentFinished;
vk::Semaphore renderFinished;
vk::Semaphore swapchainImageAcquired;
vk::Fence presentFinished;
};
SyncResources createDefaultSyncResources(const vk::Device& device);
void destroySyncResources(const vk::Device& device, const SyncResources& resources);
SyncResources createSyncResources(const vk::Device &device);
void destroySyncResources(const vk::Device &device, const SyncResources &resources);
vk::Fence createFence(const vk::Device &device);
void waitForFence(const vk::Device& device, const vk::Fence fence);
}
\ No newline at end of file
......@@ -7,6 +7,7 @@
#define NOMINMAX
#include <algorithm>
#include "Event.hpp"
struct GLFWwindow;
......@@ -23,6 +24,32 @@ namespace vkcv {
*/
explicit Window(GLFWwindow *window);
/**
* mouse callback for moving the mouse on the screen
* @param[in] window The window that received the event.
* @param[in] xpos The new cursor x-coordinate, relative to the left edge of the content area.
* @param[in] ypos The new cursor y-coordinate, relative to the top edge of the content area.
*/
static void onMouseMoveEvent(GLFWwindow *window, double x, double y);
/**
* resize callback for the resize option of the window
* @param[in] window The window that was resized.
* @param[in] width The new width, in screen coordinates, of the window.
* @param[in] height The new height, in screen coordinates, of the window.
*/
static void onResize(GLFWwindow *callbackWindow, int width, int height);
/**
* key callback for the pressed key
* @param[in] window The window that received the event.
* @param[in] key The [keyboard key](@ref keys) that was pressed or released.
* @param[in] scancode The system-specific scancode of the key.
* @param[in] action `GLFW_PRESS`, `GLFW_RELEASE` or `GLFW_REPEAT`.
* @param[in] mods Bit field describing which [modifier keys](@ref mods) were held down.
*/
static void onKeyEvent(GLFWwindow *callbackWindow, int key, int scancode, int action, int mods);
public:
/**
* creates a GLFWwindow with the parameters in the function
......@@ -41,11 +68,23 @@ namespace vkcv {
[[nodiscard]]
bool isWindowOpen() const;
/**
* binds windowEvents to lambda events
*/
void initEvents();
/**
* polls all events on the GLFWwindow
*/
static void pollEvents();
/**
* basic events of the window
*/
event< double, double > e_mouseMove;
event< int, int > e_resize;
event< int, int, int, int > e_key;
/**
* returns the current window
* @return window handle
......
......@@ -2,6 +2,7 @@
#include <vkcv/Core.hpp>
#include <vkcv/Window.hpp>
#include <vkcv/ShaderProgram.hpp>
#include <GLFW/glfw3.h>
int main(int argc, const char** argv) {
const char* applicationName = "First Triangle";
......@@ -15,6 +16,8 @@ int main(int argc, const char** argv) {
false
);
window.initEvents();
vkcv::Core core = vkcv::Core::create(
window,
applicationName,
......@@ -102,8 +105,6 @@ int main(int argc, const char** argv) {
core.beginFrame();
core.renderTriangle(trianglePass, trianglePipeline, windowWidth, windowHeight);
core.endFrame();
window.pollEvents();
}
return 0;
}
#include "vkcv/CommandResources.hpp"
#include <iostream>
namespace vkcv {
CommandResources createDefaultCommandResources(const vk::Device& device, const int graphicFamilyIndex) {
std::unordered_set<int> generateQueueFamilyIndexSet(const QueueManager &queueManager) {
std::unordered_set<int> indexSet;
for (const auto& queue : queueManager.getGraphicsQueues()) {
indexSet.insert(queue.familyIndex);
}
for (const auto& queue : queueManager.getComputeQueues()) {
indexSet.insert(queue.familyIndex);
}
for (const auto& queue : queueManager.getTransferQueues()) {
indexSet.insert(queue.familyIndex);
}
indexSet.insert(queueManager.getPresentQueue().familyIndex);
return indexSet;
}
CommandResources createCommandResources(const vk::Device& device, const std::unordered_set<int>& familyIndexSet) {
CommandResources resources;
vk::CommandPoolCreateFlags flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer;
vk::CommandPoolCreateInfo poolCreateInfo(flags, graphicFamilyIndex);
resources.commandPool = device.createCommandPool(poolCreateInfo, nullptr, {});
const size_t queueFamiliesCount = familyIndexSet.size();
resources.cmdPoolPerQueueFamily.resize(queueFamiliesCount);
const int commandPoolCount = 1;
vk::CommandBufferAllocateInfo allocateInfo(resources.commandPool, vk::CommandBufferLevel::ePrimary, commandPoolCount);
const std::vector<vk::CommandBuffer> createdBuffers = device.allocateCommandBuffers(allocateInfo, {});
assert(createdBuffers.size() == 1);
resources.commandBuffer = createdBuffers[0];
const vk::CommandPoolCreateFlags poolFlags = vk::CommandPoolCreateFlagBits::eTransient;
for (const int familyIndex : familyIndexSet) {
const vk::CommandPoolCreateInfo poolCreateInfo(poolFlags, familyIndex);
resources.cmdPoolPerQueueFamily[familyIndex] = device.createCommandPool(poolCreateInfo, nullptr, {});
}
return resources;
}
void destroyCommandResources(const vk::Device& device, const CommandResources& resources) {
device.freeCommandBuffers(resources.commandPool, resources.commandBuffer, {});
device.destroyCommandPool(resources.commandPool, {});
for (const vk::CommandPool &pool : resources.cmdPoolPerQueueFamily) {
device.destroyCommandPool(pool);
}
}
vk::CommandBuffer allocateCommandBuffer(const vk::Device& device, const vk::CommandPool cmdPool) {
const vk::CommandBufferAllocateInfo info(cmdPool, vk::CommandBufferLevel::ePrimary, 1);
return device.allocateCommandBuffers(info).front();
}
vk::CommandPool chooseCmdPool(const Queue& queue, const CommandResources& cmdResources) {
return cmdResources.cmdPoolPerQueueFamily[queue.familyIndex];
}
Queue getQueueForSubmit(const QueueType type, const QueueManager& queueManager) {
if (type == QueueType::Graphics) {
return queueManager.getGraphicsQueues().front();
}
else if (type == QueueType::Compute) {
return queueManager.getComputeQueues().front();
}
else if (type == QueueType::Transfer) {
return queueManager.getTransferQueues().front();
}
else if (type == QueueType::Present) {
return queueManager.getPresentQueue();
}
else {
std::cerr << "getQueueForSubmit error: unknown queue type" << std::endl;
return queueManager.getGraphicsQueues().front(); // graphics is the most general queue
}
}
void beginCommandBuffer(const vk::CommandBuffer cmdBuffer, const vk::CommandBufferUsageFlags flags) {
const vk::CommandBufferBeginInfo beginInfo(flags);
cmdBuffer.begin(beginInfo);
}
void submitCommandBufferToQueue(
const vk::Queue queue,
const vk::CommandBuffer cmdBuffer,
const vk::Fence fence,
const std::vector<vk::Semaphore>& waitSemaphores,
const std::vector<vk::Semaphore>& signalSemaphores) {
const std::vector<vk::PipelineStageFlags> waitDstStageMasks(waitSemaphores.size(), vk::PipelineStageFlagBits::eAllCommands);
const vk::SubmitInfo queueSubmitInfo(waitSemaphores, waitDstStageMasks, cmdBuffer, signalSemaphores);
queue.submit(queueSubmitInfo, fence);
}
}
\ No newline at end of file
......@@ -16,7 +16,7 @@
namespace vkcv
{
Core Core::create(const Window &window,
Core Core::create(Window &window,
const char *applicationName,
uint32_t applicationVersion,
std::vector<vk::QueueFlagBits> queueFlags,
......@@ -66,11 +66,16 @@ namespace vkcv
const auto& queueManager = context.getQueueManager();
const int graphicQueueFamilyIndex = queueManager.getGraphicsQueues()[0].familyIndex;
const auto defaultCommandResources = createDefaultCommandResources(context.getDevice(), graphicQueueFamilyIndex);
const auto defaultSyncResources = createDefaultSyncResources(context.getDevice());
const int graphicQueueFamilyIndex = queueManager.getGraphicsQueues()[0].familyIndex;
const std::unordered_set<int> queueFamilySet = generateQueueFamilyIndexSet(queueManager);
const auto commandResources = createCommandResources(context.getDevice(), queueFamilySet);
const auto defaultSyncResources = createSyncResources(context.getDevice());
return Core(std::move(context) , window, swapChain, imageViews, defaultCommandResources, defaultSyncResources);
window.e_resize.add([&](int width, int height){
recreateSwapchain(width,height);
});
return Core(std::move(context) , window, swapChain, imageViews, commandResources, defaultSyncResources);
}
const Context &Core::getContext() const
......@@ -78,7 +83,7 @@ namespace vkcv
return m_Context;
}
Core::Core(Context &&context, const Window &window , SwapChain swapChain, std::vector<vk::ImageView> imageViews,
Core::Core(Context &&context, Window &window , SwapChain swapChain, std::vector<vk::ImageView> imageViews,
const CommandResources& commandResources, const SyncResources& syncResources) noexcept :
m_Context(std::move(context)),
m_window(window),
......@@ -120,25 +125,17 @@ namespace vkcv
uint32_t imageIndex;
const auto& acquireResult = m_Context.getDevice().acquireNextImageKHR(
m_swapchain.getSwapchain(), std::numeric_limits<uint64_t>::max(), nullptr,
m_SyncResources.swapchainImageAcquired, &imageIndex, {}
m_swapchain.getSwapchain(),
std::numeric_limits<uint64_t>::max(),
m_SyncResources.swapchainImageAcquired,
nullptr,
&imageIndex, {}
);
if (acquireResult != vk::Result::eSuccess) {
return Result::ERROR;
}
const auto& result = m_Context.getDevice().waitForFences(
m_SyncResources.swapchainImageAcquired, true,
std::numeric_limits<uint64_t>::max()
);
m_Context.getDevice().resetFences(m_SyncResources.swapchainImageAcquired);
if (result != vk::Result::eSuccess) {
return Result::ERROR;
}
m_currentSwapchainImageIndex = imageIndex;
return Result::SUCCESS;
}
......@@ -154,12 +151,9 @@ namespace vkcv
if (acquireSwapchainImage() != Result::SUCCESS) {
return;
}
m_window.pollEvents();
m_Context.getDevice().waitIdle(); // FIMXE: this is a sin against graphics programming, but its getting late - Alex
destroyTemporaryFramebuffers();
const vk::CommandBufferUsageFlags beginFlags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit;
const vk::CommandBufferBeginInfo beginInfos(beginFlags);
m_CommandResources.commandBuffer.begin(beginInfos);
}
void Core::renderTriangle(const PassHandle renderpassHandle, const PipelineHandle pipelineHandle,
......@@ -167,22 +161,31 @@ namespace vkcv
if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) {
return;
}
const vk::RenderPass renderpass = m_PassManager->getVkPass(renderpassHandle);
const std::array<float, 4> clearColor = { 0.f, 0.f, 0.f, 1.f };
const vk::ClearValue clearValues(clearColor);
const vk::ImageView imageView = m_swapchainImageViews[m_currentSwapchainImageIndex];
const vk::Pipeline pipeline = m_PipelineManager->getVkPipeline(pipelineHandle);
const vk::Rect2D renderArea(vk::Offset2D(0, 0), vk::Extent2D(width, height));
const vk::ImageView imageView = m_swapchainImageViews[m_currentSwapchainImageIndex];
const vk::Framebuffer framebuffer = createFramebuffer(m_Context.getDevice(), renderpass, width, height, imageView);
m_TemporaryFramebuffers.push_back(framebuffer);
const vk::RenderPassBeginInfo beginInfo(renderpass, framebuffer, renderArea, 1, &clearValues);
const vk::SubpassContents subpassContents = {};
m_CommandResources.commandBuffer.beginRenderPass(beginInfo, subpassContents, {});
const vk::Pipeline pipeline = m_PipelineManager->getVkPipeline(pipelineHandle);
m_CommandResources.commandBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline, {});
m_CommandResources.commandBuffer.draw(3, 1, 0, 0, {});
m_CommandResources.commandBuffer.endRenderPass();
SubmitInfo submitInfo;
submitInfo.queueType = QueueType::Graphics;
submitInfo.signalSemaphores = { m_SyncResources.renderFinished };
submitCommands(submitInfo, [renderpass, renderArea, imageView, framebuffer, pipeline](const vk::CommandBuffer& cmdBuffer) {
const std::array<float, 4> clearColor = { 0.f, 0.f, 0.f, 1.f };
const vk::ClearValue clearValues(clearColor);
const vk::RenderPassBeginInfo beginInfo(renderpass, framebuffer, renderArea, 1, &clearValues);
const vk::SubpassContents subpassContents = {};
cmdBuffer.beginRenderPass(beginInfo, subpassContents, {});
cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline, {});
cmdBuffer.draw(3, 1, 0, 0, {});
cmdBuffer.endRenderPass();
}, nullptr);
}
void Core::endFrame() {
......@@ -192,18 +195,19 @@ namespace vkcv
const auto swapchainImages = m_Context.getDevice().getSwapchainImagesKHR(m_swapchain.getSwapchain());
const vk::Image presentImage = swapchainImages[m_currentSwapchainImageIndex];
m_CommandResources.commandBuffer.end();
const auto& queueManager = m_Context.getQueueManager();
const vk::SubmitInfo submitInfo(0, nullptr, 0, 1, &(m_CommandResources.commandBuffer), 1, &m_SyncResources.renderFinished);
queueManager.getGraphicsQueues()[0].handle.submit(submitInfo);
std::array<vk::Semaphore, 2> waitSemaphores{
m_SyncResources.renderFinished,
m_SyncResources.swapchainImageAcquired };
vk::Result presentResult;
const vk::SwapchainKHR& swapchain = m_swapchain.getSwapchain();
const vk::PresentInfoKHR presentInfo(1, &m_SyncResources.renderFinished, 1, &swapchain,
&m_currentSwapchainImageIndex, &presentResult);
const vk::PresentInfoKHR presentInfo(
waitSemaphores,
swapchain,
m_currentSwapchainImageIndex,
presentResult);
queueManager.getPresentQueue().handle.presentKHR(presentInfo);
if (presentResult != vk::Result::eSuccess) {
std::cout << "Error: swapchain present failed" << std::endl;
......@@ -213,4 +217,33 @@ namespace vkcv
vk::Format Core::getSwapchainImageFormat() {
return m_swapchain.getSurfaceFormat().format;
}
void Core::recreateSwapchain(int width, int height) {
/* boilerplate for #34 */
std::cout << "Resized to : " << width << " , " << height << std::endl;
}
void Core::submitCommands(const SubmitInfo &submitInfo, const RecordCommandFunction& record, const FinishCommandFunction& finish)
{
const vk::Device& device = m_Context.getDevice();
const vkcv::Queue queue = getQueueForSubmit(submitInfo.queueType, m_Context.getQueueManager());
const vk::CommandPool cmdPool = chooseCmdPool(queue, m_CommandResources);
const vk::CommandBuffer cmdBuffer = allocateCommandBuffer(device, cmdPool);
beginCommandBuffer(cmdBuffer, vk::CommandBufferUsageFlagBits::eOneTimeSubmit);
record(cmdBuffer);
cmdBuffer.end();
const vk::Fence waitFence = createFence(device);
submitCommandBufferToQueue(queue.handle, cmdBuffer, waitFence, submitInfo.waitSemaphores, submitInfo.signalSemaphores);
waitForFence(device, waitFence);
device.destroyFence(waitFence);
device.freeCommandBuffers(cmdPool, cmdBuffer);
if (finish) {
finish();
}
}
}
#include "vkcv/SyncResources.hpp"
namespace vkcv {
SyncResources createDefaultSyncResources(const vk::Device& device) {
SyncResources createSyncResources(const vk::Device& device) {
SyncResources resources;
const vk::SemaphoreCreateFlags semaphoreFlags = vk::SemaphoreCreateFlagBits();
const vk::SemaphoreCreateInfo semaphoreInfo(semaphoreFlags);
resources.renderFinished = device.createSemaphore(semaphoreInfo, nullptr, {});
const vk::FenceCreateFlags fenceFlags = vk::FenceCreateFlagBits();
vk::FenceCreateInfo fenceInfo(fenceFlags);
resources.presentFinished = device.createFence(fenceInfo, nullptr, {});
resources.swapchainImageAcquired = device.createFence(fenceInfo, nullptr, {});
resources.renderFinished = device.createSemaphore(semaphoreInfo, nullptr, {});
resources.swapchainImageAcquired = device.createSemaphore(semaphoreInfo);
resources.presentFinished = createFence(device);
return resources;
}
void destroySyncResources(const vk::Device& device, const SyncResources& resources) {
device.destroySemaphore(resources.renderFinished);
device.destroySemaphore(resources.swapchainImageAcquired);
device.destroyFence(resources.presentFinished);
device.destroyFence(resources.swapchainImageAcquired);
}
vk::Fence createFence(const vk::Device& device) {
const vk::FenceCreateFlags fenceFlags = vk::FenceCreateFlagBits();
vk::FenceCreateInfo fenceInfo(fenceFlags);
return device.createFence(fenceInfo, nullptr, {});
}
void waitForFence(const vk::Device& device, const vk::Fence fence) {
const auto result = device.waitForFences(fence, true, UINT64_MAX);
assert(result == vk::Result::eSuccess);
}
}
\ No newline at end of file
......@@ -42,14 +42,52 @@ namespace vkcv {
return Window(window);
}
bool Window::isWindowOpen() const {
return !glfwWindowShouldClose(m_window);
void Window::initEvents() {
glfwSetWindowUserPointer(m_window, this);
// combine Callbacks with Events
glfwSetCursorPosCallback(m_window, Window::onMouseMoveEvent);
glfwSetWindowSizeCallback(m_window, Window::onResize);
glfwSetKeyCallback(m_window, Window::onKeyEvent);
}
void Window::pollEvents() {
glfwPollEvents();
}
void Window::onMouseMoveEvent(GLFWwindow *callbackWindow, double x, double y) {
auto window = static_cast<Window *>(glfwGetWindowUserPointer(callbackWindow));
if (window != nullptr) {
window->e_mouseMove(x, y);
}
}
void Window::onResize(GLFWwindow *callbackWindow, int width, int height) {
auto window = static_cast<Window *>(glfwGetWindowUserPointer(callbackWindow));
if (window != nullptr) {
window->e_resize(width, height);
}
}
void Window::onKeyEvent(GLFWwindow *callbackWindow, int key, int scancode, int action, int mods) {
auto window = static_cast<Window *>(glfwGetWindowUserPointer(callbackWindow));
if (window != nullptr) {
window->e_key(key, scancode, action, mods);
}
}
bool Window::isWindowOpen() const {
return !glfwWindowShouldClose(m_window);
}
int Window::getWidth() const {
int width;
glfwGetWindowSize(m_window, &width, nullptr);
......