diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index 8526d51aa..219b478e3 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -190,6 +190,12 @@ if(NOT ANDROID) sdl_input_source.cpp sdl_input_source.h ) + if(ENABLE_OPENGL) + target_sources(util PRIVATE + opengl_context_sdl.cpp + opengl_context_sdl.h + ) + endif() target_compile_definitions(util PUBLIC ENABLE_SDL ) diff --git a/src/util/opengl_context.cpp b/src/util/opengl_context.cpp index c5ae86b0b..987bbe54b 100644 --- a/src/util/opengl_context.cpp +++ b/src/util/opengl_context.cpp @@ -33,6 +33,10 @@ #endif #endif +#ifdef ENABLE_SDL +#include "opengl_context_sdl.h" +#endif + LOG_CHANNEL(GPUDevice); static bool ShouldPreferESContext() @@ -175,6 +179,10 @@ std::unique_ptr OpenGLContext::Create(WindowInfo& wi, SurfaceHand if (wi.type == WindowInfo::Type::Surfaceless) context = OpenGLContextEGL::Create(wi, surface, versions_to_try, error); #endif +#ifdef ENABLE_SDL + if (wi.type == WindowInfo::Type::SDL) + context = OpenGLContextSDL::Create(wi, surface, versions_to_try, error); +#endif if (!context) return nullptr; diff --git a/src/util/opengl_context_sdl.cpp b/src/util/opengl_context_sdl.cpp new file mode 100644 index 000000000..9ff12b361 --- /dev/null +++ b/src/util/opengl_context_sdl.cpp @@ -0,0 +1,256 @@ +// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin +// SPDX-License-Identifier: CC-BY-NC-ND-4.0 + +#include "opengl_context_sdl.h" +#include "opengl_loader.h" + +#include "common/assert.h" +#include "common/error.h" +#include "common/log.h" +#include "common/scoped_guard.h" + +#include "SDL3/SDL.h" + +LOG_CHANNEL(GPUDevice); + +OpenGLContextSDL::OpenGLContextSDL() = default; + +OpenGLContextSDL::~OpenGLContextSDL() +{ + if (SDL_GL_GetCurrentContext() == m_context) + SDL_GL_MakeCurrent(nullptr, nullptr); + + if (m_context) + SDL_GL_DestroyContext(m_context); +} + +std::unique_ptr OpenGLContextSDL::Create(WindowInfo& wi, SurfaceHandle* surface, + std::span versions_to_try, Error* error) +{ + std::unique_ptr context = std::make_unique(); + if (!context->Initialize(wi, surface, versions_to_try, false, error)) + context.reset(); + + return context; +} + +bool OpenGLContextSDL::Initialize(WindowInfo& wi, SurfaceHandle* surface, std::span versions_to_try, + bool share_context, Error* error) +{ + static bool opengl_loaded = false; + if (!opengl_loaded) + { + if (!SDL_GL_LoadLibrary(nullptr)) + { + Error::SetStringFmt(error, "SDL_GL_LoadLibrary() failed: {}", SDL_GetError()); + return false; + } + + opengl_loaded = true; + } + + if (wi.IsSurfaceless()) + { + Error::SetStringView(error, "Surfaceless is not supported with OpenGLContextSDL."); + return false; + } + else if (wi.type != WindowInfo::Type::SDL) + { + Error::SetStringView(error, "Incompatible window type."); + return false; + } + + if (wi.surface_format == GPUTexture::Format::Unknown) + wi.surface_format = GPUTexture::Format::RGBA8; + + SDL_Window* const window = static_cast(wi.window_handle); + for (const Version& cv : versions_to_try) + { + if (CreateVersionContext(cv, window, wi.surface_format, share_context, !share_context)) + { + m_version = cv; + *surface = window; + UpdateWindowInfoSize(wi, window); + return true; + } + } + + Error::SetStringView(error, "Failed to create any contexts."); + return false; +} + +void* OpenGLContextSDL::GetProcAddress(const char* name) +{ + return (void*)SDL_GL_GetProcAddress(name); +} + +OpenGLContext::SurfaceHandle OpenGLContextSDL::CreateSurface(WindowInfo& wi, Error* error /*= nullptr*/) +{ + if (wi.IsSurfaceless()) [[unlikely]] + { + Error::SetStringView(error, "Trying to create a surfaceless surface."); + return nullptr; + } + else if (wi.type != WindowInfo::Type::SDL) + { + Error::SetStringView(error, "Incompatible window type."); + return nullptr; + } + + return static_cast(wi.window_handle); +} + +void OpenGLContextSDL::DestroySurface(SurfaceHandle handle) +{ + // cleaned up on window destruction? +} + +void OpenGLContextSDL::ResizeSurface(WindowInfo& wi, SurfaceHandle handle) +{ + UpdateWindowInfoSize(wi, static_cast(handle)); +} + +void OpenGLContextSDL::UpdateWindowInfoSize(WindowInfo& wi, SDL_Window* window) const +{ + int drawable_width, drawable_height; + SDL_GetWindowSizeInPixels(window, &drawable_width, &drawable_height); + wi.surface_width = static_cast(drawable_width); + wi.surface_height = static_cast(drawable_height); + if (WindowInfo::ShouldSwapDimensionsForPreRotation(wi.surface_prerotation)) + std::swap(wi.surface_width, wi.surface_height); +} + +bool OpenGLContextSDL::SwapBuffers() +{ + SDL_GL_SwapWindow(m_current_window); + return true; +} + +bool OpenGLContextSDL::IsCurrent() const +{ + return (m_context && SDL_GL_GetCurrentContext() == m_context); +} + +bool OpenGLContextSDL::MakeCurrent(SurfaceHandle surface, Error* error /* = nullptr */) +{ + SDL_Window* const window = static_cast(surface); + if (m_current_window == window) + return true; + + if (SDL_GL_MakeCurrent(window, m_context) != 0) + { + ERROR_LOG("SDL_GL_MakeCurrent() failed: {}", SDL_GetError()); + return false; + } + + m_current_window = window; + return true; +} + +bool OpenGLContextSDL::DoneCurrent() +{ + if (SDL_GL_MakeCurrent(nullptr, nullptr) != 0) + return false; + + m_current_window = nullptr; + return true; +} + +bool OpenGLContextSDL::SupportsNegativeSwapInterval() const +{ + int current_interval = 0; + if (!SDL_GL_GetSwapInterval(¤t_interval)) + { + ERROR_LOG("SDL_GL_GetSwapInterval() failed: {}", SDL_GetError()); + return false; + } + + const bool supported = SDL_GL_SetSwapInterval(-1); + SDL_GL_SetSwapInterval(current_interval); + return supported; +} + +bool OpenGLContextSDL::SetSwapInterval(s32 interval, Error* error) +{ + if (SDL_GL_SetSwapInterval(interval) != 0) + { + Error::SetStringFmt(error, "SDL_GL_SetSwapInterval() failed: ", SDL_GetError()); + return false; + } + + return true; +} + +std::unique_ptr OpenGLContextSDL::CreateSharedContext(WindowInfo& wi, SurfaceHandle* surface, + Error* error) +{ + std::unique_ptr context = std::make_unique(); + if (!context->Initialize(wi, surface, std::span(&m_version, 1), true, error)) + context.reset(); + return {}; +} + +bool OpenGLContextSDL::CreateVersionContext(const Version& version, SDL_Window* window, + GPUTexture::Format surface_format, bool share_context, bool make_current) +{ + SDL_GL_ResetAttributes(); + + switch (surface_format) + { + case GPUTexture::Format::RGBA8: + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); + break; + + case GPUTexture::Format::RGB565: + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5); + break; + + default: + ERROR_LOG("Unsupported texture format {}", GPUTexture::GetFormatName(surface_format)); + break; + } + + if (share_context) + SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, true); + + if (version.profile != Profile::NoProfile) + { + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, + (version.profile == Profile::ES) ? SDL_GL_CONTEXT_PROFILE_ES : SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, version.major_version); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, version.minor_version); + if (version.profile == Profile::Core) + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); + } + + SDL_GLContext context = SDL_GL_CreateContext(window); + if (!context) + { + DEV_LOG("SDL_GL_CreateContext({}.{}{}) failed: {}", version.major_version, version.minor_version, + ((version.profile == Profile::ES) ? " ES" : ((version.profile == Profile::Core) ? " Core" : "")), + SDL_GetError()); + return false; + } + + if (make_current) + { + if (!SDL_GL_MakeCurrent(window, context)) + { + DEV_LOG("SDL_GL_MakeCurrent({}.{}{}) failed: {}", version.major_version, version.minor_version, + ((version.profile == Profile::ES) ? " ES" : ((version.profile == Profile::Core) ? " Core" : "")), + SDL_GetError()); + SDL_GL_DestroyContext(context); + return false; + } + + m_current_window = window; + } + + m_context = context; + return true; +} diff --git a/src/util/opengl_context_sdl.h b/src/util/opengl_context_sdl.h new file mode 100644 index 000000000..b8f275c7b --- /dev/null +++ b/src/util/opengl_context_sdl.h @@ -0,0 +1,46 @@ +// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin +// SPDX-License-Identifier: CC-BY-NC-ND-4.0 + +#pragma once + +#include "opengl_context.h" +#include "opengl_loader.h" + +#include + +typedef struct SDL_GLContextState* SDL_GLContext; +struct SDL_Window; + +class OpenGLContextSDL final : public OpenGLContext +{ +public: + OpenGLContextSDL(); + ~OpenGLContextSDL() override; + + static std::unique_ptr Create(WindowInfo& wi, SurfaceHandle* surface, + std::span versions_to_try, Error* error); + + void* GetProcAddress(const char* name) override; + SurfaceHandle CreateSurface(WindowInfo& wi, Error* error = nullptr) override; + void DestroySurface(SurfaceHandle handle) override; + void ResizeSurface(WindowInfo& wi, SurfaceHandle handle) override; + bool SwapBuffers() override; + bool IsCurrent() const override; + bool MakeCurrent(SurfaceHandle surface, Error* error = nullptr) override; + bool DoneCurrent() override; + bool SupportsNegativeSwapInterval() const override; + bool SetSwapInterval(s32 interval, Error* error = nullptr) override; + std::unique_ptr CreateSharedContext(WindowInfo& wi, SurfaceHandle* surface, Error* error) override; + +private: + bool Initialize(WindowInfo& wi, SurfaceHandle* surface, std::span versions_to_try, bool share_context, + Error* error); + + bool CreateVersionContext(const Version& version, SDL_Window* window, GPUTexture::Format surface_format, + bool share_context, bool make_current); + + void UpdateWindowInfoSize(WindowInfo& wi, SDL_Window* window) const; + + SDL_GLContext m_context = nullptr; + SDL_Window* m_current_window = nullptr; +}; diff --git a/src/util/util.vcxproj b/src/util/util.vcxproj index bd53ddbeb..c08743847 100644 --- a/src/util/util.vcxproj +++ b/src/util/util.vcxproj @@ -59,6 +59,9 @@ true + + true + true @@ -166,6 +169,9 @@ true + + true + true diff --git a/src/util/util.vcxproj.filters b/src/util/util.vcxproj.filters index 60392b279..2260a86ec 100644 --- a/src/util/util.vcxproj.filters +++ b/src/util/util.vcxproj.filters @@ -74,6 +74,7 @@ + @@ -156,6 +157,7 @@ +