Compare commits

...

2 Commits

Author SHA1 Message Date
burkkyy 0cb0628101 Big code changes 2026-08-23 20:46:09 -07:00
burkkyy 7243e13df5 vscode settings change 2026-08-23 20:44:36 -07:00
27 changed files with 1716 additions and 227 deletions
+12
View File
@@ -0,0 +1,12 @@
{
"configurations": [
{
"name": "Linux",
"includePath": ["${workspaceFolder}/**", "${env:VULKAN_SDK}/include"],
"defines": [],
"compileCommands": "${workspaceFolder}/build/compile_commands.json",
"intelliSenseMode": "linux-clang-x64"
}
],
"version": 4
}
+59
View File
@@ -0,0 +1,59 @@
{
"C_Cpp.formatting": "clangFormat",
"C_Cpp.clang_format_style": "file",
"C_Cpp.clang_format_fallbackStyle": "Google",
"C_Cpp.clang_format_path": "/usr/bin/clang-format",
"C_Cpp.vcFormat.indent.namespaceContents": false,
"[cpp]": {
"editor.defaultFormatter": "ms-vscode.cpptools",
"editor.formatOnSave": true,
"editor.formatOnType": true,
"editor.rulers": [80],
"editor.tabSize": 2,
"editor.insertSpaces": true
},
"[c]": {
"editor.defaultFormatter": "ms-vscode.cpptools",
"editor.formatOnSave": true,
"editor.rulers": [80],
"editor.tabSize": 2,
"editor.insertSpaces": true
},
"C_Cpp.codeAnalysis.clangTidy.enabled": true,
"C_Cpp.codeAnalysis.clangTidy.path": "/usr/bin/clang-tidy",
"C_Cpp.codeAnalysis.clangTidy.useBuildPath": true,
"C_Cpp.codeAnalysis.runAutomatically": true,
"C_Cpp.codeAnalysis.exclude": {
"**/third_party/**": true,
"**/build/**": true
},
"C_Cpp.default.compileCommands": "${workspaceFolder}/build/compile_commands.json",
"C_Cpp.default.cppStandard": "c++20",
"C_Cpp.default.cStandard": "c17",
"C_Cpp.errorSquiggles": "enabled",
"cmake.configureEnvironment": {
"VULKAN_SDK": "/home/caleb/.local/share/vulkan-sdk/1.4.357.1/x86_64",
"CMAKE_PREFIX_PATH": "/home/caleb/.local/share/vulkan-sdk/1.4.357.1/x86_64:/home/caleb/.local/share/vulkan-sdk/1.4.357.1/x86_64/lib/VulkanLoader"
},
"cmake.environment": {
"VULKAN_SDK": "/home/caleb/.local/share/vulkan-sdk/1.4.357.1/x86_64",
"LD_LIBRARY_PATH": "/home/caleb/.local/share/vulkan-sdk/1.4.357.1/x86_64/lib/VulkanLoader/lib:/home/caleb/.local/share/vulkan-sdk/1.4.357.1/x86_64/lib",
"VK_ADD_LAYER_PATH": "/home/caleb/.local/share/vulkan-sdk/1.4.357.1/x86_64/share/vulkan/explicit_layer.d"
},
"cmake.buildDirectory": "${workspaceFolder}/build",
"cmake.generator": "Ninja",
"cmake.configureOnOpen": true,
"files.associations": {
".clang-format": "yaml",
".clang-tidy": "yaml",
"*.hpp": "cpp",
"*.inl": "cpp"
},
"files.insertFinalNewline": true,
"files.trimTrailingWhitespace": true,
"search.exclude": {
"**/build": true,
"**/third_party": true
}
}
+30
View File
@@ -0,0 +1,30 @@
#pragma once
#include <memory>
#include <string>
namespace sophia {
class Application {
public:
struct Config {
int width;
int height;
std::string name;
bool headless = false;
};
Application(const Application&) = delete;
Application& operator=(const Application&) = delete;
Application(const Config& config);
~Application();
void run();
private:
class Impl;
std::unique_ptr<Impl> impl;
};
} // namespace sophia
+137
View File
@@ -0,0 +1,137 @@
#pragma once
#include <any>
#include <functional>
#include <string>
#include <unordered_map>
#include <sophia/types.hpp>
#include <sophia/util/delegate.hpp>
#include <sophia/util/logger.hpp>
#define BIT(x) (1 << (x))
namespace sophia {
// https://github.com/TheCherno/Hazel/blob/1feb70572fa87fa1c4ba784a2cfeada5b4a500db/Hazel/src/Hazel/Events/Event.h
enum EventCategoryFlags : u32 {
None = 0,
EventCategoryApplication = BIT(0),
EventCategoryInput = BIT(1),
EventCategoryKeyboard = BIT(2),
EventCategoryMouse = BIT(3),
EventCategoryMouseButton = BIT(4)
};
class Event {
public:
virtual ~Event() = default;
virtual const char* getName() const = 0;
virtual std::string toString() const { return getName(); }
virtual int getCategoryFlags() const { return EventCategoryFlags::None; };
inline bool isInCategory(EventCategoryFlags category) {
return getCategoryFlags() & category;
}
};
template <typename DerivedEvent>
class EventDelegate : public Delegate<DerivedEvent&> {
public:
EventDelegate() {
static_assert(std::is_base_of<Event, DerivedEvent>::value,
"DerivedEvent must derive from Event");
}
};
inline std::ostream& operator<<(std::ostream& os, const Event& e) {
return os << e.toString();
}
/**
* Singleton class for managing event subscription and dispatching.
*
* TODO heavy documentation is needed
*
* @todo impl removing functors from delegate
* @warning this class is in heavy development, use at your own risk
* @warning NOT THREAD SAFE
*/
class EventSystem {
public:
using EventType = decltype(std::declval<std::type_info>().hash_code());
#define GET_EVENT_TYPE(E) typeid(E).hash_code()
EventSystem(const EventSystem&) = delete;
EventSystem& operator=(const EventSystem&) = delete;
static EventSystem& get() {
static EventSystem instance;
return instance;
}
template <class E>
void addListener(std::function<void(E&)> f) {
static_assert(std::is_base_of<Event, E>::value, "E must derive from Event");
EventType eventType = GET_EVENT_TYPE(E);
// Ensures pointer to EventDelegate<E> is not null in unordered map
if (!this->eventDelegates.count(eventType)) {
this->eventDelegates[eventType] =
std::make_unique<std::any>(EventDelegate<E>());
log::trace("EventSystem creating new event type: ", eventType);
}
EventDelegate<E>& delegate =
std::any_cast<EventDelegate<E>&>(*eventDelegates[eventType]);
delegate.add(f);
}
template <class E, class O>
void addMethodListener(void (O::*memberFn)(E&), std::shared_ptr<O> obj) {
static_assert(std::is_base_of<Event, E>::value, "E must derive from Event");
EventType eventType = GET_EVENT_TYPE(E);
// Ensures pointer to EventDelegate<E> is not null in unordered map
if (!this->eventDelegates.count(eventType)) {
this->eventDelegates[eventType] =
std::make_unique<std::any>(EventDelegate<E>());
log::trace("EventSystem creating new event type: ", eventType);
}
EventDelegate<E>& delegate =
std::any_cast<EventDelegate<E>&>(*eventDelegates[eventType]);
delegate.addMethod(memberFn, obj);
}
template <class E>
void dispatch(E& event) {
static_assert(std::is_base_of<Event, E>::value, "E must derive from Event");
EventType eventType = GET_EVENT_TYPE(E);
if (!this->eventDelegates.count(eventType)) { return; }
try {
EventDelegate<E>& delegate =
std::any_cast<EventDelegate<E>&>(*this->eventDelegates.at(eventType));
delegate.invoke(event);
} catch (const std::out_of_range& error) {
log::error("event dispatch failed: ", event.toString());
}
}
private:
EventSystem() { log::info("EventSystem Initialized: ", this); }
~EventSystem() { log::info("EventSystem Destroyed"); }
std::unordered_map<EventType, std::unique_ptr<std::any>> eventDelegates;
};
} // namespace sophia
+18
View File
@@ -0,0 +1,18 @@
#pragma once
#include <sophia/types.hpp>
#define GLM_FORCE_RADIANS
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#include <glm/glm.hpp>
namespace sophia {
struct FrameInfo {
u32 frameIndex;
double elapsedTime;
double deltaTime;
glm::vec2 currentFramebufferExtent;
};
} // namespace sophia
+141
View File
@@ -0,0 +1,141 @@
#pragma once
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#include <sophia/types.hpp>
namespace sophia {
using KeyCode = u32;
namespace Key {
enum : KeyCode {
Space = GLFW_KEY_SPACE,
Apostrophe = GLFW_KEY_APOSTROPHE, /* ' */
Comma = GLFW_KEY_COMMA, /* , */
Minus = GLFW_KEY_MINUS, /* - */
Period = GLFW_KEY_PERIOD, /* . */
Slash = GLFW_KEY_SLASH, /* / */
D0 = GLFW_KEY_0,
D1 = GLFW_KEY_1,
D2 = GLFW_KEY_2,
D3 = GLFW_KEY_3,
D4 = GLFW_KEY_4,
D5 = GLFW_KEY_5,
D6 = GLFW_KEY_6,
D7 = GLFW_KEY_7,
D8 = GLFW_KEY_8,
D9 = GLFW_KEY_9,
Semicolon = GLFW_KEY_SEMICOLON, /* ; */
Equal = GLFW_KEY_EQUAL, /* = */
A = GLFW_KEY_A,
B = GLFW_KEY_B,
C = GLFW_KEY_C,
D = GLFW_KEY_D,
E = GLFW_KEY_E,
F = GLFW_KEY_F,
G = GLFW_KEY_G,
H = GLFW_KEY_H,
I = GLFW_KEY_I,
J = GLFW_KEY_J,
K = GLFW_KEY_K,
L = GLFW_KEY_L,
M = GLFW_KEY_M,
N = GLFW_KEY_N,
O = GLFW_KEY_O,
P = GLFW_KEY_P,
Q = GLFW_KEY_Q,
R = GLFW_KEY_R,
S = GLFW_KEY_S,
T = GLFW_KEY_T,
U = GLFW_KEY_U,
V = GLFW_KEY_V,
W = GLFW_KEY_W,
X = GLFW_KEY_X,
Y = GLFW_KEY_Y,
Z = GLFW_KEY_Z,
LeftBracket = GLFW_KEY_LEFT_BRACKET, /* [ */
Backslash = GLFW_KEY_BACKSLASH, /* \ */
RightBracket = GLFW_KEY_RIGHT_BRACKET, /* ] */
GraveAccent = GLFW_KEY_GRAVE_ACCENT, /* ` */
World1 = GLFW_KEY_WORLD_1, /* non-US #1 */
World2 = GLFW_KEY_WORLD_2, /* non-US #2 */
/* Function keys */
Escape = GLFW_KEY_ESCAPE,
Enter = GLFW_KEY_ENTER,
Tab = GLFW_KEY_TAB,
Backspace = GLFW_KEY_BACKSPACE,
Insert = GLFW_KEY_INSERT,
Delete = GLFW_KEY_DELETE,
Right = GLFW_KEY_RIGHT,
Left = GLFW_KEY_LEFT,
Down = GLFW_KEY_DOWN,
Up = GLFW_KEY_UP,
PageUp = GLFW_KEY_PAGE_UP,
PageDown = GLFW_KEY_PAGE_DOWN,
Home = GLFW_KEY_HOME,
End = GLFW_KEY_END,
CapsLock = GLFW_KEY_CAPS_LOCK,
ScrollLock = GLFW_KEY_SCROLL_LOCK,
NumLock = GLFW_KEY_NUM_LOCK,
PrintScreen = GLFW_KEY_PRINT_SCREEN,
Pause = GLFW_KEY_PAUSE,
F1 = GLFW_KEY_F1,
F2 = GLFW_KEY_F2,
F3 = GLFW_KEY_F3,
F4 = GLFW_KEY_F4,
F5 = GLFW_KEY_F5,
F6 = GLFW_KEY_F6,
F7 = GLFW_KEY_F7,
F8 = GLFW_KEY_F8,
F9 = GLFW_KEY_F9,
F10 = GLFW_KEY_F10,
F11 = GLFW_KEY_F11,
F12 = GLFW_KEY_F12,
F13 = GLFW_KEY_F13,
F14 = GLFW_KEY_F14,
F15 = GLFW_KEY_F15,
F16 = GLFW_KEY_F16,
F17 = GLFW_KEY_F17,
F18 = GLFW_KEY_F18,
F19 = GLFW_KEY_F19,
F20 = GLFW_KEY_F20,
F21 = GLFW_KEY_F21,
F22 = GLFW_KEY_F22,
F23 = GLFW_KEY_F23,
F24 = GLFW_KEY_F24,
F25 = GLFW_KEY_F25,
KP0 = GLFW_KEY_KP_0,
KP1 = GLFW_KEY_KP_1,
KP2 = GLFW_KEY_KP_2,
KP3 = GLFW_KEY_KP_3,
KP4 = GLFW_KEY_KP_4,
KP5 = GLFW_KEY_KP_5,
KP6 = GLFW_KEY_KP_6,
KP7 = GLFW_KEY_KP_7,
KP8 = GLFW_KEY_KP_8,
KP9 = GLFW_KEY_KP_9,
KPDecimal = GLFW_KEY_KP_DECIMAL,
KPDivide = GLFW_KEY_KP_DIVIDE,
KPMultiply = GLFW_KEY_KP_MULTIPLY,
KPSubtract = GLFW_KEY_KP_SUBTRACT,
KPAdd = GLFW_KEY_KP_ADD,
KPEnter = GLFW_KEY_KP_ENTER,
KPEqual = GLFW_KEY_KP_EQUAL,
LeftShift = GLFW_KEY_LEFT_SHIFT,
LeftControl = GLFW_KEY_LEFT_CONTROL,
LeftAlt = GLFW_KEY_LEFT_ALT,
LeftSuper = GLFW_KEY_LEFT_SUPER,
RightShift = GLFW_KEY_RIGHT_SHIFT,
RightControl = GLFW_KEY_RIGHT_CONTROL,
RightAlt = GLFW_KEY_RIGHT_ALT,
RightSuper = GLFW_KEY_RIGHT_SUPER,
Menu = GLFW_KEY_MENU,
};
} // namespace Key
} // namespace sophia
+70
View File
@@ -0,0 +1,70 @@
#pragma once
#include <sstream>
#include <sophia/event.hpp>
#include <sophia/key_codes.hpp>
namespace sophia {
class KeyEvent : public Event {
public:
KeyCode getKeyCode() const { return this->keycode; }
virtual int getCategoryFlags() const override {
return EventCategoryKeyboard | EventCategoryInput;
}
protected:
KeyEvent(const KeyCode keycode) : keycode{keycode} {}
KeyCode keycode;
};
class KeyPressedEvent : public KeyEvent {
public:
KeyPressedEvent(const KeyCode keycode, bool repeated = false)
: KeyEvent{keycode}, repeated{repeated} {}
bool isRepeat() const { return this->repeated; }
std::string toString() const override {
std::stringstream ss;
ss << "KeyPressedEvent: " << this->keycode
<< " (repeat = " << this->repeated << ")";
return ss.str();
}
virtual const char* getName() const override { return "KeyPressed"; }
private:
bool repeated;
};
class KeyReleasedEvent : public KeyEvent {
public:
KeyReleasedEvent(const KeyCode keycode) : KeyEvent{keycode} {}
std::string toString() const override {
std::stringstream ss;
ss << "KeyReleasedEvent: " << this->keycode;
return ss.str();
}
virtual const char* getName() const override { return "KeyReleased"; }
};
class KeyTypedEvent : public KeyEvent {
public:
KeyTypedEvent(const KeyCode keycode) : KeyEvent{keycode} {}
std::string toString() const override {
std::stringstream ss;
ss << "KeyTypedEvent: " << this->keycode;
return ss.str();
}
virtual const char* getName() const override { return "KeyTyped"; }
};
} // namespace sophia
+20
View File
@@ -0,0 +1,20 @@
#pragma once
#include <stdint.h>
namespace sophia {
using u8 = uint8_t;
using u16 = uint16_t;
using u32 = uint32_t;
using u64 = uint64_t;
using s8 = int8_t;
using s16 = int16_t;
using s32 = int32_t;
using s64 = int64_t;
using f32 = float;
using f64 = double;
} // namespace sophia
@@ -7,11 +7,11 @@
#include <typeinfo> #include <typeinfo>
#include <vector> #include <vector>
#include "util/logger.hpp" #include <sophia/util/logger.hpp>
#define DEFAULT_DELEGATE_FUNCTOR_ALLOC 5 #define DEFAULT_DELEGATE_FUNCTOR_ALLOC 5
namespace hep { namespace sophia {
class expired_weak_object : public std::runtime_error { class expired_weak_object : public std::runtime_error {
public: public:
@@ -108,4 +108,4 @@ class Delegate {
std::vector<Functor> functors; std::vector<Functor> functors;
}; };
} // namespace hep } // namespace sophia
+126
View File
@@ -0,0 +1,126 @@
/**
* Lots of work still needs to be done in here.
*
* Usage:
* using namespace sophia;
* log::fatal("fatal message")
* log::info("Infomation message")
*
* TODO Impl some form of log error handling
*/
#pragma once
#include <iostream>
#include <utility>
#include <sophia/types.hpp>
namespace sophia::log {
enum class LEVEL {
NONE,
FATAL,
ERROR,
WARNING,
INFO,
VERBOSE,
DEBUGGING,
TRACE,
};
// Helper print functions
#define LOG_PRINT_HELPER(MSG) std::cout << MSG
#define LOG_PREFIX_FATAL LOG_PRINT_HELPER("\033[1;91m[FATAL]\033[0m ")
#define LOG_PREFIX_ERROR LOG_PRINT_HELPER("\033[1;31m[ERROR]\033[0m ")
#define LOG_PREFIX_WARNING LOG_PRINT_HELPER("\033[1;33m[WARNING]\033[0m ")
#define LOG_PREFIX_INFO LOG_PRINT_HELPER("\033[32m[INFO]\033[0m ")
#define LOG_PREFIX_VERBOSE LOG_PRINT_HELPER("\033[36m[VERBOSE]\033[0m ")
#define LOG_PREFIX_DEBUG LOG_PRINT_HELPER("\033[1;34m[DEBUG]\033[0m ")
#define LOG_PREFIX_TRACE LOG_PRINT_HELPER("\033[1;90m[TRACE]\033[0m ")
template <typename... Args>
void log(LEVEL level = LEVEL::NONE, Args... args) {
#ifndef NDEBUG
switch (level) {
case LEVEL::FATAL:
LOG_PREFIX_FATAL;
break;
case LEVEL::ERROR:
LOG_PREFIX_ERROR;
break;
case LEVEL::WARNING:
LOG_PREFIX_WARNING;
break;
case LEVEL::INFO:
LOG_PREFIX_INFO;
break;
case LEVEL::VERBOSE:
LOG_PREFIX_VERBOSE;
break;
// case LEVEL::DEBUGGING:
// LOG_PREFIX_DEBUG;
// break;
case LEVEL::TRACE:
LOG_PREFIX_TRACE;
break;
default:
std::cout << "\033[1;90m[LOG]\033[0m ";
break;
}
((std::cout << ' ' << std::forward<Args>(args)), ...);
std::cout << std::endl;
#else
(void)level;
(void)std::initializer_list<int>{((void)args, 0)...};
#endif // !NDEBUG
}
template <typename... Args>
void fatal(Args... args) {
log(LEVEL::FATAL, args...);
}
template <typename... Args>
void error(Args... args) {
log(LEVEL::ERROR, args...);
}
template <typename... Args>
void warning(Args... args) {
log(LEVEL::WARNING, args...);
}
template <typename... Args>
void info(Args... args) {
log(LEVEL::INFO, args...);
}
template <typename... Args>
void verbose(Args... args) {
log(LEVEL::VERBOSE, args...);
}
template <typename... Args>
void debug(const char* file, u16 line, Args... args) {
#ifndef NDEBUG
LOG_PREFIX_DEBUG;
std::cout << file << "::" << line;
((std::cout << ' ' << std::forward<Args>(args)), ...);
std::cout << std::endl;
#else
(void)file;
(void)line;
(void)std::initializer_list<int>{((void)args, 0)...};
#endif // !NDEBUG
}
// trick
#define debug(...) debug(__FILE__, __LINE__, __VA_ARGS__)
template <typename... Args>
void trace(Args... args) {
log(LEVEL::TRACE, args...);
}
} // namespace sophia::log
-21
View File
@@ -1,21 +0,0 @@
#pragma once
#include <stdint.h>
namespace hep
{
using u8 = uint8_t;
using u16 = uint16_t;
using u32 = uint32_t;
using u64 = uint64_t;
using s8 = int8_t;
using s16 = int16_t;
using s32 = int32_t;
using s64 = int64_t;
using f32 = float;
using f64 = double;
} // namespace hep
-140
View File
@@ -1,140 +0,0 @@
/**
* Lots of work still needs to be done in here.
*
* Usage:
* using namespace hep;
* log::fatal("fatal message")
* log::info("Infomation message")
*
* TODO Impl some form of log error handling
*/
#pragma once
#include <iostream>
#include <utility>
#include "types.hpp"
namespace hep
{
namespace log
{
enum class LEVEL
{
NONE,
FATAL,
ERROR,
WARNING,
INFO,
VERBOSE,
DEBUGGING,
TRACE,
};
// Helper print functions
#define LOG_PRINT_HELPER(MSG) std::cout << MSG
#define LOG_PREFIX_FATAL LOG_PRINT_HELPER("\033[1;91m[FATAL]\033[0m ")
#define LOG_PREFIX_ERROR LOG_PRINT_HELPER("\033[1;31m[ERROR]\033[0m ")
#define LOG_PREFIX_WARNING LOG_PRINT_HELPER("\033[1;33m[WARNING]\033[0m ")
#define LOG_PREFIX_INFO LOG_PRINT_HELPER("\033[32m[INFO]\033[0m ")
#define LOG_PREFIX_VERBOSE LOG_PRINT_HELPER("\033[36m[VERBOSE]\033[0m ")
#define LOG_PREFIX_DEBUG LOG_PRINT_HELPER("\033[1;34m[DEBUG]\033[0m ")
#define LOG_PREFIX_TRACE LOG_PRINT_HELPER("\033[1;90m[TRACE]\033[0m ")
template <typename... Args>
void log(LEVEL level = LEVEL::NONE, Args... args)
{
#ifndef NDEBUG
switch (level)
{
case LEVEL::FATAL:
LOG_PREFIX_FATAL;
break;
case LEVEL::ERROR:
LOG_PREFIX_ERROR;
break;
case LEVEL::WARNING:
LOG_PREFIX_WARNING;
break;
case LEVEL::INFO:
LOG_PREFIX_INFO;
break;
case LEVEL::VERBOSE:
LOG_PREFIX_VERBOSE;
break;
// case LEVEL::DEBUGGING:
// LOG_PREFIX_DEBUG;
// break;
case LEVEL::TRACE:
LOG_PREFIX_TRACE;
break;
default:
std::cout << "\033[1;90m[LOG]\033[0m ";
break;
}
((std::cout << ' ' << std::forward<Args>(args)), ...);
std::cout << std::endl;
#else
(void)level;
(void)std::initializer_list<int>{((void)args, 0)...};
#endif // !NDEBUG
}
template <typename... Args>
void fatal(Args... args)
{
log(LEVEL::FATAL, args...);
}
template <typename... Args>
void error(Args... args)
{
log(LEVEL::ERROR, args...);
}
template <typename... Args>
void warning(Args... args)
{
log(LEVEL::WARNING, args...);
}
template <typename... Args>
void info(Args... args)
{
log(LEVEL::INFO, args...);
}
template <typename... Args>
void verbose(Args... args)
{
log(LEVEL::VERBOSE, args...);
}
template <typename... Args>
void debug(const char *file, u16 line, Args... args)
{
#ifndef NDEBUG
LOG_PREFIX_DEBUG;
std::cout << file << "::" << line;
((std::cout << ' ' << std::forward<Args>(args)), ...);
std::cout << std::endl;
#else
(void)file;
(void)line;
(void)std::initializer_list<int>{((void)args, 0)...};
#endif // !NDEBUG
}
// trick
#define debug(...) debug(__FILE__, __LINE__, __VA_ARGS__)
template <typename... Args>
void trace(Args... args)
{
log(LEVEL::TRACE, args...);
}
} // namespace log
} // namespace hep
+25
View File
@@ -0,0 +1,25 @@
#include <sophia/application.hpp>
#include "engine.hpp"
namespace sophia {
class Application::Impl {
public:
Impl(const Application::Config& config)
: engine{{config.width, config.height, config.name}} {}
void run() { engine.run(); }
private:
Engine engine;
};
Application::Application(const Config& config)
: impl{std::make_unique<Impl>(config)} {}
Application::~Application() = default;
void Application::run() { impl->run(); }
} // namespace sophia
+107
View File
@@ -0,0 +1,107 @@
/**
* Based on
* https://github.com/blurrypiano/littleVulkanEngine/blob/main/src/lve_buffer.cpp
*/
#include "buffer.hpp"
namespace sophia {
VkDeviceSize Buffer::getAlignment(VkDeviceSize instanceSize,
VkDeviceSize minOffsetAlignment) {
if (minOffsetAlignment > 0) {
return (instanceSize + minOffsetAlignment - 1) & ~(minOffsetAlignment - 1);
}
return instanceSize;
}
Buffer::Buffer(Device& device, vk::DeviceSize instanceSize, u32 instanceCount,
vk::BufferUsageFlags usageFlags,
vk::MemoryPropertyFlags memoryPropertyFlags,
vk::DeviceSize minOffsetAlignment)
: device{device},
instanceSize{instanceSize},
instanceCount{instanceCount},
usageFlags{usageFlags},
memoryPropertyFlags{memoryPropertyFlags} {
alignmentSize = getAlignment(instanceSize, minOffsetAlignment);
bufferSize = alignmentSize * instanceCount;
device.createBuffer(bufferSize, usageFlags, memoryPropertyFlags, buffer,
memory);
}
Buffer::~Buffer() {
unmap();
this->device.get()->destroyBuffer(this->buffer);
this->device.get()->freeMemory(this->memory);
}
vk::Result Buffer::map(vk::DeviceSize size, vk::DeviceSize offset) {
assert(buffer && memory && "Called map on buffer before create");
vk::MemoryMapFlags emptyFlag{};
return this->device.get()->mapMemory(this->memory, offset, size, emptyFlag,
&this->mapped);
}
void Buffer::unmap() {
if (mapped) {
this->device.get()->unmapMemory(this->memory);
mapped = nullptr;
}
}
void Buffer::writeToBuffer(void* data, vk::DeviceSize size,
vk::DeviceSize offset) {
assert(mapped && "Cannot copy to unmapped buffer");
if (size == VK_WHOLE_SIZE) {
memcpy(mapped, data, bufferSize);
} else {
char* memOffset = (char*)mapped;
memOffset += offset;
memcpy(memOffset, data, size);
}
}
vk::Result Buffer::flush(vk::DeviceSize size, vk::DeviceSize offset) {
vk::MappedMemoryRange mappedRange = {};
mappedRange.memory = memory;
mappedRange.offset = offset;
mappedRange.size = size;
return this->device.get()->flushMappedMemoryRanges(1, &mappedRange);
}
vk::Result Buffer::invalidate(vk::DeviceSize size, vk::DeviceSize offset) {
vk::MappedMemoryRange mappedRange = {};
mappedRange.memory = memory;
mappedRange.offset = offset;
mappedRange.size = size;
return this->device.get()->invalidateMappedMemoryRanges(1, &mappedRange);
}
vk::DescriptorBufferInfo Buffer::descriptorInfo(vk::DeviceSize size,
vk::DeviceSize offset) {
return vk::DescriptorBufferInfo{
this->buffer,
offset,
size,
};
}
void Buffer::writeToIndex(void* data, int index) {
writeToBuffer(data, instanceSize, index * alignmentSize);
}
vk::Result Buffer::flushIndex(int index) {
return flush(alignmentSize, index * alignmentSize);
}
vk::DescriptorBufferInfo Buffer::descriptorInfoForIndex(int index) {
return descriptorInfo(alignmentSize, index * alignmentSize);
}
vk::Result Buffer::invalidateIndex(int index) {
return invalidate(alignmentSize, index * alignmentSize);
}
} // namespace sophia
+68
View File
@@ -0,0 +1,68 @@
/**
* Based on
* https://github.com/blurrypiano/littleVulkanEngine/blob/main/src/lve_buffer.hpp
*/
#pragma once
#include "device.hpp"
namespace sophia {
class Buffer {
public:
Buffer(const Buffer&) = delete;
Buffer& operator=(const Buffer&) = delete;
Buffer(Device& device, vk::DeviceSize instanceSize, u32 instanceCount,
vk::BufferUsageFlags usageFlags,
vk::MemoryPropertyFlags memoryPropertyFlags,
vk::DeviceSize minOffsetAlignment = 1);
~Buffer();
vk::Result map(vk::DeviceSize size = VK_WHOLE_SIZE,
vk::DeviceSize offset = 0);
void unmap();
void writeToBuffer(void* data, vk::DeviceSize size = VK_WHOLE_SIZE,
vk::DeviceSize offset = 0);
vk::Result flush(vk::DeviceSize size = VK_WHOLE_SIZE,
vk::DeviceSize offset = 0);
vk::DescriptorBufferInfo descriptorInfo(vk::DeviceSize size = VK_WHOLE_SIZE,
vk::DeviceSize offset = 0);
vk::Result invalidate(vk::DeviceSize size = VK_WHOLE_SIZE,
vk::DeviceSize offset = 0);
void writeToIndex(void* data, int index);
vk::Result flushIndex(int index);
vk::DescriptorBufferInfo descriptorInfoForIndex(int index);
vk::Result invalidateIndex(int index);
vk::Buffer getBuffer() const { return buffer; }
void* getMappedMemory() const { return mapped; }
uint32_t getInstanceCount() const { return instanceCount; }
vk::DeviceSize getInstanceSize() const { return instanceSize; }
vk::DeviceSize getAlignmentSize() const { return instanceSize; }
vk::BufferUsageFlags getUsageFlags() const { return usageFlags; }
vk::MemoryPropertyFlags getMemoryPropertyFlags() const {
return memoryPropertyFlags;
}
vk::DeviceSize getBufferSize() const { return bufferSize; }
private:
static vk::DeviceSize getAlignment(vk::DeviceSize instanceSize,
vk::DeviceSize minOffsetAlignment);
Device& device;
void* mapped = nullptr;
vk::Buffer buffer = VK_NULL_HANDLE;
vk::DeviceMemory memory = VK_NULL_HANDLE;
vk::DeviceSize bufferSize;
uint32_t instanceCount;
vk::DeviceSize instanceSize;
vk::DeviceSize alignmentSize;
vk::BufferUsageFlags usageFlags;
vk::MemoryPropertyFlags memoryPropertyFlags;
};
} // namespace sophia
+72
View File
@@ -0,0 +1,72 @@
#include "compute_pipeline.hpp"
#include <sophia/util/logger.hpp>
#include <stdexcept>
namespace sophia {
ComputePipeline::ComputePipeline(Device& device) : device{device} {}
ComputePipeline::~ComputePipeline() {
log::trace("destoryed vk::Pipeline (compute)");
}
void ComputePipeline::create(Shader& shader,
vk::PipelineLayout pipelineLayout) {
if (shader.getStage() != ShaderStage::COMPUTE) {
log::fatal(
"failed to create compute pipeline: shader is not a compute shader");
throw std::runtime_error(
"failed to create compute pipeline: shader is not a compute shader");
}
if (pipelineLayout == VK_NULL_HANDLE) {
log::fatal(
"failed to create compute pipeline: no vk::PipelineLayout provided");
throw std::runtime_error(
"failed to create compute pipeline: no vk::PipelineLayout provided");
}
vk::UniqueShaderModule computeShaderModule =
createShaderModule(shader.getSpirv());
log::trace("created compute shader module");
vk::PipelineShaderStageCreateInfo shaderStage{
vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eCompute,
computeShaderModule.get(), "main"};
vk::ComputePipelineCreateInfo pipelineInfo{};
pipelineInfo.stage = shaderStage;
pipelineInfo.layout = pipelineLayout;
pipelineInfo.basePipelineHandle = nullptr;
try {
this->computePipeline =
this->device.get()
->createComputePipelineUnique(nullptr, pipelineInfo)
.value;
log::trace("created vk::Pipeline (compute)");
} catch (const vk::SystemError& err) {
log::fatal("failed to create vk::Pipeline (compute)");
throw std::runtime_error("failed to create vk::Pipeline (compute)");
}
}
void ComputePipeline::bind(vk::CommandBuffer commandBuffer) {
commandBuffer.bindPipeline(vk::PipelineBindPoint::eCompute,
this->computePipeline.get());
}
vk::UniqueShaderModule ComputePipeline::createShaderModule(
const std::vector<u32>& spirv) {
try {
return device.get()->createShaderModuleUnique(
{vk::ShaderModuleCreateFlags(), spirv.size() * sizeof(u32),
spirv.data()});
} catch (const vk::SystemError& err) {
log::fatal("failed to create shader module");
throw std::runtime_error("failed to create shader module");
}
}
} // namespace sophia
+29
View File
@@ -0,0 +1,29 @@
#pragma once
#include <vulkan/vulkan.hpp>
#include "device.hpp"
#include "shader.hpp"
namespace sophia {
class ComputePipeline {
public:
ComputePipeline(const ComputePipeline&) = delete;
ComputePipeline& operator=(const ComputePipeline&) = delete;
ComputePipeline(Device& device);
~ComputePipeline();
void create(Shader& shader, vk::PipelineLayout pipelineLayout);
void bind(vk::CommandBuffer commandBuffer);
private:
vk::UniqueShaderModule createShaderModule(const std::vector<u32>& spirv);
Device& device;
vk::UniquePipeline computePipeline;
};
} // namespace sophia
+45 -37
View File
@@ -3,24 +3,23 @@
#include <string.h> #include <string.h>
#include <set> #include <set>
#include <sophia/util/logger.hpp>
#include "util/logger.hpp" namespace sophia {
namespace hep { static VKAPI_ATTR vk::Bool32 VKAPI_CALL
debugCallback(vk::DebugUtilsMessageSeverityFlagBitsEXT m_severity,
static VKAPI_ATTR VkBool32 VKAPI_CALL vk::DebugUtilsMessageTypeFlagsEXT m_type,
debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT m_severity, const vk::DebugUtilsMessengerCallbackDataEXT* pCallback_data,
VkDebugUtilsMessageTypeFlagsEXT m_type,
const VkDebugUtilsMessengerCallbackDataEXT* pCallback_data,
void* pUser_data) { void* pUser_data) {
(void)m_type; (void)m_type;
(void)pCallback_data; (void)pCallback_data;
(void)pUser_data; (void)pUser_data;
if (m_severity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) { if (m_severity == vk::DebugUtilsMessageSeverityFlagBitsEXT::eError) {
log::error(pCallback_data->pMessage); log::error(pCallback_data->pMessage);
return VK_SUCCESS; return VK_SUCCESS;
} else if (m_severity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) { } else if (m_severity == vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning) {
log::warning(pCallback_data->pMessage); log::warning(pCallback_data->pMessage);
} else { } else {
log::info(pCallback_data->pMessage); log::info(pCallback_data->pMessage);
@@ -30,8 +29,7 @@ debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT m_severity,
} }
VkResult createDebugUtilsMessengerEXT( VkResult createDebugUtilsMessengerEXT(
VkInstance instance, VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo,
const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo,
const VkAllocationCallbacks* pAllocator, const VkAllocationCallbacks* pAllocator,
VkDebugUtilsMessengerEXT* pCallback) { VkDebugUtilsMessengerEXT* pCallback) {
auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr( auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(
@@ -48,7 +46,9 @@ void destroyDebugUtilsMessengerEXT(VkInstance instance,
const VkAllocationCallbacks* pAllocator) { const VkAllocationCallbacks* pAllocator) {
auto func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr( auto func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(
instance, "vkDestroyDebugUtilsMessengerEXT"); instance, "vkDestroyDebugUtilsMessengerEXT");
if (func != nullptr) { func(instance, callback, pAllocator); } if (func != nullptr) {
func(instance, callback, pAllocator);
}
} }
Device::Device(Window& window) : window{window} { Device::Device(Window& window) : window{window} {
@@ -91,8 +91,7 @@ u32 Device::findMemoryType(u32 typeFilter, vk::MemoryPropertyFlags properties) {
} }
vk::Format Device::findSupportedFormat( vk::Format Device::findSupportedFormat(
const std::vector<vk::Format>& candidates, const std::vector<vk::Format>& candidates, vk::ImageTiling tiling,
vk::ImageTiling tiling,
vk::FormatFeatureFlags features) { vk::FormatFeatureFlags features) {
for (vk::Format format : candidates) { for (vk::Format format : candidates) {
vk::FormatProperties properties = vk::FormatProperties properties =
@@ -110,11 +109,9 @@ vk::Format Device::findSupportedFormat(
throw std::runtime_error("failed to find supported format"); throw std::runtime_error("failed to find supported format");
} }
void Device::createBuffer(vk::DeviceSize size, void Device::createBuffer(vk::DeviceSize size, vk::BufferUsageFlags usage,
vk::BufferUsageFlags usage,
vk::MemoryPropertyFlags properties, vk::MemoryPropertyFlags properties,
vk::Buffer& buffer, vk::Buffer& buffer, vk::DeviceMemory& bufferMemory) {
vk::DeviceMemory& bufferMemory) {
vk::BufferCreateInfo bufferInfo = {}; vk::BufferCreateInfo bufferInfo = {};
bufferInfo.size = size; bufferInfo.size = size;
bufferInfo.usage = usage; bufferInfo.usage = usage;
@@ -156,7 +153,9 @@ vk::CommandBuffer Device::beginSingleTimeCommands() {
try { try {
vk::Result result = vk::Result result =
this->device->allocateCommandBuffers(&allocInfo, &commandBuffer); this->device->allocateCommandBuffers(&allocInfo, &commandBuffer);
if (result != vk::Result::eSuccess) { throw vk::SystemError(result); } if (result != vk::Result::eSuccess) {
throw vk::SystemError(result);
}
} catch (const vk::SystemError& error) { } catch (const vk::SystemError& error) {
log::error("failed to allocate single time commandBuffer. Error: ", log::error("failed to allocate single time commandBuffer. Error: ",
error.what()); error.what());
@@ -168,7 +167,9 @@ vk::CommandBuffer Device::beginSingleTimeCommands() {
try { try {
vk::Result result = commandBuffer.begin(&beginInfo); vk::Result result = commandBuffer.begin(&beginInfo);
if (result != vk::Result::eSuccess) { throw vk::SystemError(result); } if (result != vk::Result::eSuccess) {
throw vk::SystemError(result);
}
} catch (const vk::SystemError& error) { } catch (const vk::SystemError& error) {
log::error("failed to begin single time commandBuffer. Error: ", log::error("failed to begin single time commandBuffer. Error: ",
error.what()); error.what());
@@ -191,8 +192,7 @@ void Device::endSingleTimeCommands(vk::CommandBuffer commandBuffer) {
this->device->freeCommandBuffers(commandPool, commandBuffer); this->device->freeCommandBuffers(commandPool, commandBuffer);
} }
void Device::copyBuffer(vk::Buffer sourceBuffer, void Device::copyBuffer(vk::Buffer sourceBuffer, vk::Buffer destinationBuffer,
vk::Buffer destinationBuffer,
vk::DeviceSize size) { vk::DeviceSize size) {
vk::CommandBuffer commandBuffer = beginSingleTimeCommands(); vk::CommandBuffer commandBuffer = beginSingleTimeCommands();
@@ -210,7 +210,9 @@ void Device::createImageWithInfo(const vk::ImageCreateInfo& imageInfo,
vk::DeviceMemory& imageMemory) { vk::DeviceMemory& imageMemory) {
try { try {
vk::Result result = this->device->createImage(&imageInfo, nullptr, &image); vk::Result result = this->device->createImage(&imageInfo, nullptr, &image);
if (result != vk::Result::eSuccess) { throw vk::SystemError(result); } if (result != vk::Result::eSuccess) {
throw vk::SystemError(result);
}
} catch (const vk::SystemError& error) { } catch (const vk::SystemError& error) {
log::fatal("failed to create image. Error: ", error.what()); log::fatal("failed to create image. Error: ", error.what());
throw std::runtime_error("failed to create image"); throw std::runtime_error("failed to create image");
@@ -227,7 +229,9 @@ void Device::createImageWithInfo(const vk::ImageCreateInfo& imageInfo,
try { try {
vk::Result result = vk::Result result =
this->device->allocateMemory(&allocateInfo, nullptr, &imageMemory); this->device->allocateMemory(&allocateInfo, nullptr, &imageMemory);
if (result != vk::Result::eSuccess) { throw vk::SystemError(result); } if (result != vk::Result::eSuccess) {
throw vk::SystemError(result);
}
} catch (const vk::SystemError& error) { } catch (const vk::SystemError& error) {
log::fatal("failed to allocate image memory. Error: ", error.what()); log::fatal("failed to allocate image memory. Error: ", error.what());
throw std::runtime_error("failed to allocate image memory"); throw std::runtime_error("failed to allocate image memory");
@@ -243,7 +247,7 @@ void Device::createImageWithInfo(const vk::ImageCreateInfo& imageInfo,
void Device::populateImGuiInitInfo(ImGui_ImplVulkan_InitInfo& initInfo) { void Device::populateImGuiInitInfo(ImGui_ImplVulkan_InitInfo& initInfo) {
initInfo.Instance = this->instance.get(); initInfo.Instance = this->instance.get();
initInfo.ApiVersion = HEP_VULKAN_API_VERSION; initInfo.ApiVersion = SOPHIA_VULKAN_API_VERSION;
initInfo.PhysicalDevice = this->physicalDevice; initInfo.PhysicalDevice = this->physicalDevice;
initInfo.Device = this->device.get(); initInfo.Device = this->device.get();
@@ -286,7 +290,9 @@ bool Device::checkValidationLayerSupport() {
} }
} }
if (!layerFound) { return false; } if (!layerFound) {
return false;
}
} }
return true; return true;
@@ -309,7 +315,9 @@ std::vector<const char*> Device::getRequiredExtensions() {
vk::Result result = vk::enumerateInstanceExtensionProperties( vk::Result result = vk::enumerateInstanceExtensionProperties(
nullptr, &available_extension_count, nullptr); nullptr, &available_extension_count, nullptr);
if (result != vk::Result::eSuccess) { throw std::exception(); } if (result != vk::Result::eSuccess) {
throw std::exception();
}
std::vector<vk::ExtensionProperties> available_extensions( std::vector<vk::ExtensionProperties> available_extensions(
available_extension_count); available_extension_count);
@@ -317,7 +325,9 @@ std::vector<const char*> Device::getRequiredExtensions() {
result = vk::enumerateInstanceExtensionProperties( result = vk::enumerateInstanceExtensionProperties(
nullptr, &available_extension_count, available_extensions.data()); nullptr, &available_extension_count, available_extensions.data());
if (result != vk::Result::eSuccess) { throw std::exception(); } if (result != vk::Result::eSuccess) {
throw std::exception();
}
log::verbose("Number of available extensions: ", available_extension_count); log::verbose("Number of available extensions: ", available_extension_count);
@@ -326,7 +336,9 @@ std::vector<const char*> Device::getRequiredExtensions() {
std::cout << '\t' << e.extensionName << '\n'; std::cout << '\t' << e.extensionName << '\n';
} }
std::cout << "Required extensions:\n"; std::cout << "Required extensions:\n";
for (const auto& e : extensions) { std::cout << "\t" << e << '\n'; } for (const auto& e : extensions) {
std::cout << "\t" << e << '\n';
}
#endif #endif
return extensions; return extensions;
@@ -373,7 +385,9 @@ QueueFamilyIndices Device::findQueueFamilies(vk::PhysicalDevice device) {
indices.presentFamily = i; indices.presentFamily = i;
} }
if (indices.isComplete()) { break; } if (indices.isComplete()) {
break;
}
i++; i++;
} }
@@ -457,12 +471,6 @@ void Device::createLogicalDevice() {
static_cast<uint32_t>(this->enabledExtensions.size()); static_cast<uint32_t>(this->enabledExtensions.size());
createInfo.ppEnabledExtensionNames = this->enabledExtensions.data(); createInfo.ppEnabledExtensionNames = this->enabledExtensions.data();
if (this->enableValidationLayers) {
createInfo.enabledLayerCount =
static_cast<uint32_t>(this->enabledLayers.size());
createInfo.ppEnabledLayerNames = this->enabledLayers.data();
}
try { try {
this->device = this->physicalDevice.createDeviceUnique(createInfo); this->device = this->physicalDevice.createDeviceUnique(createInfo);
log::trace("created vk::Device."); log::trace("created vk::Device.");
@@ -502,4 +510,4 @@ void Device::createCommandPool() {
} }
} }
} // namespace hep } // namespace sophia
@@ -4,15 +4,15 @@
#include <cassert> #include <cassert>
#include <optional> #include <optional>
#include <sophia/types.hpp>
#include <vector> #include <vector>
#include <vulkan/vulkan.hpp> #include <vulkan/vulkan.hpp>
#include "types.hpp"
#include "window.hpp" #include "window.hpp"
#define HEP_VULKAN_API_VERSION VK_API_VERSION_1_3 #define SOPHIA_VULKAN_API_VERSION VK_API_VERSION_1_3
namespace hep { namespace sophia {
struct QueueFamilyIndices { struct QueueFamilyIndices {
std::optional<uint32_t> graphicsFamily; std::optional<uint32_t> graphicsFamily;
@@ -65,23 +65,19 @@ class Device {
vk::ImageTiling tiling, vk::ImageTiling tiling,
vk::FormatFeatureFlags features); vk::FormatFeatureFlags features);
void createBuffer(vk::DeviceSize size, void createBuffer(vk::DeviceSize size, vk::BufferUsageFlags usage,
vk::BufferUsageFlags usage, vk::MemoryPropertyFlags properties, vk::Buffer& buffer,
vk::MemoryPropertyFlags properties,
vk::Buffer& buffer,
vk::DeviceMemory& bufferMemory); vk::DeviceMemory& bufferMemory);
vk::CommandBuffer beginSingleTimeCommands(); vk::CommandBuffer beginSingleTimeCommands();
void endSingleTimeCommands(vk::CommandBuffer commandBuffer); void endSingleTimeCommands(vk::CommandBuffer commandBuffer);
void copyBuffer(vk::Buffer sourceBuffer, void copyBuffer(vk::Buffer sourceBuffer, vk::Buffer destinationBuffer,
vk::Buffer destinationBuffer,
vk::DeviceSize size); vk::DeviceSize size);
void createImageWithInfo(const vk::ImageCreateInfo& imageInfo, void createImageWithInfo(const vk::ImageCreateInfo& imageInfo,
vk::MemoryPropertyFlags properties, vk::MemoryPropertyFlags properties, vk::Image& image,
vk::Image& image,
vk::DeviceMemory& imageMemory); vk::DeviceMemory& imageMemory);
void populateImGuiInitInfo(ImGui_ImplVulkan_InitInfo& initInfo); void populateImGuiInitInfo(ImGui_ImplVulkan_InitInfo& initInfo);
@@ -131,4 +127,4 @@ class Device {
VK_KHR_SWAPCHAIN_EXTENSION_NAME}; VK_KHR_SWAPCHAIN_EXTENSION_NAME};
}; };
} // namespace hep } // namespace sophia
+76
View File
@@ -0,0 +1,76 @@
#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},
window{config.width, config.height, config.name},
device{this->window} // renderer{this->window, this->device}
{
// 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) {
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
+35
View File
@@ -0,0 +1,35 @@
#pragma once
#include <string>
#include "device.hpp"
#include "window.hpp"
namespace sophia {
class Engine {
public:
struct Config {
int width;
int height;
std::string name;
};
Engine(const Engine&) = delete;
Engine& operator=(const Engine&) = delete;
Engine(const Config& config);
~Engine();
void run();
private:
Config config;
Window window;
Device device;
bool isRunning = true;
};
} // namespace sophia
+324
View File
@@ -0,0 +1,324 @@
#include "image.hpp"
#include <cstring>
#include <sophia/util/logger.hpp>
#include <stdexcept>
#include "device.hpp"
namespace sophia {
void Image::transitionLayout(vk::CommandBuffer commandBuffer,
vk::ImageLayout newLayout) {
vk::ImageMemoryBarrier barrier{};
barrier.oldLayout = this->layout;
barrier.newLayout = newLayout;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = this->get();
barrier.subresourceRange.aspectMask = this->aspectMask;
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = this->mipLevels;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = this->arrayLayers;
vk::PipelineStageFlags srcStage;
vk::PipelineStageFlags dstStage;
if (this->layout == vk::ImageLayout::eUndefined &&
newLayout == vk::ImageLayout::eTransferDstOptimal) {
barrier.srcAccessMask = {};
barrier.dstAccessMask = vk::AccessFlagBits::eTransferWrite;
srcStage = vk::PipelineStageFlagBits::eTopOfPipe;
dstStage = vk::PipelineStageFlagBits::eTransfer;
} else if (this->layout == vk::ImageLayout::eTransferDstOptimal &&
newLayout == vk::ImageLayout::eShaderReadOnlyOptimal) {
barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
srcStage = vk::PipelineStageFlagBits::eTransfer;
dstStage = vk::PipelineStageFlagBits::eFragmentShader;
} else if (this->layout == vk::ImageLayout::eUndefined &&
newLayout == vk::ImageLayout::eDepthStencilAttachmentOptimal) {
barrier.srcAccessMask = {};
barrier.dstAccessMask = vk::AccessFlagBits::eDepthStencilAttachmentRead |
vk::AccessFlagBits::eDepthStencilAttachmentWrite;
srcStage = vk::PipelineStageFlagBits::eTopOfPipe;
dstStage = vk::PipelineStageFlagBits::eEarlyFragmentTests;
} else if (this->layout == vk::ImageLayout::eUndefined &&
newLayout == vk::ImageLayout::eColorAttachmentOptimal) {
barrier.srcAccessMask = {};
barrier.dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
srcStage = vk::PipelineStageFlagBits::eTopOfPipe;
dstStage = vk::PipelineStageFlagBits::eColorAttachmentOutput;
} else if (this->layout == vk::ImageLayout::eUndefined &&
newLayout == vk::ImageLayout::eGeneral) {
barrier.srcAccessMask = {};
barrier.dstAccessMask =
vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite;
srcStage = vk::PipelineStageFlagBits::eTopOfPipe;
dstStage = vk::PipelineStageFlagBits::eComputeShader;
} else if (this->layout == vk::ImageLayout::eColorAttachmentOptimal &&
newLayout == vk::ImageLayout::ePresentSrcKHR) {
barrier.srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
barrier.dstAccessMask = {};
srcStage = vk::PipelineStageFlagBits::eColorAttachmentOutput;
dstStage = vk::PipelineStageFlagBits::eBottomOfPipe;
} else {
log::fatal("Unsupported image layout transition.");
throw std::runtime_error("Unsupported image layout transition.");
}
commandBuffer.pipelineBarrier(srcStage, dstStage, {}, nullptr, nullptr,
barrier);
this->layout = newLayout;
}
void Image::transitionLayoutImmediate(vk::ImageLayout newLayout) {
vk::CommandBuffer commandBuffer = this->device.beginSingleTimeCommands();
this->transitionLayout(commandBuffer, newLayout);
this->device.endSingleTimeCommands(commandBuffer);
}
Image::Builder& Image::Builder::setExtent(u32 width, u32 height, u32 depth) {
this->extent = vk::Extent3D{width, height, depth};
return *this;
}
Image::Builder& Image::Builder::setFormat(vk::Format format) {
this->format = format;
return *this;
}
Image::Builder& Image::Builder::setUsage(vk::ImageUsageFlags usage) {
this->usage = usage;
return *this;
}
Image::Builder& Image::Builder::setTiling(vk::ImageTiling tiling) {
this->tiling = tiling;
return *this;
}
Image::Builder& Image::Builder::setMemoryProperties(
vk::MemoryPropertyFlags properties) {
this->memoryProperties = properties;
return *this;
}
Image::Builder& Image::Builder::setSamples(vk::SampleCountFlagBits samples) {
this->samples = samples;
return *this;
}
Image::Builder& Image::Builder::setImageType(vk::ImageType imageType) {
this->imageType = imageType;
return *this;
}
Image::Builder& Image::Builder::setMipLevels(u32 mipLevels) {
this->mipLevels = mipLevels;
return *this;
}
Image::Builder& Image::Builder::setArrayLayers(u32 arrayLayers) {
this->arrayLayers = arrayLayers;
return *this;
}
Image::Builder& Image::Builder::setAspectMask(vk::ImageAspectFlags aspectMask) {
this->aspectMask = aspectMask;
return *this;
}
Image::Builder& Image::Builder::asComputeStorage() {
// Storage images commonly can't use sRGB formats without an extension, so
// default to a plain UNORM format; override with setFormat() if needed.
this->format = vk::Format::eR8G8B8A8Unorm;
this->usage = vk::ImageUsageFlagBits::eStorage;
this->aspectMask = vk::ImageAspectFlagBits::eColor;
this->tiling = vk::ImageTiling::eOptimal;
this->memoryProperties = vk::MemoryPropertyFlagBits::eDeviceLocal;
return *this;
}
Image::Builder& Image::Builder::asColorAttachment() {
this->usage = vk::ImageUsageFlagBits::eColorAttachment |
vk::ImageUsageFlagBits::eSampled;
this->aspectMask = vk::ImageAspectFlagBits::eColor;
this->tiling = vk::ImageTiling::eOptimal;
this->memoryProperties = vk::MemoryPropertyFlagBits::eDeviceLocal;
return *this;
}
Image::Builder& Image::Builder::asDepthAttachment() {
this->format = vk::Format::eD32Sfloat;
this->usage = vk::ImageUsageFlagBits::eDepthStencilAttachment;
this->aspectMask = vk::ImageAspectFlagBits::eDepth;
this->tiling = vk::ImageTiling::eOptimal;
this->memoryProperties = vk::MemoryPropertyFlagBits::eDeviceLocal;
return *this;
}
Image::Builder& Image::Builder::wrapExisting(vk::Image image) {
this->isExternalImage = true;
this->externalImage = image;
return *this;
}
Image::Builder& Image::Builder::setInitialData(const void* data,
vk::DeviceSize size) {
this->initialData = data;
this->initialDataSize = size;
return *this;
}
Image::Builder& Image::Builder::withView(bool enabled) {
this->createView = enabled;
return *this;
}
Image::Builder& Image::Builder::withSampler(
vk::Filter filter, vk::SamplerAddressMode addressMode) {
this->createSampler = true;
this->samplerFilter = filter;
this->samplerAddressMode = addressMode;
return *this;
}
std::unique_ptr<Image> Image::Builder::build() {
vk::ImageUsageFlags usage = this->usage;
auto result = std::unique_ptr<Image>(new Image(this->device));
result->format = this->format;
result->extent = this->extent;
result->aspectMask = this->aspectMask;
result->mipLevels = this->mipLevels;
result->arrayLayers = this->arrayLayers;
if (this->isExternalImage) {
if (this->initialData != nullptr) {
log::warning(
"Image::Builder::setInitialData is ignored when wrapExisting is "
"used.");
}
result->externalImage = this->externalImage;
// Wrapped images (e.g. swapchain images) are handed to us already in a
// known layout controlled by their owner; we can't observe it here, so
// leave `layout` at eUndefined and let the caller call
// transitionLayout() to bring it in sync before relying on it.
} else {
if (this->initialData != nullptr) {
usage |= vk::ImageUsageFlagBits::eTransferDst;
}
if (this->createView || this->createSampler) {
usage |= vk::ImageUsageFlagBits::eSampled;
}
vk::ImageCreateInfo imageInfo{};
imageInfo.imageType = this->imageType;
imageInfo.extent = this->extent;
imageInfo.mipLevels = this->mipLevels;
imageInfo.arrayLayers = this->arrayLayers;
imageInfo.format = this->format;
imageInfo.tiling = this->tiling;
imageInfo.initialLayout = vk::ImageLayout::eUndefined;
imageInfo.usage = usage;
imageInfo.sharingMode = vk::SharingMode::eExclusive;
imageInfo.samples = this->samples;
result->image = this->device.get()->createImageUnique(imageInfo);
vk::MemoryRequirements memoryRequirements =
this->device.get()->getImageMemoryRequirements(result->image.get());
vk::MemoryAllocateInfo allocInfo{};
allocInfo.allocationSize = memoryRequirements.size;
allocInfo.memoryTypeIndex = this->device.findMemoryType(
memoryRequirements.memoryTypeBits, this->memoryProperties);
result->memory = this->device.get()->allocateMemoryUnique(allocInfo);
this->device.get()->bindImageMemory(result->image.get(),
result->memory.get(), 0);
}
if (!this->isExternalImage && this->initialData != nullptr) {
vk::Buffer stagingBuffer;
vk::DeviceMemory stagingMemory;
this->device.createBuffer(this->initialDataSize,
vk::BufferUsageFlagBits::eTransferSrc,
vk::MemoryPropertyFlagBits::eHostVisible |
vk::MemoryPropertyFlagBits::eHostCoherent,
stagingBuffer, stagingMemory);
void* mapped =
this->device.get()->mapMemory(stagingMemory, 0, this->initialDataSize);
memcpy(mapped, this->initialData,
static_cast<size_t>(this->initialDataSize));
this->device.get()->unmapMemory(stagingMemory);
vk::BufferImageCopy region{};
region.bufferOffset = 0;
region.bufferRowLength = 0;
region.bufferImageHeight = 0;
region.imageSubresource.aspectMask = this->aspectMask;
region.imageSubresource.mipLevel = 0;
region.imageSubresource.baseArrayLayer = 0;
region.imageSubresource.layerCount = this->arrayLayers;
region.imageOffset = vk::Offset3D{0, 0, 0};
region.imageExtent = this->extent;
vk::CommandBuffer commandBuffer = this->device.beginSingleTimeCommands();
result->transitionLayout(commandBuffer, vk::ImageLayout::eTransferDstOptimal);
commandBuffer.copyBufferToImage(stagingBuffer, result->get(),
vk::ImageLayout::eTransferDstOptimal,
region);
if (usage & vk::ImageUsageFlagBits::eSampled) {
result->transitionLayout(commandBuffer,
vk::ImageLayout::eShaderReadOnlyOptimal);
}
this->device.endSingleTimeCommands(commandBuffer);
this->device.get()->destroyBuffer(stagingBuffer);
this->device.get()->freeMemory(stagingMemory);
}
if (this->createView) {
vk::ImageViewCreateInfo viewInfo{};
viewInfo.image = result->get();
viewInfo.viewType = vk::ImageViewType::e2D;
viewInfo.format = this->format;
viewInfo.subresourceRange.aspectMask = this->aspectMask;
viewInfo.subresourceRange.baseMipLevel = 0;
viewInfo.subresourceRange.levelCount = this->mipLevels;
viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = this->arrayLayers;
result->view = this->device.get()->createImageViewUnique(viewInfo);
}
if (this->createSampler) {
vk::SamplerCreateInfo samplerInfo{};
samplerInfo.magFilter = this->samplerFilter;
samplerInfo.minFilter = this->samplerFilter;
samplerInfo.addressModeU = this->samplerAddressMode;
samplerInfo.addressModeV = this->samplerAddressMode;
samplerInfo.addressModeW = this->samplerAddressMode;
samplerInfo.anisotropyEnable = VK_FALSE;
samplerInfo.maxAnisotropy = 1.0f;
samplerInfo.borderColor = vk::BorderColor::eIntOpaqueBlack;
samplerInfo.unnormalizedCoordinates = VK_FALSE;
samplerInfo.compareEnable = VK_FALSE;
samplerInfo.compareOp = vk::CompareOp::eAlways;
samplerInfo.mipmapMode = vk::SamplerMipmapMode::eLinear;
samplerInfo.minLod = 0.0f;
samplerInfo.maxLod = static_cast<float>(this->mipLevels);
result->sampler = this->device.get()->createSamplerUnique(samplerInfo);
}
log::trace("created vk::Image.");
return result;
}
} // namespace sophia
+124
View File
@@ -0,0 +1,124 @@
#pragma once
#include <memory>
#include <sophia/types.hpp>
#include <vulkan/vulkan.hpp>
namespace sophia {
class Device;
class Image {
public:
class Builder;
Image(const Image&) = delete;
Image& operator=(const Image&) = delete;
Image(Image&&) = default;
Image& operator=(Image&&) = delete;
vk::Image get() const {
return this->image ? this->image.get() : this->externalImage;
}
vk::ImageView getView() const { return this->view.get(); }
vk::Sampler getSampler() const { return this->sampler.get(); }
bool isExternal() const { return !this->image; }
vk::Format getFormat() const { return this->format; }
vk::Extent3D getExtent() const { return this->extent; }
vk::ImageLayout getLayout() const { return this->layout; }
void transitionLayout(vk::CommandBuffer commandBuffer,
vk::ImageLayout newLayout);
void transitionLayoutImmediate(vk::ImageLayout newLayout);
private:
Image(Device& device) : device{device} {}
Device& device;
vk::Image externalImage;
vk::UniqueImage image;
vk::UniqueDeviceMemory memory;
vk::UniqueImageView view;
vk::UniqueSampler sampler;
vk::Format format = vk::Format::eUndefined;
vk::Extent3D extent{};
vk::ImageAspectFlags aspectMask = vk::ImageAspectFlagBits::eColor;
u32 mipLevels = 1;
u32 arrayLayers = 1;
vk::ImageLayout layout = vk::ImageLayout::eUndefined;
};
class Image::Builder {
public:
Builder(Device& device) : device{device} {}
Builder& setExtent(u32 width, u32 height, u32 depth = 1);
Builder& setFormat(vk::Format format);
Builder& setUsage(vk::ImageUsageFlags usage);
Builder& setTiling(vk::ImageTiling tiling);
Builder& setMemoryProperties(vk::MemoryPropertyFlags properties);
Builder& setSamples(vk::SampleCountFlagBits samples);
Builder& setImageType(vk::ImageType imageType);
Builder& setMipLevels(u32 mipLevels);
Builder& setArrayLayers(u32 arrayLayers);
Builder& setAspectMask(vk::ImageAspectFlags aspectMask);
// High-level usage presets for GPGPU + Graphics. Each sets usage/aspect/
// tiling/memory-property defaults for the common case; call the
// individual setters afterward to override anything preset-specific.
Builder& asComputeStorage();
Builder& asColorAttachment();
Builder& asDepthAttachment();
// Wraps an image this Builder does not own instead of creating one —
// e.g. a swapchain image. build() will skip image/memory creation and
// teardown entirely; setUsage/setTiling/setMemoryProperties/setSamples/
// setInitialData are ignored, but setFormat/setExtent/setAspectMask still
// matter, since view/sampler creation relies on them.
Builder& wrapExisting(vk::Image image);
// Uploads `size` bytes to the image via a staging buffer once it is
// created, transitioning it to eTransferDstOptimal (and on to
// eShaderReadOnlyOptimal if the image is sampled) in the process.
Builder& setInitialData(const void* data, vk::DeviceSize size);
Builder& withView(bool enabled = true);
Builder& withSampler(
vk::Filter filter = vk::Filter::eLinear,
vk::SamplerAddressMode addressMode = vk::SamplerAddressMode::eRepeat);
std::unique_ptr<Image> build();
private:
Device& device;
vk::Extent3D extent{1, 1, 1};
vk::Format format = vk::Format::eR8G8B8A8Srgb;
vk::ImageUsageFlags usage = vk::ImageUsageFlagBits::eSampled;
vk::ImageTiling tiling = vk::ImageTiling::eOptimal;
vk::MemoryPropertyFlags memoryProperties =
vk::MemoryPropertyFlagBits::eDeviceLocal;
vk::SampleCountFlagBits samples = vk::SampleCountFlagBits::e1;
vk::ImageType imageType = vk::ImageType::e2D;
vk::ImageAspectFlags aspectMask = vk::ImageAspectFlagBits::eColor;
u32 mipLevels = 1;
u32 arrayLayers = 1;
const void* initialData = nullptr;
vk::DeviceSize initialDataSize = 0;
bool isExternalImage = false;
vk::Image externalImage;
bool createView = false;
bool createSampler = false;
vk::Filter samplerFilter = vk::Filter::eLinear;
vk::SamplerAddressMode samplerAddressMode = vk::SamplerAddressMode::eRepeat;
};
} // namespace sophia
+125
View File
@@ -0,0 +1,125 @@
#include "shader.hpp"
#include <fstream>
#include <shaderc/shaderc.hpp>
#include <sophia/util/logger.hpp>
#include <stdexcept>
namespace sophia {
namespace {
shaderc_shader_kind toShadercKind(ShaderStage shaderStage) {
switch (shaderStage) {
case ShaderStage::VERTEX:
return shaderc_vertex_shader;
case ShaderStage::FRAGMENT:
return shaderc_fragment_shader;
case ShaderStage::COMPUTE:
return shaderc_compute_shader;
}
throw std::invalid_argument("unsupported shader stage");
}
std::vector<char> readFile(const std::string& path) {
std::ifstream file(path, std::ios::ate | std::ios::binary);
if (!file.is_open()) {
log::error("failed to open shader: " + path);
throw std::runtime_error("failed to open: " + path);
}
size_t fileSize = static_cast<size_t>(file.tellg());
std::vector<char> buffer(fileSize);
file.seekg(0);
file.read(buffer.data(), fileSize);
file.close();
return buffer;
}
} // namespace
void Shader::compile(const std::string& path, ShaderStage shaderStage) {
this->stage = shaderStage;
std::vector<char> source = readFile(path);
log::info("Compiling shader: " + path);
shaderc::Compiler compiler;
shaderc::SpvCompilationResult result = compiler.CompileGlslToSpv(
source.data(), source.size(), toShadercKind(shaderStage), path.c_str());
if (result.GetCompilationStatus() != shaderc_compilation_status_success) {
log::fatal("failed to compile shader:", path, result.GetErrorMessage());
throw std::runtime_error("failed to compile shader: " + path + ": " +
result.GetErrorMessage());
}
this->spirv.assign(result.cbegin(), result.cend());
log::info("Successfully compiled shader: " + path + " (" +
std::to_string(this->spirv.size() * 4) + " bytes)");
}
void Shader::load(const std::string& path, ShaderStage shaderStage) {
this->stage = shaderStage;
std::vector<char> bytes = readFile(path);
if (bytes.size() % sizeof(u32) != 0) {
log::fatal("invalid SPIR-V binary (size not a multiple of 4):", path);
throw std::runtime_error("invalid SPIR-V binary: " + path);
}
this->spirv.assign(
reinterpret_cast<const u32*>(bytes.data()),
reinterpret_cast<const u32*>(bytes.data() + bytes.size()));
log::info("Loaded precompiled shader: " + path + " (" +
std::to_string(bytes.size()) + " bytes)");
}
Shader::Builder& Shader::Builder::fromGLSL(const std::string& path) {
this->path = path;
this->precompiled = false;
return *this;
}
Shader::Builder& Shader::Builder::fromSPIRV(const std::string& path) {
this->path = path;
this->precompiled = true;
return *this;
}
Shader::Builder& Shader::Builder::setStage(ShaderStage shaderStage) {
this->stage = shaderStage;
this->stageSet = true;
return *this;
}
std::unique_ptr<Shader> Shader::Builder::build() {
if (this->path.empty()) {
log::fatal("failed to build shader: no source path provided");
throw std::runtime_error("failed to build shader: no source path provided");
}
if (!this->stageSet) {
log::fatal("failed to build shader: no ShaderStage provided");
throw std::runtime_error("failed to build shader: no ShaderStage provided");
}
auto shader = std::unique_ptr<Shader>(new Shader());
if (this->precompiled) {
shader->load(this->path, this->stage);
} else {
shader->compile(this->path, this->stage);
}
return shader;
}
} // namespace sophia
+51
View File
@@ -0,0 +1,51 @@
#pragma once
#include <memory>
#include <sophia/types.hpp>
#include <string>
#include <vector>
namespace sophia {
enum class ShaderStage { VERTEX, FRAGMENT, COMPUTE };
class Shader {
public:
class Builder;
Shader(const Shader&) = delete;
Shader& operator=(const Shader&) = delete;
~Shader() = default;
const std::vector<u32>& getSpirv() const { return this->spirv; }
ShaderStage getStage() const { return this->stage; }
private:
Shader() = default;
void compile(const std::string& path, ShaderStage shaderStage);
void load(const std::string& path, ShaderStage shaderStage);
ShaderStage stage;
std::vector<u32> spirv;
};
class Shader::Builder {
public:
Builder() = default;
Builder& fromGLSL(const std::string& path);
Builder& fromSPIRV(const std::string& path);
Builder& setStage(ShaderStage shaderStage);
std::unique_ptr<Shader> build();
private:
std::string path;
ShaderStage stage;
bool stageSet = false;
bool precompiled = false;
};
} // namespace sophia
+6 -9
View File
@@ -1,9 +1,9 @@
#include "window.hpp" #include "window.hpp"
#include "key_event.hpp" #include <sophia/key_event.hpp>
#include "util/logger.hpp" #include <sophia/util/logger.hpp>
namespace hep { namespace sophia {
Window::Window(int width, int height, const std::string& name) Window::Window(int width, int height, const std::string& name)
: width{width}, height{height}, name{name} { : width{width}, height{height}, name{name} {
@@ -28,11 +28,8 @@ void Window::createSurface(const vk::Instance& instance,
log::trace("Created vk::SurfaceKHR."); log::trace("Created vk::SurfaceKHR.");
} }
void Window::keyEventCallback(GLFWwindow* window, void Window::keyEventCallback(GLFWwindow* window, int key, int scancode,
int key, int action, int mods) {
int scancode,
int action,
int mods) {
if (action == GLFW_PRESS) { if (action == GLFW_PRESS) {
KeyPressedEvent event{static_cast<KeyCode>(key)}; KeyPressedEvent event{static_cast<KeyCode>(key)};
EventSystem::get().dispatch(event); EventSystem::get().dispatch(event);
@@ -61,4 +58,4 @@ void Window::initialize() {
glfwSetKeyCallback(this->window, keyEventCallback); glfwSetKeyCallback(this->window, keyEventCallback);
} }
} // namespace hep } // namespace sophia
@@ -6,17 +6,17 @@
#include <string> #include <string>
#include <vulkan/vulkan.hpp> #include <vulkan/vulkan.hpp>
#include "event.hpp" #include <sophia/event.hpp>
#include "types.hpp" #include <sophia/types.hpp>
namespace hep { namespace sophia {
class Window { class Window {
public: public:
Window(const Window&) = delete; Window(const Window&) = delete;
Window& operator=(const Window&) = delete; Window& operator=(const Window&) = delete;
Window(int width, int height, const std::string& name = "hephaestus"); Window(int width, int height, const std::string& name = "sophiahaestus");
~Window(); ~Window();
bool shouldClose() { return glfwWindowShouldClose(window); } bool shouldClose() { return glfwWindowShouldClose(window); }
@@ -48,4 +48,4 @@ class Window {
GLFWwindow* window; GLFWwindow* window;
}; };
} // namespace hep } // namespace sophia