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
|
|
|
|
//
|
2022-03-14 18:51:48 +00:00
|
|
|
// Generic 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"
|
2022-03-14 19:14:18 +00:00
|
|
|
#include "renderers/RendererOpenGL.h"
|
|
|
|
#include "renderers/ShaderOpenGL.h"
|
2021-07-07 18:31:46 +00:00
|
|
|
#include "resources/ResourceManager.h"
|
2019-08-08 20:16:11 +00:00
|
|
|
|
2021-07-07 18:31:46 +00:00
|
|
|
#if defined(_WIN64)
|
2021-01-21 20:52:28 +00:00
|
|
|
#include <windows.h>
|
|
|
|
#endif
|
|
|
|
|
2022-03-14 18:51:48 +00:00
|
|
|
Renderer* Renderer::getInstance()
|
|
|
|
{
|
|
|
|
static RendererOpenGL instance;
|
|
|
|
return &instance;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Renderer::setIcon()
|
2019-08-08 20:16:11 +00:00
|
|
|
{
|
2022-03-14 18:51:48 +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)};
|
|
|
|
|
|
|
|
if (!rawData.empty()) {
|
|
|
|
ImageIO::flipPixelsVert(rawData.data(), width, height);
|
2020-06-26 15:17:35 +00:00
|
|
|
|
2022-10-27 22:08:41 +00:00
|
|
|
constexpr unsigned int bmask {0x00FF0000};
|
|
|
|
constexpr unsigned int gmask {0x0000FF00};
|
|
|
|
constexpr unsigned int rmask {0x000000FF};
|
|
|
|
constexpr unsigned int amask {0xFF000000};
|
2020-06-26 15:17:35 +00:00
|
|
|
|
2022-03-14 18:51:48 +00:00
|
|
|
// Try creating SDL surface from logo data.
|
|
|
|
SDL_Surface* logoSurface {SDL_CreateRGBSurfaceFrom(
|
|
|
|
static_cast<void*>(rawData.data()), static_cast<int>(width), static_cast<int>(height),
|
2022-10-27 22:08:41 +00:00
|
|
|
32, static_cast<int>(width * 4), bmask, gmask, rmask, amask)};
|
2020-06-26 15:17:35 +00:00
|
|
|
|
2022-03-14 18:51:48 +00:00
|
|
|
if (logoSurface != nullptr) {
|
|
|
|
SDL_SetWindowIcon(mSDLWindow, logoSurface);
|
|
|
|
SDL_FreeSurface(logoSurface);
|
2020-06-26 15:17:35 +00:00
|
|
|
}
|
|
|
|
}
|
2022-03-14 18:51:48 +00:00
|
|
|
}
|
2020-06-26 15:17:35 +00:00
|
|
|
|
2022-03-14 18:51:48 +00:00
|
|
|
bool Renderer::createWindow()
|
|
|
|
{
|
|
|
|
LOG(LogInfo) << "Creating window...";
|
2020-06-26 15:17:35 +00:00
|
|
|
|
2022-06-09 15:16:43 +00:00
|
|
|
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) {
|
2022-03-14 18:51:48 +00:00
|
|
|
LOG(LogError) << "Couldn't initialize SDL: " << SDL_GetError();
|
|
|
|
return false;
|
|
|
|
}
|
2020-06-26 15:17:35 +00:00
|
|
|
|
2022-03-14 18:51:48 +00:00
|
|
|
mInitialCursorState = (SDL_ShowCursor(0) != 0);
|
2020-06-26 15:17:35 +00:00
|
|
|
|
2022-03-14 18:51:48 +00:00
|
|
|
int displayIndex {Settings::getInstance()->getInt("DisplayIndex")};
|
|
|
|
// Check that an invalid value has not been manually entered in the es_settings.xml file.
|
|
|
|
if (displayIndex != 1 && displayIndex != 2 && displayIndex != 3 && displayIndex != 4) {
|
|
|
|
Settings::getInstance()->setInt("DisplayIndex", 1);
|
|
|
|
displayIndex = 0;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
--displayIndex;
|
|
|
|
}
|
2021-01-24 11:03:44 +00:00
|
|
|
|
2022-03-14 18:51:48 +00:00
|
|
|
int availableDisplays = SDL_GetNumVideoDisplays();
|
|
|
|
if (displayIndex > availableDisplays - 1) {
|
|
|
|
LOG(LogWarning) << "Requested display " << std::to_string(displayIndex + 1)
|
|
|
|
<< " does not exist, changing to display 1";
|
|
|
|
displayIndex = 0;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
LOG(LogInfo) << "Using display: " << std::to_string(displayIndex + 1);
|
|
|
|
}
|
2021-01-24 11:03:44 +00:00
|
|
|
|
2022-03-14 18:51:48 +00:00
|
|
|
SDL_DisplayMode displayMode;
|
|
|
|
SDL_GetDesktopDisplayMode(displayIndex, &displayMode);
|
2021-01-21 20:52:28 +00:00
|
|
|
|
2021-07-07 18:31:46 +00:00
|
|
|
#if defined(_WIN64)
|
2022-03-14 18:51:48 +00:00
|
|
|
// Tell Windows that we're DPI aware so that we can set a physical resolution and
|
|
|
|
// avoid any automatic DPI scaling.
|
|
|
|
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
|
|
|
|
2022-03-14 18:51:48 +00:00
|
|
|
mWindowWidth = Settings::getInstance()->getInt("WindowWidth") ?
|
|
|
|
Settings::getInstance()->getInt("WindowWidth") :
|
|
|
|
displayMode.w;
|
|
|
|
mWindowHeight = Settings::getInstance()->getInt("WindowHeight") ?
|
|
|
|
Settings::getInstance()->getInt("WindowHeight") :
|
|
|
|
displayMode.h;
|
|
|
|
sScreenWidth = Settings::getInstance()->getInt("ScreenWidth") ?
|
|
|
|
Settings::getInstance()->getInt("ScreenWidth") :
|
|
|
|
mWindowWidth;
|
|
|
|
sScreenHeight = Settings::getInstance()->getInt("ScreenHeight") ?
|
|
|
|
Settings::getInstance()->getInt("ScreenHeight") :
|
|
|
|
mWindowHeight;
|
|
|
|
mScreenOffsetX = Settings::getInstance()->getInt("ScreenOffsetX") ?
|
|
|
|
Settings::getInstance()->getInt("ScreenOffsetX") :
|
|
|
|
0;
|
|
|
|
mScreenOffsetY = Settings::getInstance()->getInt("ScreenOffsetY") ?
|
|
|
|
Settings::getInstance()->getInt("ScreenOffsetY") :
|
|
|
|
0;
|
|
|
|
mScreenRotated = Settings::getInstance()->getBool("ScreenRotate");
|
|
|
|
|
|
|
|
// Prevent the application window from minimizing when switching windows (when launching
|
|
|
|
// games or when manually switching windows using the task switcher).
|
|
|
|
SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
|
2020-06-26 15:17:35 +00:00
|
|
|
|
2021-07-07 18:31:46 +00:00
|
|
|
#if defined(__unix__)
|
2022-03-14 18:51:48 +00:00
|
|
|
// Disabling desktop composition can lead to better framerates and a more fluid user
|
|
|
|
// interface, but with some drivers it can cause strange behaviors when returning to
|
|
|
|
// 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
|
|
|
|
2022-03-14 18:51:48 +00:00
|
|
|
bool userResolution = false;
|
|
|
|
// Check if the user has changed the resolution from the command line.
|
|
|
|
if (mWindowWidth != displayMode.w || mWindowHeight != displayMode.h)
|
|
|
|
userResolution = true;
|
2020-06-26 15:17:35 +00:00
|
|
|
|
2022-10-11 16:11:36 +00:00
|
|
|
unsigned int windowFlags {0};
|
2022-03-14 18:51:48 +00:00
|
|
|
setup();
|
2020-06-26 15:17:35 +00:00
|
|
|
|
2021-07-07 18:31:46 +00:00
|
|
|
#if defined(_WIN64)
|
2022-03-14 18:51:48 +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)
|
|
|
|
windowFlags = SDL_WINDOW_BORDERLESS | SDL_WINDOW_OPENGL;
|
|
|
|
else
|
|
|
|
// If the resolution has been manually set from the command line, then keep the border.
|
|
|
|
windowFlags = SDL_WINDOW_OPENGL;
|
2021-07-07 18:31:46 +00:00
|
|
|
#elif defined(__APPLE__)
|
2022-03-14 18:51:48 +00:00
|
|
|
// Not sure if this could be a useful setting.
|
|
|
|
// SDL_SetHint(SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES, "0");
|
|
|
|
|
|
|
|
// 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
|
|
|
|
// has exited. With SDL_WINDOW_BORDERLESS some emulators (like RetroArch) have to be
|
|
|
|
// configured to run in fullscreen mode or switching to its window will not work, but
|
|
|
|
// apart from that this mode works fine.
|
|
|
|
if (!userResolution)
|
|
|
|
windowFlags = SDL_WINDOW_BORDERLESS | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_OPENGL;
|
|
|
|
else
|
|
|
|
windowFlags = SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_OPENGL;
|
2021-07-07 18:31:46 +00:00
|
|
|
#else
|
2022-03-14 18:51:48 +00:00
|
|
|
if (!userResolution)
|
|
|
|
windowFlags = SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_OPENGL;
|
|
|
|
else
|
|
|
|
windowFlags = SDL_WINDOW_OPENGL;
|
2021-07-07 18:31:46 +00:00
|
|
|
#endif
|
2020-06-26 15:17:35 +00:00
|
|
|
|
2022-03-14 18:51:48 +00:00
|
|
|
if ((mSDLWindow =
|
|
|
|
SDL_CreateWindow("EmulationStation", SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex),
|
|
|
|
SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex), mWindowWidth,
|
|
|
|
mWindowHeight, windowFlags)) == nullptr) {
|
|
|
|
LOG(LogError) << "Couldn't create SDL window. " << SDL_GetError();
|
|
|
|
return false;
|
|
|
|
}
|
2020-06-26 15:17:35 +00:00
|
|
|
|
2021-07-07 18:31:46 +00:00
|
|
|
#if defined(__APPLE__)
|
2022-03-14 18:51:48 +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.
|
2022-10-11 16:11:36 +00:00
|
|
|
int width {0};
|
2022-03-14 18:51:48 +00:00
|
|
|
SDL_GL_GetDrawableSize(mSDLWindow, &width, nullptr);
|
|
|
|
int scaleFactor = static_cast<int>(width / mWindowWidth);
|
|
|
|
|
|
|
|
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) << ")";
|
|
|
|
LOG(LogInfo) << "Display refresh rate: " << std::to_string(displayMode.refresh_rate) << " Hz";
|
|
|
|
LOG(LogInfo) << "EmulationStation resolution: " << std::to_string(mWindowWidth) << "x"
|
|
|
|
<< std::to_string(mWindowHeight) << " (physical resolution "
|
|
|
|
<< std::to_string(mWindowWidth * scaleFactor) << "x"
|
|
|
|
<< std::to_string(mWindowHeight * scaleFactor) << ")";
|
|
|
|
|
|
|
|
mWindowWidth *= scaleFactor;
|
|
|
|
mWindowHeight *= scaleFactor;
|
|
|
|
sScreenWidth *= scaleFactor;
|
|
|
|
sScreenHeight *= scaleFactor;
|
2021-07-07 18:31:46 +00:00
|
|
|
#else
|
2022-03-14 18:51:48 +00:00
|
|
|
LOG(LogInfo) << "Display resolution: " << std::to_string(displayMode.w) << "x"
|
|
|
|
<< std::to_string(displayMode.h);
|
|
|
|
LOG(LogInfo) << "Display refresh rate: " << std::to_string(displayMode.refresh_rate) << " Hz";
|
|
|
|
LOG(LogInfo) << "EmulationStation resolution: " << std::to_string(mWindowWidth) << "x"
|
|
|
|
<< std::to_string(mWindowHeight);
|
2021-07-07 18:31:46 +00:00
|
|
|
#endif
|
2021-05-01 12:05:40 +00:00
|
|
|
|
2022-03-14 18:51:48 +00:00
|
|
|
sScreenHeightModifier = static_cast<float>(sScreenHeight) / 1080.0f;
|
|
|
|
sScreenWidthModifier = static_cast<float>(sScreenWidth) / 1920.0f;
|
|
|
|
sScreenAspectRatio = static_cast<float>(sScreenWidth) / static_cast<float>(sScreenHeight);
|
2021-05-01 12:05:40 +00:00
|
|
|
|
2022-03-14 18:51:48 +00:00
|
|
|
LOG(LogInfo) << "Setting up OpenGL...";
|
2020-08-30 20:19:37 +00:00
|
|
|
|
2022-03-14 18:51:48 +00:00
|
|
|
if (!createContext())
|
|
|
|
return false;
|
2020-06-26 15:17:35 +00:00
|
|
|
|
2022-03-14 18:51:48 +00:00
|
|
|
setIcon();
|
|
|
|
setSwapInterval();
|
2021-03-20 07:52:08 +00:00
|
|
|
|
2021-07-07 18:31:46 +00:00
|
|
|
#if defined(_WIN64)
|
2022-03-14 18:51:48 +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.
|
|
|
|
swapBuffers();
|
2021-07-07 18:31:46 +00:00
|
|
|
#endif
|
2020-06-26 15:17:35 +00:00
|
|
|
|
2022-03-14 18:51:48 +00:00
|
|
|
return loadShaders();
|
|
|
|
}
|
2020-08-30 20:19:37 +00:00
|
|
|
|
2022-03-14 18:51:48 +00:00
|
|
|
void Renderer::destroyWindow()
|
|
|
|
{
|
|
|
|
destroyContext();
|
|
|
|
SDL_DestroyWindow(mSDLWindow);
|
2020-06-26 15:17:35 +00:00
|
|
|
|
2022-03-14 18:51:48 +00:00
|
|
|
mSDLWindow = nullptr;
|
2020-08-30 20:19:37 +00:00
|
|
|
|
2022-03-14 18:51:48 +00:00
|
|
|
SDL_ShowCursor(mInitialCursorState);
|
|
|
|
SDL_Quit();
|
|
|
|
}
|
2020-06-26 15:17:35 +00:00
|
|
|
|
2022-03-14 18:51:48 +00:00
|
|
|
bool Renderer::init()
|
|
|
|
{
|
|
|
|
if (!createWindow())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
glm::mat4 projection {getIdentity()};
|
|
|
|
Rect viewport {0, 0, 0, 0};
|
|
|
|
|
|
|
|
viewport.x = mWindowWidth - mScreenOffsetX - sScreenWidth;
|
|
|
|
viewport.y = mWindowHeight - mScreenOffsetY - sScreenHeight;
|
|
|
|
viewport.w = sScreenWidth;
|
|
|
|
viewport.h = sScreenHeight;
|
|
|
|
projection = glm::ortho(0.0f, static_cast<float>(sScreenWidth),
|
|
|
|
static_cast<float>(sScreenHeight), 0.0f, -1.0f, 1.0f);
|
|
|
|
projection = glm::rotate(projection, glm::radians(180.0f), {0.0f, 0.0f, 1.0f});
|
|
|
|
mProjectionMatrixRotated =
|
|
|
|
glm::translate(projection, {sScreenWidth * -1.0f, sScreenHeight * -1.0f, 0.0f});
|
|
|
|
|
|
|
|
viewport.x = mScreenOffsetX;
|
|
|
|
viewport.y = mScreenOffsetY;
|
|
|
|
viewport.w = sScreenWidth;
|
|
|
|
viewport.h = sScreenHeight;
|
|
|
|
mProjectionMatrix = glm::ortho(0.0f, static_cast<float>(sScreenWidth),
|
|
|
|
static_cast<float>(sScreenHeight), 0.0f, -1.0f, 1.0f);
|
|
|
|
|
|
|
|
// This is required to avoid a brief white screen flash during startup on some systems.
|
|
|
|
drawRect(0.0f, 0.0f, static_cast<float>(getScreenWidth()),
|
|
|
|
static_cast<float>(getScreenHeight()), 0x000000FF, 0x000000FF);
|
|
|
|
swapBuffers();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Renderer::deinit()
|
|
|
|
{
|
|
|
|
// Destroy the window.
|
|
|
|
destroyWindow();
|
|
|
|
}
|
2020-06-26 15:17:35 +00:00
|
|
|
|
2022-03-14 18:51:48 +00:00
|
|
|
void Renderer::pushClipRect(const glm::ivec2& pos, const glm::ivec2& size)
|
|
|
|
{
|
|
|
|
Rect box {pos.x, pos.y, size.x, size.y};
|
2020-06-26 15:17:35 +00:00
|
|
|
|
2022-03-14 18:51:48 +00:00
|
|
|
if (box.w == 0)
|
|
|
|
box.w = sScreenWidth - box.x;
|
|
|
|
if (box.h == 0)
|
|
|
|
box.h = sScreenHeight - box.y;
|
2020-06-26 15:17:35 +00:00
|
|
|
|
2022-03-14 18:51:48 +00:00
|
|
|
if (mScreenRotated) {
|
2022-10-11 16:11:36 +00:00
|
|
|
box = Rect {mWindowWidth - mScreenOffsetX - box.x - box.w,
|
|
|
|
mWindowHeight - mScreenOffsetY - box.y - box.h, box.w, box.h};
|
2020-06-26 15:17:35 +00:00
|
|
|
}
|
2022-03-14 18:51:48 +00:00
|
|
|
else {
|
2022-10-11 16:11:36 +00:00
|
|
|
box = Rect {mScreenOffsetX + box.x, mScreenOffsetY + box.y, box.w, box.h};
|
2020-06-26 15:17:35 +00:00
|
|
|
}
|
|
|
|
|
2022-03-14 18:51:48 +00:00
|
|
|
// Make sure the box fits within mClipStack.top(), and clip further accordingly.
|
|
|
|
if (mClipStack.size()) {
|
|
|
|
const Rect& top {mClipStack.top()};
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2022-03-14 18:51:48 +00:00
|
|
|
if (box.w < 0)
|
|
|
|
box.w = 0;
|
|
|
|
if (box.h < 0)
|
|
|
|
box.h = 0;
|
2020-08-30 20:19:37 +00:00
|
|
|
|
2022-03-14 18:51:48 +00:00
|
|
|
mClipStack.push(box);
|
2020-09-04 16:59:19 +00:00
|
|
|
|
2022-03-14 18:51:48 +00:00
|
|
|
setScissor(box);
|
|
|
|
}
|
2020-09-04 16:59:19 +00:00
|
|
|
|
2022-03-14 18:51:48 +00:00
|
|
|
void Renderer::popClipRect()
|
|
|
|
{
|
|
|
|
if (mClipStack.empty()) {
|
|
|
|
LOG(LogError) << "Tried to popClipRect while the stack was empty";
|
|
|
|
return;
|
2021-09-18 07:53:26 +00:00
|
|
|
}
|
2020-08-30 20:19:37 +00:00
|
|
|
|
2022-03-14 18:51:48 +00:00
|
|
|
mClipStack.pop();
|
|
|
|
|
|
|
|
if (mClipStack.empty())
|
2022-10-11 16:11:36 +00:00
|
|
|
setScissor(Rect {0, 0, 0, 0});
|
2022-03-14 18:51:48 +00:00
|
|
|
else
|
|
|
|
setScissor(mClipStack.top());
|
|
|
|
}
|
|
|
|
|
|
|
|
void Renderer::drawRect(const float x,
|
|
|
|
const float y,
|
|
|
|
const float w,
|
|
|
|
const float h,
|
|
|
|
const unsigned int color,
|
|
|
|
const unsigned int colorEnd,
|
2022-10-11 16:11:36 +00:00
|
|
|
const bool horizontalGradient,
|
2022-03-14 18:51:48 +00:00
|
|
|
const float opacity,
|
|
|
|
const float dimming,
|
2022-03-15 16:14:55 +00:00
|
|
|
const BlendFactor srcBlendFactor,
|
|
|
|
const BlendFactor dstBlendFactor)
|
2022-03-14 18:51:48 +00:00
|
|
|
{
|
|
|
|
Vertex vertices[4];
|
|
|
|
|
|
|
|
float wL {w};
|
|
|
|
float hL {h};
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
if (wL > 0.0f && wL < 1.0f)
|
|
|
|
wL = 1.0f;
|
|
|
|
if (hL > 0.0f && hL < 1.0f)
|
|
|
|
hL = 1.0f;
|
|
|
|
|
|
|
|
// clang-format off
|
|
|
|
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};
|
|
|
|
// clang-format on
|
|
|
|
|
|
|
|
// Round vertices.
|
|
|
|
for (int i = 0; i < 4; ++i)
|
|
|
|
vertices[i].position = glm::round(vertices[i].position);
|
|
|
|
|
|
|
|
vertices->opacity = opacity;
|
|
|
|
vertices->dimming = dimming;
|
|
|
|
|
|
|
|
bindTexture(0);
|
2022-03-15 16:14:55 +00:00
|
|
|
drawTriangleStrips(vertices, 4, srcBlendFactor, dstBlendFactor);
|
2022-03-14 18:51:48 +00:00
|
|
|
}
|