mirror of
https://github.com/TerryCavanagh/VVVVVV.git
synced 2026-07-27 17:41:49 +03:00
4b9f26bf81
Past solutions were just "recache the screen textures under these known circumstances" where whenever things OUTSIDE of those known circumstances happened, the issue would reoccur. I recently learned about an SDL event, `SDL_RENDER_TARGETS_RESET`, which is for this exact problem. I ripped out all of the other places `Screen::recacheTextures()` was called, and just slotted it in there, and it worked perfectly. ...well, the old behavior worked perfectly; but the old behavior was flawed as well, because it only checked for "ingame_titlemode" (if you're in the main menu during gameplay) and forgot to check for the map screen... plus, it didn't ever regenerate the minimap in custom levels, which is another "persistant" render target. Hopefully, this is the last time we'll ever have to think about this one. I'm certainly sick of it. This should 100% be backported into 2.4, as the bug occurs there as well.
397 lines
10 KiB
C++
397 lines
10 KiB
C++
#define GAMESCREEN_DEFINITION
|
|
#include "Screen.h"
|
|
|
|
#include <SDL.h>
|
|
|
|
#include "Alloc.h"
|
|
#include "Constants.h"
|
|
#include "CustomLevels.h"
|
|
#include "Enums.h"
|
|
#include "Exit.h"
|
|
#include "FileSystemUtils.h"
|
|
#include "Game.h"
|
|
#include "Graphics.h"
|
|
#include "GraphicsUtil.h"
|
|
#ifndef __APPLE__
|
|
#include "GraphicsResources.h"
|
|
#endif
|
|
#include "InterimVersion.h"
|
|
#include "Map.h"
|
|
#include "Render.h"
|
|
#include "Vlogging.h"
|
|
|
|
void ScreenSettings_default(struct ScreenSettings* _this)
|
|
{
|
|
_this->windowDisplay = 0;
|
|
_this->windowWidth = SCREEN_WIDTH_PIXELS * 2;
|
|
_this->windowHeight = SCREEN_HEIGHT_PIXELS * 2;
|
|
_this->fullscreen = false;
|
|
_this->useVsync = true; // Now that uncapped is the default...
|
|
_this->scalingMode = SCALING_INTEGER;
|
|
_this->linearFilter = false;
|
|
_this->badSignal = false;
|
|
}
|
|
|
|
void Screen::init(const struct ScreenSettings* settings)
|
|
{
|
|
m_window = NULL;
|
|
m_renderer = NULL;
|
|
windowDisplay = settings->windowDisplay;
|
|
windowWidth = settings->windowWidth;
|
|
windowHeight = settings->windowHeight;
|
|
isWindowed = !settings->fullscreen;
|
|
scalingMode = settings->scalingMode;
|
|
isFiltered = settings->linearFilter;
|
|
badSignalEffect = settings->badSignal;
|
|
vsync = settings->useVsync;
|
|
|
|
// Uncomment this next line when you need to debug -flibit
|
|
// SDL_SetHintWithPriority(SDL_HINT_RENDER_DRIVER, "software", SDL_HINT_OVERRIDE);
|
|
|
|
m_window = SDL_CreateWindow(
|
|
"VVVVVV",
|
|
SDL_WINDOWPOS_CENTERED_DISPLAY(windowDisplay),
|
|
SDL_WINDOWPOS_CENTERED_DISPLAY(windowDisplay),
|
|
SCREEN_WIDTH_PIXELS * 2,
|
|
SCREEN_HEIGHT_PIXELS * 2,
|
|
SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI
|
|
);
|
|
|
|
if (m_window == NULL)
|
|
{
|
|
vlog_error("Could not create window: %s", SDL_GetError());
|
|
VVV_exit(1);
|
|
}
|
|
|
|
m_renderer = SDL_CreateRenderer(m_window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE);
|
|
|
|
if (m_renderer == NULL)
|
|
{
|
|
vlog_error("Could not create renderer: %s", SDL_GetError());
|
|
VVV_exit(1);
|
|
}
|
|
|
|
SDL_RenderSetVSync(m_renderer, (int) vsync);
|
|
|
|
#ifdef INTERIM_VERSION_EXISTS
|
|
/* Branch name limits are ill-defined but on GitHub it's ~256 chars
|
|
* ( https://stackoverflow.com/a/24014513/ ).
|
|
* Really though, just don't use super long branch names. */
|
|
char title[256];
|
|
SDL_snprintf(title, sizeof(title), "VVVVVV [%s]", BRANCH_NAME);
|
|
SDL_SetWindowTitle(m_window, title);
|
|
#else
|
|
SDL_SetWindowTitle(m_window, "VVVVVV");
|
|
#endif
|
|
|
|
SDL_SetWindowMinimumSize(m_window, SCREEN_WIDTH_PIXELS, SCREEN_HEIGHT_PIXELS);
|
|
|
|
LoadIcon();
|
|
|
|
ResizeScreen(windowWidth, windowHeight);
|
|
}
|
|
|
|
void Screen::destroy(void)
|
|
{
|
|
/* Order matters! */
|
|
VVV_freefunc(SDL_DestroyRenderer, m_renderer);
|
|
VVV_freefunc(SDL_DestroyWindow, m_window);
|
|
}
|
|
|
|
void Screen::GetSettings(struct ScreenSettings* settings)
|
|
{
|
|
windowDisplay = SDL_GetWindowDisplayIndex(m_window);
|
|
if (windowDisplay < 0)
|
|
{
|
|
vlog_error("Error: could not get display index: %s", SDL_GetError());
|
|
windowDisplay = 0;
|
|
}
|
|
settings->windowDisplay = windowDisplay;
|
|
settings->windowWidth = windowWidth;
|
|
settings->windowHeight = windowHeight;
|
|
|
|
settings->fullscreen = !isWindowed;
|
|
settings->useVsync = vsync;
|
|
settings->scalingMode = scalingMode;
|
|
settings->linearFilter = isFiltered;
|
|
settings->badSignal = badSignalEffect;
|
|
}
|
|
|
|
#ifdef __APPLE__
|
|
/* Apple doesn't like icons anymore... */
|
|
void Screen::LoadIcon(void)
|
|
{
|
|
|
|
}
|
|
#else
|
|
void Screen::LoadIcon(void)
|
|
{
|
|
SDL_Surface* icon = LoadImageSurface("VVVVVV.png");
|
|
if (icon == NULL)
|
|
{
|
|
return;
|
|
}
|
|
SDL_SetWindowIcon(m_window, icon);
|
|
VVV_freefunc(SDL_FreeSurface, icon);
|
|
}
|
|
#endif /* __APPLE__ */
|
|
|
|
void Screen::ResizeScreen(int x, int y)
|
|
{
|
|
windowDisplay = SDL_GetWindowDisplayIndex(m_window);
|
|
if (windowDisplay < 0)
|
|
{
|
|
vlog_error("Error: could not get display index: %s", SDL_GetError());
|
|
windowDisplay = 0;
|
|
}
|
|
|
|
if (x != -1 && y != -1)
|
|
{
|
|
// This is a user resize!
|
|
windowWidth = x;
|
|
windowHeight = y;
|
|
}
|
|
|
|
if (!isWindowed || isForcedFullscreen())
|
|
{
|
|
int result = SDL_SetWindowFullscreen(m_window, SDL_WINDOW_FULLSCREEN_DESKTOP);
|
|
if (result != 0)
|
|
{
|
|
vlog_error("Error: could not set the game to fullscreen mode: %s", SDL_GetError());
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int result = SDL_SetWindowFullscreen(m_window, 0);
|
|
if (result != 0)
|
|
{
|
|
vlog_error("Error: could not set the game to windowed mode: %s", SDL_GetError());
|
|
}
|
|
else if (x != -1 && y != -1)
|
|
{
|
|
SDL_SetWindowSize(m_window, windowWidth, windowHeight);
|
|
SDL_SetWindowPosition(
|
|
m_window,
|
|
SDL_WINDOWPOS_CENTERED_DISPLAY(windowDisplay),
|
|
SDL_WINDOWPOS_CENTERED_DISPLAY(windowDisplay)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void constrain_to_desktop(int display_index, int* width, int* height)
|
|
{
|
|
SDL_DisplayMode display_mode = {};
|
|
int success = SDL_GetDesktopDisplayMode(display_index, &display_mode);
|
|
if (success != 0)
|
|
{
|
|
vlog_error("Could not get desktop display mode: %s", SDL_GetError());
|
|
return;
|
|
}
|
|
|
|
while ((*width > display_mode.w || *height > display_mode.h)
|
|
&& *width > SCREEN_WIDTH_PIXELS && *height > SCREEN_HEIGHT_PIXELS)
|
|
{
|
|
// We are too big, take away one multiple
|
|
*width -= SCREEN_WIDTH_PIXELS;
|
|
*height -= SCREEN_HEIGHT_PIXELS;
|
|
}
|
|
}
|
|
|
|
void Screen::ResizeToNearestMultiple(void)
|
|
{
|
|
int w, h;
|
|
GetScreenSize(&w, &h);
|
|
|
|
// Check aspect ratio first
|
|
bool using_width;
|
|
int usethisdimension, usethisratio;
|
|
|
|
if ((float) w / (float) h > 4.0 / 3.0)
|
|
{
|
|
// Width is bigger, so it's limited by height
|
|
usethisdimension = h;
|
|
usethisratio = SCREEN_HEIGHT_PIXELS;
|
|
using_width = false;
|
|
}
|
|
else
|
|
{
|
|
// Height is bigger, so it's limited by width. Or we're exactly 4:3 already
|
|
usethisdimension = w;
|
|
usethisratio = SCREEN_WIDTH_PIXELS;
|
|
using_width = true;
|
|
}
|
|
|
|
int floor = (usethisdimension / usethisratio) * usethisratio;
|
|
int ceiling = floor + usethisratio;
|
|
|
|
int final_dimension;
|
|
|
|
if (usethisdimension - floor < ceiling - usethisdimension)
|
|
{
|
|
// Floor is nearest
|
|
final_dimension = floor;
|
|
}
|
|
else
|
|
{
|
|
// Ceiling is nearest. Or we're exactly on a multiple already
|
|
final_dimension = ceiling;
|
|
}
|
|
|
|
if (final_dimension == 0)
|
|
{
|
|
// We're way too small!
|
|
ResizeScreen(SCREEN_WIDTH_PIXELS, SCREEN_HEIGHT_PIXELS);
|
|
return;
|
|
}
|
|
|
|
if (using_width)
|
|
{
|
|
w = final_dimension;
|
|
h = final_dimension / 4 * 3;
|
|
}
|
|
else
|
|
{
|
|
w = final_dimension * 4 / 3;
|
|
h = final_dimension;
|
|
}
|
|
|
|
windowDisplay = SDL_GetWindowDisplayIndex(m_window);
|
|
if (windowDisplay < 0)
|
|
{
|
|
vlog_error("Could not get display index: %s", SDL_GetError());
|
|
windowDisplay = 0;
|
|
}
|
|
|
|
constrain_to_desktop(windowDisplay, &w, &h);
|
|
|
|
ResizeScreen(w, h);
|
|
}
|
|
|
|
void Screen::GetScreenSize(int* x, int* y)
|
|
{
|
|
if (SDL_GetRendererOutputSize(m_renderer, x, y) != 0)
|
|
{
|
|
vlog_error("Could not get window size: %s", SDL_GetError());
|
|
/* Initialize to safe defaults */
|
|
*x = SCREEN_WIDTH_PIXELS;
|
|
*y = SCREEN_HEIGHT_PIXELS;
|
|
}
|
|
}
|
|
|
|
void Screen::RenderPresent(void)
|
|
{
|
|
SDL_RenderPresent(m_renderer);
|
|
graphics.clear();
|
|
}
|
|
|
|
void Screen::toggleFullScreen(void)
|
|
{
|
|
isWindowed = !isWindowed;
|
|
ResizeScreen(windowWidth, windowHeight);
|
|
|
|
if (game.currentmenuname == Menu::graphicoptions)
|
|
{
|
|
/* Recreate menu to update "resize to nearest" */
|
|
game.createmenu(game.currentmenuname, true);
|
|
}
|
|
}
|
|
|
|
void Screen::toggleScalingMode(void)
|
|
{
|
|
scalingMode = (scalingMode + 1) % NUM_SCALING_MODES;
|
|
}
|
|
|
|
void Screen::toggleLinearFilter(void)
|
|
{
|
|
isFiltered = !isFiltered;
|
|
|
|
SDL_DestroyTexture(graphics.gameTexture);
|
|
SDL_DestroyTexture(graphics.tempShakeTexture);
|
|
|
|
graphics.gameTexture = SDL_CreateTexture(
|
|
m_renderer,
|
|
SDL_PIXELFORMAT_ARGB8888,
|
|
SDL_TEXTUREACCESS_TARGET,
|
|
SCREEN_WIDTH_PIXELS,
|
|
SCREEN_HEIGHT_PIXELS
|
|
);
|
|
|
|
graphics.tempShakeTexture = SDL_CreateTexture(
|
|
m_renderer,
|
|
SDL_PIXELFORMAT_ARGB8888,
|
|
SDL_TEXTUREACCESS_TARGET,
|
|
SCREEN_WIDTH_PIXELS,
|
|
SCREEN_HEIGHT_PIXELS
|
|
);
|
|
|
|
if (graphics.gameTexture == NULL)
|
|
{
|
|
vlog_error("Could not create game texture: %s", SDL_GetError());
|
|
return;
|
|
}
|
|
|
|
if (graphics.tempShakeTexture == NULL)
|
|
{
|
|
vlog_error("Could not create temp shake texture: %s", SDL_GetError());
|
|
return;
|
|
}
|
|
|
|
SDL_SetTextureScaleMode(
|
|
graphics.gameTexture,
|
|
isFiltered ? SDL_ScaleModeLinear : SDL_ScaleModeNearest
|
|
);
|
|
|
|
SDL_SetTextureScaleMode(
|
|
graphics.tempShakeTexture,
|
|
isFiltered ? SDL_ScaleModeLinear : SDL_ScaleModeNearest
|
|
);
|
|
}
|
|
|
|
void Screen::toggleVSync(void)
|
|
{
|
|
vsync = !vsync;
|
|
SDL_RenderSetVSync(m_renderer, (int) vsync);
|
|
}
|
|
|
|
void Screen::recacheTextures(void)
|
|
{
|
|
// Fix for d3d9, which clears target textures sometimes (ex. toggling vsync, switching fullscreen, etc...)
|
|
|
|
// Signal cached textures to be redrawn fully
|
|
graphics.backgrounddrawn = false;
|
|
graphics.foregrounddrawn = false;
|
|
graphics.towerbg.tdrawback = true;
|
|
graphics.titlebg.tdrawback = true;
|
|
|
|
if (game.gamestate == MAPMODE || game.ingame_titlemode)
|
|
{
|
|
// Redraw the cached gameplay texture if we're in the map screen.
|
|
// Additionally, reset alpha so things don't jitter when re-entering gameplay.
|
|
float oldAlpha = graphics.alpha;
|
|
graphics.alpha = 0;
|
|
gamerender();
|
|
graphics.alpha = oldAlpha;
|
|
}
|
|
|
|
if (map.custommode)
|
|
{
|
|
// If we're in a custom level, regenerate the minimap, which also got cleared.
|
|
cl.generatecustomminimap();
|
|
}
|
|
}
|
|
|
|
bool Screen::isForcedFullscreen(void)
|
|
{
|
|
/* This is just a check to see if we're on a desktop or tenfoot setup.
|
|
* If you're working on a tenfoot-only build, add a def that always
|
|
* returns true!
|
|
*/
|
|
#ifdef __ANDROID__
|
|
return true;
|
|
#else
|
|
return SDL_GetHintBoolean("SteamTenfoot", SDL_FALSE);
|
|
#endif
|
|
}
|