2020-09-16 20:14:35 +00:00
|
|
|
// SPDX-License-Identifier: MIT
|
2020-06-26 15:17:35 +00:00
|
|
|
//
|
2020-09-16 20:14:35 +00:00
|
|
|
// EmulationStation Desktop Edition
|
2020-06-26 15:17:35 +00:00
|
|
|
// Renderer.cpp
|
|
|
|
//
|
2020-06-28 16:39:18 +00:00
|
|
|
// General rendering functions.
|
2020-06-26 15:17:35 +00:00
|
|
|
//
|
|
|
|
|
2019-08-08 20:16:11 +00:00
|
|
|
#include "renderers/Renderer.h"
|
|
|
|
|
|
|
|
#include "ImageIO.h"
|
|
|
|
#include "Log.h"
|
|
|
|
#include "Settings.h"
|
2020-09-16 20:14:35 +00:00
|
|
|
#include "Shader_GL21.h"
|
2021-07-07 18:31:46 +00:00
|
|
|
#include "resources/ResourceManager.h"
|
2019-08-08 20:16:11 +00:00
|
|
|
|
2020-06-26 16:03:55 +00:00
|
|
|
#include <SDL2/SDL.h>
|
2019-08-08 20:16:11 +00:00
|
|
|
#include <stack>
|
|
|
|
|
2021-07-07 18:31:46 +00:00
|
|
|
#if defined(_WIN64)
|
2021-01-21 20:52:28 +00:00
|
|
|
#include <windows.h>
|
|
|
|
#endif
|
|
|
|
|
2019-08-08 20:16:11 +00:00
|
|
|
namespace Renderer
|
|
|
|
{
|
2020-06-26 15:17:35 +00:00
|
|
|
static std::stack<Rect> clipStack;
|
2022-01-16 17:18:28 +00:00
|
|
|
static SDL_Window* sdlWindow {nullptr};
|
|
|
|
static glm::mat4 mProjectionMatrix {};
|
2022-03-11 22:17:04 +00:00
|
|
|
static glm::mat4 mProjectionMatrixRotated {};
|
2022-01-16 17:18:28 +00:00
|
|
|
static int windowWidth {0};
|
|
|
|
static int windowHeight {0};
|
|
|
|
static int screenWidth {0};
|
|
|
|
static int screenHeight {0};
|
|
|
|
static int screenOffsetX {0};
|
|
|
|
static int screenOffsetY {0};
|
2022-03-11 22:17:04 +00:00
|
|
|
static bool screenRotated {0};
|
2022-01-16 17:18:28 +00:00
|
|
|
static bool initialCursorState {1};
|
2021-01-13 18:42:06 +00:00
|
|
|
// Screen resolution modifiers relative to the 1920x1080 reference.
|
2022-01-16 17:18:28 +00:00
|
|
|
static float screenHeightModifier {0.0f};
|
|
|
|
static float screenWidthModifier {0.0f};
|
|
|
|
static float screenAspectRatio {0.0f};
|
2020-06-26 15:17:35 +00:00
|
|
|
|
|
|
|
static void setIcon()
|
|
|
|
{
|
2022-01-16 17:18:28 +00:00
|
|
|
size_t width {0};
|
|
|
|
size_t height {0};
|
|
|
|
ResourceData resData {
|
|
|
|
ResourceManager::getInstance().getFileData(":/graphics/window_icon_256.png")};
|
|
|
|
std::vector<unsigned char> rawData {
|
|
|
|
ImageIO::loadFromMemoryRGBA32(resData.ptr.get(), resData.length, width, height)};
|
2020-06-26 15:17:35 +00:00
|
|
|
|
|
|
|
if (!rawData.empty()) {
|
|
|
|
ImageIO::flipPixelsVert(rawData.data(), width, height);
|
|
|
|
|
2021-07-07 18:31:46 +00:00
|
|
|
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
|
2022-01-16 17:18:28 +00:00
|
|
|
unsigned int rmask {0xFF000000};
|
|
|
|
unsigned int gmask {0x00FF0000};
|
|
|
|
unsigned int bmask {0x0000FF00};
|
|
|
|
unsigned int amask {0x000000FF};
|
2021-07-07 18:31:46 +00:00
|
|
|
#else
|
2022-01-16 17:18:28 +00:00
|
|
|
unsigned int rmask {0x000000FF};
|
|
|
|
unsigned int gmask {0x0000FF00};
|
|
|
|
unsigned int bmask {0x00FF0000};
|
|
|
|
unsigned int amask {0xFF000000};
|
2021-07-07 18:31:46 +00:00
|
|
|
#endif
|
2020-06-26 15:17:35 +00:00
|
|
|
|
|
|
|
// Try creating SDL surface from logo data.
|
2022-01-16 17:18:28 +00:00
|
|
|
SDL_Surface* logoSurface {SDL_CreateRGBSurfaceFrom(
|
|
|
|
static_cast<void*>(rawData.data()), static_cast<int>(width),
|
|
|
|
static_cast<int>(height), 32, static_cast<int>((width * 4)), rmask, gmask, bmask,
|
|
|
|
amask)};
|
2020-06-26 15:17:35 +00:00
|
|
|
|
|
|
|
if (logoSurface != nullptr) {
|
|
|
|
SDL_SetWindowIcon(sdlWindow, logoSurface);
|
|
|
|
SDL_FreeSurface(logoSurface);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool createWindow()
|
|
|
|
{
|
|
|
|
LOG(LogInfo) << "Creating window...";
|
|
|
|
|
|
|
|
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
|
2021-03-19 17:25:37 +00:00
|
|
|
LOG(LogError) << "Couldn't initialize SDL: " << SDL_GetError();
|
2020-06-26 15:17:35 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
initialCursorState = (SDL_ShowCursor(0) != 0);
|
|
|
|
|
2022-01-16 17:18:28 +00:00
|
|
|
int displayIndex {Settings::getInstance()->getInt("DisplayIndex")};
|
2021-06-16 17:05:24 +00:00
|
|
|
// Check that an invalid value has not been manually entered in the es_settings.xml file.
|
2021-01-24 22:44:50 +00:00
|
|
|
if (displayIndex != 1 && displayIndex != 2 && displayIndex != 3 && displayIndex != 4) {
|
|
|
|
Settings::getInstance()->setInt("DisplayIndex", 1);
|
|
|
|
displayIndex = 0;
|
|
|
|
}
|
|
|
|
else {
|
2021-11-17 16:48:49 +00:00
|
|
|
--displayIndex;
|
2021-01-24 22:44:50 +00:00
|
|
|
}
|
2021-01-24 11:03:44 +00:00
|
|
|
|
2021-01-24 22:44:50 +00:00
|
|
|
int availableDisplays = SDL_GetNumVideoDisplays();
|
2021-01-24 11:03:44 +00:00
|
|
|
if (displayIndex > availableDisplays - 1) {
|
2021-07-07 18:31:46 +00:00
|
|
|
LOG(LogWarning) << "Requested display " << std::to_string(displayIndex + 1)
|
|
|
|
<< " does not exist, changing to display 1";
|
2021-01-24 11:03:44 +00:00
|
|
|
displayIndex = 0;
|
|
|
|
}
|
|
|
|
else {
|
2021-01-24 22:44:50 +00:00
|
|
|
LOG(LogInfo) << "Using display: " << std::to_string(displayIndex + 1);
|
2021-01-24 11:03:44 +00:00
|
|
|
}
|
|
|
|
|
2020-09-16 20:14:35 +00:00
|
|
|
SDL_DisplayMode displayMode;
|
2021-01-24 11:03:44 +00:00
|
|
|
SDL_GetDesktopDisplayMode(displayIndex, &displayMode);
|
2021-01-21 20:52:28 +00:00
|
|
|
|
2021-07-07 18:31:46 +00:00
|
|
|
#if defined(_WIN64)
|
2021-01-21 20:52:28 +00:00
|
|
|
// Tell Windows that we're DPI aware so that we can set a physical resolution and
|
|
|
|
// avoid any automatic DPI scaling.
|
2021-01-24 11:03:44 +00:00
|
|
|
SetProcessDPIAware();
|
|
|
|
// We need to set the resolution based on the actual display bounds as the numbers
|
|
|
|
// returned by SDL_GetDesktopDisplayMode are calculated based on DPI scaling and
|
|
|
|
// therefore do not necessarily reflect the physical display resolution.
|
|
|
|
SDL_Rect displayBounds;
|
|
|
|
SDL_GetDisplayBounds(displayIndex, &displayBounds);
|
|
|
|
displayMode.w = displayBounds.w;
|
|
|
|
displayMode.h = displayBounds.h;
|
2021-07-07 18:31:46 +00:00
|
|
|
#endif
|
2021-01-21 20:52:28 +00:00
|
|
|
|
2020-06-26 15:17:35 +00:00
|
|
|
windowWidth = Settings::getInstance()->getInt("WindowWidth") ?
|
2021-07-07 18:31:46 +00:00
|
|
|
Settings::getInstance()->getInt("WindowWidth") :
|
|
|
|
displayMode.w;
|
2020-06-26 15:17:35 +00:00
|
|
|
windowHeight = Settings::getInstance()->getInt("WindowHeight") ?
|
2021-07-07 18:31:46 +00:00
|
|
|
Settings::getInstance()->getInt("WindowHeight") :
|
|
|
|
displayMode.h;
|
2020-06-26 15:17:35 +00:00
|
|
|
screenWidth = Settings::getInstance()->getInt("ScreenWidth") ?
|
2021-07-07 18:31:46 +00:00
|
|
|
Settings::getInstance()->getInt("ScreenWidth") :
|
|
|
|
windowWidth;
|
2020-06-26 15:17:35 +00:00
|
|
|
screenHeight = Settings::getInstance()->getInt("ScreenHeight") ?
|
2021-07-07 18:31:46 +00:00
|
|
|
Settings::getInstance()->getInt("ScreenHeight") :
|
|
|
|
windowHeight;
|
2020-06-26 15:17:35 +00:00
|
|
|
screenOffsetX = Settings::getInstance()->getInt("ScreenOffsetX") ?
|
2021-07-07 18:31:46 +00:00
|
|
|
Settings::getInstance()->getInt("ScreenOffsetX") :
|
|
|
|
0;
|
2020-06-26 15:17:35 +00:00
|
|
|
screenOffsetY = Settings::getInstance()->getInt("ScreenOffsetY") ?
|
2021-07-07 18:31:46 +00:00
|
|
|
Settings::getInstance()->getInt("ScreenOffsetY") :
|
|
|
|
0;
|
2022-03-11 23:40:03 +00:00
|
|
|
screenRotated = Settings::getInstance()->getBool("ScreenRotate");
|
2020-06-26 15:17:35 +00:00
|
|
|
|
2021-01-24 11:03:44 +00:00
|
|
|
// Prevent the application window from minimizing when switching windows (when launching
|
|
|
|
// games or when manually switching windows using the task switcher).
|
2020-06-26 15:17:35 +00:00
|
|
|
SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
|
|
|
|
|
2021-07-07 18:31:46 +00:00
|
|
|
#if defined(__unix__)
|
2021-02-22 20:13:06 +00:00
|
|
|
// Disabling desktop composition can lead to better framerates and a more fluid user
|
2021-09-21 20:53:08 +00:00
|
|
|
// interface, but with some drivers it can cause strange behaviors when returning to
|
2021-02-22 20:13:06 +00:00
|
|
|
// the desktop.
|
|
|
|
if (Settings::getInstance()->getBool("DisableComposition"))
|
|
|
|
SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "1");
|
|
|
|
else
|
|
|
|
SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0");
|
2021-07-07 18:31:46 +00:00
|
|
|
#endif
|
2021-02-22 20:13:06 +00:00
|
|
|
|
2020-08-23 20:19:37 +00:00
|
|
|
bool userResolution = false;
|
|
|
|
// Check if the user has changed the resolution from the command line.
|
2020-09-16 20:14:35 +00:00
|
|
|
if (windowWidth != displayMode.w || windowHeight != displayMode.h)
|
2020-08-23 20:19:37 +00:00
|
|
|
userResolution = true;
|
2020-06-26 15:17:35 +00:00
|
|
|
|
|
|
|
unsigned int windowFlags;
|
2021-07-07 18:31:46 +00:00
|
|
|
setupWindow();
|
2020-06-26 15:17:35 +00:00
|
|
|
|
2021-07-07 18:31:46 +00:00
|
|
|
#if defined(_WIN64)
|
2021-12-09 17:53:22 +00:00
|
|
|
// For Windows we use SDL_WINDOW_BORDERLESS as "real" full screen doesn't work properly.
|
|
|
|
// The borderless mode seems to behave well and it's almost completely seamless, especially
|
|
|
|
// with a hidden taskbar.
|
|
|
|
if (!userResolution)
|
2022-01-14 17:23:51 +00:00
|
|
|
windowFlags = SDL_WINDOW_BORDERLESS | SDL_WINDOW_OPENGL;
|
2021-12-09 17:53:22 +00:00
|
|
|
else
|
|
|
|
// If the resolution has been manually set from the command line, then keep the border.
|
2022-01-14 17:23:51 +00:00
|
|
|
windowFlags = SDL_WINDOW_OPENGL;
|
2021-07-07 18:31:46 +00:00
|
|
|
#elif defined(__APPLE__)
|
2021-12-09 17:53:22 +00:00
|
|
|
// Not sure if this could be a useful setting.
|
|
|
|
// SDL_SetHint(SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES, "0");
|
|
|
|
|
2021-12-05 12:51:39 +00:00
|
|
|
// The SDL_WINDOW_BORDERLESS mode seems to be the only mode that somehow works on macOS
|
|
|
|
// as a real fullscreen mode will do lots of weird stuff like preventing window switching
|
|
|
|
// or refusing to let emulators run at all. SDL_WINDOW_FULLSCREEN_DESKTOP almost works, but
|
|
|
|
// it "shuffles" windows when starting the emulator and won't return properly when the game
|
2021-12-08 20:19:24 +00:00
|
|
|
// has exited. With SDL_WINDOW_BORDERLESS some emulators (like RetroArch) have to be
|
2021-12-09 17:53:22 +00:00
|
|
|
// configured to run in fullscreen mode or switching to its window will not work, but
|
|
|
|
// apart from that this mode works fine.
|
2020-08-23 20:19:37 +00:00
|
|
|
if (!userResolution)
|
2022-01-14 17:23:51 +00:00
|
|
|
windowFlags = SDL_WINDOW_BORDERLESS | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_OPENGL;
|
2020-08-23 20:19:37 +00:00
|
|
|
else
|
2022-01-14 17:23:51 +00:00
|
|
|
windowFlags = SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_OPENGL;
|
2021-07-07 18:31:46 +00:00
|
|
|
#else
|
2021-12-05 12:51:39 +00:00
|
|
|
if (!userResolution)
|
2022-01-14 17:23:51 +00:00
|
|
|
windowFlags = SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_OPENGL;
|
2021-12-05 12:51:39 +00:00
|
|
|
else
|
2022-01-14 17:23:51 +00:00
|
|
|
windowFlags = SDL_WINDOW_OPENGL;
|
2021-07-07 18:31:46 +00:00
|
|
|
#endif
|
2020-06-26 15:17:35 +00:00
|
|
|
|
2021-07-07 18:31:46 +00:00
|
|
|
if ((sdlWindow =
|
|
|
|
SDL_CreateWindow("EmulationStation", SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex),
|
|
|
|
SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex), windowWidth,
|
|
|
|
windowHeight, windowFlags)) == nullptr) {
|
2020-08-18 15:48:21 +00:00
|
|
|
LOG(LogError) << "Couldn't create SDL window. " << SDL_GetError();
|
2020-06-26 15:17:35 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-07-07 18:31:46 +00:00
|
|
|
#if defined(__APPLE__)
|
2021-05-01 12:05:40 +00:00
|
|
|
// The code below is required as the high DPI scaling on macOS is very bizarre and is
|
|
|
|
// measured in "points" rather than pixels (even though the naming convention sure looks
|
|
|
|
// like pixels). For example there could be a 1920x1080 entry in the OS display settings
|
|
|
|
// that actually corresponds to something like 3840x2160 pixels while at the same time
|
|
|
|
// there is a separate 1080p entry which corresponds to a "real" 1920x1080 resolution.
|
|
|
|
// Therefore the --resolution flag results in different things depending on whether a high
|
|
|
|
// DPI screen is used. E.g. 1280x720 on a 4K display would actually end up as 2560x1440
|
|
|
|
// which is incredibly strange. No point in struggling with this strangeness though,
|
|
|
|
// instead we simply indicate the physical pixel dimensions in parenthesis in the log
|
|
|
|
// file and make sure to double the window and screen sizes in case of a high DPI
|
|
|
|
// display so that the full application window is used for rendering.
|
|
|
|
int width = 0;
|
|
|
|
SDL_GL_GetDrawableSize(sdlWindow, &width, nullptr);
|
|
|
|
int scaleFactor = static_cast<int>(width / windowWidth);
|
|
|
|
|
2021-07-07 18:31:46 +00:00
|
|
|
LOG(LogInfo) << "Display resolution: " << std::to_string(displayMode.w) << "x"
|
|
|
|
<< std::to_string(displayMode.h) << " (physical resolution "
|
|
|
|
<< std::to_string(displayMode.w * scaleFactor) << "x"
|
|
|
|
<< std::to_string(displayMode.h * scaleFactor) << ")";
|
2022-01-12 20:26:43 +00:00
|
|
|
LOG(LogInfo) << "Display refresh rate: " << std::to_string(displayMode.refresh_rate)
|
|
|
|
<< " Hz";
|
2021-07-07 18:31:46 +00:00
|
|
|
LOG(LogInfo) << "EmulationStation resolution: " << std::to_string(windowWidth) << "x"
|
|
|
|
<< std::to_string(windowHeight) << " (physical resolution "
|
|
|
|
<< std::to_string(windowWidth * scaleFactor) << "x"
|
|
|
|
<< std::to_string(windowHeight * scaleFactor) << ")";
|
2021-05-01 12:05:40 +00:00
|
|
|
|
|
|
|
windowWidth *= scaleFactor;
|
|
|
|
windowHeight *= scaleFactor;
|
|
|
|
screenWidth *= scaleFactor;
|
|
|
|
screenHeight *= scaleFactor;
|
2021-07-07 18:31:46 +00:00
|
|
|
#else
|
|
|
|
LOG(LogInfo) << "Display resolution: " << std::to_string(displayMode.w) << "x"
|
|
|
|
<< std::to_string(displayMode.h);
|
2022-01-12 20:26:43 +00:00
|
|
|
LOG(LogInfo) << "Display refresh rate: " << std::to_string(displayMode.refresh_rate)
|
|
|
|
<< " Hz";
|
2021-07-07 18:31:46 +00:00
|
|
|
LOG(LogInfo) << "EmulationStation resolution: " << std::to_string(windowWidth) << "x"
|
|
|
|
<< std::to_string(windowHeight);
|
|
|
|
#endif
|
2021-05-01 12:05:40 +00:00
|
|
|
|
|
|
|
screenHeightModifier = static_cast<float>(screenHeight) / 1080.0f;
|
|
|
|
screenWidthModifier = static_cast<float>(screenWidth) / 1920.0f;
|
|
|
|
screenAspectRatio = static_cast<float>(screenWidth) / static_cast<float>(screenHeight);
|
|
|
|
|
2020-08-30 20:19:37 +00:00
|
|
|
LOG(LogInfo) << "Setting up OpenGL...";
|
|
|
|
|
|
|
|
if (!createContext())
|
|
|
|
return false;
|
2020-06-26 15:17:35 +00:00
|
|
|
|
|
|
|
setIcon();
|
|
|
|
setSwapInterval();
|
2021-03-20 07:52:08 +00:00
|
|
|
|
2021-07-07 18:31:46 +00:00
|
|
|
#if defined(_WIN64)
|
2021-03-20 07:52:08 +00:00
|
|
|
// It seems as if Windows needs this to avoid a brief white screen flash on startup.
|
|
|
|
// Possibly this is driver-specific rather than OS-specific. There is additional code
|
|
|
|
// in init() to work around the white screen flash issue on all operating systems.
|
2021-03-19 19:05:34 +00:00
|
|
|
swapBuffers();
|
2021-07-07 18:31:46 +00:00
|
|
|
#endif
|
2020-06-26 15:17:35 +00:00
|
|
|
|
2020-08-30 20:19:37 +00:00
|
|
|
LOG(LogInfo) << "Loading shaders...";
|
|
|
|
|
2020-09-04 16:59:19 +00:00
|
|
|
std::vector<std::string> shaderFiles;
|
2022-03-11 22:17:04 +00:00
|
|
|
shaderFiles.push_back(":/shaders/glsl/core.glsl");
|
2020-09-04 16:59:19 +00:00
|
|
|
shaderFiles.push_back(":/shaders/glsl/blur_horizontal.glsl");
|
|
|
|
shaderFiles.push_back(":/shaders/glsl/blur_vertical.glsl");
|
|
|
|
shaderFiles.push_back(":/shaders/glsl/scanlines.glsl");
|
2020-08-30 20:19:37 +00:00
|
|
|
|
2021-11-17 16:48:49 +00:00
|
|
|
for (auto it = shaderFiles.cbegin(); it != shaderFiles.cend(); ++it) {
|
2020-09-04 16:59:19 +00:00
|
|
|
Shader* loadShader = new Shader();
|
2020-08-30 20:19:37 +00:00
|
|
|
|
2020-09-04 16:59:19 +00:00
|
|
|
loadShader->loadShaderFile(*it, GL_VERTEX_SHADER);
|
|
|
|
loadShader->loadShaderFile(*it, GL_FRAGMENT_SHADER);
|
|
|
|
|
|
|
|
if (!loadShader->createProgram()) {
|
|
|
|
LOG(LogError) << "Could not create shader program.";
|
|
|
|
return false;
|
|
|
|
}
|
2020-08-30 20:19:37 +00:00
|
|
|
|
2020-09-04 16:59:19 +00:00
|
|
|
sShaderProgramVector.push_back(loadShader);
|
|
|
|
}
|
2020-08-30 20:19:37 +00:00
|
|
|
|
2020-06-26 15:17:35 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void destroyWindow()
|
|
|
|
{
|
2021-11-17 16:48:49 +00:00
|
|
|
for (auto it = sShaderProgramVector.cbegin(); it != sShaderProgramVector.cend(); ++it)
|
2020-08-30 20:19:37 +00:00
|
|
|
delete *it;
|
|
|
|
|
2020-06-26 15:17:35 +00:00
|
|
|
destroyContext();
|
|
|
|
SDL_DestroyWindow(sdlWindow);
|
|
|
|
|
|
|
|
sdlWindow = nullptr;
|
|
|
|
|
|
|
|
SDL_ShowCursor(initialCursorState);
|
|
|
|
SDL_Quit();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool init()
|
|
|
|
{
|
|
|
|
if (!createWindow())
|
|
|
|
return false;
|
|
|
|
|
2022-01-16 11:09:55 +00:00
|
|
|
glm::mat4 projection {getIdentity()};
|
|
|
|
Rect viewport {0, 0, 0, 0};
|
2020-06-26 15:17:35 +00:00
|
|
|
|
2022-03-11 22:17:04 +00:00
|
|
|
viewport.x = windowWidth - screenOffsetX - screenWidth;
|
|
|
|
viewport.y = windowHeight - screenOffsetY - screenHeight;
|
|
|
|
viewport.w = screenWidth;
|
|
|
|
viewport.h = screenHeight;
|
|
|
|
projection = glm::ortho(0.0f, static_cast<float>(screenWidth),
|
|
|
|
static_cast<float>(screenHeight), 0.0f, -1.0f, 1.0f);
|
|
|
|
projection = glm::rotate(projection, glm::radians(180.0f), {0.0f, 0.0f, 1.0f});
|
|
|
|
mProjectionMatrixRotated =
|
|
|
|
glm::translate(projection, {screenWidth * -1.0f, screenHeight * -1.0f, 0.0f});
|
|
|
|
|
|
|
|
viewport.x = screenOffsetX;
|
|
|
|
viewport.y = screenOffsetY;
|
|
|
|
viewport.w = screenWidth;
|
|
|
|
viewport.h = screenHeight;
|
|
|
|
mProjectionMatrix = glm::ortho(0.0f, static_cast<float>(screenWidth),
|
|
|
|
static_cast<float>(screenHeight), 0.0f, -1.0f, 1.0f);
|
2020-06-26 15:17:35 +00:00
|
|
|
|
2021-03-20 07:52:08 +00:00
|
|
|
// This is required to avoid a brief white screen flash during startup on some systems.
|
2021-03-19 19:05:34 +00:00
|
|
|
Renderer::drawRect(0.0f, 0.0f, static_cast<float>(Renderer::getScreenWidth()),
|
2021-07-07 18:31:46 +00:00
|
|
|
static_cast<float>(Renderer::getScreenHeight()), 0x000000FF, 0x000000FF);
|
2021-03-19 19:05:34 +00:00
|
|
|
swapBuffers();
|
|
|
|
|
2020-06-26 15:17:35 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void deinit()
|
|
|
|
{
|
2021-07-07 18:31:46 +00:00
|
|
|
// Destroy the window.
|
2020-06-26 15:17:35 +00:00
|
|
|
destroyWindow();
|
|
|
|
}
|
|
|
|
|
2021-08-19 19:39:01 +00:00
|
|
|
void pushClipRect(const glm::ivec2& pos, const glm::ivec2& size)
|
2020-06-26 15:17:35 +00:00
|
|
|
{
|
2021-08-19 19:39:01 +00:00
|
|
|
Rect box(pos.x, pos.y, size.x, size.y);
|
2020-06-26 15:17:35 +00:00
|
|
|
|
|
|
|
if (box.w == 0)
|
|
|
|
box.w = screenWidth - box.x;
|
|
|
|
if (box.h == 0)
|
|
|
|
box.h = screenHeight - box.y;
|
|
|
|
|
2022-03-11 22:17:04 +00:00
|
|
|
if (screenRotated) {
|
|
|
|
box = Rect(windowWidth - screenOffsetX - box.x - box.w,
|
|
|
|
windowHeight - screenOffsetY - box.y - box.h, box.w, box.h);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
box = Rect(screenOffsetX + box.x, screenOffsetY + box.y, box.w, box.h);
|
2020-06-26 15:17:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure the box fits within clipStack.top(), and clip further accordingly.
|
|
|
|
if (clipStack.size()) {
|
|
|
|
const Rect& top = clipStack.top();
|
2021-07-07 18:31:46 +00:00
|
|
|
if (top.x > box.x)
|
|
|
|
box.x = top.x;
|
|
|
|
if (top.y > box.y)
|
|
|
|
box.y = top.y;
|
|
|
|
if ((top.x + top.w) < (box.x + box.w))
|
|
|
|
box.w = (top.x + top.w) - box.x;
|
|
|
|
if ((top.y + top.h) < (box.y + box.h))
|
|
|
|
box.h = (top.y + top.h) - box.y;
|
2020-06-26 15:17:35 +00:00
|
|
|
}
|
|
|
|
|
2021-07-07 18:31:46 +00:00
|
|
|
if (box.w < 0)
|
|
|
|
box.w = 0;
|
|
|
|
if (box.h < 0)
|
|
|
|
box.h = 0;
|
2020-06-26 15:17:35 +00:00
|
|
|
|
|
|
|
clipStack.push(box);
|
|
|
|
|
|
|
|
setScissor(box);
|
|
|
|
}
|
|
|
|
|
|
|
|
void popClipRect()
|
|
|
|
{
|
|
|
|
if (clipStack.empty()) {
|
2021-01-15 17:47:01 +00:00
|
|
|
LOG(LogError) << "Tried to popClipRect while the stack was empty";
|
2020-06-26 15:17:35 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
clipStack.pop();
|
|
|
|
|
|
|
|
if (clipStack.empty())
|
|
|
|
setScissor(Rect(0, 0, 0, 0));
|
|
|
|
else
|
|
|
|
setScissor(clipStack.top());
|
|
|
|
}
|
|
|
|
|
2021-08-19 19:39:01 +00:00
|
|
|
void drawRect(const float x,
|
|
|
|
const float y,
|
|
|
|
const float w,
|
|
|
|
const float h,
|
2022-03-11 22:17:04 +00:00
|
|
|
const unsigned int color,
|
|
|
|
const unsigned int colorEnd,
|
2021-07-07 18:31:46 +00:00
|
|
|
bool horizontalGradient,
|
2021-08-19 19:39:01 +00:00
|
|
|
const float opacity,
|
2022-03-11 22:51:41 +00:00
|
|
|
const float dimming,
|
2021-08-19 19:39:01 +00:00
|
|
|
const Blend::Factor srcBlendFactor,
|
|
|
|
const Blend::Factor dstBlendFactor)
|
2020-06-26 15:17:35 +00:00
|
|
|
{
|
|
|
|
Vertex vertices[4];
|
|
|
|
|
2022-03-11 22:17:04 +00:00
|
|
|
float wL {w};
|
|
|
|
float hL {h};
|
2021-01-16 21:55:38 +00:00
|
|
|
|
|
|
|
// If the width or height was scaled down to less than 1 pixel, then set it to
|
|
|
|
// 1 pixel so that it will still render on lower resolutions.
|
2021-08-19 19:39:01 +00:00
|
|
|
if (wL > 0.0f && wL < 1.0f)
|
|
|
|
wL = 1.0f;
|
|
|
|
if (hL > 0.0f && hL < 1.0f)
|
|
|
|
hL = 1.0f;
|
2021-01-16 21:55:38 +00:00
|
|
|
|
2021-07-07 18:31:46 +00:00
|
|
|
// clang-format off
|
2022-03-11 22:17:04 +00:00
|
|
|
vertices[0] = {{x, y }, {0.0f, 0.0f}, color};
|
|
|
|
vertices[1] = {{x, y + hL}, {0.0f, 0.0f}, horizontalGradient ? color : colorEnd};
|
|
|
|
vertices[2] = {{x + wL, y }, {0.0f, 0.0f}, horizontalGradient ? colorEnd : color};
|
|
|
|
vertices[3] = {{x + wL, y + hL}, {0.0f, 0.0f}, colorEnd};
|
2021-07-07 18:31:46 +00:00
|
|
|
// clang-format on
|
2020-06-26 15:17:35 +00:00
|
|
|
|
|
|
|
// Round vertices.
|
2021-11-17 16:48:49 +00:00
|
|
|
for (int i = 0; i < 4; ++i)
|
2022-03-11 22:51:41 +00:00
|
|
|
vertices[i].position = glm::round(vertices[i].position);
|
2020-06-26 15:17:35 +00:00
|
|
|
|
2022-03-11 22:17:04 +00:00
|
|
|
vertices->opacity = opacity;
|
2022-03-11 22:51:41 +00:00
|
|
|
vertices->dimming = dimming;
|
2020-08-30 20:19:37 +00:00
|
|
|
|
2022-03-11 22:17:04 +00:00
|
|
|
bindTexture(0);
|
|
|
|
drawTriangleStrips(vertices, 4, srcBlendFactor, dstBlendFactor);
|
2020-08-30 20:19:37 +00:00
|
|
|
}
|
|
|
|
|
2020-09-04 16:59:19 +00:00
|
|
|
Shader* getShaderProgram(unsigned int shaderID)
|
2020-08-30 20:19:37 +00:00
|
|
|
{
|
2020-09-04 16:59:19 +00:00
|
|
|
unsigned int index = 0;
|
|
|
|
|
|
|
|
// Find the index in sShaderProgramVector by counting the number
|
|
|
|
// of shifts required to reach 0.
|
|
|
|
while (shaderID > 0) {
|
|
|
|
shaderID = shaderID >> 1;
|
2021-11-17 16:48:49 +00:00
|
|
|
++index;
|
2020-09-04 16:59:19 +00:00
|
|
|
}
|
|
|
|
|
2021-07-07 18:31:46 +00:00
|
|
|
if (sShaderProgramVector.size() > index - 1)
|
|
|
|
return sShaderProgramVector[index - 1];
|
2020-08-30 20:19:37 +00:00
|
|
|
else
|
|
|
|
return nullptr;
|
2021-09-18 07:53:26 +00:00
|
|
|
}
|
2020-08-30 20:19:37 +00:00
|
|
|
|
2022-03-11 22:17:04 +00:00
|
|
|
const glm::mat4& getProjectionMatrix()
|
|
|
|
{
|
|
|
|
if (screenRotated)
|
|
|
|
return mProjectionMatrixRotated;
|
|
|
|
else
|
|
|
|
return mProjectionMatrix;
|
|
|
|
}
|
|
|
|
const glm::mat4& getProjectionMatrixNormal() { return mProjectionMatrix; }
|
2020-06-26 15:17:35 +00:00
|
|
|
SDL_Window* getSDLWindow() { return sdlWindow; }
|
2022-02-11 22:38:23 +00:00
|
|
|
const float getWindowWidth() { return static_cast<float>(windowWidth); }
|
|
|
|
const float getWindowHeight() { return static_cast<float>(windowHeight); }
|
|
|
|
const float getScreenWidth() { return static_cast<float>(screenWidth); }
|
|
|
|
const float getScreenHeight() { return static_cast<float>(screenHeight); }
|
|
|
|
const float getScreenOffsetX() { return static_cast<float>(screenOffsetX); }
|
|
|
|
const float getScreenOffsetY() { return static_cast<float>(screenOffsetY); }
|
2022-03-11 22:17:04 +00:00
|
|
|
const bool getScreenRotated() { return screenRotated; }
|
2021-11-09 21:40:08 +00:00
|
|
|
const float getScreenWidthModifier() { return screenWidthModifier; }
|
|
|
|
const float getScreenHeightModifier() { return screenHeightModifier; }
|
|
|
|
const float getScreenAspectRatio() { return screenAspectRatio; }
|
2019-08-08 20:16:11 +00:00
|
|
|
|
2021-07-07 18:31:46 +00:00
|
|
|
} // namespace Renderer
|