diff --git a/engine/core/src/engine.cpp b/engine/core/src/engine.cpp index 2ef3b3b..b32658f 100755 --- a/engine/core/src/engine.cpp +++ b/engine/core/src/engine.cpp @@ -28,6 +28,12 @@ using prism::engine; engine::engine(const int argc, char* argv[]) { log("Prism Engine loading..."); + log("starting gfx debug:"); + + for(auto [t, t_name] : magic_enum::enum_entries()) { + log("does platform support {}? {}", t_name, platform::supports_context(t)); + } + console::register_command("echo", console::argument_format(1), [](const console::arguments& args) { log(std::get(args[0].data)); }); @@ -99,6 +105,8 @@ void engine::prepare_quit() { void engine::set_gfx(GFX* p_gfx) { Expects(p_gfx != nullptr); + prism::log("this gfx has a context of {}", utility::enum_to_string(p_gfx->required_context())); + this->gfx = p_gfx; } diff --git a/engine/gfx/CMakeLists.txt b/engine/gfx/CMakeLists.txt index de37abf..c3888bf 100755 --- a/engine/gfx/CMakeLists.txt +++ b/engine/gfx/CMakeLists.txt @@ -13,7 +13,8 @@ add_custom_target(GFXInterface SOURCES public/gfx_framebuffer.hpp public/gfx_renderpass.hpp public/gfx_object.hpp - public/gfx_sampler.hpp) + public/gfx_sampler.hpp + public/gfx_context.hpp) set_target_properties(GFXInterface PROPERTIES CMAKE_FOLDER "GFX") add_subdirectory(dummy) diff --git a/engine/gfx/public/gfx.hpp b/engine/gfx/public/gfx.hpp index 6db386a..49186f9 100755 --- a/engine/gfx/public/gfx.hpp +++ b/engine/gfx/public/gfx.hpp @@ -6,6 +6,7 @@ #include "shadercompiler.hpp" #include "platform.hpp" +#include "gfx_context.hpp" class GFXBuffer; class GFXPipeline; @@ -280,14 +281,6 @@ struct GFXSamplerCreateInfo { using GFXSize = uint64_t; -enum class GFXContext { - None, - Metal, - OpenGL, - DirectX, - Vulkan -}; - struct GFXCreateInfo { bool api_validation_enabled = false; }; diff --git a/engine/gfx/public/gfx_context.hpp b/engine/gfx/public/gfx_context.hpp new file mode 100644 index 0000000..e8926af --- /dev/null +++ b/engine/gfx/public/gfx_context.hpp @@ -0,0 +1,9 @@ +#pragma once + +enum class GFXContext { + None, + Metal, + OpenGL, + DirectX, + Vulkan +}; diff --git a/engine/gfx/vulkan/src/gfx_vulkan.cpp b/engine/gfx/vulkan/src/gfx_vulkan.cpp index 6117a3a..b49a479 100755 --- a/engine/gfx/vulkan/src/gfx_vulkan.cpp +++ b/engine/gfx/vulkan/src/gfx_vulkan.cpp @@ -220,7 +220,9 @@ bool GFXVulkan::initialize(const GFXCreateInfo& info) { enabledExtensions.push_back("VK_EXT_debug_utils"); } - auto required_extensions = platform::get_native_surface_extension(); + auto ctx_info = (vulkan_information*)platform::get_context_information(); + + auto required_extensions = ctx_info->surface_extensions; enabledExtensions.insert(enabledExtensions.end(), required_extensions.begin(), required_extensions.end()); createInstance({}, enabledExtensions); @@ -1957,7 +1959,11 @@ void GFXVulkan::createLogicalDevice(std::vector extensions) { void GFXVulkan::createSwapchain(NativeSurface* native_surface, VkSwapchainKHR oldSwapchain) { if(native_surface->surface == VK_NULL_HANDLE) { - native_surface->surface = (VkSurfaceKHR)platform::create_native_surface(native_surface->identifier, (void*)instance); + auto surface_creation_info = new vulkan_surface_creation_info(); + surface_creation_info->instance = instance; + + auto vk_surface = (vulkan_surface*)platform::create_surface(native_surface->identifier, surface_creation_info); + native_surface->surface = vk_surface->surface; } // TODO: fix this pls diff --git a/engine/platform/include/platform.hpp b/engine/platform/include/platform.hpp index 3b5494f..0f2e2c8 100755 --- a/engine/platform/include/platform.hpp +++ b/engine/platform/include/platform.hpp @@ -5,6 +5,7 @@ #include #include "common.hpp" +#include "gfx_context.hpp" /// Requestable window flags, which may or may not be respected by the platform. enum class WindowFlags { @@ -73,6 +74,46 @@ enum class PlatformTheme { Dark }; +// note: a platform may be built with support for a specific context (e.g. ENABLE_VULKAN, ENABLE_DIRECTX, etc) +// this only determines it at build-time, not at runtime. +#ifdef ENABLE_VULKAN +#include + +struct vulkan_information { + std::vector surface_extensions; +}; + +struct vulkan_surface_creation_info { + VkInstance instance; +}; + +struct vulkan_surface { + VkSurfaceKHR surface; +}; +#endif + +#ifdef ENABLE_DIRECTX +struct directx_surface_creation_info { + // TODO: stub +}; +#endif + +#ifdef ENABLE_METAL +#import + +struct metal_surface_creation_info { + MTLDevice device; +}; + +struct metal_surface { + MTL::PixelFormat format; +}; + +struct metal_next_image { + CA::MetalDrawable* next_drawable; +}; +#endif + namespace platform { using window_ptr = void*; @@ -85,6 +126,28 @@ namespace platform { /// Queries whether or not the platform supports a certain feature. bool supports_feature(PlatformFeature feature); + /* + * This queries whether the context is supported by the platform at build-time. This does not mean the + * context can be used properly at runtime, this is handled by GFX::is_supported(). + */ + bool supports_context(GFXContext context); + + /** + * This initializes before window creation, for a specific gfx context type. This is designed to be called within + * helper classes such as gfx_chooser, but may also be called within a platform implementation as well. + * @param context + */ + void initialize_context(GFXContext context); + + /* + * This is used in specific cases where the gfx backend may need to know important information before window creation. + * A good example is the surface extension required by a specific window backend, but this is between initialize_context + * and create_surface calls. + * + * the returned structs are in a naming scheme of X_information. + */ + void* get_context_information(); + /** Opens a new window. @param title The title of the window. @param rect The requested size and position of the window. @@ -97,10 +160,23 @@ namespace platform { bool is_main_window(window_ptr index); - // for vulkan usage - void* create_native_surface(window_ptr window, void* instance); + /* + * This creates a new surface, with the gfx context type already set ahead of time (see initialize_context) + * The parameter 'surface_creation_info' must be a type of the gfx context you want to use, and support must be + * built at build-time (ENABLE_VULKAN, gfx context is Vulkan, and the struct would be vulkan_surface_creation_info.) + * + * The return type is usually {Your GFX Context}_surface, such as vulkan_surface. See each gfx context's struct for + * more information. + */ + void* create_surface(window_ptr window, void* surface_creation_info); - std::vector get_native_surface_extension(); + /* + * This needs to be implemented under certain gfx contexts (such as metal) which calls [layer nextDrawable] underneath + * can be no-op for contexts such as vulkan which can happen within it's own API. + * + * the return type is named as X_next_image. may be null. + */ + void* get_next_image(window_ptr window); /** Closes a window. @param index The window to close. @@ -177,10 +253,4 @@ namespace platform { void begin_text_input(); void end_text_input(); - - // metal specific stuff -#ifdef ENABLE_METAL - unsigned int initialize_metal_layer(window_ptr window, void* device); - void* get_next_drawable(window_ptr window); -#endif } diff --git a/platforms/sdl/CMakeLists.txt b/platforms/sdl/CMakeLists.txt index ddfc767..80be609 100644 --- a/platforms/sdl/CMakeLists.txt +++ b/platforms/sdl/CMakeLists.txt @@ -7,6 +7,10 @@ if(ENABLE_MACOS) set(EXTRA_SRC ${CMAKE_CURRENT_SOURCE_DIR}/sdl_metal.mm) endif() +if(ENABLE_VULKAN) + set(EXTRA_SRC ${CMAKE_CURRENT_SOURCE_DIR}/sdl_vulkan.cpp) +endif() + if(TARGET SDL2::Main) set(EXTRA_LIBRARIES SDL2::Main) endif() diff --git a/platforms/sdl/main.cpp.in b/platforms/sdl/main.cpp.in index d7c6080..7014d3d 100644 --- a/platforms/sdl/main.cpp.in +++ b/platforms/sdl/main.cpp.in @@ -46,6 +46,15 @@ SDL_Window* get_window_by_sdl_id(const Uint32 id) { return nullptr; } +#ifdef ENABLE_METAL +void* create_metal_surface(platform::window_ptr window, void* surface_creation_info); +void* get_next_metal_drawable(platform::window_ptr window); +#endif + +#ifdef ENABLE_VULKAN +void* create_vulkan_surface(platform::window_ptr window, void* surface_creation_info); +#endif + static std::map inputToKeyCode = { { {InputButton::C, SDL_SCANCODE_C}, {InputButton::V, SDL_SCANCODE_V}, @@ -89,9 +98,8 @@ platform::window_ptr platform::open_window(const std::string_view title, const p auto& win = windows.emplace_back(); int sdl_flags = SDL_WINDOW_ALLOW_HIGHDPI; - #ifndef ENABLE_METAL - sdl_flags |= SDL_WINDOW_VULKAN; - #endif + if(gfx_interface->required_context() == GFXContext::Vulkan) + sdl_flags |= SDL_WINDOW_VULKAN; if(flags & WindowFlags::Borderless) sdl_flags |= SDL_WINDOW_BORDERLESS; @@ -127,10 +135,12 @@ platform::window_ptr platform::open_window(const std::string_view title, const p if(windows.size() == 1) main_window = win; - #ifdef ENABLE_METAL - SDL_Renderer* renderer = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); - renderers[win] = renderer; - #endif +#ifdef ENABLE_METAL + if(gfx_interface->required_context() == GFXContext::Metal) { + SDL_Renderer* renderer = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); + renderers[win] = renderer; + } +#endif engine->add_window((void*)win, win, {static_cast(real_width), static_cast(real_height)}); app->initialize_render(); @@ -306,13 +316,93 @@ void platform::end_text_input() { SDL_StopTextInput(); } +bool platform::supports_context(GFXContext context) { +#ifdef ENABLE_VULKAN + if(context == GFXContext::Vulkan) + return true; +#endif + +#ifdef ENABLE_METAL + if(context == GFXContext::Metal) + return true; +#endif + + if(context == GFXContext::None) + return true; + + return false; +} + +void platform::initialize_context(const GFXContext context) { +#ifdef ENABLE_METAL + if(context == GFXContext::Metal) { + SDL_SetHint(SDL_HINT_RENDER_DRIVER, "metal"); + } +#endif +} + +void* platform::get_context_information() { +#ifdef ENABLE_VULKAN + if(gfx_interface->required_context() == GFXContext::Vulkan) { + // dummy window + auto dummy = SDL_CreateWindow("", 0, 0, 1, 1, SDL_WINDOW_VULKAN); + + unsigned int count = 0; + SDL_Vulkan_GetInstanceExtensions(dummy, &count, nullptr); + + std::vector extensions(count); + SDL_Vulkan_GetInstanceExtensions(dummy, &count, extensions.data()); + + SDL_DestroyWindow(dummy); + + auto info = new vulkan_information(); + info->surface_extensions = extensions; + + return (void*)info; + } +#endif + + return nullptr; +} + +void* platform::create_surface(window_ptr window, void* surface_creation_info) { +#ifdef ENABLE_VULKAN + if(gfx_interface->required_context() == GFXContext::Vulkan) { + return create_vulkan_surface(window, surface_creation_info); + } +#endif + +#ifdef ENABLE_METAL + if(gfx_interface->required_context() == GFXContext::Metal) { + return create_metal_surface(window, surface_creation_info); + } +#endif + + return nullptr; +} + +void* platform::get_next_image(window_ptr window) { +#ifdef ENABLE_METAL + if(gfx_interface->required_context() == GFXContext::Metal) { + return get_next_metal_drawable(window); + } +#endif + + return nullptr; +} + int main(int argc, char* argv[]) { SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER); + // determine gfx context at the beginning #ifdef ENABLE_METAL - SDL_SetHint(SDL_HINT_RENDER_DRIVER, "metal"); + gfx_interface = new GFXMetal(); +#else + gfx_interface = new GFXVulkan(); #endif + platform::initialize_context(gfx_interface->required_context()); + engine = new prism::engine(argc, argv); app = new @APP_CLASS@(); @@ -320,11 +410,6 @@ int main(int argc, char* argv[]) { GFXCreateInfo info = {}; -#ifdef ENABLE_METAL - gfx_interface = new GFXMetal(); -#else - gfx_interface = new GFXVulkan(); -#endif if(gfx_interface->initialize(info)) { engine->set_gfx(gfx_interface); } else { @@ -431,29 +516,4 @@ PlatformTheme platform::get_theme() { PlatformTheme platform::get_theme() { return PlatformTheme::Light; } -#endif - -void* platform::create_native_surface(platform::window_ptr index, void* instance) { - auto window = get_window(index); - - VkSurfaceKHR surface; - - SDL_Vulkan_CreateSurface(window, (VkInstance)instance, &surface); - - return surface; -} - -std::vector platform::get_native_surface_extension() { - // dummy window - auto dummy = SDL_CreateWindow("", 0, 0, 1, 1, SDL_WINDOW_VULKAN); - - unsigned int count = 0; - SDL_Vulkan_GetInstanceExtensions(dummy, &count, nullptr); - - std::vector extensions(count); - SDL_Vulkan_GetInstanceExtensions(dummy, &count, extensions.data()); - - SDL_DestroyWindow(dummy); - - return extensions; -} +#endif \ No newline at end of file diff --git a/platforms/sdl/sdl_metal.mm b/platforms/sdl/sdl_metal.mm index 1bf5123..cb170a6 100644 --- a/platforms/sdl/sdl_metal.mm +++ b/platforms/sdl/sdl_metal.mm @@ -15,17 +15,22 @@ CAMetalLayer* get_layer(platform::window_ptr index) { return (__bridge CAMetalLayer*)SDL_RenderGetMetalLayer(renderers[get_window(index)]); } -// instance == MTL::Device -unsigned int platform::initialize_metal_layer(platform::window_ptr index, void* device) { +metal_surface* create_metal_surface(platform::window_ptr window, void* surface_creation_info) { auto layer = get_layer(index); - layer.device = (__bridge id)device; + auto layer = (__bridge CAMetalLayer*)SDL_RenderGetMetalLayer(renderers[get_window(window)]); + auto metal_surface_info = (metal_surface_creation_info*)surface_creation_info; + + layer.device = (__bridge id)metal_surface_info->device; layer.allowsNextDrawableTimeout = true; - return (unsigned int)layer.pixelFormat; + auto return_surface = new metal_surface(); + return_surface->format = layer.pixelFormat; + + return return_surface; } -void* platform::get_next_drawable(window_ptr window) { +void* get_next_metal_drawable(platform::window_ptr window) { auto renderer = renderers[get_window(window)]; auto layer = get_layer(window); diff --git a/platforms/sdl/sdl_vulkan.cpp b/platforms/sdl/sdl_vulkan.cpp new file mode 100644 index 0000000..0a6f6e1 --- /dev/null +++ b/platforms/sdl/sdl_vulkan.cpp @@ -0,0 +1,18 @@ +#include "platform.hpp" + +#include +#include + +extern std::vector windows; + +SDL_Window* get_window(platform::window_ptr index); + +void* create_vulkan_surface(platform::window_ptr window, void* surface_creation_info) { + auto vulkan_surface_info = (vulkan_surface_creation_info*)surface_creation_info; + auto window_ptr = get_window(window); + + auto vk_surface = new vulkan_surface(); + SDL_Vulkan_CreateSurface(window_ptr, vulkan_surface_info->instance, &vk_surface->surface); + + return vk_surface; +} \ No newline at end of file