Start rewrite of vulkan backend (again)

This commit is contained in:
bailwillharr 2023-03-12 16:14:55 +00:00
parent 68d54667ec
commit 3e40ae3e1e
40 changed files with 1268 additions and 1478 deletions

View File

@ -2,7 +2,6 @@ cmake_minimum_required(VERSION 3.24)
# options # options
option(ENGINE_BUILD_TEST "Compile the test program" ON) option(ENGINE_BUILD_TEST "Compile the test program" ON)
option(ENGINE_BUILD_VULKAN "Use Vulkan 1.3 for graphics" ON)
project(engine LANGUAGES CXX project(engine LANGUAGES CXX
VERSION "0.1.0" VERSION "0.1.0"
@ -27,7 +26,15 @@ set(SRC_FILES
src/scene.cpp src/scene.cpp
src/gfx_device_vulkan.cpp src/gfx_device_vulkan.cpp
src/gfx_device_null.cpp
src/vulkan/device.cpp
src/vulkan/device.h
src/vulkan/gpu_allocator.cpp
src/vulkan/gpu_allocator.h
src/vulkan/instance.cpp
src/vulkan/instance.h
src/vulkan/swapchain.cpp
src/vulkan/swapchain.h
src/util/files.cpp src/util/files.cpp
src/util/model_loader.cpp src/util/model_loader.cpp
@ -90,6 +97,10 @@ source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/include" PREFIX "Include" FILES $
target_compile_definitions(${PROJECT_NAME} PRIVATE DEFINITIONS "ENGINE_EXPORTS") target_compile_definitions(${PROJECT_NAME} PRIVATE DEFINITIONS "ENGINE_EXPORTS")
if (WIN32)
target_compile_definitions(${PROJECT_NAME} PRIVATE DEFINITIONS "NOMINMAX") # stop windows.h conflicting with 'std::max'
endif()
set_property(TARGET ${PROJECT_NAME} PROPERTY WINDOWS_EXPORT_ALL_SYMBOLS ON) set_property(TARGET ${PROJECT_NAME} PROPERTY WINDOWS_EXPORT_ALL_SYMBOLS ON)
set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 20) set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 20)
@ -114,13 +125,6 @@ target_include_directories(${PROJECT_NAME} PRIVATE src)
configure_file(config.h.in config.h) configure_file(config.h.in config.h)
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
# figure out what graphics api to use
if (ENGINE_BUILD_VULKAN)
target_compile_definitions(${PROJECT_NAME} PRIVATE "ENGINE_BUILD_VULKAN")
else()
target_compile_definitions(${PROJECT_NAME} PRIVATE "ENGINE_BUILD_NULL")
endif()
# Build the test # Build the test
if (ENGINE_BUILD_TEST) if (ENGINE_BUILD_TEST)
add_subdirectory(test) add_subdirectory(test)
@ -134,24 +138,25 @@ if (MINGW)
target_link_libraries(${PROJECT_NAME} PUBLIC mingw32) target_link_libraries(${PROJECT_NAME} PUBLIC mingw32)
endif() endif()
if(ENGINE_BUILD_VULKAN) # Volk
# Volk set(VOLK_STATIC_DEFINES "")
set(VOLK_STATIC_DEFINES "") set(VOLK_PULL_IN_VULKAN ON)
set(VOLK_PULL_IN_VULKAN ON) set(VOLK_INSTALL OFF)
set(VOLK_INSTALL OFF) set(VOLK_HEADERS_ONLY OFF)
set(VOLK_HEADERS_ONLY OFF) if (WIN32)
add_subdirectory(dependencies/volk) set(VOLK_STATIC_DEFINES VK_USE_PLATFORM_WIN32_KHR)
target_link_libraries(${PROJECT_NAME} PRIVATE volk::volk) endif()
# Vulkan Memory Allocator add_subdirectory(dependencies/volk)
target_include_directories(${PROJECT_NAME} SYSTEM PRIVATE dependencies/VulkanMemoryAllocator/include) target_link_libraries(${PROJECT_NAME} PRIVATE volk::volk)
# shaderc # Vulkan Memory Allocator
if (MSVC) target_include_directories(${PROJECT_NAME} SYSTEM PRIVATE dependencies/VulkanMemoryAllocator/include)
# shaderc
if (MSVC)
include(FindVulkan) include(FindVulkan)
find_package(Vulkan COMPONENTS shaderc_combined) find_package(Vulkan COMPONENTS shaderc_combined)
target_link_libraries(${PROJECT_NAME} PRIVATE Vulkan::shaderc_combined) target_link_libraries(${PROJECT_NAME} PRIVATE Vulkan::shaderc_combined)
else() else()
target_link_libraries(${PROJECT_NAME} PRIVATE shaderc_shared) target_link_libraries(${PROJECT_NAME} PRIVATE shaderc_shared)
endif()
endif() endif()
# SDL2: # SDL2:

View File

@ -1,6 +0,0 @@
#!/bin/sh
set -e
cd Debug
cmake --build .
cd test
./enginetest $@

View File

@ -77,6 +77,8 @@ namespace engine {
bool m_enableFrameLimiter = true; bool m_enableFrameLimiter = true;
gfx::CommandBuffer *m_drawCommandBuffer;
/* resource stuff */ /* resource stuff */
std::unordered_map<size_t, std::unique_ptr<IResourceManager>> m_resourceManagers{}; std::unordered_map<size_t, std::unique_ptr<IResourceManager>> m_resourceManagers{};

View File

@ -90,5 +90,6 @@ namespace engine::gfx {
struct Pipeline; struct Pipeline;
struct Buffer; struct Buffer;
struct Texture; struct Texture;
struct CommandBuffer;
} }

View File

@ -19,12 +19,12 @@ namespace engine {
void getViewportSize(uint32_t *w, uint32_t *h); void getViewportSize(uint32_t *w, uint32_t *h);
// adds a draw call to the queue gfx::CommandBuffer* beginRender();
// vertexBuffer is required, indexBuffer can be NULL, uniformData is required void finishRender(gfx::CommandBuffer* commandBuffer);
void draw(const gfx::Pipeline* pipeline, const gfx::Buffer* vertexBuffer, const gfx::Buffer* indexBuffer, uint32_t count, const void* pushConstantData, size_t pushConstantSize, const gfx::Texture* texture);
// Call once per frame. Executes all queued draw calls and renders to the screen. void cmdBindPipeline(gfx::CommandBuffer* commandBuffer, const gfx::Pipeline* pipeline);
void renderFrame(); void cmdBindDescriptorSetTexture(gfx::CommandBuffer* commandBuffer, uint32_t set, uint32_t binding, const gfx::Texture* texture);
void cmdBindDescriptorSetBuffer(gfx::CommandBuffer* commandBuffer, uint32_t set, uint32_t binding, const gfx::Texture* texture);
// creates the equivalent of an OpenGL shader program & vertex attrib configuration // creates the equivalent of an OpenGL shader program & vertex attrib configuration
gfx::Pipeline* createPipeline(const char* vertShaderPath, const char* fragShaderPath, const gfx::VertexFormat& vertexFormat, uint64_t uniformBufferSize, bool alphaBlending, bool backfaceCulling); gfx::Pipeline* createPipeline(const char* vertShaderPath, const char* fragShaderPath, const gfx::VertexFormat& vertexFormat, uint64_t uniformBufferSize, bool alphaBlending, bool backfaceCulling);

View File

@ -8,9 +8,9 @@
#include <spdlog/spdlog.h> #include <spdlog/spdlog.h>
#define TRACE SPDLOG_TRACE #define LOG_TRACE SPDLOG_TRACE
#define DEBUG SPDLOG_DEBUG #define LOG_DEBUG SPDLOG_DEBUG
#define INFO SPDLOG_INFO #define LOG_INFO SPDLOG_INFO
#define WARN SPDLOG_WARN #define LOG_WARN SPDLOG_WARN
#define ERROR SPDLOG_ERROR #define LOG_ERROR SPDLOG_ERROR
#define CRITICAL SPDLOG_CRITICAL #define LOG_CRITICAL SPDLOG_CRITICAL

View File

@ -40,7 +40,7 @@ namespace engine {
spdlog::set_default_logger(logger); spdlog::set_default_logger(logger);
spdlog::flush_every(std::chrono::seconds(60)); spdlog::flush_every(std::chrono::seconds(60));
INFO("Created log with path: {}", log_path.string()); LOG_INFO("Created log with path: {}", log_path.string());
} }

View File

@ -1,10 +0,0 @@
#!/bin/sh
set -e
mkdir -p Debug
cd Debug
cmake -G Ninja -D CMAKE_BUILD_TYPE=Debug -D CMAKE_EXPORT_COMPILE_COMMANDS=ON ..
cd ..
ln -sf Debug/compile_commands.json .
mkdir -p Release
cd Release
cmake -G Ninja -D CMAKE_BUILD_TYPE=Release -D CMAKE_EXPORT_COMPILE_COMMANDS=ON ..

View File

@ -1,6 +0,0 @@
#!/bin/sh
set -e
cd Release
cmake --build .
cd test
./enginetest $@

View File

@ -79,13 +79,13 @@ namespace engine {
vertParams.hasUV0 = true; vertParams.hasUV0 = true;
auto texturedShader = std::make_unique<resources::Shader>( auto texturedShader = std::make_unique<resources::Shader>(
gfx(), gfx(),
getResourcePath("engine/shaders/textured.vert").c_str(), getResourcePath("engine/shaders/standard.vert").c_str(),
getResourcePath("engine/shaders/textured.frag").c_str(), getResourcePath("engine/shaders/standard.frag").c_str(),
vertParams, vertParams,
false, false,
true true
); );
getResourceManager<resources::Shader>()->addPersistent("engine.textured", std::move(texturedShader)); getResourceManager<resources::Shader>()->addPersistent("builtin.standard", std::move(texturedShader));
} }
{ {
resources::Shader::VertexParams vertParams{}; resources::Shader::VertexParams vertParams{};
@ -99,7 +99,7 @@ namespace engine {
false, false,
true true
); );
getResourceManager<resources::Shader>()->addPersistent("engine.skybox", std::move(texturedShader)); getResourceManager<resources::Shader>()->addPersistent("builtin.skybox", std::move(texturedShader));
} }
{ {
auto whiteTexture = std::make_unique<resources::Texture>( auto whiteTexture = std::make_unique<resources::Texture>(
@ -109,7 +109,7 @@ namespace engine {
false, false,
false false
); );
getResourceManager<resources::Texture>()->addPersistent("engine.white", std::move(whiteTexture)); getResourceManager<resources::Texture>()->addPersistent("builtin.white", std::move(whiteTexture));
} }
} }
@ -117,7 +117,7 @@ namespace engine {
void Application::gameLoop() void Application::gameLoop()
{ {
TRACE("Begin game loop..."); LOG_TRACE("Begin game loop...");
constexpr int FPS_LIMIT = 240; constexpr int FPS_LIMIT = 240;
constexpr auto FRAMETIME_LIMIT = std::chrono::nanoseconds(1000000000 / FPS_LIMIT); constexpr auto FRAMETIME_LIMIT = std::chrono::nanoseconds(1000000000 / FPS_LIMIT);
@ -129,6 +129,9 @@ namespace engine {
// single-threaded game loop // single-threaded game loop
while (m_window->isRunning()) { while (m_window->isRunning()) {
/* begin rendering */
m_drawCommandBuffer = m_gfx->beginRender();
/* logic */ /* logic */
m_sceneManager->updateActiveScene(m_window->dt()); m_sceneManager->updateActiveScene(m_window->dt());
@ -139,12 +142,12 @@ namespace engine {
uint64_t now = m_window->getNanos(); uint64_t now = m_window->getNanos();
if (now - lastTick >= 1000000000LL * 5LL) [[unlikely]] { if (now - lastTick >= 1000000000LL * 5LL) [[unlikely]] {
lastTick = now; lastTick = now;
INFO("fps: {}", m_window->getAvgFPS()); LOG_INFO("fps: {}", m_window->getAvgFPS());
m_window->resetAvgFPS(); m_window->resetAvgFPS();
} }
/* draw */ /* draw */
m_gfx->renderFrame(); m_gfx->finishRender(m_drawCommandBuffer);
/* poll events */ /* poll events */
m_window->getInputAndEvents(); m_window->getInputAndEvents();

View File

@ -1,127 +0,0 @@
// The implementation of the graphics layer using Vulkan 1.3.
#ifdef ENGINE_BUILD_NULL
#include "gfx_device.hpp"
#include <assert.h>
#include <unordered_set>
#include <array>
#include <fstream>
#include <filesystem>
#include <optional>
#include <queue>
#include <map>
#include <iostream>
namespace engine {
struct GFXDevice::Impl {
int FRAMECOUNT = 0;
};
GFXDevice::GFXDevice(const char* appName, const char* appVersion, SDL_Window* window, bool vsync)
{
(void)appName;
(void)appVersion;
(void)window;
(void)vsync;
pimpl = std::make_unique<Impl>();
}
GFXDevice::~GFXDevice()
{
}
void GFXDevice::getViewportSize(uint32_t *w, uint32_t *h)
{
*w = 1024;
*h = 768;
}
void GFXDevice::draw(const gfx::Pipeline* pipeline, const gfx::Buffer* vertexBuffer, const gfx::Buffer* indexBuffer, uint32_t count, const void* pushConstantData, size_t pushConstantSize, const gfx::Texture* texture)
{
(void)pipeline;
(void)vertexBuffer;
(void)indexBuffer;
(void)count;
(void)pushConstantData;
(void)pushConstantSize;
(void)texture;
}
void GFXDevice::renderFrame()
{
pimpl->FRAMECOUNT++;
}
gfx::Pipeline* GFXDevice::createPipeline(const char* vertShaderPath, const char* fragShaderPath, const gfx::VertexFormat& vertexFormat, uint64_t uniformBufferSize, bool alphaBlending, bool backfaceCulling)
{
(void)vertShaderPath;
(void)fragShaderPath;
(void)vertexFormat;
(void)uniformBufferSize;
(void)alphaBlending;
(void)backfaceCulling;
return nullptr;
}
void GFXDevice::destroyPipeline(const gfx::Pipeline* pipeline)
{
(void)pipeline;
}
void GFXDevice::updateUniformBuffer(const gfx::Pipeline* pipeline, const void* data, size_t size, uint32_t offset)
{
(void)pipeline;
(void)data;
(void)size;
(void)offset;
}
gfx::Buffer* GFXDevice::createBuffer(gfx::BufferType type, uint64_t size, const void* data)
{
(void)type;
(void)size;
(void)data;
return nullptr;
}
void GFXDevice::destroyBuffer(const gfx::Buffer* buffer)
{
(void)buffer;
}
gfx::Texture* GFXDevice::createTexture(
const void* imageData,
uint32_t width,
uint32_t height,
gfx::TextureFilter minFilter,
gfx::TextureFilter magFilter,
gfx::MipmapSetting mipmapSetting,
bool useAnisotropy)
{
(void)imageData;
(void)width;
(void)height;
(void)minFilter;
(void)magFilter;
(void)mipmapSetting;
(void)useAnisotropy;
return nullptr;
}
void GFXDevice::destroyTexture(const gfx::Texture* texture)
{
(void)texture;
}
void GFXDevice::waitIdle()
{
}
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -41,7 +41,7 @@ namespace engine::resources {
m_vb = m_gfx->createBuffer(gfx::BufferType::VERTEX, vertices.size() * sizeof(Vertex), vertices.data()); m_vb = m_gfx->createBuffer(gfx::BufferType::VERTEX, vertices.size() * sizeof(Vertex), vertices.data());
m_ib = m_gfx->createBuffer(gfx::BufferType::INDEX, indices.size() * sizeof(uint32_t), indices.data()); m_ib = m_gfx->createBuffer(gfx::BufferType::INDEX, indices.size() * sizeof(uint32_t), indices.data());
m_count = (uint32_t)indices.size(); m_count = (uint32_t)indices.size();
INFO("Loaded mesh, vertices: {}, indices: {}", vertices.size(), indices.size()); LOG_INFO("Loaded mesh, vertices: {}, indices: {}", vertices.size(), indices.size());
} }
} }

View File

@ -38,7 +38,7 @@ namespace engine::resources {
m_pipeline = m_gfx->createPipeline(vertPath, fragPath, vertFormat, sizeof(glm::mat4) * 2, alphaBlending, cullBackFace); m_pipeline = m_gfx->createPipeline(vertPath, fragPath, vertFormat, sizeof(glm::mat4) * 2, alphaBlending, cullBackFace);
INFO("Loaded shader: {}, vertex attribs: {}", vertPath, vertFormat.attributeDescriptions.size()); LOG_INFO("Loaded shader: {}, vertex attribs: {}", vertPath, vertFormat.attributeDescriptions.size());
} }
Shader::~Shader() Shader::~Shader()

View File

@ -57,7 +57,7 @@ Texture::Texture(GFXDevice* gfxDevice, const std::string& path, Filtering filter
mipmapSetting, mipmapSetting,
anisotropyEnable); anisotropyEnable);
INFO("Loaded texture: {}, width: {} height: {}", path, width, height); LOG_INFO("Loaded texture: {}, width: {} height: {}", path, width, height);
} }

View File

@ -79,7 +79,7 @@ namespace engine {
m_dynamicAABBs.reserve(size); m_dynamicAABBs.reserve(size);
m_possibleCollisions.reserve(size); m_possibleCollisions.reserve(size);
m_collisionInfos.reserve(size); m_collisionInfos.reserve(size);
TRACE("added entity {} to collider system", entity); LOG_TRACE("added entity {} to collider system", entity);
} }
void PhysicsSystem::onUpdate(float ts) void PhysicsSystem::onUpdate(float ts)

