mirror of
https://github.com/bailwillharr/engine.git
synced 2024-09-21 04:51:18 +00:00
Start rewrite of vulkan backend (again)
This commit is contained in:
parent
68d54667ec
commit
3e40ae3e1e
@ -2,7 +2,6 @@ cmake_minimum_required(VERSION 3.24)
|
||||
|
||||
# options
|
||||
option(ENGINE_BUILD_TEST "Compile the test program" ON)
|
||||
option(ENGINE_BUILD_VULKAN "Use Vulkan 1.3 for graphics" ON)
|
||||
|
||||
project(engine LANGUAGES CXX
|
||||
VERSION "0.1.0"
|
||||
@ -27,7 +26,15 @@ set(SRC_FILES
|
||||
src/scene.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/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")
|
||||
|
||||
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 CXX_STANDARD 20)
|
||||
@ -114,13 +125,6 @@ target_include_directories(${PROJECT_NAME} PRIVATE src)
|
||||
configure_file(config.h.in config.h)
|
||||
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
|
||||
if (ENGINE_BUILD_TEST)
|
||||
add_subdirectory(test)
|
||||
@ -134,12 +138,14 @@ if (MINGW)
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC mingw32)
|
||||
endif()
|
||||
|
||||
if(ENGINE_BUILD_VULKAN)
|
||||
# Volk
|
||||
set(VOLK_STATIC_DEFINES "")
|
||||
set(VOLK_PULL_IN_VULKAN ON)
|
||||
set(VOLK_INSTALL OFF)
|
||||
set(VOLK_HEADERS_ONLY OFF)
|
||||
if (WIN32)
|
||||
set(VOLK_STATIC_DEFINES VK_USE_PLATFORM_WIN32_KHR)
|
||||
endif()
|
||||
add_subdirectory(dependencies/volk)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE volk::volk)
|
||||
# Vulkan Memory Allocator
|
||||
@ -152,7 +158,6 @@ if(ENGINE_BUILD_VULKAN)
|
||||
else()
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE shaderc_shared)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# SDL2:
|
||||
find_package(SDL2)
|
||||
|
6
debug.sh
6
debug.sh
@ -1,6 +0,0 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
cd Debug
|
||||
cmake --build .
|
||||
cd test
|
||||
./enginetest $@
|
@ -77,6 +77,8 @@ namespace engine {
|
||||
|
||||
bool m_enableFrameLimiter = true;
|
||||
|
||||
gfx::CommandBuffer *m_drawCommandBuffer;
|
||||
|
||||
/* resource stuff */
|
||||
|
||||
std::unordered_map<size_t, std::unique_ptr<IResourceManager>> m_resourceManagers{};
|
||||
|
@ -90,5 +90,6 @@ namespace engine::gfx {
|
||||
struct Pipeline;
|
||||
struct Buffer;
|
||||
struct Texture;
|
||||
struct CommandBuffer;
|
||||
|
||||
}
|
||||
|
@ -19,12 +19,12 @@ namespace engine {
|
||||
|
||||
void getViewportSize(uint32_t *w, uint32_t *h);
|
||||
|
||||
// adds a draw call to the queue
|
||||
// vertexBuffer is required, indexBuffer can be NULL, uniformData is required
|
||||
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);
|
||||
gfx::CommandBuffer* beginRender();
|
||||
void finishRender(gfx::CommandBuffer* commandBuffer);
|
||||
|
||||
// Call once per frame. Executes all queued draw calls and renders to the screen.
|
||||
void renderFrame();
|
||||
void cmdBindPipeline(gfx::CommandBuffer* commandBuffer, const gfx::Pipeline* pipeline);
|
||||
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
|
||||
gfx::Pipeline* createPipeline(const char* vertShaderPath, const char* fragShaderPath, const gfx::VertexFormat& vertexFormat, uint64_t uniformBufferSize, bool alphaBlending, bool backfaceCulling);
|
||||
|
@ -8,9 +8,9 @@
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#define TRACE SPDLOG_TRACE
|
||||
#define DEBUG SPDLOG_DEBUG
|
||||
#define INFO SPDLOG_INFO
|
||||
#define WARN SPDLOG_WARN
|
||||
#define ERROR SPDLOG_ERROR
|
||||
#define CRITICAL SPDLOG_CRITICAL
|
||||
#define LOG_TRACE SPDLOG_TRACE
|
||||
#define LOG_DEBUG SPDLOG_DEBUG
|
||||
#define LOG_INFO SPDLOG_INFO
|
||||
#define LOG_WARN SPDLOG_WARN
|
||||
#define LOG_ERROR SPDLOG_ERROR
|
||||
#define LOG_CRITICAL SPDLOG_CRITICAL
|
@ -40,7 +40,7 @@ namespace engine {
|
||||
spdlog::set_default_logger(logger);
|
||||
spdlog::flush_every(std::chrono::seconds(60));
|
||||
|
||||
INFO("Created log with path: {}", log_path.string());
|
||||
LOG_INFO("Created log with path: {}", log_path.string());
|
||||
|
||||
}
|
||||
|
||||
|
10
prebuild.sh
10
prebuild.sh
@ -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 ..
|
@ -1,6 +0,0 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
cd Release
|
||||
cmake --build .
|
||||
cd test
|
||||
./enginetest $@
|
@ -79,13 +79,13 @@ namespace engine {
|
||||
vertParams.hasUV0 = true;
|
||||
auto texturedShader = std::make_unique<resources::Shader>(
|
||||
gfx(),
|
||||
getResourcePath("engine/shaders/textured.vert").c_str(),
|
||||
getResourcePath("engine/shaders/textured.frag").c_str(),
|
||||
getResourcePath("engine/shaders/standard.vert").c_str(),
|
||||
getResourcePath("engine/shaders/standard.frag").c_str(),
|
||||
vertParams,
|
||||
false,
|
||||
true
|
||||
);
|
||||
getResourceManager<resources::Shader>()->addPersistent("engine.textured", std::move(texturedShader));
|
||||
getResourceManager<resources::Shader>()->addPersistent("builtin.standard", std::move(texturedShader));
|
||||
}
|
||||
{
|
||||
resources::Shader::VertexParams vertParams{};
|
||||
@ -99,7 +99,7 @@ namespace engine {
|
||||
false,
|
||||
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>(
|
||||
@ -109,7 +109,7 @@ namespace engine {
|
||||
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()
|
||||
{
|
||||
TRACE("Begin game loop...");
|
||||
LOG_TRACE("Begin game loop...");
|
||||
|
||||
constexpr int FPS_LIMIT = 240;
|
||||
constexpr auto FRAMETIME_LIMIT = std::chrono::nanoseconds(1000000000 / FPS_LIMIT);
|
||||
@ -129,6 +129,9 @@ namespace engine {
|
||||
// single-threaded game loop
|
||||
while (m_window->isRunning()) {
|
||||
|
||||
/* begin rendering */
|
||||
m_drawCommandBuffer = m_gfx->beginRender();
|
||||
|
||||
/* logic */
|
||||
m_sceneManager->updateActiveScene(m_window->dt());
|
||||
|
||||
@ -139,12 +142,12 @@ namespace engine {
|
||||
uint64_t now = m_window->getNanos();
|
||||
if (now - lastTick >= 1000000000LL * 5LL) [[unlikely]] {
|
||||
lastTick = now;
|
||||
INFO("fps: {}", m_window->getAvgFPS());
|
||||
LOG_INFO("fps: {}", m_window->getAvgFPS());
|
||||
m_window->resetAvgFPS();
|
||||
}
|
||||
|
||||
/* draw */
|
||||
m_gfx->renderFrame();
|
||||
m_gfx->finishRender(m_drawCommandBuffer);
|
||||
|
||||
/* poll events */
|
||||
m_window->getInputAndEvents();
|
||||
|
@ -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
@ -41,7 +41,7 @@ namespace engine::resources {
|
||||
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_count = (uint32_t)indices.size();
|
||||
INFO("Loaded mesh, vertices: {}, indices: {}", vertices.size(), indices.size());
|
||||
LOG_INFO("Loaded mesh, vertices: {}, indices: {}", vertices.size(), indices.size());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ namespace engine::resources {
|
||||
|
||||
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()
|
||||
|
@ -57,7 +57,7 @@ Texture::Texture(GFXDevice* gfxDevice, const std::string& path, Filtering filter
|
||||
mipmapSetting,
|
||||
anisotropyEnable);
|
||||
|
||||
INFO("Loaded texture: {}, width: {} height: {}", path, width, height);
|
||||
LOG_INFO("Loaded texture: {}, width: {} height: {}", path, width, height);
|
||||
|
||||
}
|
||||
|
||||
|
@ -79,7 +79,7 @@ namespace engine {
|
||||
m_dynamicAABBs.reserve(size);
|
||||
m_possibleCollisions.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)
|
||||
|
@ -76,6 +76,7 @@ namespace engine {
|
||||
pushConsts.model = t->worldMatrix;
|
||||
pushConsts.view = viewMatrix;
|
||||
|
||||
/*
|
||||
gfx->draw(
|
||||
r->material->getShader()->getPipeline(),
|
||||
r->mesh->getVB(),
|
||||
@ -84,7 +85,7 @@ namespace engine {
|
||||
&pushConsts,
|
||||
sizeof(pushConsts),
|
||||
r->material->m_texture->getHandle()
|
||||
);
|
||||
);*/
|
||||
|
||||
}
|
||||
|
||||
|
@ -77,11 +77,11 @@ namespace engine::util {
|
||||
auto child = scene->createEntity("_mesh" + std::to_string(i), parentObj);
|
||||
auto childRenderer = scene->addComponent<RenderableComponent>(child);
|
||||
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]])) {
|
||||
childRenderer->material->m_texture = textures.at(meshTextureIndices[parentNode->mMeshes[i]]);
|
||||
} 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:
|
||||
void write(const char* message) override {
|
||||
(void)message;
|
||||
TRACE("ASSIMP: {}", message);
|
||||
LOG_TRACE("ASSIMP: {}", message);
|
||||
}
|
||||
};
|
||||
|
||||
@ -160,23 +160,23 @@ namespace engine::util {
|
||||
assert(scene->HasLights() == 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{};
|
||||
|
||||
for (uint32_t i = 0; i < scene->mNumMaterials; i++) {
|
||||
const aiMaterial* m = scene->mMaterials[i];
|
||||
TRACE("Material {}:", i);
|
||||
TRACE(" Name: {}", m->GetName().C_Str());
|
||||
LOG_TRACE("Material {}:", i);
|
||||
LOG_TRACE(" Name: {}", m->GetName().C_Str());
|
||||
for (uint32_t j = 0; j < m->mNumProperties; 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) {
|
||||
aiString 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;
|
||||
absPath = absPath.parent_path();
|
||||
absPath /= texPath.C_Str();
|
||||
@ -185,7 +185,7 @@ namespace engine::util {
|
||||
parent->app()->gfx(), absPath.string(),
|
||||
resources::Texture::Filtering::TRILINEAR, true, true);
|
||||
} 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);
|
||||
std::vector<Vertex> vertices(m->mNumVertices);
|
||||
std::vector<uint32_t> indices(m->mNumFaces * 3);
|
||||
TRACE("Mesh {}: vertex count {}", i, vertices.size());
|
||||
TRACE("Mesh {}: index count {}", i, indices.size());
|
||||
LOG_TRACE("Mesh {}: vertex count {}", i, vertices.size());
|
||||
LOG_TRACE("Mesh {}: index count {}", i, indices.size());
|
||||
|
||||
for (uint32_t j = 0; j < vertices.size(); j++) {
|
||||
Vertex v{};
|
||||
@ -230,7 +230,7 @@ namespace engine::util {
|
||||
|
||||
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();
|
||||
return obj;
|
||||
|
333
src/vulkan/device.cpp
Normal file
333
src/vulkan/device.cpp
Normal 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
39
src/vulkan/device.h
Normal 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);
|
||||
|
||||
}
|
74
src/vulkan/gpu_allocator.cpp
Normal file
74
src/vulkan/gpu_allocator.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
9
src/vulkan/gpu_allocator.h
Normal file
9
src/vulkan/gpu_allocator.h
Normal 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
200
src/vulkan/instance.cpp
Normal 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
17
src/vulkan/instance.h
Normal 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
250
src/vulkan/swapchain.cpp
Normal 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
34
src/vulkan/swapchain.h
Normal 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);
|
||||
|
||||
}
|
@ -24,12 +24,8 @@ namespace engine {
|
||||
|
||||
Uint32 windowFlags = SDL_WINDOW_SHOWN;
|
||||
|
||||
#ifdef ENGINE_BUILD_VULKAN
|
||||
// use Vulkan 1.3
|
||||
windowFlags |= SDL_WINDOW_VULKAN;
|
||||
#endif
|
||||
#ifdef ENGINE_BUILD_OPENGL
|
||||
windowFlags |= SDL_WINDOW_OPENGL;
|
||||
#endif
|
||||
|
||||
if (m_resizable) {
|
||||
windowFlags |= SDL_WINDOW_RESIZABLE;
|
||||
|
@ -19,7 +19,8 @@ set(GAME_SOURCES
|
||||
)
|
||||
|
||||
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()
|
||||
add_executable(${PROJECT_NAME} ${GAME_SOURCES})
|
||||
endif()
|
||||
|
BIN
test/res/models/uvsphere.dae
(Stored with Git LFS)
Normal file
BIN
test/res/models/uvsphere.dae
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
test/res/textures/grass_pbr/Grass001_2K_AmbientOcclusion.png
Normal file
BIN
test/res/textures/grass_pbr/Grass001_2K_AmbientOcclusion.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.7 MiB |
BIN
test/res/textures/grass_pbr/Grass001_2K_Color.png
Normal file
BIN
test/res/textures/grass_pbr/Grass001_2K_Color.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 24 MiB |
BIN
test/res/textures/grass_pbr/Grass001_2K_Displacement.png
Normal file
BIN
test/res/textures/grass_pbr/Grass001_2K_Displacement.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.5 MiB |
BIN
test/res/textures/grass_pbr/Grass001_2K_NormalDX.png
Normal file
BIN
test/res/textures/grass_pbr/Grass001_2K_NormalDX.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 26 MiB |
BIN
test/res/textures/grass_pbr/Grass001_2K_NormalGL.png
Normal file
BIN
test/res/textures/grass_pbr/Grass001_2K_NormalGL.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 26 MiB |
BIN
test/res/textures/grass_pbr/Grass001_2K_Roughness.png
Normal file
BIN
test/res/textures/grass_pbr/Grass001_2K_Roughness.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.5 MiB |
@ -154,7 +154,7 @@ void CameraControllerSystem::onUpdate(float ts)
|
||||
" z: " + std::to_string(t->position.z)
|
||||
};
|
||||
m_scene->app()->window()->infoBox("POSITION", pos_string);
|
||||
INFO("position: " + pos_string);
|
||||
LOG_INFO("position: " + pos_string);
|
||||
}
|
||||
|
||||
if (m_scene->app()->inputManager()->getButtonPress("fullscreen")) {
|
||||
|
@ -40,7 +40,7 @@ static void configureInputs(engine::InputManager* inputManager)
|
||||
|
||||
void playGame(bool enableFrameLimiter)
|
||||
{
|
||||
INFO("FPS limiter: {}", enableFrameLimiter ? "ON" : "OFF");
|
||||
LOG_INFO("FPS limiter: {}", enableFrameLimiter ? "ON" : "OFF");
|
||||
|
||||
engine::gfx::GraphicsSettings graphicsSettings{};
|
||||
graphicsSettings.vsync = true;
|
||||
@ -56,6 +56,8 @@ void playGame(bool enableFrameLimiter)
|
||||
|
||||
auto myScene = app.sceneManager()->createEmptyScene();
|
||||
|
||||
#if 0
|
||||
|
||||
/* create camera */
|
||||
{
|
||||
myScene->registerComponent<CameraControllerComponent>();
|
||||
@ -96,48 +98,19 @@ void playGame(bool enableFrameLimiter)
|
||||
{
|
||||
uint32_t skybox = myScene->createEntity("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->mesh = genSphereMesh(app.gfx(), 1.0f, 50, 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 };
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
{
|
||||
uint32_t floor = myScene->createEntity("floor");
|
||||
myScene->getComponent<engine::TransformComponent>(floor)->position = glm::vec3{-5000.0f, -1.0f, -5000.0f};
|
||||
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->mesh = genCuboidMesh(app.gfx(), 10000.0f, 1.0f, 10000.0f, 5000.0f);
|
||||
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 } };
|
||||
}
|
||||
|
||||
// cubes!
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
|
||||
app.gameLoop();
|
||||
|
||||
|
@ -1,39 +1,32 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "game.hpp"
|
||||
|
||||
// engine
|
||||
#include "logger.hpp"
|
||||
#include "window.hpp"
|
||||
|
||||
// standard library
|
||||
#include <exception>
|
||||
#include <string.h>
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
|
||||
bool enableFrameLimiter = true;
|
||||
|
||||
if (argc == 2) {
|
||||
const std::string arg { argv[1] };
|
||||
if (arg == "nofpslimit") enableFrameLimiter = false;
|
||||
if (strcmp(argv[2], "nofpslimit") == 0) enableFrameLimiter = false;
|
||||
}
|
||||
|
||||
engine::setupLog(PROJECT_NAME);
|
||||
|
||||
INFO("{} v{}", PROJECT_NAME, PROJECT_VERSION);
|
||||
LOG_INFO("{} v{}", PROJECT_NAME, PROJECT_VERSION);
|
||||
|
||||
try {
|
||||
playGame(enableFrameLimiter);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
|
||||
CRITICAL("{}", e.what());
|
||||
|
||||
LOG_CRITICAL("{}", e.what());
|
||||
engine::Window::errorBox(e.what());
|
||||
#ifndef NDEBUG
|
||||
fputs(e.what(), stderr);
|
||||
fputc('\n', stderr);
|
||||
#endif
|
||||
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user