Files
fs2open.github.com/freespace2/SDLGraphicsOperations.cpp
T
Shivansps 1d0dd687cd Android Build (#6992)
* android build support

* Update es_compatibility.h

* Support reporting flags in android

* include pref_path in json v2

* add vulkan headers

* shaderc includes

* remove headers

* force use prebuilt vulkan

* fix vulkan inverted screen

* fix find sdl3 precompiled android

* Update CMakeLists.txt

* replace sdl2 android function names for sdl3 versions

* change sdl includes in osapi

* fix sdl3 includes, again

* rename function to sdl3 name

* remove unused definition

* copy libshaderc_shared.so to output in android

* do not look for vulkan-loader in android

* revert changes to SDL2.cmake

* add comment to vulkan pretransform setup

* cleanup maxsize function

* rename new android functions in osapi

* android trap back button

* Remove calculate_max_resize_max_size

* touch ui opacity control

* change opacity control to int

* fix opacity slider

* Corrected android working folder fallback and set borderless mode as default

* Implement shared helper for JNI calls

* Add version tag and sha256 to android prebuilt libs

* vulkan android check eSuboptimal

* An already requested rebuild also avoids redundant Android surface queries

* add cache bool to cmakelist

* set string ids
2026-11-05 20:56:36 +01:00

347 lines
10 KiB
C++

//
//
#include "SDLGraphicsOperations.h"
#ifdef WITH_VULKAN
#include "backends/imgui_impl_vulkan.h"
#endif
#include "cmdline/cmdline.h"
#include <SDL3/SDL_vulkan.h>
#include "backends/imgui_impl_sdl3.h"
#include "backends/imgui_impl_opengl3.h"
namespace {
void setOGLProperties(const os::ViewPortProperties& props)
{
SDL_GL_ResetAttributes();
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, props.pixel_format.red_size);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, props.pixel_format.green_size);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, props.pixel_format.blue_size);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, props.pixel_format.depth_size);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, props.pixel_format.stencil_size);
#ifndef USE_OPENGL_ES
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
#else
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 0);
#endif
// disabled due to issues with implementation; may be re-enabled in future
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
mprintf((" Requested SDL Pixel values = R: %d, G: %d, B: %d, depth: %d, stencil: %d, double-buffer: %d, FSAA: %d\n",
props.pixel_format.red_size, props.pixel_format.green_size, props.pixel_format.blue_size,
props.pixel_format.depth_size, props.pixel_format.stencil_size, 1, props.pixel_format.multi_samples));
SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, props.gl_attributes.major_version);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, props.gl_attributes.minor_version);
#ifdef USE_OPENGL_ES
SDL_SetHint(SDL_HINT_OPENGL_ES_DRIVER, "1");
#endif
int profile;
switch (props.gl_attributes.profile) {
case os::OpenGLProfile::Core:
profile = SDL_GL_CONTEXT_PROFILE_CORE;
break;
case os::OpenGLProfile::Compatibility:
profile = SDL_GL_CONTEXT_PROFILE_COMPATIBILITY;
break;
case os::OpenGLProfile::ES:
profile = SDL_GL_CONTEXT_PROFILE_ES;
break;
default:
UNREACHABLE("Unhandled profile value %d!", static_cast<int>(props.gl_attributes.profile));
return;
}
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profile);
int flags = 0;
if (props.gl_attributes.flags[os::OpenGLContextFlags::Debug]) {
flags |= SDL_GL_CONTEXT_DEBUG_FLAG;
}
if (props.gl_attributes.flags[os::OpenGLContextFlags::ForwardCompatible]) {
flags |= SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG;
}
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, flags);
}
}
class SDLOpenGLContext: public os::OpenGLContext {
SDL_GLContext _glCtx;
public:
SDLOpenGLContext(SDL_GLContext sdl_gl_context) : _glCtx(sdl_gl_context) {
}
~SDLOpenGLContext() override {
SDL_GL_DestroyContext(_glCtx);
}
os::OpenGLLoadProc getLoaderFunction() override {
return reinterpret_cast<os::OpenGLLoadProc>(SDL_GL_GetProcAddress);
}
void makeCurrent(SDL_Window* window) {
SDL_GL_MakeCurrent(window, _glCtx);
}
bool setSwapInterval(int status) override {
return SDL_GL_SetSwapInterval(status);
}
};
class SDLWindowViewPort: public os::Viewport {
SDL_Window* _window;
os::ViewPortProperties _props;
public:
SDLWindowViewPort(SDL_Window* window, const os::ViewPortProperties& props) : _window(window), _props(props) {
Assertion(window != nullptr, "Invalid window specified");
}
~SDLWindowViewPort() override {
SDL_DestroyWindow(_window);
_window = nullptr;
}
const os::ViewPortProperties& getProps() const {
return _props;
}
SDL_Window* toSDLWindow() override {
return _window;
}
std::pair<uint32_t, uint32_t> getSize() override {
int width, height;
SDL_GetWindowSizeInPixels(_window, &width, &height);
return std::make_pair(width, height);
}
void swapBuffers() override {
SDL_GL_SwapWindow(_window);
}
void setState(os::ViewportState state) override {
switch (state) {
case os::ViewportState::Windowed:
SDL_SetWindowFullscreen(_window, false);
SDL_SetWindowBordered(_window, true);
break;
case os::ViewportState::Borderless:
SDL_SetWindowFullscreen(_window, false);
SDL_SetWindowBordered(_window, false);
break;
case os::ViewportState::Fullscreen: {
SDL_DisplayMode target;
int width, height;
if (SDL_GetWindowSizeInPixels(_window, &width, &height)) {
if (SDL_GetClosestFullscreenDisplayMode(SDL_GetDisplayForWindow(_window),
width, height, 0.0f, true, &target))
{
SDL_SetWindowFullscreenMode(_window, &target);
}
}
// NOTE: This can be buggy if the mode failed to set since FSO
// doesn't account for a difference between assumed window size
// and actual window size. This can present as screen anomalies
// such as distortion, mirroring, or flickering.
SDL_SetWindowFullscreen(_window, true);
break;
}
default:
UNREACHABLE("Invalid window state %d!", static_cast<int>(state));
break;
}
}
void minimize() override {
// lets not minimize if we are in windowed mode
if (!(SDL_GetWindowFlags(_window) & SDL_WINDOW_FULLSCREEN)) {
return;
}
SDL_MinimizeWindow(_window);
}
void restore() override {
SDL_RestoreWindow(_window);
}
};
SDLGraphicsOperations::SDLGraphicsOperations() {
mprintf((" Initializing SDL video...\n"));
#ifdef SCP_UNIX
// Slight hack to make Mesa advertise S3TC support without libtxc_dxtn
setenv("force_s3tc_enable", "true", 1);
#endif
if ( !SDL_InitSubSystem(SDL_INIT_VIDEO) ) {
Error(LOCATION, "Couldn't init SDL video: %s", SDL_GetError());
return;
}
}
SDLGraphicsOperations::~SDLGraphicsOperations() {
// make sure imgui stuff is initialized before trying to shut it down
if (ImGui::GetCurrentContext()) {
if (ImGui::GetIO().BackendPlatformUserData) {
ImGui_ImplSDL3_Shutdown();
}
if ( ImGui::GetIO().BackendRendererUserData ) {
switch (gr_screen.mode) {
case GraphicsAPI::OpenGL:
ImGui_ImplOpenGL3_Shutdown();
break;
case GraphicsAPI::Vulkan:
#ifdef WITH_VULKAN
ImGui_ImplVulkan_Shutdown();
#endif
break;
default:
break;
}
}
}
SDL_QuitSubSystem(SDL_INIT_VIDEO);
}
std::unique_ptr<os::Viewport> SDLGraphicsOperations::createViewport(const os::ViewPortProperties& props)
{
uint32_t windowflags = 0;
if (props.enable_opengl) {
windowflags |= SDL_WINDOW_OPENGL;
setOGLProperties(props);
}
if (props.enable_vulkan) {
windowflags |= SDL_WINDOW_VULKAN;
}
if (props.flags[os::ViewPortFlags::Borderless]) {
windowflags |= SDL_WINDOW_BORDERLESS;
}
if (props.flags[os::ViewPortFlags::Fullscreen]) {
// don't set window flag here since we need to alter the display mode
// first and that can only be done after the window is created
//
// windowflags |= SDL_WINDOW_FULLSCREEN;
}
if (props.flags[os::ViewPortFlags::Resizeable]) {
windowflags |= SDL_WINDOW_RESIZABLE;
}
if (props.flags[os::ViewPortFlags::Capture_Mouse]) {
windowflags |= SDL_WINDOW_MOUSE_GRABBED;
}
#ifdef __ANDROID__
SDL_SetHint(SDL_HINT_ANDROID_TRAP_BACK_BUTTON, "1");
SDL_SetHint(SDL_HINT_ORIENTATIONS, "LandscapeLeft LandscapeRight");
os_android_touch_overlay_init();
#endif
SDL_Rect bounds;
if ( !SDL_GetDisplayBounds(props.display, &bounds) ) {
mprintf(("Failed to get display bounds: %s\n", SDL_GetError()));
return nullptr;
}
int x;
int y;
uint32_t width = props.width;
uint32_t height = props.height;
if (Cmdline_window_res) {
width = Cmdline_window_res->first;
height = Cmdline_window_res->second;
}
if (bounds.w == (int)width && bounds.h == (int)height) {
// If we have the same size as the desktop we explicitly specify 0,0 to make sure that the window borders aren't hidden
mprintf(("SDL: Creating window at %d,%d because window has same size as desktop.\n", bounds.x, bounds.y));
x = bounds.x;
y = bounds.y;
} else {
x = SDL_WINDOWPOS_CENTERED_DISPLAY(props.display);
y = SDL_WINDOWPOS_CENTERED_DISPLAY(props.display);
}
SDL_Window* window = SDL_CreateWindow(props.title.c_str(),
width,
height,
windowflags);
if (window == nullptr) {
mprintf(("Failed to create SDL Window: %s\n", SDL_GetError()));
return nullptr;
}
// switch to fullscreen if we should
if (props.flags[os::ViewPortFlags::Fullscreen]) {
SDL_DisplayMode target;
if (SDL_GetClosestFullscreenDisplayMode(props.display, width, height, 0.0f, true, &target)) {
SDL_SetWindowFullscreenMode(window, &target);
}
// NOTE: This can be buggy if the mode failed to set since FSO doesn't
// account for a difference between assumed window size and actual window
// size. This can present as screen anomalies such as distortion, mirroring,
// or flickering.
SDL_SetWindowFullscreen(window, true);
}
SDL_SetWindowPosition(window, x, y);
SDL_RaiseWindow(window);
return std::unique_ptr<os::Viewport>(new SDLWindowViewPort(window, props));
}
void SDLGraphicsOperations::makeOpenGLContextCurrent(os::Viewport* view, os::OpenGLContext* ctx) {
if (view == nullptr && ctx == nullptr) {
SDL_GL_MakeCurrent(nullptr, nullptr);
return;
}
Assertion(view != nullptr, "Both viewport of context must be valid at this point!");
Assertion(ctx != nullptr, "Both viewport of context must be valid at this point!");
auto sdlCtx = reinterpret_cast<SDLOpenGLContext*>(ctx);
sdlCtx->makeCurrent(view->toSDLWindow());
}
std::unique_ptr<os::OpenGLContext> SDLGraphicsOperations::createOpenGLContext(os::Viewport* viewport,
const os::OpenGLContextAttributes& gl_attrs) {
auto sdlViewport = reinterpret_cast<SDLWindowViewPort*>(viewport);
auto props = sdlViewport->getProps();
props.gl_attributes = gl_attrs;
setOGLProperties(props);
auto ctx = SDL_GL_CreateContext(viewport->toSDLWindow());
if (ctx == nullptr) {
mprintf(("Could not create OpenGL Context: %s\n", SDL_GetError()));
return nullptr;
}
int r, g, b, depth, stencil, db, fsaa_samples;
SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &r);
SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE, &g);
SDL_GL_GetAttribute(SDL_GL_BLUE_SIZE, &b);
SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &depth);
SDL_GL_GetAttribute(SDL_GL_DOUBLEBUFFER, &db);
SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &stencil);
SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &fsaa_samples);
mprintf((" Actual SDL Video values = R: %d, G: %d, B: %d, depth: %d, stencil: %d, double-buffer: %d, FSAA: %d\n",
r, g, b, depth, stencil, db, fsaa_samples));
Assertion(ImGui::GetCurrentContext() != nullptr, "Can't use ImGui without a valid context!");
ImGui_ImplSDL3_InitForOpenGL(viewport->toSDLWindow(), ctx);
#ifndef USE_OPENGL_ES
ImGui_ImplOpenGL3_Init();
#else
ImGui_ImplOpenGL3_Init("#version 300 es");
#endif
return std::unique_ptr<os::OpenGLContext>(new SDLOpenGLContext(ctx));
}