mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 08:15:59 -10:00
* GLES Shaders and Vulkan Clip fallback * clip fallback: change defined name, set from engine side and invert usage * Proposed changes and fixes Fixed: -The OpenGL ES option stays on when OpenGL is off (issue #7984) -Missing precision in copy-f, volumetric-f and smaa-resolve-f -msaa-f including z-compress before precision quantification -missing float literals in volumetric-f -Fixed decals blending -Restored glBindFragDataLocation() for use in legacy external shaders -Enabled GL_ARB_explicit_attrib_location ext for GLSL version below 330 in GL -Guarded enabling/disabling of GL_CLIP_DISTANCE0_EXT and GL_DEPTH_CLAMP -Restored PI in lighting.sdr -removed "External shaders only" indication from cmakelists.txt
341 lines
10 KiB
C++
341 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;
|
|
}
|
|
|
|
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));
|
|
}
|