View File

@ -76,6 +76,7 @@ namespace engine {
pushConsts.model = t->worldMatrix; pushConsts.model = t->worldMatrix;
pushConsts.view = viewMatrix; pushConsts.view = viewMatrix;
/*
gfx->draw( gfx->draw(
r->material->getShader()->getPipeline(), r->material->getShader()->getPipeline(),
r->mesh->getVB(), r->mesh->getVB(),
@ -84,7 +85,7 @@ namespace engine {
&pushConsts, &pushConsts,
sizeof(pushConsts), sizeof(pushConsts),
r->material->m_texture->getHandle() r->material->m_texture->getHandle()
); );*/
} }

View File

@ -77,11 +77,11 @@ namespace engine::util {
auto child = scene->createEntity("_mesh" + std::to_string(i), parentObj); auto child = scene->createEntity("_mesh" + std::to_string(i), parentObj);
auto childRenderer = scene->addComponent<RenderableComponent>(child); auto childRenderer = scene->addComponent<RenderableComponent>(child);
childRenderer->mesh = meshes[parentNode->mMeshes[i]]; childRenderer->mesh = meshes[parentNode->mMeshes[i]];
childRenderer->material = std::make_shared<resources::Material>(scene->app()->getResource<resources::Shader>("engine.textured")); childRenderer->material = std::make_shared<resources::Material>(scene->app()->getResource<resources::Shader>("builtin.standard"));
if (textures.contains(meshTextureIndices[parentNode->mMeshes[i]])) { if (textures.contains(meshTextureIndices[parentNode->mMeshes[i]])) {
childRenderer->material->m_texture = textures.at(meshTextureIndices[parentNode->mMeshes[i]]); childRenderer->material->m_texture = textures.at(meshTextureIndices[parentNode->mMeshes[i]]);
} else { } else {
childRenderer->material->m_texture = scene->app()->getResource<resources::Texture>("engine.white"); childRenderer->material->m_texture = scene->app()->getResource<resources::Texture>("builtin.white");
} }
} }
@ -105,7 +105,7 @@ namespace engine::util {
public: public:
void write(const char* message) override { void write(const char* message) override {
(void)message; (void)message;
TRACE("ASSIMP: {}", message); LOG_TRACE("ASSIMP: {}", message);
} }
}; };
@ -160,23 +160,23 @@ namespace engine::util {
assert(scene->HasLights() == false); assert(scene->HasLights() == false);
assert(scene->hasSkeletons() == false); assert(scene->hasSkeletons() == false);
TRACE("material count: {}, mesh count: {}", scene->mNumMaterials, scene->mNumMeshes); LOG_TRACE("material count: {}, mesh count: {}", scene->mNumMaterials, scene->mNumMeshes);
std::map<int, std::shared_ptr<resources::Texture>> textures{}; std::map<int, std::shared_ptr<resources::Texture>> textures{};
for (uint32_t i = 0; i < scene->mNumMaterials; i++) { for (uint32_t i = 0; i < scene->mNumMaterials; i++) {
const aiMaterial* m = scene->mMaterials[i]; const aiMaterial* m = scene->mMaterials[i];
TRACE("Material {}:", i); LOG_TRACE("Material {}:", i);
TRACE(" Name: {}", m->GetName().C_Str()); LOG_TRACE(" Name: {}", m->GetName().C_Str());
for (uint32_t j = 0; j < m->mNumProperties; j++) { for (uint32_t j = 0; j < m->mNumProperties; j++) {
[[maybe_unused]] const aiMaterialProperty* p = m->mProperties[j]; [[maybe_unused]] const aiMaterialProperty* p = m->mProperties[j];
TRACE(" prop {}, key: {}", j, p->mKey.C_Str()); LOG_TRACE(" prop {}, key: {}", j, p->mKey.C_Str());
} }
if (aiGetMaterialTextureCount(m, aiTextureType_DIFFUSE) >= 1) { if (aiGetMaterialTextureCount(m, aiTextureType_DIFFUSE) >= 1) {
aiString texPath{}; aiString texPath{};
aiGetMaterialTexture(m, aiTextureType_DIFFUSE, 0, &texPath); aiGetMaterialTexture(m, aiTextureType_DIFFUSE, 0, &texPath);
TRACE(" Diffuse tex: {}", texPath.C_Str()); LOG_TRACE(" Diffuse tex: {}", texPath.C_Str());
std::filesystem::path absPath = path; std::filesystem::path absPath = path;
absPath = absPath.parent_path(); absPath = absPath.parent_path();
absPath /= texPath.C_Str(); absPath /= texPath.C_Str();
@ -185,7 +185,7 @@ namespace engine::util {
parent->app()->gfx(), absPath.string(), parent->app()->gfx(), absPath.string(),
resources::Texture::Filtering::TRILINEAR, true, true); resources::Texture::Filtering::TRILINEAR, true, true);
} catch (const std::runtime_error&) { } catch (const std::runtime_error&) {
textures[i] = parent->app()->getResource<resources::Texture>("engine.white"); textures[i] = parent->app()->getResource<resources::Texture>("builtin.white");
} }
} }
} }
@ -197,8 +197,8 @@ namespace engine::util {
meshMaterialIndices.push_back(m->mMaterialIndex); meshMaterialIndices.push_back(m->mMaterialIndex);
std::vector<Vertex> vertices(m->mNumVertices); std::vector<Vertex> vertices(m->mNumVertices);
std::vector<uint32_t> indices(m->mNumFaces * 3); std::vector<uint32_t> indices(m->mNumFaces * 3);
TRACE("Mesh {}: vertex count {}", i, vertices.size()); LOG_TRACE("Mesh {}: vertex count {}", i, vertices.size());
TRACE("Mesh {}: index count {}", i, indices.size()); LOG_TRACE("Mesh {}: index count {}", i, indices.size());
for (uint32_t j = 0; j < vertices.size(); j++) { for (uint32_t j = 0; j < vertices.size(); j++) {
Vertex v{}; Vertex v{};
@ -230,7 +230,7 @@ namespace engine::util {
buildGraph(textures, meshes, meshMaterialIndices, scene->mRootNode, parent, obj); buildGraph(textures, meshes, meshMaterialIndices, scene->mRootNode, parent, obj);
INFO("Loaded model: {}, meshes: {}, textures: {}", scene->GetShortFilename(path.c_str()), meshes.size(), textures.size()); LOG_INFO("Loaded model: {}, meshes: {}, textures: {}", scene->GetShortFilename(path.c_str()), meshes.size(), textures.size());
Assimp::DefaultLogger::kill(); Assimp::DefaultLogger::kill();
return obj; return obj;

333
src/vulkan/device.cpp Normal file
View File

@ -0,0 +1,333 @@
#include <stdexcept>
#include <vector>
#include <array>
#include <string>
#include <assert.h>
#include <Volk/volk.h>
#include "device.h"
namespace engine {
/* chooses a device, creates it, gets its function pointers, and creates command pools */
Device createDevice(VkInstance instance, DeviceRequirements requirements, VkSurfaceKHR surface)
{
Device d{};
// enumerate physical devices
uint32_t physDeviceCount = 0;
VkResult res;
res = vkEnumeratePhysicalDevices(instance, &physDeviceCount, nullptr);
assert(res == VK_SUCCESS);
if (physDeviceCount == 0) {
throw std::runtime_error("No GPU found with vulkan support!");
}
std::vector<VkPhysicalDevice> physicalDevices(physDeviceCount);
res = vkEnumeratePhysicalDevices(instance, &physDeviceCount, physicalDevices.data());
assert(res == VK_SUCCESS);
for (VkPhysicalDevice physDev : physicalDevices) {
// first, check extension support
uint32_t extensionCount;
res = vkEnumerateDeviceExtensionProperties(physDev, nullptr, &extensionCount, nullptr);
assert(res == VK_SUCCESS);
std::vector<VkExtensionProperties> availableExtensions(extensionCount);
res = vkEnumerateDeviceExtensionProperties(physDev, nullptr, &extensionCount, availableExtensions.data());
assert(res == VK_SUCCESS);
bool foundRequiredExtensions = true;
for (const char* extToFind : requirements.requiredExtensions) {
bool extFound = false;
for (const auto& ext : availableExtensions) {
if (strcmp(extToFind, ext.extensionName) == 0) {
extFound = true;
break;
}
}
if (!extFound) {
foundRequiredExtensions = false;
break;
}
}
if (!foundRequiredExtensions) continue; // NEXT!
VkPhysicalDeviceProperties devProps;
vkGetPhysicalDeviceProperties(physDev, &devProps);
// check that the device supports vulkan 1.3
if (devProps.apiVersion < VK_API_VERSION_1_3) {
continue;
}
/* check features */
VkPhysicalDeviceFeatures devFeatures;
vkGetPhysicalDeviceFeatures(physDev, &devFeatures);
{
if (requirements.requiredFeatures.robustBufferAccess)
if (devFeatures.robustBufferAccess == VK_FALSE) continue;
if (requirements.requiredFeatures.fullDrawIndexUint32)
if (devFeatures.fullDrawIndexUint32 == VK_FALSE) continue;
if (requirements.requiredFeatures.imageCubeArray == VK_TRUE)
if (devFeatures.imageCubeArray == VK_FALSE) continue;
if (requirements.requiredFeatures.independentBlend == VK_TRUE)
if (devFeatures.independentBlend == VK_FALSE) continue;
if (requirements.requiredFeatures.geometryShader == VK_TRUE)
if (devFeatures.geometryShader == VK_FALSE) continue;
if (requirements.requiredFeatures.tessellationShader == VK_TRUE)
if (devFeatures.tessellationShader == VK_FALSE) continue;
if (requirements.requiredFeatures.sampleRateShading == VK_TRUE)
if (devFeatures.sampleRateShading == VK_FALSE) continue;
if (requirements.requiredFeatures.dualSrcBlend == VK_TRUE)
if (devFeatures.dualSrcBlend == VK_FALSE) continue;
if (requirements.requiredFeatures.logicOp == VK_TRUE)
if (devFeatures.logicOp == VK_FALSE) continue;
if (requirements.requiredFeatures.multiDrawIndirect == VK_TRUE)
if (devFeatures.multiDrawIndirect == VK_FALSE) continue;
if (requirements.requiredFeatures.drawIndirectFirstInstance == VK_TRUE)
if (devFeatures.drawIndirectFirstInstance == VK_FALSE) continue;
if (requirements.requiredFeatures.depthClamp == VK_TRUE)
if (devFeatures.depthClamp == VK_FALSE) continue;
if (requirements.requiredFeatures.depthBiasClamp == VK_TRUE)
if (devFeatures.depthBiasClamp == VK_FALSE) continue;
if (requirements.requiredFeatures.fillModeNonSolid == VK_TRUE)
if (devFeatures.fillModeNonSolid == VK_FALSE) continue;
if (requirements.requiredFeatures.depthBounds == VK_TRUE)
if (devFeatures.depthBounds == VK_FALSE) continue;
if (requirements.requiredFeatures.wideLines == VK_TRUE)
if (devFeatures.wideLines == VK_FALSE) continue;
if (requirements.requiredFeatures.largePoints == VK_TRUE)
if (devFeatures.largePoints == VK_FALSE) continue;
if (requirements.requiredFeatures.alphaToOne == VK_TRUE)
if (devFeatures.alphaToOne == VK_FALSE) continue;
if (requirements.requiredFeatures.multiViewport == VK_TRUE)
if (devFeatures.multiViewport == VK_FALSE) continue;
if (requirements.requiredFeatures.samplerAnisotropy == VK_TRUE)
if (devFeatures.samplerAnisotropy == VK_FALSE) continue;
if (requirements.requiredFeatures.textureCompressionETC2 == VK_TRUE)
if (devFeatures.textureCompressionETC2 == VK_FALSE) continue;
if (requirements.requiredFeatures.textureCompressionASTC_LDR == VK_TRUE)
if (devFeatures.textureCompressionASTC_LDR == VK_FALSE) continue;
if (requirements.requiredFeatures.textureCompressionBC == VK_TRUE)
if (devFeatures.textureCompressionBC == VK_FALSE) continue;
if (requirements.requiredFeatures.occlusionQueryPrecise == VK_TRUE)
if (devFeatures.occlusionQueryPrecise == VK_FALSE) continue;
if (requirements.requiredFeatures.pipelineStatisticsQuery == VK_TRUE)
if (devFeatures.pipelineStatisticsQuery == VK_FALSE) continue;
if (requirements.requiredFeatures.vertexPipelineStoresAndAtomics == VK_TRUE)
if (devFeatures.vertexPipelineStoresAndAtomics == VK_FALSE) continue;
if (requirements.requiredFeatures.fragmentStoresAndAtomics == VK_TRUE)
if (devFeatures.fragmentStoresAndAtomics == VK_FALSE) continue;
if (requirements.requiredFeatures.shaderTessellationAndGeometryPointSize == VK_TRUE)
if (devFeatures.shaderTessellationAndGeometryPointSize == VK_FALSE) continue;
if (requirements.requiredFeatures.shaderImageGatherExtended == VK_TRUE)
if (devFeatures.shaderImageGatherExtended == VK_FALSE) continue;
if (requirements.requiredFeatures.shaderStorageImageExtendedFormats == VK_TRUE)
if (devFeatures.shaderStorageImageExtendedFormats == VK_FALSE) continue;
if (requirements.requiredFeatures.shaderStorageImageMultisample == VK_TRUE)
if (devFeatures.shaderStorageImageMultisample == VK_FALSE) continue;
if (requirements.requiredFeatures.shaderStorageImageReadWithoutFormat == VK_TRUE)
if (devFeatures.shaderStorageImageReadWithoutFormat == VK_FALSE) continue;
if (requirements.requiredFeatures.shaderStorageImageWriteWithoutFormat == VK_TRUE)
if (devFeatures.shaderStorageImageWriteWithoutFormat == VK_FALSE) continue;
if (requirements.requiredFeatures.shaderUniformBufferArrayDynamicIndexing == VK_TRUE)
if (devFeatures.shaderUniformBufferArrayDynamicIndexing == VK_FALSE) continue;
if (requirements.requiredFeatures.shaderSampledImageArrayDynamicIndexing == VK_TRUE)
if (devFeatures.shaderSampledImageArrayDynamicIndexing == VK_FALSE) continue;
if (requirements.requiredFeatures.shaderStorageBufferArrayDynamicIndexing == VK_TRUE)
if (devFeatures.shaderStorageBufferArrayDynamicIndexing == VK_FALSE) continue;
if (requirements.requiredFeatures.shaderStorageImageArrayDynamicIndexing == VK_TRUE)
if (devFeatures.shaderStorageImageArrayDynamicIndexing == VK_FALSE) continue;
if (requirements.requiredFeatures.shaderClipDistance == VK_TRUE)
if (devFeatures.shaderClipDistance == VK_FALSE) continue;
if (requirements.requiredFeatures.shaderCullDistance == VK_TRUE)
if (devFeatures.shaderCullDistance == VK_FALSE) continue;
if (requirements.requiredFeatures.shaderFloat64 == VK_TRUE)
if (devFeatures.shaderFloat64 == VK_FALSE) continue;
if (requirements.requiredFeatures.shaderInt64 == VK_TRUE)
if (devFeatures.shaderInt64 == VK_FALSE) continue;
if (requirements.requiredFeatures.shaderInt16 == VK_TRUE)
if (devFeatures.shaderInt16 == VK_FALSE) continue;
if (requirements.requiredFeatures.shaderResourceResidency == VK_TRUE)
if (devFeatures.shaderResourceResidency == VK_FALSE) continue;
if (requirements.requiredFeatures.shaderResourceMinLod == VK_TRUE)
if (devFeatures.shaderResourceMinLod == VK_FALSE) continue;
if (requirements.requiredFeatures.sparseBinding == VK_TRUE)
if (devFeatures.sparseBinding == VK_FALSE) continue;
if (requirements.requiredFeatures.sparseResidencyBuffer == VK_TRUE)
if (devFeatures.sparseResidencyBuffer == VK_FALSE) continue;
if (requirements.requiredFeatures.sparseResidencyImage2D == VK_TRUE)
if (devFeatures.sparseResidencyImage2D == VK_FALSE) continue;
if (requirements.requiredFeatures.sparseResidencyImage3D == VK_TRUE)
if (devFeatures.sparseResidencyImage3D == VK_FALSE) continue;
if (requirements.requiredFeatures.sparseResidency2Samples == VK_TRUE)
if (devFeatures.sparseResidency2Samples == VK_FALSE) continue;
if (requirements.requiredFeatures.sparseResidency4Samples == VK_TRUE)
if (devFeatures.sparseResidency4Samples == VK_FALSE) continue;
if (requirements.requiredFeatures.sparseResidency8Samples == VK_TRUE)
if (devFeatures.sparseResidency8Samples == VK_FALSE) continue;
if (requirements.requiredFeatures.sparseResidency16Samples == VK_TRUE)
if (devFeatures.sparseResidency16Samples == VK_FALSE) continue;
if (requirements.requiredFeatures.sparseResidencyAliased == VK_TRUE)
if (devFeatures.sparseResidencyAliased == VK_FALSE) continue;
if (requirements.requiredFeatures.variableMultisampleRate == VK_TRUE)
if (devFeatures.variableMultisampleRate == VK_FALSE) continue;
if (requirements.requiredFeatures.inheritedQueries == VK_TRUE)
if (devFeatures.inheritedQueries == VK_FALSE) continue;
}
if (requirements.sampledImageLinearFilter == true) {
// check for linear filtering for mipmaps
VkFormatProperties formatProperties{};
vkGetPhysicalDeviceFormatProperties(physDev, VK_FORMAT_R8G8B8A8_SRGB, &formatProperties);
if (!(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)) {
continue;
}
}
/* USE THIS PHYSICAL DEVICE */
d.physicalDevice = physDev;
d.properties = devProps;
d.features = requirements.requiredFeatures; // to be safe, only advertise requested features
//deviceInfo->features = devFeatures;
break;
}
if (d.physicalDevice == VK_NULL_HANDLE) {
throw std::runtime_error("No suitable Vulkan physical device found");
}
/* queue families */
uint32_t queueFamilyCount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(d.physicalDevice, &queueFamilyCount, nullptr);
std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
vkGetPhysicalDeviceQueueFamilyProperties(d.physicalDevice, &queueFamilyCount, queueFamilies.data());
// find a graphics/present family
uint32_t graphicsFamily = UINT32_MAX;
for (size_t i = 0; i < queueFamilies.size(); i++) {
VkQueueFamilyProperties p = queueFamilies[i];
if (p.queueCount < 2) break; // need one queue for presenting and one-or-more for rendering
if (p.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
VkBool32 supportsPresent;
res = vkGetPhysicalDeviceSurfaceSupportKHR(d.physicalDevice, static_cast<uint32_t>(i), surface, &supportsPresent);
if (res != VK_SUCCESS) throw std::runtime_error("Failed to check for queue family present support!");
if (supportsPresent) {
graphicsFamily = static_cast<uint32_t>(i);
break;
}
}
}
if (graphicsFamily == UINT32_MAX) throw std::runtime_error("Unable to find a graphics/present queue family!");
// find a transfer queue family (image layout transitions, buffer upload)
uint32_t transferFamily = UINT32_MAX;
for (size_t i = 0; i < queueFamilies.size(); i++) {
VkQueueFamilyProperties p = queueFamilies[i];
if (((p.queueFlags & VK_QUEUE_TRANSFER_BIT) != 0) &&
((p.queueFlags & VK_QUEUE_COMPUTE_BIT) == 0) &&
((p.queueFlags & VK_QUEUE_GRAPHICS_BIT) == 0)) {
transferFamily = static_cast<uint32_t>(i);
break;
}
}
if (transferFamily == UINT32_MAX) throw std::runtime_error("Unable to find a transfer queue family!");
// queue priorities
std::vector<float> graphicsQueuePriorities(queueFamilies[graphicsFamily].queueCount);
std::fill(graphicsQueuePriorities.begin(), graphicsQueuePriorities.end(), 1.0f);
std::vector<float> transferQueuePriorities(queueFamilies[transferFamily].queueCount);
std::fill(transferQueuePriorities.begin(), transferQueuePriorities.end(), 1.0f);
std::array<VkDeviceQueueCreateInfo, 2> queueCreateInfos{
VkDeviceQueueCreateInfo{
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.queueFamilyIndex = graphicsFamily,
.queueCount = queueFamilies[graphicsFamily].queueCount,
.pQueuePriorities = graphicsQueuePriorities.data(),
},
VkDeviceQueueCreateInfo{
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.queueFamilyIndex = transferFamily,
.queueCount = queueFamilies[transferFamily].queueCount,
.pQueuePriorities = transferQueuePriorities.data(),
}
};
/* create device now */
VkDeviceCreateInfo deviceCreateInfo{
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size()),
.pQueueCreateInfos = queueCreateInfos.data(),
.enabledLayerCount = 0, // deprecated and ignored
.ppEnabledLayerNames = nullptr, // deprecated and ignored
.enabledExtensionCount = static_cast<uint32_t>(requirements.requiredExtensions.size()),
.ppEnabledExtensionNames = requirements.requiredExtensions.data(),
.pEnabledFeatures = &requirements.requiredFeatures
};
res = vkCreateDevice(d.physicalDevice, &deviceCreateInfo, nullptr, &d.device);
if (res != VK_SUCCESS) {
throw std::runtime_error("Unable to create Vulkan logical device, error code: " + std::to_string(res));
}
volkLoadDevice(d.device);
vkGetDeviceQueue(d.device, graphicsFamily, 0, &d.queues.presentQueue);
d.queues.drawQueues.resize(queueFamilies[graphicsFamily].queueCount - 1);
for (uint32_t i = 0; i < d.queues.drawQueues.size(); i++) {
vkGetDeviceQueue(d.device, graphicsFamily, i + 1, &d.queues.drawQueues[i]);
}
d.queues.transferQueues.resize(queueFamilies[transferFamily].queueCount);
for (uint32_t i = 0; i < d.queues.transferQueues.size(); i++) {
vkGetDeviceQueue(d.device, transferFamily, i, &d.queues.transferQueues[i]);
}
d.queues.presentAndDrawQueueFamily = graphicsFamily;
d.queues.transferQueueFamily = transferFamily;
/* generate command pools */
VkCommandPoolCreateInfo poolCreateInfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.pNext = nullptr,
.flags = 0, // set individually after
.queueFamilyIndex = 0, // set individually after
};
// present queue does not need a command pool as it does not use command buffers
// draw command pools:
poolCreateInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
poolCreateInfo.queueFamilyIndex = graphicsFamily;
res = vkCreateCommandPool(d.device, &poolCreateInfo, nullptr, &d.commandPools.draw);
if (res != VK_SUCCESS) throw std::runtime_error("Failed to create command pool");
// transfer command pools:
poolCreateInfo.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT; // buffers from this pool are often short-lived,
// as is usually the case for transfer operations
poolCreateInfo.queueFamilyIndex = transferFamily;
res = vkCreateCommandPool(d.device, &poolCreateInfo, nullptr, &d.commandPools.transfer);
if (res != VK_SUCCESS) throw std::runtime_error("Failed to create command pool");
return d;
}
void destroyDevice(Device device)
{
vkDestroyCommandPool(device.device, device.commandPools.transfer, nullptr);
vkDestroyCommandPool(device.device, device.commandPools.draw, nullptr);
vkDestroyDevice(device.device, nullptr);
}
}

39
src/vulkan/device.h Normal file
View File

@ -0,0 +1,39 @@
#pragma once
#include <vector>
#include <Volk/volk.h>
namespace engine {
struct DeviceRequirements {
std::vector<const char*> requiredExtensions;
VkPhysicalDeviceFeatures requiredFeatures;
bool sampledImageLinearFilter;
};
struct Device {
VkDevice device = VK_NULL_HANDLE;
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
VkPhysicalDeviceProperties properties{};
VkPhysicalDeviceFeatures features{};
struct DeviceQueues {
VkQueue presentQueue = VK_NULL_HANDLE;
std::vector<VkQueue> drawQueues{};
std::vector<VkQueue> transferQueues{};
uint32_t presentAndDrawQueueFamily = UINT32_MAX;
uint32_t transferQueueFamily = UINT32_MAX;
} queues{};
struct CommandPools {
VkCommandPool draw = VK_NULL_HANDLE; // use with the drawQueues
VkCommandPool transfer = VK_NULL_HANDLE; // use with the transferQueues
} commandPools{};
};
Device createDevice(VkInstance instance, DeviceRequirements requirements, VkSurfaceKHR surface);
void destroyDevice(Device device);
}

View File

@ -0,0 +1,74 @@
#include <assert.h>
#include <Volk/volk.h>
#define VMA_STATIC_VULKAN_FUNCTIONS 0
#define VMA_DYNAMIC_VULKAN_FUNCTIONS 0
#define VMA_VULKAN_VERSION 1003000
#define VMA_IMPLEMENTATION
#include <vma/vk_mem_alloc.h>
#include "gpu_allocator.h"
namespace engine {
VmaAllocator createAllocator(VkInstance instance, VkDevice device, VkPhysicalDevice physicalDevice)
{
VmaVulkanFunctions functions{
.vkGetInstanceProcAddr = nullptr,
.vkGetDeviceProcAddr = nullptr,
.vkGetPhysicalDeviceProperties = vkGetPhysicalDeviceProperties,
.vkGetPhysicalDeviceMemoryProperties = vkGetPhysicalDeviceMemoryProperties,
.vkAllocateMemory = vkAllocateMemory,
.vkFreeMemory = vkFreeMemory,
.vkMapMemory = vkMapMemory,
.vkUnmapMemory = vkUnmapMemory,
.vkFlushMappedMemoryRanges = vkFlushMappedMemoryRanges,
.vkInvalidateMappedMemoryRanges = vkInvalidateMappedMemoryRanges,
.vkBindBufferMemory = vkBindBufferMemory,
.vkBindImageMemory = vkBindImageMemory,
.vkGetBufferMemoryRequirements = vkGetBufferMemoryRequirements,
.vkGetImageMemoryRequirements = vkGetImageMemoryRequirements,
.vkCreateBuffer = vkCreateBuffer,
.vkDestroyBuffer = vkDestroyBuffer,
.vkCreateImage = vkCreateImage,
.vkDestroyImage = vkDestroyImage,
.vkCmdCopyBuffer = vkCmdCopyBuffer,
.vkGetBufferMemoryRequirements2KHR = vkGetBufferMemoryRequirements2,
.vkGetImageMemoryRequirements2KHR = vkGetImageMemoryRequirements2,
.vkBindBufferMemory2KHR = vkBindBufferMemory2,
.vkBindImageMemory2KHR = vkBindImageMemory2,
.vkGetPhysicalDeviceMemoryProperties2KHR = vkGetPhysicalDeviceMemoryProperties2,
.vkGetDeviceBufferMemoryRequirements = vkGetDeviceBufferMemoryRequirements,
.vkGetDeviceImageMemoryRequirements = vkGetDeviceImageMemoryRequirements,
};
VmaAllocatorCreateInfo createInfo{
.flags = 0,
.physicalDevice = physicalDevice,
.device = device,
.preferredLargeHeapBlockSize = 0,
.pAllocationCallbacks = nullptr,
.pDeviceMemoryCallbacks = nullptr,
.pHeapSizeLimit = nullptr,
.pVulkanFunctions = &functions,
.instance = instance,
.vulkanApiVersion = VK_API_VERSION_1_3,
.pTypeExternalMemoryHandleTypes = nullptr
};
[[maybe_unused]] VkResult res;
VmaAllocator allocator;
res = vmaCreateAllocator(&createInfo, &allocator);
assert(res == VK_SUCCESS);
return allocator;
}
void destroyAllocator(VmaAllocator allocator)
{
vmaDestroyAllocator(allocator);
}
}

View File

@ -0,0 +1,9 @@
#pragma once
#include <vma/vk_mem_alloc.h>
namespace engine {
VmaAllocator createAllocator(VkInstance instance, VkDevice device, VkPhysicalDevice physicalDevice);
void destroyAllocator(VmaAllocator allocator);
}

200
src/vulkan/instance.cpp Normal file
View File

@ -0,0 +1,200 @@
#include <vector>
#include <stdexcept>
#include <string>
#include <assert.h>
#include <SDL2/SDL_vulkan.h>
#include "util.hpp"
#include "config.h"
#include "log.hpp"
#include "instance.h"
namespace engine {
static std::vector<const char*> getWindowExtensions(SDL_Window* window)
{
[[maybe_unused]] SDL_bool res;
unsigned int sdlExtensionCount = 0;
res = SDL_Vulkan_GetInstanceExtensions(window, &sdlExtensionCount, nullptr);
assert(res == SDL_TRUE);
std::vector<const char*> requiredExtensions(sdlExtensionCount);
res = SDL_Vulkan_GetInstanceExtensions(window, &sdlExtensionCount, requiredExtensions.data());
assert(res == SDL_TRUE);
return requiredExtensions;
}
static const char* getValidationLayer()
{
const char* const VALIDATION_LAYER_NAME = "VK_LAYER_KHRONOS_validation";
[[maybe_unused]] VkResult res;
uint32_t layerCount;
res = vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
assert(res == VK_SUCCESS);
std::vector<VkLayerProperties> layers(layerCount);
res = vkEnumerateInstanceLayerProperties(&layerCount, layers.data());
assert(res == VK_SUCCESS);
// find validation layer and print all layers to log
for (const auto& layer : layers) {
if (strncmp(layer.layerName, VALIDATION_LAYER_NAME, 256) == 0) {
return VALIDATION_LAYER_NAME;
}
}
throw std::runtime_error("Unable to find validation layer!");
}
static VkBool32 messengerCallback(
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageTypes,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* pUserData)
{
(void)pUserData;
std::string msgType{};
if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT)
msgType += " (GENERAL)";
if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT)
msgType += " (PERF.)";
if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT)
msgType += " (VALID.)";
switch (messageSeverity) {
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT:
LOG_DEBUG("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage);
break;
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT:
LOG_INFO("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage);
break;
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT:
LOG_WARN("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage);
break;
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:
LOG_ERROR("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage);
break;
default:
break;
}
return VK_FALSE;
}
static VkDebugUtilsMessengerCreateInfoEXT getDebugMessengerCreateInfo()
{
VkDebugUtilsMessengerCreateInfoEXT debugMessengerInfo{
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
.pNext = nullptr,
.flags = 0,
.messageSeverity = 0,
.messageType =
VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT,
.pfnUserCallback = messengerCallback,
.pUserData = nullptr,
};
enum class MessageSeverity {
SEV_VERBOSE,
SEV_INFO,
SEV_WARNING,
SEV_ERROR // windows.h defines ERROR annoyingly
};
constexpr MessageSeverity MESSAGE_LEVEL = MessageSeverity::SEV_WARNING;
switch (MESSAGE_LEVEL) {
case MessageSeverity::SEV_VERBOSE:
debugMessengerInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT;
[[fallthrough]];
case MessageSeverity::SEV_INFO:
debugMessengerInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT;
[[fallthrough]];
case MessageSeverity::SEV_WARNING:
debugMessengerInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT;
[[fallthrough]];
case MessageSeverity::SEV_ERROR:
debugMessengerInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
[[fallthrough]];
default:
break;
}
return debugMessengerInfo;
}
Instance createVulkanInstance(SDL_Window* window, const char* appName, const char* appVersion)
{
Instance instance;
// get the both the engine and application versions
int appVersionMajor = 0, appVersionMinor = 0, appVersionPatch = 0;
versionFromCharArray(appVersion, &appVersionMajor, &appVersionMinor, &appVersionPatch);
int engineVersionMajor = 0, engineVersionMinor = 0, engineVersionPatch = 0;
versionFromCharArray(ENGINE_VERSION, &engineVersionMajor, &engineVersionMinor, &engineVersionPatch);
VkApplicationInfo applicationInfo{
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
.pNext = nullptr,
.pApplicationName = appName,
.applicationVersion = VK_MAKE_VERSION(appVersionMajor, appVersionMinor, appVersionPatch),
.pEngineName = "engine",
.engineVersion = VK_MAKE_VERSION(engineVersionMajor, engineVersionMinor, engineVersionPatch),
.apiVersion = VK_API_VERSION_1_3,
};
const std::vector<const char*> windowExtensions = getWindowExtensions(window);
std::vector<const char*> instanceExtensionsToUse = windowExtensions;
instanceExtensionsToUse.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
const char* const validationLayer = getValidationLayer();
VkDebugUtilsMessengerCreateInfoEXT debugMessengerInfo = getDebugMessengerCreateInfo();
VkInstanceCreateInfo instanceInfo{
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pNext = &debugMessengerInfo,
.flags = 0,
.pApplicationInfo = &applicationInfo,
.enabledLayerCount = 1,
.ppEnabledLayerNames = &validationLayer,
.enabledExtensionCount = (uint32_t)instanceExtensionsToUse.size(),
.ppEnabledExtensionNames = instanceExtensionsToUse.data(),
};
VkResult res;
res = vkCreateInstance(&instanceInfo, nullptr, &instance.instance);
if (res == VK_ERROR_INCOMPATIBLE_DRIVER) {
throw std::runtime_error("The graphics driver is incompatible with vulkan");
}
else if (res != VK_SUCCESS) {
throw std::runtime_error("vkCreateInstance failed: " + std::to_string(res));
}
volkLoadInstanceOnly(instance.instance);
// create the debug messenger
VkDebugUtilsMessengerCreateInfoEXT createInfo = getDebugMessengerCreateInfo();
res = vkCreateDebugUtilsMessengerEXT(instance.instance, &createInfo, nullptr, &instance.debugMessenger);
if (res != VK_SUCCESS) {
throw std::runtime_error("vkCreateDebugUtilsMessengerExt failed: " + std::to_string(res));
}
return instance;
}
void destroyVulkanInstance(Instance instance)
{
vkDestroyDebugUtilsMessengerEXT(instance.instance, instance.debugMessenger, nullptr);
vkDestroyInstance(instance.instance, nullptr);
}
}

17
src/vulkan/instance.h Normal file
View File

@ -0,0 +1,17 @@
#pragma once
#include <SDL2/SDL_video.h>
#include <Volk/volk.h>
namespace engine {
struct Instance {
VkInstance instance = VK_NULL_HANDLE;
VkDebugUtilsMessengerEXT debugMessenger = VK_NULL_HANDLE;
};
Instance createVulkanInstance(SDL_Window* window, const char* appName, const char* appVersion);
void destroyVulkanInstance(Instance instance);
}

250
src/vulkan/swapchain.cpp Normal file
View File

@ -0,0 +1,250 @@
#include <stdexcept>
#include <vector>
#include <algorithm>
#include <assert.h>
#include <SDL2/SDL_vulkan.h>
#include "swapchain.h"
namespace engine {
void createSwapchain(Swapchain* sc, const SwapchainInfo& info)
{
sc->device = info.device;
printf("Recreating swapchain!\n");
// get surface caps and features
VkResult res;
res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(info.physicalDevice, info.surface, &sc->surfaceCapabilities);
if (res != VK_SUCCESS) throw std::runtime_error("Unable to get surface capabilities!");
// check there is at least one supported surface format
uint32_t surfaceFormatCount = 0;
std::vector<VkSurfaceFormatKHR> formats{};
res = vkGetPhysicalDeviceSurfaceFormatsKHR(info.physicalDevice, info.surface, &surfaceFormatCount, nullptr);
assert(res == VK_SUCCESS);
formats.resize(surfaceFormatCount);
res = vkGetPhysicalDeviceSurfaceFormatsKHR(info.physicalDevice, info.surface, &surfaceFormatCount, formats.data());
assert(res == VK_SUCCESS);
sc->surfaceFormat = formats[0];
for (const auto& format : formats) {
if (format.format == VK_FORMAT_B8G8R8A8_SRGB &&
format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
sc->surfaceFormat = format; // prefer using srgb non linear colors
}
}
// check there is at least one supported present mode
uint32_t surfacePresentModeCount = 0;
std::vector<VkPresentModeKHR> presentModes{};
res = vkGetPhysicalDeviceSurfacePresentModesKHR(info.physicalDevice, info.surface, &surfacePresentModeCount, nullptr);
assert(res == VK_SUCCESS);
presentModes.resize(surfacePresentModeCount);
res = vkGetPhysicalDeviceSurfacePresentModesKHR(info.physicalDevice, info.surface, &surfacePresentModeCount, presentModes.data());
assert(res == VK_SUCCESS);
sc->presentMode = VK_PRESENT_MODE_FIFO_KHR; // This mode is always available
if (info.vsync == true) {
if (info.waitForPresent == false) {
for (const auto& presMode : presentModes) {
if (presMode == VK_PRESENT_MODE_MAILBOX_KHR) {
sc->presentMode = presMode; // this mode allows V-sync without fixing FPS to refresh rate
}
}
}
}
else {
for (const auto& presMode : presentModes) {
if (presMode == VK_PRESENT_MODE_IMMEDIATE_KHR) {
sc->presentMode = presMode; // V-sync off
}
}
}
/* get min image count */
uint32_t minImageCount = sc->surfaceCapabilities.minImageCount + 1;
if (sc->surfaceCapabilities.maxImageCount > 0 && minImageCount > sc->surfaceCapabilities.maxImageCount) {
minImageCount = sc->surfaceCapabilities.maxImageCount;
}
/* get extent */
VkExtent2D oldExtent = sc->extent;
if (sc->surfaceCapabilities.currentExtent.width != std::numeric_limits<uint32_t>::max()) {
sc->extent = sc->surfaceCapabilities.currentExtent;
}
else {
// if fb size isn't already found, get it from SDL
int width, height;
SDL_Vulkan_GetDrawableSize(info.window, &width, &height);
sc->extent.width = static_cast<uint32_t>(width);
sc->extent.height = static_cast<uint32_t>(height);
sc->extent.width = std::clamp(
sc->extent.width,
sc->surfaceCapabilities.minImageExtent.width, sc->surfaceCapabilities.maxImageExtent.width);
sc->extent.height = std::clamp(
sc->extent.height,
sc->surfaceCapabilities.minImageExtent.height, sc->surfaceCapabilities.maxImageExtent.height);
}
if (sc->extent.width == 0 || sc->extent.height == 0) { // preserve extent if the window is minimised
sc->extent = oldExtent;
}
/* TODO: delete old framebuffers and image views */
/* create swapchain */
VkSwapchainCreateInfoKHR scInfo{
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
.pNext = nullptr,
.flags = 0,
.surface = info.surface,
.minImageCount = minImageCount,
.imageFormat = sc->surfaceFormat.format,
.imageColorSpace = sc->surfaceFormat.colorSpace,
.imageExtent = sc->extent,
.imageArrayLayers = 1,
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0, // ignored
.pQueueFamilyIndices = nullptr, // ignored
.preTransform = sc->surfaceCapabilities.currentTransform,
.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
.presentMode = sc->presentMode,
.clipped = VK_TRUE, // discard parts of the screen not visible
.oldSwapchain = sc->swapchain
};
res = vkCreateSwapchainKHR(sc->device, &scInfo, nullptr, &sc->swapchain);
if (res != VK_SUCCESS) throw std::runtime_error("Failed to create swapchain!");
/* if it existed, destroy the old swapchain */
if (scInfo.oldSwapchain != VK_NULL_HANDLE) {
vkDestroySwapchainKHR(info.device, scInfo.oldSwapchain, nullptr);
}
/* create the render pass */
if (sc->renderpass != VK_NULL_HANDLE) {
vkDestroyRenderPass(sc->device, sc->renderpass, nullptr);
}
VkAttachmentDescription colorAttachment{
.flags = 0,
.format = sc->surfaceFormat.format,
.samples = VK_SAMPLE_COUNT_1_BIT,
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, // ignored
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, // ignored
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
};
VkAttachmentReference colorAttachmentRef{
.attachment = 0,
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
};
VkSubpassDescription subpass{
.flags = 0,
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
.inputAttachmentCount = 0,
.pInputAttachments = nullptr,
.colorAttachmentCount = 1,
.pColorAttachments = &colorAttachmentRef,
.pResolveAttachments = nullptr,
.pDepthStencilAttachment = nullptr,
.preserveAttachmentCount = 0,
.pPreserveAttachments = nullptr,
};
VkSubpassDependency dependency{
.srcSubpass = VK_SUBPASS_EXTERNAL,
.dstSubpass = 0,
.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
.srcAccessMask = 0,
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
.dependencyFlags = 0
};
VkRenderPassCreateInfo renderPassInfo{
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.attachmentCount = 1,
.pAttachments = &colorAttachment,
.subpassCount = 1,
.pSubpasses = &subpass,
.dependencyCount = 1,
.pDependencies = &dependency,
};
res = vkCreateRenderPass(sc->device, &renderPassInfo, nullptr, &sc->renderpass);
if (res != EXIT_SUCCESS) throw std::runtime_error("Failed to create renderpass!");
// get all the image handles
uint32_t swapchainImageCount = 0;
res = vkGetSwapchainImagesKHR(info.device, sc->swapchain, &swapchainImageCount, nullptr);
assert(res == VK_SUCCESS);
std::vector<VkImage> swapchainImages(swapchainImageCount);
res = vkGetSwapchainImagesKHR(info.device, sc->swapchain, &swapchainImageCount, swapchainImages.data());
assert(res == VK_SUCCESS);
/* create image view and framebuffer for each image */
sc->images.resize(swapchainImageCount);
for (uint32_t i = 0; i < swapchainImageCount; i++) {
auto& [image, view, framebuffer] = sc->images.at(i);
if (view != VK_NULL_HANDLE) vkDestroyImageView(sc->device, view, nullptr);
if (framebuffer != VK_NULL_HANDLE) vkDestroyFramebuffer(sc->device, framebuffer, nullptr);
image = swapchainImages[i];
/* make the image view */
VkImageViewCreateInfo viewInfo{};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.pNext = nullptr;
viewInfo.flags = 0;
viewInfo.image = image;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.format = sc->surfaceFormat.format;
viewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
viewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
viewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
viewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
viewInfo.subresourceRange.baseMipLevel = 0;
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = 1;
VkResult res = vkCreateImageView(sc->device, &viewInfo, nullptr, &view);
if (res != VK_SUCCESS) throw std::runtime_error("Failed to create image view from swapchain image!");
VkFramebufferCreateInfo fbInfo{};
fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
fbInfo.pNext = nullptr;
fbInfo.flags = 0;
fbInfo.renderPass = sc->renderpass;
fbInfo.attachmentCount = 1;
fbInfo.pAttachments = &view;
fbInfo.width = sc->extent.width;
fbInfo.height = sc->extent.height;
fbInfo.layers = 1;
res = vkCreateFramebuffer(sc->device, &fbInfo, nullptr, &framebuffer);
if (res != VK_SUCCESS) throw std::runtime_error("Failed to create framebuffer for swapchain image!");
}
}
void destroySwapchain(const Swapchain& sc)
{
for (const auto& [image, view, framebuffer] : sc.images) {
vkDestroyFramebuffer(sc.device, framebuffer, nullptr);
vkDestroyImageView(sc.device, view, nullptr);
}
vkDestroyRenderPass(sc.device, sc.renderpass, nullptr);
vkDestroySwapchainKHR(sc.device, sc.swapchain, nullptr);
}
}

34
src/vulkan/swapchain.h Normal file
View File

@ -0,0 +1,34 @@
#pragma once
#include <tuple>
#include <SDL2/SDL_vulkan.h>
#include <Volk/volk.h>
namespace engine {
struct Swapchain {
VkSwapchainKHR swapchain = VK_NULL_HANDLE;
VkDevice device = VK_NULL_HANDLE; // the associated device
VkSurfaceFormatKHR surfaceFormat{};
VkSurfaceCapabilitiesKHR surfaceCapabilities{};
VkPresentModeKHR presentMode{};
VkExtent2D extent{};
VkRenderPass renderpass = VK_NULL_HANDLE;
std::vector<std::tuple<VkImage, VkImageView, VkFramebuffer>> images{};
};
struct SwapchainInfo {
VkDevice device;
VkPhysicalDevice physicalDevice;
VkSurfaceKHR surface;
SDL_Window* window;
bool vsync;
bool waitForPresent;
};
void createSwapchain(Swapchain* sc, const SwapchainInfo& info);
void destroySwapchain(const Swapchain& sc);
}

View File

@ -24,12 +24,8 @@ namespace engine {
Uint32 windowFlags = SDL_WINDOW_SHOWN; Uint32 windowFlags = SDL_WINDOW_SHOWN;
#ifdef ENGINE_BUILD_VULKAN // use Vulkan 1.3
windowFlags |= SDL_WINDOW_VULKAN; windowFlags |= SDL_WINDOW_VULKAN;
#endif
#ifdef ENGINE_BUILD_OPENGL
windowFlags |= SDL_WINDOW_OPENGL;
#endif
if (m_resizable) { if (m_resizable) {
windowFlags |= SDL_WINDOW_RESIZABLE; windowFlags |= SDL_WINDOW_RESIZABLE;

View File

@ -19,7 +19,8 @@ set(GAME_SOURCES
) )
if (WIN32) if (WIN32)
add_executable(${PROJECT_NAME} WIN32 ${GAME_SOURCES} "game.rc") #add_executable(${PROJECT_NAME} WIN32 ${GAME_SOURCES} "game.rc")
add_executable(${PROJECT_NAME} ${GAME_SOURCES})
else() else()
add_executable(${PROJECT_NAME} ${GAME_SOURCES}) add_executable(${PROJECT_NAME} ${GAME_SOURCES})
endif() endif()

BIN
test/res/models/uvsphere.dae (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 MiB

View File

@ -154,7 +154,7 @@ void CameraControllerSystem::onUpdate(float ts)
" z: " + std::to_string(t->position.z) " z: " + std::to_string(t->position.z)
}; };
m_scene->app()->window()->infoBox("POSITION", pos_string); m_scene->app()->window()->infoBox("POSITION", pos_string);
INFO("position: " + pos_string); LOG_INFO("position: " + pos_string);
} }
if (m_scene->app()->inputManager()->getButtonPress("fullscreen")) { if (m_scene->app()->inputManager()->getButtonPress("fullscreen")) {

View File

@ -40,7 +40,7 @@ static void configureInputs(engine::InputManager* inputManager)
void playGame(bool enableFrameLimiter) void playGame(bool enableFrameLimiter)
{ {
INFO("FPS limiter: {}", enableFrameLimiter ? "ON" : "OFF"); LOG_INFO("FPS limiter: {}", enableFrameLimiter ? "ON" : "OFF");
engine::gfx::GraphicsSettings graphicsSettings{}; engine::gfx::GraphicsSettings graphicsSettings{};
graphicsSettings.vsync = true; graphicsSettings.vsync = true;
@ -56,6 +56,8 @@ void playGame(bool enableFrameLimiter)
auto myScene = app.sceneManager()->createEmptyScene(); auto myScene = app.sceneManager()->createEmptyScene();
#if 0
/* create camera */ /* create camera */
{ {
myScene->registerComponent<CameraControllerComponent>(); myScene->registerComponent<CameraControllerComponent>();
@ -96,48 +98,19 @@ void playGame(bool enableFrameLimiter)
{ {
uint32_t skybox = myScene->createEntity("skybox"); uint32_t skybox = myScene->createEntity("skybox");
auto skyboxRenderable = myScene->addComponent<engine::RenderableComponent>(skybox); auto skyboxRenderable = myScene->addComponent<engine::RenderableComponent>(skybox);
skyboxRenderable->material = std::make_unique<engine::resources::Material>(app.getResource<engine::resources::Shader>("engine.skybox")); skyboxRenderable->material = std::make_unique<engine::resources::Material>(app.getResource<engine::resources::Shader>("builtin.skybox"));
skyboxRenderable->material->m_texture = spaceTexture; skyboxRenderable->material->m_texture = spaceTexture;
// skyboxRenderable->mesh = genSphereMesh(app.gfx(), 1.0f, 50, true); // skyboxRenderable->mesh = genSphereMesh(app.gfx(), 1.0f, 50, true);
skyboxRenderable->mesh = genCuboidMesh(app.gfx(), 2.0f, 2.0f, 2.0f, 1.0f, true); skyboxRenderable->mesh = genCuboidMesh(app.gfx(), 2.0f, 2.0f, 2.0f, 1.0f, true);
myScene->getComponent<engine::TransformComponent>(skybox)->position = { -1.0f, -1.0f, -1.0f }; myScene->getComponent<engine::TransformComponent>(skybox)->position = { -1.0f, -1.0f, -1.0f };
} }
/* enemy cube */
{
uint32_t enemy = myScene->createEntity("enemy");
auto enemyRenderable = myScene->addComponent<engine::RenderableComponent>(enemy);
enemyRenderable->material = std::make_unique<engine::resources::Material>(app.getResource<engine::resources::Shader>("engine.textured"));
enemyRenderable->material->m_texture = app.getResource<engine::resources::Texture>("engine.white");
enemyRenderable->mesh = genCuboidMesh(app.gfx(), 10.0f, 10.0f, 10.0f);
auto enemyTransform = myScene->getComponent<engine::TransformComponent>(enemy);
enemyTransform->position.x = 10.0f;
enemyTransform->position.y = 0.0f;
enemyTransform->position.z = 14.0f;
auto enemyCollider = myScene->addComponent<engine::ColliderComponent>(enemy);
enemyCollider->isStatic = true;
enemyCollider->aabb = { { 0.0f, 0.0f, 0.0f }, { 10.0f, 10.0f, 10.0f } }; // A box enclosing the sphere
}
/* sun */
{
uint32_t sun = myScene->createEntity("sun");
auto sunRenderable = myScene->addComponent<engine::RenderableComponent>(sun);
sunRenderable->material = std::make_unique<engine::resources::Material>(app.getResource<engine::resources::Shader>("engine.textured"));
sunRenderable->material->m_texture = app.getResource<engine::resources::Texture>("engine.white");
sunRenderable->mesh = genSphereMesh(app.gfx(), 500.0f, 32, false, true);
auto sunTransform = myScene->getComponent<engine::TransformComponent>(sun);
sunTransform->position.x = 2000.0f;
sunTransform->position.y = 2000.0f;
sunTransform->position.z = -2000.0f;
}
/* floor */ /* floor */
{ {
uint32_t floor = myScene->createEntity("floor"); uint32_t floor = myScene->createEntity("floor");
myScene->getComponent<engine::TransformComponent>(floor)->position = glm::vec3{-5000.0f, -1.0f, -5000.0f}; myScene->getComponent<engine::TransformComponent>(floor)->position = glm::vec3{-5000.0f, -1.0f, -5000.0f};
auto floorRenderable = myScene->addComponent<engine::RenderableComponent>(floor); auto floorRenderable = myScene->addComponent<engine::RenderableComponent>(floor);
floorRenderable->material = std::make_shared<engine::resources::Material>(app.getResource<engine::resources::Shader>("engine.textured")); floorRenderable->material = std::make_shared<engine::resources::Material>(app.getResource<engine::resources::Shader>("builtin.standard"));
floorRenderable->material->m_texture = grassTexture; floorRenderable->material->m_texture = grassTexture;
floorRenderable->mesh = genCuboidMesh(app.gfx(), 10000.0f, 1.0f, 10000.0f, 5000.0f); floorRenderable->mesh = genCuboidMesh(app.gfx(), 10000.0f, 1.0f, 10000.0f, 5000.0f);
auto floorCollider = myScene->addComponent<engine::ColliderComponent>(floor); auto floorCollider = myScene->addComponent<engine::ColliderComponent>(floor);
@ -145,44 +118,7 @@ void playGame(bool enableFrameLimiter)
floorCollider->aabb = { { 0.0f, 0.0f, 0.0f }, { 10000.0f, 1.0f, 10000.0f } }; floorCollider->aabb = { { 0.0f, 0.0f, 0.0f }, { 10000.0f, 1.0f, 10000.0f } };
} }
// cubes! #endif
if (false) { // disabled
constexpr int SIZE = 10;
const uint32_t cubeParent = myScene->createEntity("cubeParent");
engine::TransformComponent* cubeParentTransform = myScene->getComponent<engine::TransformComponent>(cubeParent);
cubeParentTransform->position = { 100.0f, 0.0f, 100.0f };
cubeParentTransform->scale = { 100.0f, 100.0f, 100.0f };
std::shared_ptr<engine::resources::Mesh> cubeMesh = genCuboidMesh(app.gfx(), 0.1f, 0.1f, 0.1f);
const auto cubeMaterial = std::make_shared<engine::resources::Material>(app.getResource<engine::resources::Shader>("engine.textured"));
cubeMaterial->m_texture = app.getResource<engine::resources::Texture>("engine.white");
uint32_t cubes[SIZE][SIZE][SIZE];
for (int x = 0; x < SIZE; x++) {
for (int y = 0; y < SIZE; y++) {
for (int z = 0; z < SIZE; z++) {
const uint32_t cube = myScene->createEntity("cube" + std::to_string(x * 100 + y * 10 + z), cubeParent);
auto transform = myScene->getComponent<engine::TransformComponent>(cube);
auto renderable = myScene->addComponent<engine::RenderableComponent>(cube);
auto collider = myScene->addComponent<engine::ColliderComponent>(cube);
collider->aabb.pos1 = { 0.0f, 0.0f, 0.0f };
collider->aabb.pos2 = { 10.0f, 10.0f, 10.0f };
collider->isStatic = true;
collider->isTrigger = false;
transform->position = { (float)x, (float)y, (float)z };
renderable->mesh = cubeMesh;
renderable->material = cubeMaterial;
cubes[x][y][z] = cube;
}
}
}
(void)cubes;
}
app.gameLoop(); app.gameLoop();

View File

@ -1,39 +1,32 @@
#include "config.h" #include "config.h"
#include "game.hpp" #include "game.hpp"
// engine
#include "logger.hpp" #include "logger.hpp"
#include "window.hpp" #include "window.hpp"
// standard library
#include <exception> #include <exception>
#include <string.h>
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
bool enableFrameLimiter = true; bool enableFrameLimiter = true;
if (argc == 2) { if (argc == 2) {
const std::string arg { argv[1] }; if (strcmp(argv[2], "nofpslimit") == 0) enableFrameLimiter = false;
if (arg == "nofpslimit") enableFrameLimiter = false;
} }
engine::setupLog(PROJECT_NAME); engine::setupLog(PROJECT_NAME);
INFO("{} v{}", PROJECT_NAME, PROJECT_VERSION); LOG_INFO("{} v{}", PROJECT_NAME, PROJECT_VERSION);
try { try {
playGame(enableFrameLimiter); playGame(enableFrameLimiter);
} }
catch (const std::exception& e) { catch (const std::exception& e) {
LOG_CRITICAL("{}", e.what());
CRITICAL("{}", e.what());
engine::Window::errorBox(e.what()); engine::Window::errorBox(e.what());
#ifndef NDEBUG
fputs(e.what(), stderr);
fputc('\n', stderr);
#endif
return EXIT_FAILURE; return EXIT_FAILURE;
} }