mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 08:15:59 -10:00
* avoid undefined behavior from UNREACHABLE on reachable code paths
UNREACHABLE expands to __assume(false) / __builtin_unreachable() in
release builds, which is undefined behavior if the code is ever actually
reached. Many UNREACHABLE sites only guard against unexpected-but-
possible runtime states (values from tables, mission/save files, network
packets, scripting, or user input), so reaching them should degrade
gracefully rather than invoke UB.
Replace UNREACHABLE with Assertion(false, ...) at all such sites. It
still pops an error in debug builds, but in release it is a harmless
no-op that simply falls through to the following code. Sites that are
genuinely unreachable by local invariants (e.g. a switch over a bounded
random number) are left as UNREACHABLE.
A few converted sites had no safe fall-through -- an uninitialized read,
null dereference, or null call would have followed -- so they also get a
minimal recovery: initialize the value, guard the dereference, return
early, or skip the offending item.
Where the converted site was a simple guard of the form
if (x) { Assertion(false, ...); return; }
hoist the check onto the invariant instead:
Assertion(!x, ...);
if (x) { return; }
This states the expectation directly and shows the offending value in
the debug popup, while keeping the same graceful runtime degradation.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* addendum
After consultation with Lafiel, use UNREACHABLE in situations that are effectively impossible even if not provably impossible. UNREACHABLE = "this shouldn't ever happen"; Assertion = "if this ever happens, it's a coding error". Also fix a few other sites and change a couple to Warnings.
---------
Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
295 lines
8.8 KiB
C++
295 lines
8.8 KiB
C++
//
|
|
//
|
|
|
|
#include "SDLGraphicsOperations.h"
|
|
|
|
#include "cmdline/cmdline.h"
|
|
|
|
#if SDL_VERSION_ATLEAST(2, 0, 6)
|
|
#include <SDL_vulkan.h>
|
|
#include "backends/imgui_impl_sdl.h"
|
|
#include "backends/imgui_impl_opengl3.h"
|
|
#endif
|
|
|
|
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_DeleteContext(_glCtx);
|
|
}
|
|
|
|
os::OpenGLLoadProc getLoaderFunction() override {
|
|
return SDL_GL_GetProcAddress;
|
|
}
|
|
|
|
void makeCurrent(SDL_Window* window) {
|
|
SDL_GL_MakeCurrent(window, _glCtx);
|
|
}
|
|
|
|
bool setSwapInterval(int status) override {
|
|
return SDL_GL_SetSwapInterval(status) == 0;
|
|
}
|
|
};
|
|
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_GetWindowSize(_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, 0);
|
|
SDL_SetWindowBordered(_window, SDL_TRUE);
|
|
break;
|
|
case os::ViewportState::Borderless:
|
|
SDL_SetWindowFullscreen(_window, 0);
|
|
SDL_SetWindowBordered(_window, SDL_FALSE);
|
|
break;
|
|
case os::ViewportState::Fullscreen:
|
|
SDL_SetWindowFullscreen(_window, SDL_WINDOW_FULLSCREEN);
|
|
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) < 0) {
|
|
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_ImplSDL2_Shutdown();
|
|
}
|
|
|
|
if ( !Cmdline_vulkan && ImGui::GetIO().BackendRendererUserData ) {
|
|
ImGui_ImplOpenGL3_Shutdown();
|
|
}
|
|
}
|
|
|
|
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
|
}
|
|
std::unique_ptr<os::Viewport> SDLGraphicsOperations::createViewport(const os::ViewPortProperties& props)
|
|
{
|
|
uint32_t windowflags = SDL_WINDOW_SHOWN;
|
|
if (props.enable_opengl) {
|
|
windowflags |= SDL_WINDOW_OPENGL;
|
|
setOGLProperties(props);
|
|
}
|
|
#if SDL_VERSION_ATLEAST(2, 0, 6)
|
|
if (props.enable_vulkan) {
|
|
windowflags |= SDL_WINDOW_VULKAN;
|
|
}
|
|
#endif
|
|
if (props.flags[os::ViewPortFlags::Borderless]) {
|
|
windowflags |= SDL_WINDOW_BORDERLESS;
|
|
}
|
|
if (props.flags[os::ViewPortFlags::Fullscreen]) {
|
|
windowflags |= SDL_WINDOW_FULLSCREEN;
|
|
}
|
|
if (props.flags[os::ViewPortFlags::Resizeable]) {
|
|
windowflags |= SDL_WINDOW_RESIZABLE;
|
|
}
|
|
if (props.flags[os::ViewPortFlags::Capture_Mouse]) {
|
|
windowflags |= SDL_WINDOW_INPUT_GRABBED;
|
|
}
|
|
|
|
SDL_Rect bounds;
|
|
if (SDL_GetDisplayBounds(props.display, &bounds) != 0) {
|
|
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(),
|
|
x,
|
|
y,
|
|
width,
|
|
height,
|
|
windowflags);
|
|
if (window == nullptr) {
|
|
mprintf(("Failed to create SDL Window: %s\n", SDL_GetError()));
|
|
return nullptr;
|
|
}
|
|
|
|
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_ImplSDL2_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));
|
|
}
|