summaryrefslogtreecommitdiff
path: root/src/core.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core.cpp')
-rw-r--r--src/core.cpp490
1 files changed, 339 insertions, 151 deletions
diff --git a/src/core.cpp b/src/core.cpp
index 12e5b01..7e13f75 100644
--- a/src/core.cpp
+++ b/src/core.cpp
@@ -14,57 +14,45 @@
* limitations under the License.
*/
-#include "core.h"
+#include "core.hpp"
-#include <yaml-cpp/yaml.h>
-
-/*
- Stretches the image to fill the screen vertically, horizontally, or both;
- without changing the game aspect ratio.
- */
-void
-cg_Engine_calculate_full_scale()
-{
- double screen_ratio, game_ratio, scale;
+#include <iostream>
- screen_ratio =
- (double)cg_core.screen_width/(double)cg_core.screen_height;
- game_ratio = (double)cg_core.game_width/(double)cg_core.game_height;
-
- // If screen is proportionally taller than game.
- if(screen_ratio < game_ratio)
- {
- scale = (double)cg_core.screen_width/(double)cg_core.game_width;
+#include <yaml-cpp/yaml.h>
- cg_core.screen_rect.w = cg_core.game_width * scale;
- cg_core.screen_rect.h = cg_core.game_height * scale;
- cg_core.screen_rect.x = 0;
- cg_core.screen_rect.y = cg_core.screen_height/2 -
- cg_core.screen_rect.h/2;
- }
- // If screen is proportionally wider than game.
- else if(screen_ratio > game_ratio)
- {
- scale = (double)cg_core.screen_height/(double)cg_core.game_height;
+std::random_device random_seed;
+std::mt19937 random_number_generator;
- cg_core.screen_rect.w = cg_core.game_width * scale;
- cg_core.screen_rect.h = cg_core.game_height * scale;
- cg_core.screen_rect.x = cg_core.screen_width/2 -
- cg_core.screen_rect.w/2;
- cg_core.screen_rect.y = 0;
- }
- // If they have the same aspect ratio.
- else
- {
- cg_core.screen_rect.x = 0;
- cg_core.screen_rect.y = 0;
- cg_core.screen_rect.w = cg_core.screen_width;
- cg_core.screen_rect.h = cg_core.screen_height;
- }
+#ifdef DEBUG
+static VKAPI_ATTR VkBool32 VKAPI_CALL
+vk_debug_callback(
+ VkDebugUtilsMessageSeverityFlagBitsEXT message_severity,
+ VkDebugUtilsMessageTypeFlagsEXT message_type,
+ const VkDebugUtilsMessengerCallbackDataEXT* callback_data,
+ void* _obj)
+{
+ // Print message severy code.
+ std::cout << "[\e[1;0mVK\e[0;0m ";
+ if(message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT)
+ std::cout << "\e[1;32mV\e[0;0m";
+ else if(message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT)
+ std::cout << "\e[1;34mI\e[0;0m";
+ else if(message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT)
+ std::cout << "\e[1;33mW\e[0;0m";
+ else if(message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT)
+ std::cout << "\e[1;31mE\e[0;0m";
+
+ std::cout << "]";
+
+ // Log message.
+ std::cout << callback_data->pMessage << std::endl;
+
+ return VK_FALSE;
}
+#endif
-static SDL_bool
-load_variables(void *obj, LoaderStack *ls)
+static void
+load_variables(void *obj)
{
YAML::Node root = YAML::LoadFile(cg_core.config_file);
@@ -72,221 +60,421 @@ load_variables(void *obj, LoaderStack *ls)
cg_core.game_name = new char[game_name.size() + 1];
strcpy(cg_core.game_name, game_name.c_str());
- // Based on NES.
cg_core.game_width = root["game"]["width"].as<int>();
cg_core.game_height = root["game"]["height"].as<int>();
cg_core.screen_width = root["screen"]["width"].as<int>();
cg_core.screen_height = root["screen"]["height"].as<int>();
+ cg_core.game_version_major = 0;
+ cg_core.game_version_minor = 1;
+ cg_core.game_version_patch = 0;
+
cg_core.fps = 30;
cg_core.max_frame_duration_ms = 1000 / cg_core.fps;
-
- return SDL_TRUE;
}
static void
-unload_variables(void *obj, LoaderStack *ls)
+unload_variables(void *obj)
{
delete[] cg_core.game_name;
}
-static SDL_bool
-load_sdl(void *obj, LoaderStack *ls)
+static void
+load_sdl(void *obj)
{
if(SDL_Init(SDL_INIT_EVERYTHING) < 0)
{
- const char* base_error = "SDL could not initialize! SDL Error → ";
- LoaderStack_set_error(ls, base_error, SDL_GetError());
- return SDL_FALSE;
+ std::string error{"SDL could not initialize! SDL Error → "};
+ error += SDL_GetError();
+ throw error;
}
- return SDL_TRUE;
+ if(SDL_Vulkan_LoadLibrary(nullptr) != 0)
+ {
+ SDL_Quit();
+ std::string error{"SDL could not initialize Vulkan! SDL_Error → "};
+ error += SDL_GetError();
+ throw CommandError{error};
+ }
}
static void
-unload_sdl(void *obj, LoaderStack *ls)
+unload_sdl(void *obj)
{
+ SDL_Vulkan_UnloadLibrary();
SDL_Quit();
}
-static SDL_bool
-load_sdl_image(void *obj, LoaderStack *ls)
+static void
+load_sdl_image(void *obj)
{
int flags = IMG_INIT_JPG|IMG_INIT_PNG|IMG_INIT_TIF;
if(!(IMG_Init(flags) & flags))
{
- const char* base_error = "Could not initialize SDL image → ";
- LoaderStack_set_error(ls, base_error, IMG_GetError());
- return SDL_FALSE;
+ std::string error{"Could not initialize SDL image → "};
+ error += IMG_GetError();
+ throw CommandError{error};
}
-
- return SDL_TRUE;
}
static void
-unload_sdl_image(void *obj, LoaderStack *ls)
+unload_sdl_image(void *obj)
{
IMG_Quit();
}
-static SDL_bool
-load_sdl_mixer(void *obj, LoaderStack *ls)
+static void
+load_sdl_mixer(void *obj)
{
int flags = MIX_INIT_OGG|MIX_INIT_MOD;
int initted = Mix_Init(flags);
if(initted&flags != flags)
{
- const char* base_error = "Could not initialize SDL mixer → ";
- LoaderStack_set_error(ls, base_error, Mix_GetError());
- return SDL_FALSE;
+ std::string error{"Could not initialize SDL mixer → "};
+ error += Mix_GetError();
+ throw CommandError{error};
}
-
- return SDL_TRUE;
}
static void
-unload_sdl_mixer(void *obj, LoaderStack *ls)
+unload_sdl_mixer(void *obj)
{
while(Mix_Init(0)) Mix_Quit();
}
-static SDL_bool
-load_sdl_open_audio(void *obj, LoaderStack *ls)
+static void
+load_sdl_open_audio(void *obj)
{
if(Mix_OpenAudio(22050, MIX_DEFAULT_FORMAT, 2, 1024) == -1)
{
- const char* base_error = "Could not open SDL mixer audio → ";
- LoaderStack_set_error(ls, base_error, Mix_GetError());
- return SDL_FALSE;
+ std::string error{"Could not open SDL mixer audio → "};
+ error += Mix_GetError();
+ throw CommandError{error};
}
-
- return SDL_TRUE;
}
static void
-unload_sdl_open_audio(void *obj, LoaderStack *ls)
+unload_sdl_open_audio(void *obj)
{
Mix_CloseAudio();
}
-static SDL_bool
-load_sdl_ttf(void *obj, LoaderStack *ls)
+static void
+load_window(void *obj)
{
- if(TTF_Init()==-1)
+ cg_core.window = NULL;
+ cg_core.window = SDL_CreateWindow(
+ cg_core.game_name, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
+ cg_core.screen_width, cg_core.screen_height, SDL_WINDOW_VULKAN);
+ if(cg_core.window == NULL)
{
- const char* base_error = "Could not initialize SDL ttf → ";
- LoaderStack_set_error(ls, base_error, TTF_GetError());
- return SDL_FALSE;
+ std::string error{"Window could not be created! SDL_Error → "};
+ error += SDL_GetError();
+ throw CommandError{error};
}
-
- return SDL_TRUE;
}
static void
-unload_sdl_ttf(void *obj, LoaderStack *ls)
+unload_window(void *obj)
{
- TTF_Quit();
+ SDL_DestroyWindow(cg_core.window);
}
-static SDL_bool
-load_window(void *obj, LoaderStack *ls)
+static void
+load_vk_instance(void *obj)
{
- cg_core.window = NULL;
- cg_core.window = SDL_CreateWindow(
- cg_core.game_name, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
- cg_core.screen_width, cg_core.screen_height, SDL_WINDOW_SHOWN);
- if(cg_core.window == NULL)
+ std::vector<const char*> vk_extensions;
+ std::vector<const char*> vk_required_layers_names;
+
+ // Get extensions.
{
- const char* base_error = "Window could not be created! SDL_Error → ";
- LoaderStack_set_error(ls, base_error, SDL_GetError());
- return SDL_FALSE;
+ uint32_t vk_extensions_count;
+ std::vector<const char*> vk_required_extensions;
+
+ // Load debuging layers.
+#ifdef DEBUG
+ vk_required_extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
+ vk_required_layers_names.push_back("VK_LAYER_KHRONOS_validation");
+#endif
+
+ // Get extensions for SDL.
+ {
+ uint32_t vk_sdl_extension_count;
+ std::vector<const char*> vk_sdl_extensions;
+
+ if(!SDL_Vulkan_GetInstanceExtensions(
+ cg_core.window, &vk_sdl_extension_count, nullptr))
+ {
+ std::string error{
+ "Vulkan extensions could not be loaded by SDL! SDL_Error: "};
+ error += SDL_GetError();
+ throw CommandError{error};
+ }
+
+ vk_sdl_extensions.resize(vk_sdl_extension_count);
+ SDL_Vulkan_GetInstanceExtensions(
+ cg_core.window, &vk_sdl_extension_count, vk_sdl_extensions.data());
+
+ // Combine all extensions.
+ vk_extensions_count = vk_sdl_extension_count +
+ vk_required_extensions.size();
+ vk_extensions.resize(vk_extensions_count);
+ for(auto i{0}; i < vk_sdl_extension_count; i++)
+ vk_extensions[i] = vk_sdl_extensions[i];
+ for(auto i{0}; i < vk_required_extensions.size(); i++)
+ vk_extensions[i + vk_sdl_extension_count] = vk_required_extensions[i];
+ }
+
+#ifdef DEBUG
+ std::cout << "Enabled VK extensions." << std::endl;
+ for(auto vk_extension: vk_extensions)
+ std::cout << "Extension name: " << vk_extension << std::endl;
+#endif
}
- return SDL_TRUE;
+ // Get available instance layers.
+ {
+ uint32_t vk_available_layers_count;
+ std::vector<VkLayerProperties> vk_available_layers;
+ std::vector<const char*> vk_available_layers_names;
+
+ vkEnumerateInstanceLayerProperties(&vk_available_layers_count, nullptr);
+ vk_available_layers.resize(vk_available_layers_count);
+ vkEnumerateInstanceLayerProperties(&vk_available_layers_count,
+ vk_available_layers.data());
+ vk_available_layers_names.resize(vk_available_layers_count);
+#ifdef DEBUG
+ std::cout << "Available VK instance layers." << std::endl;
+#endif
+ for(uint32_t i = 0; i < vk_available_layers_count; i++)
+ {
+#ifdef DEBUG
+ std::cout << "\nname: " << vk_available_layers[i].layerName <<
+ std::endl;
+ std::cout << "Description: " << vk_available_layers[i].description <<
+ std::endl;
+ std::cout << "Spec version: " << vk_available_layers[i].specVersion <<
+ std::endl;
+ std::cout << "Implementation version: " <<
+ vk_available_layers[i].implementationVersion << std::endl;
+#endif
+
+ vk_available_layers_names[i] = vk_available_layers[i].layerName;
+ }
+
+ // If required layers are not all available.
+ if(!std::includes(
+ vk_available_layers_names.begin(), vk_available_layers_names.end(),
+ vk_required_layers_names.begin(), vk_required_layers_names.begin()))
+ throw CommandError{"Some required Vulkan layers are not available."};
+ }
+
+ {
+ VkApplicationInfo app_info;
+ app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
+ app_info.pNext = nullptr;
+ app_info.pApplicationName = cg_core.game_name;
+ app_info.applicationVersion = VK_MAKE_VERSION(
+ cg_core.game_version_major,
+ cg_core.game_version_minor,
+ cg_core.game_version_patch);
+ app_info.pEngineName = "CandyGear";
+ app_info.engineVersion = VK_MAKE_VERSION(
+ CANDY_GEAR_VERSION_MAJOR,
+ CANDY_GEAR_VERSION_MINOR,
+ CANDY_GEAR_VERSION_PATCH);
+ app_info.apiVersion = VK_API_VERSION_1_0;
+
+ VkInstanceCreateInfo create_info;
+ create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
+ create_info.pNext = nullptr;
+ create_info.flags = 0;
+ create_info.pApplicationInfo = &app_info;
+ create_info.enabledExtensionCount = vk_extensions.size();
+ create_info.ppEnabledExtensionNames = vk_extensions.data();
+ create_info.enabledLayerCount = vk_required_layers_names.size();
+ create_info.ppEnabledLayerNames = vk_required_layers_names.data();
+
+ VkResult result =
+ vkCreateInstance(&create_info, nullptr, &cg_core.vk_instance);
+ if(result != VK_SUCCESS)
+ {
+ std::string error{""};
+ switch(result)
+ {
+ case VK_ERROR_LAYER_NOT_PRESENT:
+ error = " Layer not present.";
+ break;
+ case VK_ERROR_EXTENSION_NOT_PRESENT:
+ error = " Extension not present.";
+ }
+ error = "Failed to create Vulkan instance." + error;
+ throw CommandError{error};
+ }
+ }
}
static void
-unload_window(void *obj, LoaderStack *ls)
+unload_vk_instance(void *obj)
{
- SDL_DestroyWindow(cg_core.window);
+ vkDestroyInstance(cg_core.vk_instance, nullptr);
}
-static SDL_bool
-load_sdl_renderer(void *obj, LoaderStack *ls)
+static void
+load_window_surface(void *obj)
{
- cg_core.renderer = NULL;
- cg_core.renderer = SDL_CreateRenderer(
- cg_core.window, -1, SDL_RENDERER_ACCELERATED |
- SDL_RENDERER_TARGETTEXTURE);
- if(cg_core.renderer == NULL)
+ if(!SDL_Vulkan_CreateSurface(
+ cg_core.window, cg_core.vk_instance, &cg_core.window_surface))
{
- const char* base_error = "Could not create SDL renderer → ";
- LoaderStack_set_error(ls, base_error, SDL_GetError());
- return SDL_FALSE;
+ std::string error{"Failed to create a window surface → "};
+ error += SDL_GetError();
+ throw CommandError{error};
}
+}
- return SDL_TRUE;
+static void
+unload_window_surface(void *obj)
+{
+ vkDestroySurfaceKHR(cg_core.vk_instance, cg_core.window_surface, nullptr);
}
+#ifdef DEBUG
static void
-unload_sdl_renderer(void *obj, LoaderStack *ls)
+load_vk_debug_callback(void *obj)
{
- SDL_DestroyRenderer(cg_core.renderer);
+ PFN_vkCreateDebugUtilsMessengerEXT debug_messenger;
+
+ // A Vulkan instance extension named VK_EXT_debug_utils and a Vulkan instance
+ // layer named VK_LAYER_LUNARG_standard_validation are required to enable
+ // this callback. These instance extension and instance layer are loaded at
+ // Instance::load_vk_instance.
+ VkDebugUtilsMessengerCreateInfoEXT create_info;
+ create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
+ create_info.pNext = nullptr;
+ create_info.messageSeverity =
+ VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
+ VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT |
+ VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
+ VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
+ create_info.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;
+ create_info.pfnUserCallback = vk_debug_callback;
+ create_info.pUserData = nullptr;
+ create_info.flags = 0;
+
+ debug_messenger = (PFN_vkCreateDebugUtilsMessengerEXT)
+ vkGetInstanceProcAddr(cg_core.vk_instance,
+ "vkCreateDebugUtilsMessengerEXT");
+
+ if(debug_messenger(cg_core.vk_instance, &create_info, nullptr,
+ &cg_core.vk_callback) != VK_SUCCESS)
+ CommandError{"Failed to setup debug callback for Vulkan."};
+}
+
+static void
+unload_vk_debug_callback(void *obj)
+{
+ PFN_vkDestroyDebugUtilsMessengerEXT debug_messenger;
+
+ debug_messenger = (PFN_vkDestroyDebugUtilsMessengerEXT)
+ vkGetInstanceProcAddr(cg_core.vk_instance,
+ "vkDestroyDebugUtilsMessengerEXT");
+
+ debug_messenger(cg_core.vk_instance, cg_core.vk_callback, nullptr);
}
+#endif
-static SDL_bool
-load_pre_screen(void *obj, LoaderStack *ls)
+static void
+load_vk_devices(void *obj)
{
- cg_core.pre_screen_buffer = SDL_CreateTexture(
- cg_core.renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET,
- cg_core.game_width, cg_core.game_height);
+ uint32_t devices_count;
+ std::vector<VkPhysicalDevice> vk_physical_devices;
- if(cg_core.pre_screen_buffer == NULL)
+ // Enumerate physical devices
{
- const char* base_error = "Could not create renderering buffer → ";
- LoaderStack_set_error(ls, base_error, SDL_GetError());
- return SDL_FALSE;
+ vkEnumeratePhysicalDevices(cg_core.vk_instance, &devices_count, nullptr);
+ if(devices_count < 1)
+ CommandError{"Failed to find GPUs with Vulkan support."};
+ vk_physical_devices.resize(devices_count);
+ vkEnumeratePhysicalDevices(
+ cg_core.vk_instance, &devices_count, vk_physical_devices.data());
}
- cg_Engine_calculate_full_scale();
-
- SDL_SetRenderTarget(cg_core.renderer, cg_core.pre_screen_buffer);
+#ifdef DEBUG
+ std::cout << "Physical devices properties" << std::endl;
+#endif
- return SDL_TRUE;
+ cg_core.vk_devices.reserve(devices_count);
+ for(auto i = 0; i < devices_count; i++)
+ {
+ // Use swapchain on first device.
+ if(i == 0)
+ {
+ cg_core.vk_devices.emplace_back(vk_physical_devices[i], true);
+ cg_core.vk_device_with_swapchain = &cg_core.vk_devices[i];
+ }
+ else
+ cg_core.vk_devices.emplace_back(vk_physical_devices[i], false);
+ }
}
static void
-unload_pre_screen(void *obj, LoaderStack *ls)
+unload_vk_devices(void *obj)
{
- SDL_DestroyTexture(cg_core.pre_screen_buffer);
+ cg_core.vk_devices.clear();
}
-void
-cg_Core_init(LoaderStack *loader)
+static void
+load_vk_swapchain(void *obj)
{
- LoaderStack_constructor(loader, NULL);
-
- LoaderStack_add(loader, &load_variables, &unload_variables);
- LoaderStack_add(loader, &load_sdl, &unload_sdl);
- LoaderStack_add(loader, &load_sdl_image, &unload_sdl_image);
- LoaderStack_add(loader, &load_sdl_mixer, &unload_sdl_mixer);
- LoaderStack_add(loader, &load_sdl_open_audio, &unload_sdl_open_audio);
- LoaderStack_add(loader, &load_sdl_ttf, &unload_sdl_ttf);
- LoaderStack_add(loader, &load_window, &unload_window);
- LoaderStack_add(loader, &load_sdl_renderer, &unload_sdl_renderer);
- LoaderStack_add(loader, &load_pre_screen, &unload_pre_screen);
+ try { cg_core.vk_swapchain = new VK::Swapchain(); }
+ catch(const CommandError &error)
+ {
+ std::string error_message{"Failed to create swapchain → "};
+ error_message += error.what();
+ throw CommandError{error_message};
+ }
}
-SDL_bool
-cg_Core_load(LoaderStack *loader)
+static void
+unload_vk_swapchain(void *obj)
{
- if(!LoaderStack_load(loader)) return SDL_FALSE;
+ delete cg_core.vk_swapchain;
+}
- return SDL_TRUE;
+static void
+load_vk_graphics_pipeline(void *obj)
+{
+ try
+ {
+ cg_core.vk_graphics_pipeline = new VK::GraphicsPipeline();
+ }
+ catch(const CommandError &e)
+ {
+ throw CommandError{"Failed to create graphics pipeline."};
+ }
}
-void
-cg_Core_unload(LoaderStack *loader)
+static void
+unload_vk_graphics_pipeline(void *obj)
{
- LoaderStack_destructor(loader);
+ delete cg_core.vk_graphics_pipeline;
}
+
+const CommandChain cg_sCore::loader{
+ {&load_variables, &unload_variables},
+ {&load_sdl, &unload_sdl},
+ {&load_sdl_image, &unload_sdl_image},
+ {&load_sdl_mixer, &unload_sdl_mixer},
+ {&load_sdl_open_audio, &unload_sdl_open_audio},
+ {&load_window, &unload_window},
+ {&load_vk_instance, &unload_vk_instance},
+ {&load_window_surface, &unload_window_surface},
+#ifdef DEBUG
+ {&load_vk_debug_callback, &unload_vk_debug_callback},
+#endif
+ {&load_vk_devices, &unload_vk_devices},
+ {&load_vk_swapchain, &unload_vk_swapchain},
+ {&load_vk_graphics_pipeline, &unload_vk_graphics_pipeline}
+};