copying code over from alphane engine
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
cmake_minimum_required(VERSION 3.22.2)
|
||||
|
||||
set(NAME sophia)
|
||||
|
||||
project(${NAME} VERSION 0.0.0)
|
||||
|
||||
enable_language(CXX)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
if(NOT DEFINED CMAKE_BUILD_TYPE OR CMAKE_BUILD_TYPE STREQUAL "")
|
||||
set(CMAKE_BUILD_TYPE Debug CACHE STRING "Build type" FORCE)
|
||||
endif()
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
message(STATUS "Debug build: enabling debug logging")
|
||||
else()
|
||||
add_compile_definitions(NDEBUG)
|
||||
endif()
|
||||
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
||||
|
||||
find_package(Git QUIET)
|
||||
if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git")
|
||||
option(GIT_SUBMODULE "Check submodules during build" ON)
|
||||
if(GIT_SUBMODULE)
|
||||
message(STATUS "Submodule update")
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
RESULT_VARIABLE GIT_SUBMOD_RESULT)
|
||||
if(NOT GIT_SUBMOD_RESULT EQUAL "0")
|
||||
message(FATAL_ERROR "git submodule update --init failed with ${GIT_SUBMOD_RESULT}, checkout submodules")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_subdirectory(third_party)
|
||||
add_subdirectory(engine)
|
||||
add_subdirectory(testbed)
|
||||
@@ -0,0 +1,20 @@
|
||||
# sophia
|
||||
|
||||
## Getting Started
|
||||
|
||||
COMING SOON
|
||||
|
||||
## Developing on the engine
|
||||
|
||||
### Dependencies
|
||||
|
||||
- [cmake](https://cmake.org/)
|
||||
- [Vulkan SDK](https://vulkan.lunarg.com/) - With validation layers
|
||||
|
||||
### Build instructions
|
||||
|
||||
```sh
|
||||
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug
|
||||
cd build
|
||||
make -j
|
||||
```
|
||||
@@ -0,0 +1,18 @@
|
||||
set(ENGINE_NAME ${PROJECT_NAME})
|
||||
|
||||
add_library(${ENGINE_NAME} STATIC)
|
||||
|
||||
file(GLOB_RECURSE SOURCES src/*.cpp)
|
||||
file(GLOB_RECURSE HEADERS include/*.hpp)
|
||||
|
||||
target_sources(${ENGINE_NAME} PRIVATE
|
||||
${SOURCES}
|
||||
# ${HEADERS}
|
||||
)
|
||||
|
||||
target_include_directories(${ENGINE_NAME} PUBLIC
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/include"
|
||||
"${CMAKE_SOURCE_DIR}/third_party/imgui"
|
||||
"${CMAKE_SOURCE_DIR}/third_party/imgui/backends"
|
||||
)
|
||||
@@ -0,0 +1,134 @@
|
||||
#pragma once
|
||||
|
||||
#include <imgui_impl_vulkan.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
#include "types.hpp"
|
||||
#include "window.hpp"
|
||||
|
||||
#define HEP_VULKAN_API_VERSION VK_API_VERSION_1_3
|
||||
|
||||
namespace hep {
|
||||
|
||||
struct QueueFamilyIndices {
|
||||
std::optional<uint32_t> graphicsFamily;
|
||||
std::optional<uint32_t> presentFamily;
|
||||
|
||||
bool isComplete() {
|
||||
return graphicsFamily.has_value() && presentFamily.has_value();
|
||||
}
|
||||
};
|
||||
|
||||
struct SwapchainSupportDetails {
|
||||
std::vector<vk::SurfaceFormatKHR> formats;
|
||||
std::vector<vk::PresentModeKHR> presentModes;
|
||||
vk::SurfaceCapabilitiesKHR capabilities;
|
||||
};
|
||||
|
||||
class Device {
|
||||
public:
|
||||
Device(const Device&) = delete;
|
||||
Device& operator=(const Device&) = delete;
|
||||
Device(Device&&) = delete;
|
||||
Device& operator=(Device&&) = delete;
|
||||
|
||||
Device(Window& window);
|
||||
~Device();
|
||||
|
||||
const vk::Device* get() const {
|
||||
assert(this->device);
|
||||
return &this->device.get();
|
||||
}
|
||||
|
||||
void waitIdle() { this->device->waitIdle(); }
|
||||
|
||||
vk::CommandPool getCommandPool() const { return this->commandPool; }
|
||||
vk::SurfaceKHR getSurface() const { return surface; }
|
||||
vk::Queue getGraphicsQueue() const { return graphicsQueue; }
|
||||
vk::Queue getPresentQueue() const { return presentQueue; }
|
||||
|
||||
QueueFamilyIndices getQueueIndices() {
|
||||
return findQueueFamilies(this->physicalDevice);
|
||||
}
|
||||
|
||||
SwapchainSupportDetails getSwapchainSupport() {
|
||||
return querySwapchainSupport(this->physicalDevice);
|
||||
}
|
||||
|
||||
u32 findMemoryType(u32 typeFilter, vk::MemoryPropertyFlags properties);
|
||||
|
||||
vk::Format findSupportedFormat(const std::vector<vk::Format>& candidates,
|
||||
vk::ImageTiling tiling,
|
||||
vk::FormatFeatureFlags features);
|
||||
|
||||
void createBuffer(vk::DeviceSize size,
|
||||
vk::BufferUsageFlags usage,
|
||||
vk::MemoryPropertyFlags properties,
|
||||
vk::Buffer& buffer,
|
||||
vk::DeviceMemory& bufferMemory);
|
||||
|
||||
vk::CommandBuffer beginSingleTimeCommands();
|
||||
|
||||
void endSingleTimeCommands(vk::CommandBuffer commandBuffer);
|
||||
|
||||
void copyBuffer(vk::Buffer sourceBuffer,
|
||||
vk::Buffer destinationBuffer,
|
||||
vk::DeviceSize size);
|
||||
|
||||
void createImageWithInfo(const vk::ImageCreateInfo& imageInfo,
|
||||
vk::MemoryPropertyFlags properties,
|
||||
vk::Image& image,
|
||||
vk::DeviceMemory& imageMemory);
|
||||
|
||||
void populateImGuiInitInfo(ImGui_ImplVulkan_InitInfo& initInfo);
|
||||
|
||||
vk::PhysicalDeviceProperties properties;
|
||||
|
||||
private:
|
||||
void setupDebugMessenger();
|
||||
|
||||
bool checkValidationLayerSupport();
|
||||
std::vector<const char*> getRequiredExtensions();
|
||||
void createVulkanInstance();
|
||||
|
||||
QueueFamilyIndices findQueueFamilies(vk::PhysicalDevice device);
|
||||
bool checkDeviceExtensionSupport(const vk::PhysicalDevice& device);
|
||||
bool isPhysicalDeviceSuitable(const vk::PhysicalDevice& device);
|
||||
void pickPhysicalDevice();
|
||||
|
||||
SwapchainSupportDetails querySwapchainSupport(vk::PhysicalDevice device);
|
||||
|
||||
void createLogicalDevice();
|
||||
void createCommandPool();
|
||||
|
||||
vk::UniqueInstance instance;
|
||||
VkDebugUtilsMessengerEXT debugMessenger;
|
||||
|
||||
Window& window;
|
||||
vk::SurfaceKHR surface;
|
||||
|
||||
vk::PhysicalDevice physicalDevice = VK_NULL_HANDLE;
|
||||
vk::UniqueDevice device;
|
||||
|
||||
vk::Queue graphicsQueue;
|
||||
vk::Queue presentQueue;
|
||||
|
||||
vk::CommandPool commandPool;
|
||||
|
||||
#ifdef NDEBUG
|
||||
const bool enableValidationLayers = false;
|
||||
const std::vector<const char*> enabledLayers;
|
||||
#else
|
||||
const bool enableValidationLayers = true;
|
||||
const std::vector<const char*> enabledLayers = {
|
||||
"VK_LAYER_KHRONOS_validation"};
|
||||
#endif
|
||||
const std::vector<const char*> enabledExtensions = {
|
||||
VK_KHR_SWAPCHAIN_EXTENSION_NAME};
|
||||
};
|
||||
|
||||
} // namespace hep
|
||||
@@ -0,0 +1,21 @@
|
||||
#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
|
||||
@@ -0,0 +1,111 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <typeinfo>
|
||||
#include <vector>
|
||||
|
||||
#include "util/logger.hpp"
|
||||
|
||||
#define DEFAULT_DELEGATE_FUNCTOR_ALLOC 5
|
||||
|
||||
namespace hep {
|
||||
|
||||
class expired_weak_object : public std::runtime_error {
|
||||
public:
|
||||
explicit expired_weak_object(const std::string& message)
|
||||
: std::runtime_error(message) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a weak function that safely calls a method if the
|
||||
* object is alive.
|
||||
*
|
||||
* @tparam O Class owning the method
|
||||
* @tparam A Variadic template parameters for the method arguments
|
||||
*
|
||||
* @param method Pointer to the static method to be called
|
||||
* @param obj Shared pointer to the object instance
|
||||
* @returns std::function that calls method on obj if alive, or throws if
|
||||
* expired
|
||||
* @throws std::invalid_argument If obj is null.
|
||||
* @throws expired_weak_object If the object has expired when called
|
||||
*
|
||||
* @note Could investigate removing throwing exceptions, and instead return a
|
||||
* bool if the weak pointer is alive. This may improve performance
|
||||
*/
|
||||
template <class O, typename... A>
|
||||
std::function<void(A...)> createWeakFunction(void (O::*method)(A...),
|
||||
std::shared_ptr<O> obj) {
|
||||
if (!obj) { throw std::invalid_argument("method and obj must not be null"); }
|
||||
|
||||
std::weak_ptr<O> weakObj = obj;
|
||||
|
||||
return std::function<void(A...)>([method = std::move(method),
|
||||
weakObj = std::move(weakObj)](A... args) {
|
||||
if (auto lockedObj = weakObj.lock()) {
|
||||
(lockedObj.get()->*method)(std::forward<A>(args)...);
|
||||
} else {
|
||||
throw expired_weak_object("Attempting to call function of a dead class");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template <typename... A>
|
||||
class Delegate {
|
||||
public:
|
||||
using Functor = std::function<void(A...)>;
|
||||
|
||||
Delegate() { this->functors.reserve(DEFAULT_DELEGATE_FUNCTOR_ALLOC); }
|
||||
|
||||
void add(const Functor& func) { functors.push_back(func); }
|
||||
|
||||
template <class O>
|
||||
void addMethod(void (O::*memberFn)(A...), std::shared_ptr<O> obj) {
|
||||
functors.push_back(createWeakFunction(memberFn, obj));
|
||||
}
|
||||
|
||||
void clear() { this->functors.clear(); }
|
||||
|
||||
void invoke(A... args) {
|
||||
if (this->functors.empty()) {
|
||||
log::warning("invoking empty delegate");
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<size_t> deadFunctorIndexs;
|
||||
|
||||
for (size_t i = 0; i < functors.size(); i++) {
|
||||
if (this->functors[i]) {
|
||||
try {
|
||||
this->functors[i](std::forward<A>(args)...);
|
||||
} catch (const expired_weak_object& e) {
|
||||
log::error("delegate invoke error: ", e.what());
|
||||
deadFunctorIndexs.push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = deadFunctorIndexs.rbegin(); it != deadFunctorIndexs.rend();
|
||||
++it) {
|
||||
functors.erase(functors.begin() + *it);
|
||||
}
|
||||
}
|
||||
|
||||
void operator=(Functor c) {
|
||||
clear();
|
||||
|
||||
if (c != nullptr) { add(c); }
|
||||
}
|
||||
|
||||
void operator+=(Functor c) { add(c); }
|
||||
|
||||
void operator()() { invoke(); }
|
||||
|
||||
private:
|
||||
std::vector<Functor> functors;
|
||||
};
|
||||
|
||||
} // namespace hep
|
||||
@@ -0,0 +1,140 @@
|
||||
/**
|
||||
* 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
|
||||
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#define GLFW_INCLUDE_VULKAN
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#include <string>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
#include "event.hpp"
|
||||
#include "types.hpp"
|
||||
|
||||
namespace hep {
|
||||
|
||||
class Window {
|
||||
public:
|
||||
Window(const Window&) = delete;
|
||||
Window& operator=(const Window&) = delete;
|
||||
|
||||
Window(int width, int height, const std::string& name = "hephaestus");
|
||||
~Window();
|
||||
|
||||
bool shouldClose() { return glfwWindowShouldClose(window); }
|
||||
|
||||
int getWidth() const { return this->width; }
|
||||
int getHeight() const { return this->height; }
|
||||
|
||||
vk::Extent2D getExtent() {
|
||||
return {static_cast<u32>(width), static_cast<u32>(height)};
|
||||
}
|
||||
void createSurface(const vk::Instance& instance, vk::SurfaceKHR& surface);
|
||||
bool wasResized() const { return this->resized; };
|
||||
void resetResizedFlag() { this->resized = false; }
|
||||
|
||||
GLFWwindow* getGLFWwindow() { return this->window; }
|
||||
|
||||
private:
|
||||
static void resizeCallback(GLFWwindow* window, int width, int height);
|
||||
static void keyEventCallback(GLFWwindow* window,
|
||||
int key,
|
||||
int scancode,
|
||||
int action,
|
||||
int mods);
|
||||
void initialize();
|
||||
|
||||
int width, height;
|
||||
bool resized = false;
|
||||
std::string name;
|
||||
GLFWwindow* window;
|
||||
};
|
||||
|
||||
} // namespace hep
|
||||
@@ -0,0 +1,505 @@
|
||||
#include "device.hpp"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "util/logger.hpp"
|
||||
|
||||
namespace hep {
|
||||
|
||||
static VKAPI_ATTR VkBool32 VKAPI_CALL
|
||||
debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT m_severity,
|
||||
VkDebugUtilsMessageTypeFlagsEXT m_type,
|
||||
const VkDebugUtilsMessengerCallbackDataEXT* pCallback_data,
|
||||
void* pUser_data) {
|
||||
(void)m_type;
|
||||
(void)pCallback_data;
|
||||
(void)pUser_data;
|
||||
|
||||
if (m_severity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
|
||||
log::error(pCallback_data->pMessage);
|
||||
return VK_SUCCESS;
|
||||
} else if (m_severity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
|
||||
log::warning(pCallback_data->pMessage);
|
||||
} else {
|
||||
log::info(pCallback_data->pMessage);
|
||||
}
|
||||
|
||||
return VK_FALSE;
|
||||
}
|
||||
|
||||
VkResult createDebugUtilsMessengerEXT(
|
||||
VkInstance instance,
|
||||
const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo,
|
||||
const VkAllocationCallbacks* pAllocator,
|
||||
VkDebugUtilsMessengerEXT* pCallback) {
|
||||
auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(
|
||||
instance, "vkCreateDebugUtilsMessengerEXT");
|
||||
if (func != nullptr) {
|
||||
return func(instance, pCreateInfo, pAllocator, pCallback);
|
||||
} else {
|
||||
return VK_ERROR_EXTENSION_NOT_PRESENT;
|
||||
}
|
||||
}
|
||||
|
||||
void destroyDebugUtilsMessengerEXT(VkInstance instance,
|
||||
VkDebugUtilsMessengerEXT callback,
|
||||
const VkAllocationCallbacks* pAllocator) {
|
||||
auto func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(
|
||||
instance, "vkDestroyDebugUtilsMessengerEXT");
|
||||
if (func != nullptr) { func(instance, callback, pAllocator); }
|
||||
}
|
||||
|
||||
Device::Device(Window& window) : window{window} {
|
||||
createVulkanInstance();
|
||||
setupDebugMessenger();
|
||||
this->window.createSurface(*instance, surface);
|
||||
pickPhysicalDevice();
|
||||
createLogicalDevice();
|
||||
createCommandPool();
|
||||
}
|
||||
|
||||
Device::~Device() {
|
||||
if (this->enableValidationLayers) {
|
||||
destroyDebugUtilsMessengerEXT(this->instance.get(), this->debugMessenger,
|
||||
nullptr);
|
||||
log::trace("destroyed Vulkan Debugger");
|
||||
}
|
||||
|
||||
this->device->destroyCommandPool(commandPool);
|
||||
log::trace("destroyed vk::CommandPool");
|
||||
|
||||
this->instance->destroySurfaceKHR(surface);
|
||||
log::trace("destroyed vk::SurfaceKHR");
|
||||
}
|
||||
|
||||
u32 Device::findMemoryType(u32 typeFilter, vk::MemoryPropertyFlags properties) {
|
||||
vk::PhysicalDeviceMemoryProperties memoryProperties =
|
||||
this->physicalDevice.getMemoryProperties();
|
||||
|
||||
for (u32 i = 0; i < memoryProperties.memoryTypeCount; i++) {
|
||||
if ((typeFilter & (1 << i)) &&
|
||||
(memoryProperties.memoryTypes[i].propertyFlags & properties) ==
|
||||
properties) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
log::fatal("failed to find suitable memory type");
|
||||
throw std::runtime_error("failed to find suitable memory type");
|
||||
}
|
||||
|
||||
vk::Format Device::findSupportedFormat(
|
||||
const std::vector<vk::Format>& candidates,
|
||||
vk::ImageTiling tiling,
|
||||
vk::FormatFeatureFlags features) {
|
||||
for (vk::Format format : candidates) {
|
||||
vk::FormatProperties properties =
|
||||
physicalDevice.getFormatProperties(format);
|
||||
|
||||
if ((tiling == vk::ImageTiling::eLinear &&
|
||||
(properties.linearTilingFeatures & features) == features) ||
|
||||
(tiling == vk::ImageTiling::eOptimal &&
|
||||
(properties.optimalTilingFeatures & features) == features)) {
|
||||
return format;
|
||||
}
|
||||
}
|
||||
|
||||
log::fatal("failed to find supported format");
|
||||
throw std::runtime_error("failed to find supported format");
|
||||
}
|
||||
|
||||
void Device::createBuffer(vk::DeviceSize size,
|
||||
vk::BufferUsageFlags usage,
|
||||
vk::MemoryPropertyFlags properties,
|
||||
vk::Buffer& buffer,
|
||||
vk::DeviceMemory& bufferMemory) {
|
||||
vk::BufferCreateInfo bufferInfo = {};
|
||||
bufferInfo.size = size;
|
||||
bufferInfo.usage = usage;
|
||||
bufferInfo.sharingMode = vk::SharingMode::eExclusive;
|
||||
|
||||
try {
|
||||
buffer = this->device->createBuffer(bufferInfo);
|
||||
} catch (const vk::SystemError& error) {
|
||||
log::fatal("failed to create vertex buffer");
|
||||
throw std::runtime_error("failed to create vertex buffer");
|
||||
}
|
||||
|
||||
vk::MemoryRequirements memoryRequirements =
|
||||
this->device->getBufferMemoryRequirements(buffer);
|
||||
|
||||
vk::MemoryAllocateInfo allocInfo = {};
|
||||
allocInfo.allocationSize = memoryRequirements.size;
|
||||
allocInfo.memoryTypeIndex =
|
||||
findMemoryType(memoryRequirements.memoryTypeBits, properties);
|
||||
|
||||
try {
|
||||
bufferMemory = this->device->allocateMemory(allocInfo);
|
||||
} catch (const vk::SystemError& error) {
|
||||
log::fatal("failed to allocate vertex buffer memory");
|
||||
throw std::runtime_error("failed to allocate vertex buffer memory");
|
||||
}
|
||||
|
||||
this->device->bindBufferMemory(buffer, bufferMemory, 0);
|
||||
}
|
||||
|
||||
vk::CommandBuffer Device::beginSingleTimeCommands() {
|
||||
vk::CommandBufferAllocateInfo allocInfo{};
|
||||
allocInfo.level = vk::CommandBufferLevel::ePrimary;
|
||||
allocInfo.commandPool = this->commandPool;
|
||||
allocInfo.commandBufferCount = 1;
|
||||
|
||||
vk::CommandBuffer commandBuffer;
|
||||
|
||||
try {
|
||||
vk::Result result =
|
||||
this->device->allocateCommandBuffers(&allocInfo, &commandBuffer);
|
||||
if (result != vk::Result::eSuccess) { throw vk::SystemError(result); }
|
||||
} catch (const vk::SystemError& error) {
|
||||
log::error("failed to allocate single time commandBuffer. Error: ",
|
||||
error.what());
|
||||
throw std::runtime_error("failed to allocate single time commandBuffer");
|
||||
}
|
||||
|
||||
vk::CommandBufferBeginInfo beginInfo{};
|
||||
beginInfo.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit;
|
||||
|
||||
try {
|
||||
vk::Result result = commandBuffer.begin(&beginInfo);
|
||||
if (result != vk::Result::eSuccess) { throw vk::SystemError(result); }
|
||||
} catch (const vk::SystemError& error) {
|
||||
log::error("failed to begin single time commandBuffer. Error: ",
|
||||
error.what());
|
||||
throw std::runtime_error("failed to begin single time commandBuffer");
|
||||
}
|
||||
|
||||
return commandBuffer;
|
||||
}
|
||||
|
||||
void Device::endSingleTimeCommands(vk::CommandBuffer commandBuffer) {
|
||||
commandBuffer.end();
|
||||
|
||||
vk::SubmitInfo submitInfo = {};
|
||||
submitInfo.commandBufferCount = 1;
|
||||
submitInfo.pCommandBuffers = &commandBuffer;
|
||||
|
||||
this->graphicsQueue.submit(submitInfo, nullptr);
|
||||
this->graphicsQueue.waitIdle();
|
||||
|
||||
this->device->freeCommandBuffers(commandPool, commandBuffer);
|
||||
}
|
||||
|
||||
void Device::copyBuffer(vk::Buffer sourceBuffer,
|
||||
vk::Buffer destinationBuffer,
|
||||
vk::DeviceSize size) {
|
||||
vk::CommandBuffer commandBuffer = beginSingleTimeCommands();
|
||||
|
||||
vk::BufferCopy copyRegion = {};
|
||||
copyRegion.size = size;
|
||||
|
||||
commandBuffer.copyBuffer(sourceBuffer, destinationBuffer, copyRegion);
|
||||
|
||||
endSingleTimeCommands(commandBuffer);
|
||||
}
|
||||
|
||||
void Device::createImageWithInfo(const vk::ImageCreateInfo& imageInfo,
|
||||
vk::MemoryPropertyFlags properties,
|
||||
vk::Image& image,
|
||||
vk::DeviceMemory& imageMemory) {
|
||||
try {
|
||||
vk::Result result = this->device->createImage(&imageInfo, nullptr, &image);
|
||||
if (result != vk::Result::eSuccess) { throw vk::SystemError(result); }
|
||||
} catch (const vk::SystemError& error) {
|
||||
log::fatal("failed to create image. Error: ", error.what());
|
||||
throw std::runtime_error("failed to create image");
|
||||
}
|
||||
|
||||
vk::MemoryRequirements memoryRequirements =
|
||||
this->device->getImageMemoryRequirements(image);
|
||||
|
||||
vk::MemoryAllocateInfo allocateInfo{};
|
||||
allocateInfo.allocationSize = memoryRequirements.size;
|
||||
allocateInfo.memoryTypeIndex =
|
||||
findMemoryType(memoryRequirements.memoryTypeBits, properties);
|
||||
|
||||
try {
|
||||
vk::Result result =
|
||||
this->device->allocateMemory(&allocateInfo, nullptr, &imageMemory);
|
||||
if (result != vk::Result::eSuccess) { throw vk::SystemError(result); }
|
||||
} catch (const vk::SystemError& error) {
|
||||
log::fatal("failed to allocate image memory. Error: ", error.what());
|
||||
throw std::runtime_error("failed to allocate image memory");
|
||||
}
|
||||
|
||||
try {
|
||||
this->device->bindImageMemory(image, imageMemory, 0);
|
||||
} catch (const vk::SystemError& error) {
|
||||
log::fatal("failed to bind image memory. Error: ", error.what());
|
||||
throw std::runtime_error("failed to bind image memory");
|
||||
}
|
||||
}
|
||||
|
||||
void Device::populateImGuiInitInfo(ImGui_ImplVulkan_InitInfo& initInfo) {
|
||||
initInfo.Instance = this->instance.get();
|
||||
initInfo.ApiVersion = HEP_VULKAN_API_VERSION;
|
||||
initInfo.PhysicalDevice = this->physicalDevice;
|
||||
initInfo.Device = this->device.get();
|
||||
|
||||
QueueFamilyIndices indices = findQueueFamilies(this->physicalDevice);
|
||||
initInfo.QueueFamily = indices.graphicsFamily.value();
|
||||
initInfo.Queue = getGraphicsQueue();
|
||||
}
|
||||
|
||||
void Device::setupDebugMessenger() {
|
||||
if (!this->enableValidationLayers) return;
|
||||
|
||||
auto createInfo = vk::DebugUtilsMessengerCreateInfoEXT(
|
||||
vk::DebugUtilsMessengerCreateFlagsEXT(),
|
||||
vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose |
|
||||
vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning |
|
||||
vk::DebugUtilsMessageSeverityFlagBitsEXT::eError,
|
||||
vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral |
|
||||
vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation |
|
||||
vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance,
|
||||
debugCallback, nullptr);
|
||||
|
||||
if (createDebugUtilsMessengerEXT(
|
||||
*instance,
|
||||
reinterpret_cast<const VkDebugUtilsMessengerCreateInfoEXT*>(
|
||||
&createInfo),
|
||||
nullptr, &this->debugMessenger) != VK_SUCCESS) {
|
||||
throw std::runtime_error("failed to set up debug callback!");
|
||||
}
|
||||
}
|
||||
|
||||
bool Device::checkValidationLayerSupport() {
|
||||
auto availableLayers = vk::enumerateInstanceLayerProperties();
|
||||
for (const char* layerName : enabledLayers) {
|
||||
bool layerFound = false;
|
||||
|
||||
for (const auto& layerProperties : availableLayers) {
|
||||
if (strcmp(layerName, layerProperties.layerName) == 0) {
|
||||
layerFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!layerFound) { return false; }
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<const char*> Device::getRequiredExtensions() {
|
||||
u32 glfw_extension_count = 0;
|
||||
const char** glfw_extensions;
|
||||
glfw_extensions = glfwGetRequiredInstanceExtensions(&glfw_extension_count);
|
||||
|
||||
std::vector<const char*> extensions(glfw_extensions,
|
||||
glfw_extensions + glfw_extension_count);
|
||||
|
||||
if (enableValidationLayers) {
|
||||
extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
u32 available_extension_count = 0;
|
||||
vk::Result result = vk::enumerateInstanceExtensionProperties(
|
||||
nullptr, &available_extension_count, nullptr);
|
||||
|
||||
if (result != vk::Result::eSuccess) { throw std::exception(); }
|
||||
|
||||
std::vector<vk::ExtensionProperties> available_extensions(
|
||||
available_extension_count);
|
||||
|
||||
result = vk::enumerateInstanceExtensionProperties(
|
||||
nullptr, &available_extension_count, available_extensions.data());
|
||||
|
||||
if (result != vk::Result::eSuccess) { throw std::exception(); }
|
||||
|
||||
log::verbose("Number of available extensions: ", available_extension_count);
|
||||
|
||||
log::verbose("Available extensions:");
|
||||
for (const auto& e : available_extensions) {
|
||||
std::cout << '\t' << e.extensionName << '\n';
|
||||
}
|
||||
std::cout << "Required extensions:\n";
|
||||
for (const auto& e : extensions) { std::cout << "\t" << e << '\n'; }
|
||||
#endif
|
||||
|
||||
return extensions;
|
||||
}
|
||||
|
||||
void Device::createVulkanInstance() {
|
||||
if (this->enableValidationLayers && !checkValidationLayerSupport()) {
|
||||
log::fatal("Validation layers requested but not available.");
|
||||
throw std::exception();
|
||||
}
|
||||
|
||||
vk::ApplicationInfo app_info = vk::ApplicationInfo(
|
||||
"Hephaestus", VK_MAKE_VERSION(1, 0, 0), "Hephaestus Vulkan Engine",
|
||||
VK_MAKE_VERSION(1, 0, 0), VK_API_VERSION_1_3);
|
||||
|
||||
auto extensions = getRequiredExtensions();
|
||||
|
||||
auto createInfo = vk::InstanceCreateInfo(
|
||||
vk::InstanceCreateFlags(), &app_info, 0, nullptr,
|
||||
static_cast<uint32_t>(extensions.size()), extensions.data());
|
||||
|
||||
createInfo.enabledLayerCount = static_cast<uint32_t>(enabledLayers.size());
|
||||
createInfo.ppEnabledLayerNames = enabledLayers.data();
|
||||
|
||||
try {
|
||||
this->instance = vk::createInstanceUnique(createInfo, nullptr);
|
||||
} catch (const vk::SystemError& err) {
|
||||
throw std::runtime_error("Failed to create instance!");
|
||||
}
|
||||
}
|
||||
|
||||
QueueFamilyIndices Device::findQueueFamilies(vk::PhysicalDevice device) {
|
||||
QueueFamilyIndices indices;
|
||||
auto queueFamilies = device.getQueueFamilyProperties();
|
||||
int i = 0;
|
||||
|
||||
for (const auto& queueFamily : queueFamilies) {
|
||||
if (queueFamily.queueCount > 0 &&
|
||||
queueFamily.queueFlags & vk::QueueFlagBits::eGraphics) {
|
||||
indices.graphicsFamily = i;
|
||||
}
|
||||
|
||||
if (queueFamily.queueCount > 0 && device.getSurfaceSupportKHR(i, surface)) {
|
||||
indices.presentFamily = i;
|
||||
}
|
||||
|
||||
if (indices.isComplete()) { break; }
|
||||
i++;
|
||||
}
|
||||
|
||||
return indices;
|
||||
}
|
||||
|
||||
bool Device::checkDeviceExtensionSupport(const vk::PhysicalDevice& device) {
|
||||
std::set<std::string> requiredExtensions(this->enabledExtensions.begin(),
|
||||
this->enabledExtensions.end());
|
||||
|
||||
for (const auto& extension : device.enumerateDeviceExtensionProperties()) {
|
||||
requiredExtensions.erase(extension.extensionName);
|
||||
}
|
||||
|
||||
return requiredExtensions.empty();
|
||||
}
|
||||
|
||||
bool Device::isPhysicalDeviceSuitable(const vk::PhysicalDevice& device) {
|
||||
QueueFamilyIndices indices = findQueueFamilies(device);
|
||||
|
||||
bool extensionsSupported = checkDeviceExtensionSupport(device);
|
||||
|
||||
bool swapchainAdequate = false;
|
||||
if (extensionsSupported) {
|
||||
SwapchainSupportDetails swapchainSupport = querySwapchainSupport(device);
|
||||
swapchainAdequate = !swapchainSupport.formats.empty() &&
|
||||
!swapchainSupport.presentModes.empty();
|
||||
}
|
||||
return indices.isComplete() && extensionsSupported && swapchainAdequate;
|
||||
}
|
||||
|
||||
void Device::pickPhysicalDevice() {
|
||||
// Get all physical devies
|
||||
std::vector<vk::PhysicalDevice> physicalDevices =
|
||||
instance->enumeratePhysicalDevices();
|
||||
|
||||
if (physicalDevices.empty()) {
|
||||
log::fatal("Failed to find any physical devices.");
|
||||
throw std::exception();
|
||||
}
|
||||
log::verbose("Physical Devices count: ", physicalDevices.size());
|
||||
|
||||
// Select first physical device that is suitable
|
||||
for (const auto& device : physicalDevices) {
|
||||
if (isPhysicalDeviceSuitable(device)) {
|
||||
this->physicalDevice = device;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (this->physicalDevice == VK_NULL_HANDLE) {
|
||||
log::fatal("Failed to find a suitable physical device");
|
||||
throw std::exception();
|
||||
}
|
||||
|
||||
this->properties = this->physicalDevice.getProperties();
|
||||
log::verbose("Physical Device: ", this->properties.deviceName);
|
||||
}
|
||||
|
||||
void Device::createLogicalDevice() {
|
||||
QueueFamilyIndices indices = findQueueFamilies(this->physicalDevice);
|
||||
|
||||
std::vector<vk::DeviceQueueCreateInfo> queueCreateInfos;
|
||||
std::set<uint32_t> uniqueQueueFamilies = {indices.graphicsFamily.value(),
|
||||
indices.presentFamily.value()};
|
||||
|
||||
float queuePriority = 1.0f;
|
||||
|
||||
for (uint32_t queueFamily : uniqueQueueFamilies) {
|
||||
queueCreateInfos.push_back(
|
||||
{vk::DeviceQueueCreateFlags(), queueFamily, 1, &queuePriority});
|
||||
}
|
||||
|
||||
auto deviceFeatures = vk::PhysicalDeviceFeatures();
|
||||
auto createInfo = vk::DeviceCreateInfo(
|
||||
vk::DeviceCreateFlags(), static_cast<uint32_t>(queueCreateInfos.size()),
|
||||
queueCreateInfos.data());
|
||||
createInfo.pEnabledFeatures = &deviceFeatures;
|
||||
|
||||
createInfo.enabledExtensionCount =
|
||||
static_cast<uint32_t>(this->enabledExtensions.size());
|
||||
createInfo.ppEnabledExtensionNames = this->enabledExtensions.data();
|
||||
|
||||
if (this->enableValidationLayers) {
|
||||
createInfo.enabledLayerCount =
|
||||
static_cast<uint32_t>(this->enabledLayers.size());
|
||||
createInfo.ppEnabledLayerNames = this->enabledLayers.data();
|
||||
}
|
||||
|
||||
try {
|
||||
this->device = this->physicalDevice.createDeviceUnique(createInfo);
|
||||
log::trace("created vk::Device.");
|
||||
} catch (const vk::SystemError& err) {
|
||||
log::fatal("failed to create logical device.");
|
||||
throw std::exception();
|
||||
}
|
||||
|
||||
this->graphicsQueue = device->getQueue(indices.graphicsFamily.value(), 0);
|
||||
this->presentQueue = device->getQueue(indices.presentFamily.value(), 0);
|
||||
}
|
||||
|
||||
SwapchainSupportDetails Device::querySwapchainSupport(
|
||||
vk::PhysicalDevice device) {
|
||||
SwapchainSupportDetails details;
|
||||
details.capabilities = device.getSurfaceCapabilitiesKHR(surface);
|
||||
details.formats = device.getSurfaceFormatsKHR(surface);
|
||||
details.presentModes = device.getSurfacePresentModesKHR(surface);
|
||||
|
||||
return details;
|
||||
}
|
||||
|
||||
void Device::createCommandPool() {
|
||||
QueueFamilyIndices queueFamilyIndices =
|
||||
findQueueFamilies(this->physicalDevice);
|
||||
|
||||
vk::CommandPoolCreateInfo createInfo = {};
|
||||
createInfo.flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer;
|
||||
createInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value();
|
||||
|
||||
try {
|
||||
this->commandPool = this->device->createCommandPool(createInfo);
|
||||
log::trace("created vk::CommandPool");
|
||||
} catch (const vk::SystemError& err) {
|
||||
log::fatal("failed to create vk::CommandPool");
|
||||
throw std::runtime_error("failed to create vk::CommandPool");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace hep
|
||||
@@ -0,0 +1,64 @@
|
||||
#include "window.hpp"
|
||||
|
||||
#include "key_event.hpp"
|
||||
#include "util/logger.hpp"
|
||||
|
||||
namespace hep {
|
||||
|
||||
Window::Window(int width, int height, const std::string& name)
|
||||
: width{width}, height{height}, name{name} {
|
||||
initialize();
|
||||
}
|
||||
|
||||
Window::~Window() {
|
||||
glfwDestroyWindow(window);
|
||||
glfwTerminate();
|
||||
}
|
||||
|
||||
void Window::createSurface(const vk::Instance& instance,
|
||||
vk::SurfaceKHR& surface) {
|
||||
VkSurfaceKHR rawSurface;
|
||||
if (glfwCreateWindowSurface(static_cast<VkInstance>(instance), window,
|
||||
nullptr, &rawSurface) != VK_SUCCESS) {
|
||||
log::fatal("Failed to create vk::SurfaceKHR.");
|
||||
throw std::exception();
|
||||
}
|
||||
|
||||
surface = vk::SurfaceKHR(rawSurface);
|
||||
log::trace("Created vk::SurfaceKHR.");
|
||||
}
|
||||
|
||||
void Window::keyEventCallback(GLFWwindow* window,
|
||||
int key,
|
||||
int scancode,
|
||||
int action,
|
||||
int mods) {
|
||||
if (action == GLFW_PRESS) {
|
||||
KeyPressedEvent event{static_cast<KeyCode>(key)};
|
||||
EventSystem::get().dispatch(event);
|
||||
} else if (action == GLFW_RELEASE) {
|
||||
KeyReleasedEvent event{static_cast<KeyCode>(key)};
|
||||
EventSystem::get().dispatch(event);
|
||||
}
|
||||
}
|
||||
|
||||
void Window::resizeCallback(GLFWwindow* window, int width, int height) {
|
||||
auto newWindow = reinterpret_cast<Window*>(glfwGetWindowUserPointer(window));
|
||||
newWindow->resized = true;
|
||||
newWindow->width = width;
|
||||
newWindow->height = height;
|
||||
}
|
||||
|
||||
void Window::initialize() {
|
||||
glfwInit();
|
||||
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
||||
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
|
||||
|
||||
this->window =
|
||||
glfwCreateWindow(width, height, name.c_str(), nullptr, nullptr);
|
||||
glfwSetWindowUserPointer(this->window, this);
|
||||
glfwSetFramebufferSizeCallback(this->window, resizeCallback);
|
||||
glfwSetKeyCallback(this->window, keyEventCallback);
|
||||
}
|
||||
|
||||
} // namespace hep
|
||||
@@ -0,0 +1,27 @@
|
||||
set(NAME testbed)
|
||||
set(ENGINE_NAME ${PROJECT_NAME})
|
||||
|
||||
file(GLOB_RECURSE SOURCES *.cpp src/*.cpp)
|
||||
|
||||
add_library(deps INTERFACE)
|
||||
|
||||
target_link_libraries(deps INTERFACE
|
||||
${ENGINE_NAME}
|
||||
vulkan
|
||||
shaderc
|
||||
glfw
|
||||
glm
|
||||
imgui
|
||||
)
|
||||
|
||||
target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_17)
|
||||
set_property(TARGET ${PROJECT_NAME} PROPERTY VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/build")
|
||||
|
||||
add_executable(testbed main.cpp)
|
||||
|
||||
target_link_libraries(testbed PRIVATE deps)
|
||||
|
||||
target_include_directories(testbed PRIVATE
|
||||
"${CMAKE_SOURCE_DIR}/engine/include"
|
||||
"${CMAKE_SOURCE_DIR}/external/imgui"
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
std::cout << __FILE__ << "::" << __LINE__ << '\n';
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
Vendored
+59
@@ -0,0 +1,59 @@
|
||||
# 1. Vulkan - shaderc, spirv-reflect
|
||||
add_library(vulkan INTERFACE)
|
||||
|
||||
find_package(Vulkan REQUIRED COMPONENTS shaderc_combined)
|
||||
|
||||
# Adding SPIRV-Reflect to vulkan interface
|
||||
target_sources(vulkan INTERFACE
|
||||
${Vulkan_INCLUDE_DIRS}/SPIRV-Reflect/spirv_reflect.c
|
||||
)
|
||||
|
||||
target_include_directories(vulkan INTERFACE
|
||||
${Vulkan_INCLUDE_DIRS}/SPIRV-Reflect
|
||||
)
|
||||
|
||||
target_link_libraries(vulkan INTERFACE
|
||||
Vulkan::Vulkan
|
||||
Vulkan::shaderc_combined
|
||||
)
|
||||
|
||||
# 3. GLFW (static)
|
||||
option(GLFW_BUILD_EXAMPLES "Build the GLFW example programs" OFF)
|
||||
option(GLFW_BUILD_TESTS "Build the GLFW test programs" OFF)
|
||||
option(GLFW_BUILD_DOCS "Build the GLFW documentation" OFF)
|
||||
option(GLFW_INSTALL "Generate installation target" OFF)
|
||||
option(GLFW_DOCUMENT_INTERNALS "Include internals in documentation" OFF)
|
||||
set(GLFW_VULKAN_STATIC ON CACHE BOOL "Link Vulkan statically with GLFW" FORCE)
|
||||
add_subdirectory(glfw)
|
||||
|
||||
# 4. GLM (static)
|
||||
add_library(glm INTERFACE)
|
||||
target_include_directories(glm INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/glm)
|
||||
|
||||
# 5. IMGUI (static)
|
||||
set(IMGUI_DIR ${CMAKE_CURRENT_SOURCE_DIR}/imgui)
|
||||
|
||||
add_library(imgui STATIC
|
||||
${IMGUI_DIR}/imgui.cpp
|
||||
${IMGUI_DIR}/imgui_draw.cpp
|
||||
${IMGUI_DIR}/imgui_demo.cpp # remove this
|
||||
${IMGUI_DIR}/imgui_tables.cpp
|
||||
${IMGUI_DIR}/imgui_widgets.cpp
|
||||
${IMGUI_DIR}/backends/imgui_impl_glfw.cpp
|
||||
${IMGUI_DIR}/backends/imgui_impl_vulkan.cpp
|
||||
)
|
||||
|
||||
target_include_directories(imgui PUBLIC
|
||||
${IMGUI_DIR}
|
||||
${IMGUI_DIR}/backends
|
||||
${Vulkan_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(imgui PRIVATE
|
||||
glfw
|
||||
vulkan
|
||||
)
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
target_compile_options(imgui PRIVATE -fPIC)
|
||||
endif()
|
||||
Reference in New Issue
Block a user