Modernized the OpenGL renderer and replaced most fixed function pipeline code with shaders.

Also greatly improved the shader post processing performance and added component dimming support.
This commit is contained in:
Leon Styhre 2022-03-11 23:17:04 +01:00
parent 9867c3d595
commit 63af859618
25 changed files with 553 additions and 522 deletions

View file

@ -87,7 +87,7 @@ void MediaViewer::render(const glm::mat4& /*parentTrans*/)
mVideo->render(trans); mVideo->render(trans);
#if defined(USE_OPENGL_21) #if defined(USE_OPENGL_21)
Renderer::shaderParameters videoParameters; Renderer::postProcessingParams videoParameters;
unsigned int shaders {0}; unsigned int shaders {0};
if (Settings::getInstance()->getBool("MediaViewerVideoScanlines")) if (Settings::getInstance()->getBool("MediaViewerVideoScanlines"))
shaders = Renderer::SHADER_SCANLINES; shaders = Renderer::SHADER_SCANLINES;

View file

@ -237,22 +237,20 @@ void Screensaver::goToGame()
void Screensaver::renderScreensaver() void Screensaver::renderScreensaver()
{ {
std::string screensaverType = Settings::getInstance()->getString("ScreensaverType"); std::string screensaverType = Settings::getInstance()->getString("ScreensaverType");
glm::mat4 trans {Renderer::getIdentity()};
Renderer::setMatrix(trans);
if (mVideoScreensaver && screensaverType == "video") { if (mVideoScreensaver && screensaverType == "video") {
// Render a black background below the video. // Render a black background below the video.
Renderer::setMatrix(Renderer::getIdentity());
Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), Renderer::getScreenHeight(), Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), Renderer::getScreenHeight(),
0x000000FF, 0x000000FF); 0x000000FF, 0x000000FF);
// Only render the video if the state requires it. // Only render the video if the state requires it.
if (static_cast<int>(mState) >= STATE_FADE_IN_VIDEO) { if (static_cast<int>(mState) >= STATE_FADE_IN_VIDEO)
glm::mat4 trans {Renderer::getIdentity()};
mVideoScreensaver->render(trans); mVideoScreensaver->render(trans);
}
} }
else if (mImageScreensaver && screensaverType == "slideshow") { else if (mImageScreensaver && screensaverType == "slideshow") {
// Render a black background below the image. // Render a black background below the image.
Renderer::setMatrix(Renderer::getIdentity());
Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), Renderer::getScreenHeight(), Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), Renderer::getScreenHeight(),
0x000000FF, 0x000000FF); 0x000000FF, 0x000000FF);
@ -267,7 +265,6 @@ void Screensaver::renderScreensaver()
} }
if (isScreensaverActive()) { if (isScreensaverActive()) {
Renderer::setMatrix(Renderer::getIdentity());
if (Settings::getInstance()->getString("ScreensaverType") == "slideshow") { if (Settings::getInstance()->getString("ScreensaverType") == "slideshow") {
if (mHasMediaFiles) { if (mHasMediaFiles) {
#if defined(USE_OPENGL_21) #if defined(USE_OPENGL_21)
@ -299,8 +296,8 @@ void Screensaver::renderScreensaver()
else if (Settings::getInstance()->getString("ScreensaverType") == "video") { else if (Settings::getInstance()->getString("ScreensaverType") == "video") {
if (mHasMediaFiles) { if (mHasMediaFiles) {
#if defined(USE_OPENGL_21) #if defined(USE_OPENGL_21)
Renderer::shaderParameters videoParameters; Renderer::postProcessingParams videoParameters;
unsigned int shaders = 0; unsigned int shaders {0};
if (Settings::getInstance()->getBool("ScreensaverVideoScanlines")) if (Settings::getInstance()->getBool("ScreensaverVideoScanlines"))
shaders = Renderer::SHADER_SCANLINES; shaders = Renderer::SHADER_SCANLINES;
if (Settings::getInstance()->getBool("ScreensaverVideoBlur")) { if (Settings::getInstance()->getBool("ScreensaverVideoBlur")) {
@ -327,9 +324,6 @@ void Screensaver::renderScreensaver()
#endif #endif
if (Settings::getInstance()->getBool("ScreensaverVideoGameInfo") && mGameOverlay) { if (Settings::getInstance()->getBool("ScreensaverVideoGameInfo") && mGameOverlay) {
if (mGameOverlayRectangleCoords.size() == 4) { if (mGameOverlayRectangleCoords.size() == 4) {
#if defined(USE_OPENGL_21)
Renderer::shaderPostprocessing(Renderer::SHADER_OPACITY);
#endif
Renderer::drawRect( Renderer::drawRect(
mGameOverlayRectangleCoords[0], mGameOverlayRectangleCoords[1], mGameOverlayRectangleCoords[0], mGameOverlayRectangleCoords[1],
mGameOverlayRectangleCoords[2], mGameOverlayRectangleCoords[3], mGameOverlayRectangleCoords[2], mGameOverlayRectangleCoords[3],
@ -352,13 +346,13 @@ void Screensaver::renderScreensaver()
if (mFallbackScreensaver || if (mFallbackScreensaver ||
Settings::getInstance()->getString("ScreensaverType") == "dim") { Settings::getInstance()->getString("ScreensaverType") == "dim") {
#if defined(USE_OPENGL_21) #if defined(USE_OPENGL_21)
Renderer::shaderParameters dimParameters; Renderer::postProcessingParams dimParameters;
dimParameters.fragmentDimValue = mDimValue; dimParameters.dim = mDimValue;
Renderer::shaderPostprocessing(Renderer::SHADER_DIM, dimParameters); Renderer::shaderPostprocessing(Renderer::SHADER_CORE, dimParameters);
if (mDimValue > 0.4) if (mDimValue > 0.63)
mDimValue = glm::clamp(mDimValue - 0.021f, 0.4f, 1.0f); mDimValue = glm::clamp(mDimValue - 0.015f, 0.68f, 1.0f);
dimParameters.fragmentSaturation = mSaturationAmount; dimParameters.saturation = mSaturationAmount;
Renderer::shaderPostprocessing(Renderer::SHADER_DESATURATE, dimParameters); Renderer::shaderPostprocessing(Renderer::SHADER_CORE, dimParameters);
if (mSaturationAmount > 0.0) if (mSaturationAmount > 0.0)
mSaturationAmount = glm::clamp(mSaturationAmount - 0.035f, 0.0f, 1.0f); mSaturationAmount = glm::clamp(mSaturationAmount - 0.035f, 0.0f, 1.0f);
#else #else
@ -368,9 +362,9 @@ void Screensaver::renderScreensaver()
} }
else if (Settings::getInstance()->getString("ScreensaverType") == "black") { else if (Settings::getInstance()->getString("ScreensaverType") == "black") {
#if defined(USE_OPENGL_21) #if defined(USE_OPENGL_21)
Renderer::shaderParameters blackParameters; Renderer::postProcessingParams blackParameters;
blackParameters.fragmentDimValue = mDimValue; blackParameters.dim = mDimValue;
Renderer::shaderPostprocessing(Renderer::SHADER_DIM, blackParameters); Renderer::shaderPostprocessing(Renderer::SHADER_CORE, blackParameters);
if (mDimValue > 0.0) if (mDimValue > 0.0)
mDimValue = glm::clamp(mDimValue - 0.045f, 0.0f, 1.0f); mDimValue = glm::clamp(mDimValue - 0.045f, 0.0f, 1.0f);
#else #else

View file

@ -20,7 +20,6 @@ GuiComponent::GuiComponent()
: mWindow {Window::getInstance()} : mWindow {Window::getInstance()}
, mParent {nullptr} , mParent {nullptr}
, mColor {0} , mColor {0}
, mSaturation {1.0f}
, mColorShift {0} , mColorShift {0}
, mColorShiftEnd {0} , mColorShiftEnd {0}
, mColorOriginalValue {0} , mColorOriginalValue {0}
@ -31,6 +30,8 @@ GuiComponent::GuiComponent()
, mRotationOrigin {0.5f, 0.5f} , mRotationOrigin {0.5f, 0.5f}
, mSize {0.0f, 0.0f} , mSize {0.0f, 0.0f}
, mOpacity {1.0f} , mOpacity {1.0f}
, mSaturation {1.0f}
, mDim {1.0f}
, mThemeOpacity {1.0f} , mThemeOpacity {1.0f}
, mRotation {0.0f} , mRotation {0.0f}
, mScale {1.0f} , mScale {1.0f}
@ -191,6 +192,16 @@ void GuiComponent::setOpacity(float opacity)
(*it)->setOpacity(opacity); (*it)->setOpacity(opacity);
} }
void GuiComponent::setDim(float dim)
{
if (mDim == dim)
return;
mDim = dim;
for (auto it = mChildren.cbegin(); it != mChildren.cend(); ++it)
(*it)->setDim(dim);
}
const glm::mat4& GuiComponent::getTransform() const glm::mat4& GuiComponent::getTransform()
{ {
mTransform = Renderer::getIdentity(); mTransform = Renderer::getIdentity();

View file

@ -194,12 +194,14 @@ public:
virtual void stopListScrolling() {} virtual void stopListScrolling() {}
virtual const float getOpacity() const { return mOpacity; } virtual const float getOpacity() const { return mOpacity; }
virtual void setOpacity(float opacity); virtual void setOpacity(float opacity);
virtual float getSaturation() const { return static_cast<float>(mColor); }
virtual void setSaturation(float saturation) { mSaturation = saturation; }
virtual const float getDim() const { return mDim; }
virtual void setDim(float dim);
virtual unsigned int getColor() const { return mColor; } virtual unsigned int getColor() const { return mColor; }
virtual unsigned int getColorShift() const { return mColorShift; } virtual unsigned int getColorShift() const { return mColorShift; }
virtual float getLineSpacing() { return 0.0f; } virtual float getLineSpacing() { return 0.0f; }
virtual void setColor(unsigned int color) { mColor = color; } virtual void setColor(unsigned int color) { mColor = color; }
virtual float getSaturation() const { return static_cast<float>(mColor); }
virtual void setSaturation(float saturation) { mSaturation = saturation; }
virtual void setColorShift(unsigned int color) virtual void setColorShift(unsigned int color)
{ {
mColorShift = color; mColorShift = color;
@ -286,7 +288,6 @@ protected:
std::string mThemeGameSelector; std::string mThemeGameSelector;
unsigned int mColor; unsigned int mColor;
float mSaturation;
unsigned int mColorShift; unsigned int mColorShift;
unsigned int mColorShiftEnd; unsigned int mColorShiftEnd;
unsigned int mColorOriginalValue; unsigned int mColorOriginalValue;
@ -299,6 +300,8 @@ protected:
glm::vec2 mSize; glm::vec2 mSize;
float mOpacity; float mOpacity;
float mSaturation;
float mDim;
float mThemeOpacity; float mThemeOpacity;
float mRotation; float mRotation;
float mScale; float mScale;

View file

@ -453,13 +453,13 @@ void Window::render()
#if (CLOCK_BACKGROUND_CREATION) #if (CLOCK_BACKGROUND_CREATION)
const auto backgroundStartTime = std::chrono::system_clock::now(); const auto backgroundStartTime = std::chrono::system_clock::now();
#endif #endif
unsigned char* processedTexture { std::vector<unsigned char> processedTexture(
new unsigned char[static_cast<size_t>(Renderer::getScreenWidth()) * static_cast<size_t>(Renderer::getScreenWidth()) *
static_cast<size_t>(Renderer::getScreenHeight()) * 4]}; static_cast<size_t>(Renderer::getScreenHeight()) * 4);
// De-focus the background using multiple passes of gaussian blur, with the number // De-focus the background using multiple passes of gaussian blur, with the number
// of iterations relative to the screen resolution. // of iterations relative to the screen resolution.
Renderer::shaderParameters backgroundParameters; Renderer::postProcessingParams backgroundParameters;
if (Settings::getInstance()->getBool("MenuBlurBackground")) { if (Settings::getInstance()->getBool("MenuBlurBackground")) {
float heightModifier = Renderer::getScreenHeightModifier(); float heightModifier = Renderer::getScreenHeightModifier();
@ -481,22 +481,21 @@ void Window::render()
// clang-format on // clang-format on
// Also dim the background slightly. // Also dim the background slightly.
backgroundParameters.fragmentDimValue = 0.60f; backgroundParameters.dim = 0.60f;
Renderer::shaderPostprocessing(Renderer::SHADER_BLUR_HORIZONTAL | Renderer::shaderPostprocessing(Renderer::SHADER_BLUR_HORIZONTAL |
Renderer::SHADER_BLUR_VERTICAL | Renderer::SHADER_BLUR_VERTICAL,
Renderer::SHADER_DIM, backgroundParameters, &processedTexture[0]);
backgroundParameters, processedTexture);
} }
else { else {
// Dim the background slightly. // Dim the background slightly.
backgroundParameters.fragmentDimValue = 0.60f; backgroundParameters.dim = 0.60f;
Renderer::shaderPostprocessing(Renderer::SHADER_DIM, backgroundParameters, Renderer::shaderPostprocessing(Renderer::SHADER_CORE, backgroundParameters,
processedTexture); &processedTexture[0]);
} }
mPostprocessedBackground->initFromPixels( mPostprocessedBackground->initFromPixels(
processedTexture, static_cast<size_t>(Renderer::getScreenWidth()), &processedTexture[0], static_cast<size_t>(Renderer::getScreenWidth()),
static_cast<size_t>(Renderer::getScreenHeight())); static_cast<size_t>(Renderer::getScreenHeight()));
mBackgroundOverlay->setImage(mPostprocessedBackground); mBackgroundOverlay->setImage(mPostprocessedBackground);
@ -511,7 +510,6 @@ void Window::render()
mBackgroundOverlayOpacity = 0.1f; mBackgroundOverlayOpacity = 0.1f;
} }
delete[] processedTexture;
mCachedBackground = true; mCachedBackground = true;
#if (CLOCK_BACKGROUND_CREATION) #if (CLOCK_BACKGROUND_CREATION)

View file

@ -3,7 +3,7 @@
// EmulationStation Desktop Edition // EmulationStation Desktop Edition
// LambdaAnimation.h // LambdaAnimation.h
// //
// Basic animation controls, to be used in lambda expressions. // Custom animations, expressed as lambdas.
// //
#ifndef ES_CORE_ANIMATIONS_LAMBDA_ANIMATION_H #ifndef ES_CORE_ANIMATIONS_LAMBDA_ANIMATION_H
@ -13,8 +13,6 @@
#include <functional> #include <functional>
// Useful for simple one-off animations, you can supply the animation's apply(t)
// function directly in the constructor as a lambda.
class LambdaAnimation : public Animation class LambdaAnimation : public Animation
{ {
public: public:

View file

@ -382,11 +382,11 @@ void ComponentList::render(const glm::mat4& parentTrans)
if (mOpacity == 1.0f) { if (mOpacity == 1.0f) {
Renderer::drawRect(0.0f, mSelectorBarOffset, std::ceil(mSize.x), selectedRowHeight, Renderer::drawRect(0.0f, mSelectorBarOffset, std::ceil(mSize.x), selectedRowHeight,
0xFFFFFFFF, 0xFFFFFFFF, false, mOpacity, trans, 0xFFFFFFFF, 0xFFFFFFFF, false, mOpacity, mDim,
Renderer::Blend::ONE_MINUS_DST_COLOR, Renderer::Blend::ZERO); Renderer::Blend::ONE_MINUS_DST_COLOR, Renderer::Blend::ZERO);
Renderer::drawRect(0.0f, mSelectorBarOffset, std::ceil(mSize.x), selectedRowHeight, Renderer::drawRect(0.0f, mSelectorBarOffset, std::ceil(mSize.x), selectedRowHeight,
0x777777FF, 0x777777FF, false, mOpacity, trans, Renderer::Blend::ONE, 0x777777FF, 0x777777FF, false, mOpacity, mDim, Renderer::Blend::ONE,
Renderer::Blend::ONE); Renderer::Blend::ONE);
} }
@ -402,12 +402,12 @@ void ComponentList::render(const glm::mat4& parentTrans)
float y = 0; float y = 0;
for (unsigned int i = 0; i < mEntries.size(); ++i) { for (unsigned int i = 0; i < mEntries.size(); ++i) {
Renderer::drawRect(0.0f, y, std::ceil(mSize.x), 1.0f * Renderer::getScreenHeightModifier(), Renderer::drawRect(0.0f, y, std::ceil(mSize.x), 1.0f * Renderer::getScreenHeightModifier(),
0xC6C7C6FF, 0xC6C7C6FF, false, mOpacity, trans); 0xC6C7C6FF, 0xC6C7C6FF, false, mOpacity, mDim);
y += getRowHeight(mEntries.at(i).data); y += getRowHeight(mEntries.at(i).data);
} }
Renderer::drawRect(0.0f, y, std::ceil(mSize.x), 1.0f * Renderer::getScreenHeightModifier(), Renderer::drawRect(0.0f, y, std::ceil(mSize.x), 1.0f * Renderer::getScreenHeightModifier(),
0xC6C7C6FF, 0xC6C7C6FF, false, mOpacity, trans); 0xC6C7C6FF, 0xC6C7C6FF, false, mOpacity, mDim);
Renderer::popClipRect(); Renderer::popClipRect();
} }

View file

@ -487,14 +487,13 @@ void GIFAnimComponent::render(const glm::mat4& parentTrans)
for (int i = 0; i < 4; ++i) for (int i = 0; i < 4; ++i)
vertices[i].pos = glm::round(vertices[i].pos); vertices[i].pos = glm::round(vertices[i].pos);
#if defined(USE_OPENGL_21) vertices->saturation = mSaturation;
// Perform color space conversion from BGRA to RGBA. vertices->opacity = mOpacity * mThemeOpacity;
vertices[0].opacity = mThemeOpacity; vertices->dim = mDim;
vertices[0].shaders = Renderer::SHADER_BGRA_TO_RGBA; vertices->convertBGRAToRGBA = true;
#endif
// Render it. // Render it.
Renderer::setMatrix(trans); Renderer::setMatrix(trans);
Renderer::drawTriangleStrips(&vertices[0], 4, trans); Renderer::drawTriangleStrips(&vertices[0], 4);
} }
} }

View file

@ -333,6 +333,12 @@ void ImageComponent::setSaturation(float saturation)
updateColors(); updateColors();
} }
void ImageComponent::setDim(float dim)
{
// Set dim value.
mDim = dim;
}
void ImageComponent::updateVertices() void ImageComponent::updateVertices()
{ {
if (!mTexture) if (!mTexture)
@ -371,12 +377,11 @@ void ImageComponent::updateVertices()
void ImageComponent::updateColors() void ImageComponent::updateColors()
{ {
const float opacity = (mOpacity * mThemeOpacity * (mFading ? mFadeOpacity : 1.0f)); const float opacity = (mOpacity * (mFading ? mFadeOpacity : 1.0f));
const unsigned int color = Renderer::convertRGBAToABGR( const unsigned int color {(mColorShift & 0xFFFFFF00) |
(mColorShift & 0xFFFFFF00) | static_cast<unsigned char>((mColorShift & 0xFF) * opacity)); static_cast<unsigned char>((mColorShift & 0xFF) * opacity)};
const unsigned int colorEnd = const unsigned int colorEnd {(mColorShiftEnd & 0xFFFFFF00) |
Renderer::convertRGBAToABGR((mColorShiftEnd & 0xFFFFFF00) | static_cast<unsigned char>((mColorShiftEnd & 0xFF) * opacity)};
static_cast<unsigned char>((mColorShiftEnd & 0xFF) * opacity));
mVertices[0].col = color; mVertices[0].col = color;
mVertices[1].col = mColorGradientHorizontal ? color : colorEnd; mVertices[1].col = mColorGradientHorizontal ? color : colorEnd;
@ -413,13 +418,11 @@ void ImageComponent::render(const glm::mat4& parentTrans)
else else
fadeIn(mTexture->bind()); fadeIn(mTexture->bind());
#if defined(USE_OPENGL_21) mVertices->saturation = mSaturation;
if (mSaturation < 1.0) { mVertices->opacity = mThemeOpacity;
mVertices[0].shaders = Renderer::SHADER_DESATURATE; mVertices->dim = mDim;
mVertices[0].saturation = mSaturation;
} Renderer::drawTriangleStrips(&mVertices[0], 4);
#endif
Renderer::drawTriangleStrips(&mVertices[0], 4, trans);
} }
else { else {
if (!mTexture) { if (!mTexture) {

View file

@ -73,6 +73,7 @@ public:
void setOpacity(float opacity) override; void setOpacity(float opacity) override;
void setSaturation(float saturation) override; void setSaturation(float saturation) override;
void setDim(float dim) override;
void setFlipX(bool flip); // Mirror on the X axis. void setFlipX(bool flip); // Mirror on the X axis.
void setFlipY(bool flip); // Mirror on the Y axis. void setFlipY(bool flip); // Mirror on the Y axis.

View file

@ -472,13 +472,12 @@ void LottieAnimComponent::render(const glm::mat4& parentTrans)
for (int i = 0; i < 4; ++i) for (int i = 0; i < 4; ++i)
vertices[i].pos = glm::round(vertices[i].pos); vertices[i].pos = glm::round(vertices[i].pos);
#if defined(USE_OPENGL_21) vertices->saturation = mSaturation;
// Perform color space conversion from BGRA to RGBA. vertices->opacity = mOpacity * mThemeOpacity;
vertices[0].opacity = mThemeOpacity; vertices->dim = mDim;
vertices[0].shaders = Renderer::SHADER_BGRA_TO_RGBA; vertices->convertBGRAToRGBA = true;
#endif
// Render it. // Render it.
Renderer::drawTriangleStrips(&vertices[0], 4, trans); Renderer::drawTriangleStrips(&vertices[0], 4);
} }
} }

View file

@ -35,14 +35,11 @@ NinePatchComponent::~NinePatchComponent()
void NinePatchComponent::updateColors() void NinePatchComponent::updateColors()
{ {
const unsigned int edgeColor {Renderer::convertRGBAToABGR(mEdgeColor)};
const unsigned int centerColor {Renderer::convertRGBAToABGR(mCenterColor)};
for (int i = 0; i < 6 * 9; ++i) for (int i = 0; i < 6 * 9; ++i)
mVertices[i].col = edgeColor; mVertices[i].col = mEdgeColor;
for (int i = 6 * 4; i < 6; ++i) for (int i = 6 * 4; i < 6; ++i)
mVertices[(6 * 4) + i].col = centerColor; mVertices[(6 * 4) + i].col = mCenterColor;
} }
void NinePatchComponent::buildVertices() void NinePatchComponent::buildVertices()
@ -135,18 +132,9 @@ void NinePatchComponent::render(const glm::mat4& parentTrans)
if (mTexture && mVertices != nullptr) { if (mTexture && mVertices != nullptr) {
Renderer::setMatrix(trans); Renderer::setMatrix(trans);
if (mOpacity < 1.0f) { mVertices->opacity = mOpacity;
mVertices[0].shaders = Renderer::SHADER_OPACITY;
mVertices[0].opacity = mOpacity;
}
else if (mVertices[0].shaders & Renderer::SHADER_OPACITY) {
// We have reached full opacity, so disable the opacity shader and set
// the vertex opacity to 1.0.
mVertices[0].shaders ^= Renderer::SHADER_OPACITY;
mVertices[0].opacity = 1.0f;
}
mTexture->bind(); mTexture->bind();
Renderer::drawTriangleStrips(&mVertices[0], 6 * 9, trans); Renderer::drawTriangleStrips(&mVertices[0], 6 * 9);
} }
renderChildren(trans); renderChildren(trans);

View file

@ -124,27 +124,24 @@ void RatingComponent::updateVertices()
const float h {getSize().y}; // Ss the same as a single star's width. const float h {getSize().y}; // Ss the same as a single star's width.
const float w {getSize().y * mValue * numStars}; const float w {getSize().y * mValue * numStars};
const float fw {getSize().y * numStars}; const float fw {getSize().y * numStars};
const unsigned int color {Renderer::convertRGBAToABGR(mColorShift)};
// clang-format off // clang-format off
mVertices[0] = {{0.0f, 0.0f}, {0.0f, 1.0f}, color}; mVertices[0] = {{0.0f, 0.0f}, {0.0f, 1.0f}, mColorShift};
mVertices[1] = {{0.0f, h }, {0.0f, 0.0f}, color}; mVertices[1] = {{0.0f, h }, {0.0f, 0.0f}, mColorShift};
mVertices[2] = {{w, 0.0f}, {mValue * numStars, 1.0f}, color}; mVertices[2] = {{w, 0.0f}, {mValue * numStars, 1.0f}, mColorShift};
mVertices[3] = {{w, h }, {mValue * numStars, 0.0f}, color}; mVertices[3] = {{w, h }, {mValue * numStars, 0.0f}, mColorShift};
mVertices[4] = {{0.0f, 0.0f}, {0.0f, 1.0f}, color}; mVertices[4] = {{0.0f, 0.0f}, {0.0f, 1.0f}, mColorShift};
mVertices[5] = {{0.0f, h }, {0.0f, 0.0f}, color}; mVertices[5] = {{0.0f, h }, {0.0f, 0.0f}, mColorShift};
mVertices[6] = {{fw, 0.0f}, {numStars, 1.0f}, color}; mVertices[6] = {{fw, 0.0f}, {numStars, 1.0f}, mColorShift};
mVertices[7] = {{fw, h }, {numStars, 0.0f}, color}; mVertices[7] = {{fw, h }, {numStars, 0.0f}, mColorShift};
// clang-format on // clang-format on
} }
void RatingComponent::updateColors() void RatingComponent::updateColors()
{ {
const unsigned int color {Renderer::convertRGBAToABGR(mColorShift)};
for (int i = 0; i < 8; ++i) for (int i = 0; i < 8; ++i)
mVertices[i].col = color; mVertices[i].col = mColorShift;
} }
void RatingComponent::render(const glm::mat4& parentTrans) void RatingComponent::render(const glm::mat4& parentTrans)
@ -165,9 +162,9 @@ void RatingComponent::render(const glm::mat4& parentTrans)
if (mUnfilledTexture->bind()) { if (mUnfilledTexture->bind()) {
if (mUnfilledColor != mColorShift) { if (mUnfilledColor != mColorShift) {
const unsigned int color = Renderer::convertRGBAToABGR(mUnfilledColor);
for (int i = 0; i < 8; ++i) for (int i = 0; i < 8; ++i)
mVertices[i].col = (color & 0x00FFFFFF) + (mVertices[i].col & 0xFF000000); mVertices[i].col =
(mUnfilledColor & 0xFFFFFF00) + (mVertices[i].col & 0x000000FF);
} }
Renderer::drawTriangleStrips(&mVertices[4], 4); Renderer::drawTriangleStrips(&mVertices[4], 4);

View file

@ -92,17 +92,26 @@ void TextComponent::setBackgroundColor(unsigned int color)
mBgColorOpacity = static_cast<float>(mBgColor & 0x000000FF) / 255.0f; mBgColorOpacity = static_cast<float>(mBgColor & 0x000000FF) / 255.0f;
} }
// Scale the opacity.
void TextComponent::setOpacity(float opacity) void TextComponent::setOpacity(float opacity)
{ {
float textOpacity {opacity * mColorOpacity * mThemeOpacity}; float textOpacity {opacity * mColorOpacity};
mColor = (mColor & 0xFFFFFF00) | static_cast<unsigned char>(textOpacity * 255.0f); mColor = (mColor & 0xFFFFFF00) | static_cast<unsigned char>(textOpacity * 255.0f);
float textBackgroundOpacity {opacity * mBgColorOpacity * mThemeOpacity}; float textBackgroundOpacity {opacity * mBgColorOpacity};
mBgColor = (mBgColor & 0xFFFFFF00) | static_cast<unsigned char>(textBackgroundOpacity * 255.0f); mBgColor = (mBgColor & 0xFFFFFF00) | static_cast<unsigned char>(textBackgroundOpacity * 255.0f);
onColorChanged(); onColorChanged();
GuiComponent::setOpacity(opacity); GuiComponent::setOpacity(opacity);
if (mTextCache)
mTextCache->setOpacity(mThemeOpacity);
}
void TextComponent::setDim(float dim)
{
mDim = dim;
if (mTextCache)
mTextCache->setDim(dim);
} }
void TextComponent::setText(const std::string& text, bool update) void TextComponent::setText(const std::string& text, bool update)
@ -152,11 +161,11 @@ void TextComponent::render(const glm::mat4& parentTrans)
return; return;
glm::mat4 trans {parentTrans * getTransform()}; glm::mat4 trans {parentTrans * getTransform()};
Renderer::setMatrix(trans);
if (mRenderBackground) { if (mRenderBackground)
Renderer::setMatrix(trans); Renderer::drawRect(0.0f, 0.0f, mSize.x, mSize.y, mBgColor, mBgColor, false,
Renderer::drawRect(0.0f, 0.0f, mSize.x, mSize.y, mBgColor, mBgColor); mOpacity * mThemeOpacity, mDim);
}
if (mTextCache) { if (mTextCache) {
const glm::vec2& textSize {mTextCache->metrics.size}; const glm::vec2& textSize {mTextCache->metrics.size};
@ -180,11 +189,9 @@ void TextComponent::render(const glm::mat4& parentTrans)
} }
glm::vec3 off {0.0f, yOff, 0.0f}; glm::vec3 off {0.0f, yOff, 0.0f};
if (Settings::getInstance()->getBool("DebugText")) { // Draw the "textbox" area, what we are aligned within.
// Draw the "textbox" area, what we are aligned within. if (Settings::getInstance()->getBool("DebugText"))
Renderer::setMatrix(trans);
Renderer::drawRect(0.0f, 0.0f, mSize.x, mSize.y, 0x0000FF33, 0x0000FF33); Renderer::drawRect(0.0f, 0.0f, mSize.x, mSize.y, 0x0000FF33, 0x0000FF33);
}
trans = glm::translate(trans, off); trans = glm::translate(trans, off);
Renderer::setMatrix(trans); Renderer::setMatrix(trans);

View file

@ -62,6 +62,7 @@ public:
return static_cast<float>((mColor & 0x000000FF) / 255.0f); return static_cast<float>((mColor & 0x000000FF) / 255.0f);
} }
void setOpacity(float opacity) override; void setOpacity(float opacity) override;
void setDim(float dim) override;
void setSelectable(bool status) { mSelectable = status; } void setSelectable(bool status) { mSelectable = status; }

View file

@ -277,6 +277,7 @@ void VideoComponent::renderSnapshot(const glm::mat4& parentTrans)
if (mStaticImagePath != "") { if (mStaticImagePath != "") {
mStaticImage.setOpacity(mOpacity * mThemeOpacity); mStaticImage.setOpacity(mOpacity * mThemeOpacity);
mStaticImage.setDim(mDim);
mStaticImage.render(parentTrans); mStaticImage.render(parentTrans);
} }
} }

View file

@ -132,25 +132,13 @@ void VideoFFmpegComponent::render(const glm::mat4& parentTrans)
GuiComponent::renderChildren(trans); GuiComponent::renderChildren(trans);
if (mIsPlaying && mFormatContext) { if (mIsPlaying && mFormatContext) {
unsigned int color;
if (mDecodedFrame && mFadeIn < 1) {
const unsigned int fadeIn = static_cast<int>(mFadeIn * 255.0f);
color =
Renderer::convertRGBAToABGR((fadeIn << 24) | (fadeIn << 16) | (fadeIn << 8) | 255);
}
else {
color = 0xFFFFFFFF;
}
Renderer::Vertex vertices[4]; Renderer::Vertex vertices[4];
Renderer::setMatrix(parentTrans); Renderer::setMatrix(parentTrans);
unsigned int rectColor {0x000000FF}; unsigned int rectColor {0x000000FF};
if (mThemeOpacity != 1.0f) { if (mThemeOpacity != 1.0f)
color = (static_cast<int>(mThemeOpacity * mFadeIn * 255.0f) << 24) + 0x00FFFFFF;
rectColor = static_cast<int>(mThemeOpacity * mFadeIn * 255.0f); rectColor = static_cast<int>(mThemeOpacity * mFadeIn * 255.0f);
}
// Render the black rectangle behind the video. // Render the black rectangle behind the video.
if (mVideoRectangleCoords.size() == 4) { if (mVideoRectangleCoords.size() == 4) {
@ -159,20 +147,26 @@ void VideoFFmpegComponent::render(const glm::mat4& parentTrans)
rectColor, rectColor); rectColor, rectColor);
} }
// This is needed to avoid a slight gap before the video starts playing.
if (!mDecodedFrame)
return;
// clang-format off // clang-format off
vertices[0] = {{0.0f + mRectangleOffset.x, 0.0f + mRectangleOffset.y }, {0.0f, 0.0f}, color}; vertices[0] = {{0.0f + mRectangleOffset.x, 0.0f + mRectangleOffset.y }, {0.0f, 0.0f}, 0xFFFFFFFF};
vertices[1] = {{0.0f + mRectangleOffset.x, mSize.y + mRectangleOffset.y }, {0.0f, 1.0f}, color}; vertices[1] = {{0.0f + mRectangleOffset.x, mSize.y + mRectangleOffset.y }, {0.0f, 1.0f}, 0xFFFFFFFF};
vertices[2] = {{mSize.x + mRectangleOffset.x, 0.0f + + mRectangleOffset.y }, {1.0f, 0.0f}, color}; vertices[2] = {{mSize.x + mRectangleOffset.x, 0.0f + + mRectangleOffset.y }, {1.0f, 0.0f}, 0xFFFFFFFF};
vertices[3] = {{mSize.x + mRectangleOffset.x, mSize.y + + mRectangleOffset.y}, {1.0f, 1.0f}, color}; vertices[3] = {{mSize.x + mRectangleOffset.x, mSize.y + + mRectangleOffset.y}, {1.0f, 1.0f}, 0xFFFFFFFF};
// clang-format on // clang-format on
// Round vertices. // Round vertices.
for (int i = 0; i < 4; ++i) for (int i = 0; i < 4; ++i)
vertices[i].pos = glm::round(vertices[i].pos); vertices[i].pos = glm::round(vertices[i].pos);
// This is needed to avoid a slight gap before the video starts playing. if (mDecodedFrame && (mFadeIn < 1.0f || mThemeOpacity < 1.0f))
if (!mDecodedFrame) vertices->opacity = mFadeIn * mThemeOpacity;
return;
vertices->saturation = mSaturation;
vertices->dim = mDim;
std::unique_lock<std::mutex> pictureLock(mPictureMutex); std::unique_lock<std::mutex> pictureLock(mPictureMutex);
@ -213,25 +207,21 @@ void VideoFFmpegComponent::render(const glm::mat4& parentTrans)
if (mTexture != nullptr) if (mTexture != nullptr)
mTexture->bind(); mTexture->bind();
#if defined(USE_OPENGL_21)
// Render scanlines if this option is enabled. However, if this is the media viewer // Render scanlines if this option is enabled. However, if this is the media viewer
// or the video screensaver, then skip this as the scanline rendering is then handled // or the video screensaver, then skip this as the scanline rendering is then handled
// in those modules as a postprocessing step. // in those modules as a postprocessing step.
if (!mScreensaverMode && !mMediaViewerMode) { if (!mScreensaverMode && !mMediaViewerMode) {
vertices[0].opacity = mFadeIn * mThemeOpacity; vertices[0].opacity = mFadeIn * mThemeOpacity;
if ((mLegacyTheme && Settings::getInstance()->getBool("GamelistVideoScanlines")) || if ((mLegacyTheme && Settings::getInstance()->getBool("GamelistVideoScanlines")) ||
(!mLegacyTheme && mRenderScanlines)) { (!mLegacyTheme && mRenderScanlines)) {
vertices[0].shaders = Renderer::SHADER_SCANLINES; vertices[0].shaders = Renderer::SHADER_SCANLINES;
} }
else {
vertices[0].shaders = Renderer::SHADER_OPACITY;
}
} }
#endif
// Render it. // Render it.
Renderer::setMatrix(trans); Renderer::setMatrix(trans);
Renderer::drawTriangleStrips(&vertices[0], 4, trans); Renderer::drawTriangleStrips(&vertices[0], 4);
} }
else { else {
if (mVisible) if (mVisible)

View file

@ -26,13 +26,14 @@ namespace Renderer
static std::stack<Rect> clipStack; static std::stack<Rect> clipStack;
static SDL_Window* sdlWindow {nullptr}; static SDL_Window* sdlWindow {nullptr};
static glm::mat4 mProjectionMatrix {}; static glm::mat4 mProjectionMatrix {};
static glm::mat4 mProjectionMatrixRotated {};
static int windowWidth {0}; static int windowWidth {0};
static int windowHeight {0}; static int windowHeight {0};
static int screenWidth {0}; static int screenWidth {0};
static int screenHeight {0}; static int screenHeight {0};
static int screenOffsetX {0}; static int screenOffsetX {0};
static int screenOffsetY {0}; static int screenOffsetY {0};
static int screenRotate {0}; static bool screenRotated {0};
static bool initialCursorState {1}; static bool initialCursorState {1};
// Screen resolution modifiers relative to the 1920x1080 reference. // Screen resolution modifiers relative to the 1920x1080 reference.
static float screenHeightModifier {0.0f}; static float screenHeightModifier {0.0f};
@ -141,9 +142,7 @@ namespace Renderer
screenOffsetY = Settings::getInstance()->getInt("ScreenOffsetY") ? screenOffsetY = Settings::getInstance()->getInt("ScreenOffsetY") ?
Settings::getInstance()->getInt("ScreenOffsetY") : Settings::getInstance()->getInt("ScreenOffsetY") :
0; 0;
screenRotate = Settings::getInstance()->getInt("ScreenRotate") ? screenRotated = Settings::getInstance()->getInt("ScreenRotate") == 2;
Settings::getInstance()->getInt("ScreenRotate") :
0;
// Prevent the application window from minimizing when switching windows (when launching // Prevent the application window from minimizing when switching windows (when launching
// games or when manually switching windows using the task switcher). // games or when manually switching windows using the task switcher).
@ -265,17 +264,13 @@ namespace Renderer
swapBuffers(); swapBuffers();
#endif #endif
#if defined(USE_OPENGL_21)
LOG(LogInfo) << "Loading shaders..."; LOG(LogInfo) << "Loading shaders...";
std::vector<std::string> shaderFiles; std::vector<std::string> shaderFiles;
shaderFiles.push_back(":/shaders/glsl/desaturate.glsl"); shaderFiles.push_back(":/shaders/glsl/core.glsl");
shaderFiles.push_back(":/shaders/glsl/opacity.glsl");
shaderFiles.push_back(":/shaders/glsl/dim.glsl");
shaderFiles.push_back(":/shaders/glsl/blur_horizontal.glsl"); shaderFiles.push_back(":/shaders/glsl/blur_horizontal.glsl");
shaderFiles.push_back(":/shaders/glsl/blur_vertical.glsl"); shaderFiles.push_back(":/shaders/glsl/blur_vertical.glsl");
shaderFiles.push_back(":/shaders/glsl/scanlines.glsl"); shaderFiles.push_back(":/shaders/glsl/scanlines.glsl");
shaderFiles.push_back(":/shaders/glsl/bgra_to_rgba.glsl");
for (auto it = shaderFiles.cbegin(); it != shaderFiles.cend(); ++it) { for (auto it = shaderFiles.cbegin(); it != shaderFiles.cend(); ++it) {
Shader* loadShader = new Shader(); Shader* loadShader = new Shader();
@ -290,17 +285,14 @@ namespace Renderer
sShaderProgramVector.push_back(loadShader); sShaderProgramVector.push_back(loadShader);
} }
#endif
return true; return true;
} }
static void destroyWindow() static void destroyWindow()
{ {
#if defined(USE_OPENGL_21)
for (auto it = sShaderProgramVector.cbegin(); it != sShaderProgramVector.cend(); ++it) for (auto it = sShaderProgramVector.cbegin(); it != sShaderProgramVector.cend(); ++it)
delete *it; delete *it;
#endif
destroyContext(); destroyContext();
SDL_DestroyWindow(sdlWindow); SDL_DestroyWindow(sdlWindow);
@ -319,56 +311,22 @@ namespace Renderer
glm::mat4 projection {getIdentity()}; glm::mat4 projection {getIdentity()};
Rect viewport {0, 0, 0, 0}; Rect viewport {0, 0, 0, 0};
switch (screenRotate) { viewport.x = windowWidth - screenOffsetX - screenWidth;
case 1: { viewport.y = windowHeight - screenOffsetY - screenHeight;
viewport.x = windowWidth - screenOffsetY - screenHeight; viewport.w = screenWidth;
viewport.y = screenOffsetX; viewport.h = screenHeight;
viewport.w = screenHeight; projection = glm::ortho(0.0f, static_cast<float>(screenWidth),
viewport.h = screenWidth; static_cast<float>(screenHeight), 0.0f, -1.0f, 1.0f);
projection = glm::ortho(0.0f, static_cast<float>(screenHeight), projection = glm::rotate(projection, glm::radians(180.0f), {0.0f, 0.0f, 1.0f});
static_cast<float>(screenWidth), 0.0f, -1.0f, 1.0f); mProjectionMatrixRotated =
projection = glm::rotate(projection, glm::radians(90.0f), {0.0f, 0.0f, 1.0f}); glm::translate(projection, {screenWidth * -1.0f, screenHeight * -1.0f, 0.0f});
projection = glm::translate(projection, {0.0f, screenHeight * -1.0f, 0.0f});
break;
}
case 2: {
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});
projection =
glm::translate(projection, {screenWidth * -1.0f, screenHeight * -1.0f, 0.0f});
break;
}
case 3: {
viewport.x = screenOffsetY;
viewport.y = windowHeight - screenOffsetX - screenWidth;
viewport.w = screenHeight;
viewport.h = screenWidth;
projection = glm::ortho(0.0f, static_cast<float>(screenHeight),
static_cast<float>(screenWidth), 0.0f, -1.0f, 1.0f);
projection = glm::rotate(projection, glm::radians(270.0f), {0.0f, 0.0f, 1.0f});
projection = glm::translate(projection, {screenWidth * -1.0f, 0.0f, 0.0f});
break;
}
default: {
viewport.x = screenOffsetX;
viewport.y = screenOffsetY;
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);
break;
}
}
mProjectionMatrix = projection; viewport.x = screenOffsetX;
viewport.y = screenOffsetY;
setViewport(viewport); viewport.w = screenWidth;
setProjection(projection); viewport.h = screenHeight;
mProjectionMatrix = glm::ortho(0.0f, static_cast<float>(screenWidth),
static_cast<float>(screenHeight), 0.0f, -1.0f, 1.0f);
// This is required to avoid a brief white screen flash during startup on some systems. // This is required to avoid a brief white screen flash during startup on some systems.
Renderer::drawRect(0.0f, 0.0f, static_cast<float>(Renderer::getScreenWidth()), Renderer::drawRect(0.0f, 0.0f, static_cast<float>(Renderer::getScreenWidth()),
@ -393,22 +351,12 @@ namespace Renderer
if (box.h == 0) if (box.h == 0)
box.h = screenHeight - box.y; box.h = screenHeight - box.y;
switch (screenRotate) { if (screenRotated) {
case 0: box = Rect(windowWidth - screenOffsetX - box.x - box.w,
box = Rect(screenOffsetX + box.x, screenOffsetY + box.y, box.w, box.h); windowHeight - screenOffsetY - box.y - box.h, box.w, box.h);
break; }
case 1: else {
box = Rect(windowWidth - screenOffsetY - box.y - box.h, screenOffsetX + box.x, box = Rect(screenOffsetX + box.x, screenOffsetY + box.y, box.w, box.h);
box.h, box.w);
break;
case 2:
box = Rect(windowWidth - screenOffsetX - box.x - box.w,
windowHeight - screenOffsetY - box.y - box.h, box.w, box.h);
break;
case 3:
box = Rect(screenOffsetY + box.y, windowHeight - screenOffsetX - box.x - box.w,
box.h, box.w);
break;
} }
// Make sure the box fits within clipStack.top(), and clip further accordingly. // Make sure the box fits within clipStack.top(), and clip further accordingly.
@ -453,20 +401,18 @@ namespace Renderer
const float y, const float y,
const float w, const float w,
const float h, const float h,
const unsigned int _color, const unsigned int color,
const unsigned int _colorEnd, const unsigned int colorEnd,
bool horizontalGradient, bool horizontalGradient,
const float opacity, const float opacity,
const glm::mat4& trans, const float dim,
const Blend::Factor srcBlendFactor, const Blend::Factor srcBlendFactor,
const Blend::Factor dstBlendFactor) const Blend::Factor dstBlendFactor)
{ {
const unsigned int rColor = convertRGBAToABGR(_color);
const unsigned int rColorEnd = convertRGBAToABGR(_colorEnd);
Vertex vertices[4]; Vertex vertices[4];
float wL = w; float wL {w};
float hL = h; float hL {h};
// If the width or height was scaled down to less than 1 pixel, then set it to // 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. // 1 pixel so that it will still render on lower resolutions.
@ -476,44 +422,21 @@ namespace Renderer
hL = 1.0f; hL = 1.0f;
// clang-format off // clang-format off
vertices[0] = {{x, y }, {0.0f, 0.0f}, rColor}; vertices[0] = {{x, y }, {0.0f, 0.0f}, color};
vertices[1] = {{x, y + hL}, {0.0f, 0.0f}, horizontalGradient ? rColor : rColorEnd}; vertices[1] = {{x, y + hL}, {0.0f, 0.0f}, horizontalGradient ? color : colorEnd};
vertices[2] = {{x + wL, y }, {0.0f, 0.0f}, horizontalGradient ? rColorEnd : rColor}; vertices[2] = {{x + wL, y }, {0.0f, 0.0f}, horizontalGradient ? colorEnd : color};
vertices[3] = {{x + wL, y + hL}, {0.0f, 0.0f}, rColorEnd}; vertices[3] = {{x + wL, y + hL}, {0.0f, 0.0f}, colorEnd};
// clang-format on // clang-format on
// Round vertices. // Round vertices.
for (int i = 0; i < 4; ++i) for (int i = 0; i < 4; ++i)
vertices[i].pos = glm::round(vertices[i].pos); vertices[i].pos = glm::round(vertices[i].pos);
if (opacity < 1.0) { vertices->opacity = opacity;
vertices[0].shaders = SHADER_OPACITY; vertices->dim = dim;
vertices[0].opacity = opacity;
}
else {
bindTexture(0);
}
drawTriangleStrips(vertices, 4, trans, srcBlendFactor, dstBlendFactor);
}
const unsigned int convertRGBAToABGR(const unsigned int _color) bindTexture(0);
{ drawTriangleStrips(vertices, 4, srcBlendFactor, dstBlendFactor);
unsigned char red = ((_color & 0xff000000) >> 24) & 255;
unsigned char green = ((_color & 0x00ff0000) >> 16) & 255;
unsigned char blue = ((_color & 0x0000ff00) >> 8) & 255;
unsigned char alpha = ((_color & 0x000000ff)) & 255;
return alpha << 24 | blue << 16 | green << 8 | red;
}
const unsigned int convertABGRToRGBA(const unsigned int _color)
{
unsigned char alpha = ((_color & 0xff000000) >> 24) & 255;
unsigned char blue = ((_color & 0x00ff0000) >> 16) & 255;
unsigned char green = ((_color & 0x0000ff00) >> 8) & 255;
unsigned char red = ((_color & 0x000000ff)) & 255;
return red << 24 | green << 16 | blue << 8 | alpha;
} }
Shader* getShaderProgram(unsigned int shaderID) Shader* getShaderProgram(unsigned int shaderID)
@ -533,7 +456,14 @@ namespace Renderer
return nullptr; return nullptr;
} }
const glm::mat4& getProjectionMatrix() { return mProjectionMatrix; } const glm::mat4& getProjectionMatrix()
{
if (screenRotated)
return mProjectionMatrixRotated;
else
return mProjectionMatrix;
}
const glm::mat4& getProjectionMatrixNormal() { return mProjectionMatrix; }
SDL_Window* getSDLWindow() { return sdlWindow; } SDL_Window* getSDLWindow() { return sdlWindow; }
const float getWindowWidth() { return static_cast<float>(windowWidth); } const float getWindowWidth() { return static_cast<float>(windowWidth); }
const float getWindowHeight() { return static_cast<float>(windowHeight); } const float getWindowHeight() { return static_cast<float>(windowHeight); }
@ -541,7 +471,7 @@ namespace Renderer
const float getScreenHeight() { return static_cast<float>(screenHeight); } const float getScreenHeight() { return static_cast<float>(screenHeight); }
const float getScreenOffsetX() { return static_cast<float>(screenOffsetX); } const float getScreenOffsetX() { return static_cast<float>(screenOffsetX); }
const float getScreenOffsetY() { return static_cast<float>(screenOffsetY); } const float getScreenOffsetY() { return static_cast<float>(screenOffsetY); }
const int getScreenRotate() { return screenRotate; } const bool getScreenRotated() { return screenRotated; }
const float getScreenWidthModifier() { return screenWidthModifier; } const float getScreenWidthModifier() { return screenWidthModifier; }
const float getScreenHeightModifier() { return screenHeightModifier; } const float getScreenHeightModifier() { return screenHeightModifier; }
const float getScreenAspectRatio() { return screenAspectRatio; } const float getScreenAspectRatio() { return screenAspectRatio; }

View file

@ -20,39 +20,41 @@ struct SDL_Window;
namespace Renderer namespace Renderer
{ {
const unsigned int SHADER_DESATURATE {1}; // clang-format off
const unsigned int SHADER_OPACITY {2}; const unsigned int SHADER_CORE {0x00000001};
const unsigned int SHADER_DIM {4}; const unsigned int SHADER_BLUR_HORIZONTAL {0x00000002};
const unsigned int SHADER_BLUR_HORIZONTAL {8}; const unsigned int SHADER_BLUR_VERTICAL {0x00000004};
const unsigned int SHADER_BLUR_VERTICAL {16}; const unsigned int SHADER_SCANLINES {0x00000008};
const unsigned int SHADER_SCANLINES {32}; // clang-format on
const unsigned int SHADER_BGRA_TO_RGBA {64};
struct shaderParameters { struct postProcessingParams {
std::array<GLfloat, 2> textureSize; float opacity;
std::array<GLfloat, 4> textureCoordinates; float saturation;
float fragmentSaturation; float dim;
float fragmentDimValue; bool convertBGRAToRGBA;
float fragmentOpacity;
unsigned int blurPasses; unsigned int blurPasses;
unsigned int shaders;
shaderParameters() postProcessingParams()
: textureSize {0.0f, 0.0f} : opacity {1.0f}
, textureCoordinates {0.0f, 0.0f, 0.0f, 0.0f} , saturation {1.0f}
, fragmentSaturation {1.0f} , dim {1.0f}
, fragmentDimValue {0.4f} , convertBGRAToRGBA {false}
, fragmentOpacity {1.0f}
, blurPasses {1} , blurPasses {1}
, shaders {0}
{ {
} }
}; };
static std::vector<Shader*> sShaderProgramVector; static std::vector<Shader*> sShaderProgramVector;
static GLuint shaderFBO; static GLuint shaderFBO1;
static GLuint shaderFBO2;
// This is simply to get rid of a GCC false positive -Wunused-variable compiler warning. // This is simply to get rid of a GCC false positive -Wunused-variable compiler warning.
static GLuint shaderFBODummy = shaderFBO; static GLuint shaderFBODummy1 {shaderFBO1};
static GLuint shaderFBODummy2 {shaderFBO2};
static constexpr glm::mat4 getIdentity() { return glm::mat4 {1.0f}; } static constexpr glm::mat4 getIdentity() { return glm::mat4 {1.0f}; }
static inline glm::mat4 mTrans {getIdentity()};
#if !defined(NDEBUG) #if !defined(NDEBUG)
#define GL_CHECK_ERROR(Function) (Function, _GLCheckError(#Function)) #define GL_CHECK_ERROR(Function) (Function, _GLCheckError(#Function))
@ -115,19 +117,34 @@ namespace Renderer
}; };
struct Vertex { struct Vertex {
Vertex() {} glm::vec2 pos;
glm::vec2 tex;
unsigned int col;
float opacity;
float saturation;
float dim;
bool convertBGRAToRGBA;
unsigned int shaders;
Vertex()
: opacity {1.0f}
, saturation {1.0f}
, dim {1.0f}
, convertBGRAToRGBA {false}
, shaders {0}
{
}
Vertex(const glm::vec2& position, const glm::vec2& textureCoord, const unsigned int color) Vertex(const glm::vec2& position, const glm::vec2& textureCoord, const unsigned int color)
: pos(position) : pos(position)
, tex(textureCoord) , tex(textureCoord)
, col(color) , col(color)
, opacity {1.0f}
, saturation {1.0f}
, dim {1.0f}
, convertBGRAToRGBA {false}
, shaders {0}
{ {
} }
glm::vec2 pos;
glm::vec2 tex;
unsigned int col;
float saturation {1.0};
float opacity {1.0};
unsigned int shaders {0};
}; };
bool init(); bool init();
@ -142,7 +159,7 @@ namespace Renderer
const unsigned int colorEnd, const unsigned int colorEnd,
bool horizontalGradient = false, bool horizontalGradient = false,
const float opacity = 1.0, const float opacity = 1.0,
const glm::mat4& trans = getIdentity(), const float dim = 1.0,
const Blend::Factor srcBlendFactor = Blend::SRC_ALPHA, const Blend::Factor srcBlendFactor = Blend::SRC_ALPHA,
const Blend::Factor dstBlendFactor = Blend::ONE_MINUS_SRC_ALPHA); const Blend::Factor dstBlendFactor = Blend::ONE_MINUS_SRC_ALPHA);
SDL_Window* getSDLWindow(); SDL_Window* getSDLWindow();
@ -152,19 +169,18 @@ namespace Renderer
const float getScreenHeight(); const float getScreenHeight();
const float getScreenOffsetX(); const float getScreenOffsetX();
const float getScreenOffsetY(); const float getScreenOffsetY();
const int getScreenRotate(); const bool getScreenRotated();
const float getScreenWidthModifier(); const float getScreenWidthModifier();
const float getScreenHeightModifier(); const float getScreenHeightModifier();
const float getScreenAspectRatio(); const float getScreenAspectRatio();
const unsigned int convertRGBAToABGR(const unsigned int color);
const unsigned int convertABGRToRGBA(const unsigned int color);
Shader* getShaderProgram(unsigned int shaderID); Shader* getShaderProgram(unsigned int shaderID);
const glm::mat4& getProjectionMatrix(); const glm::mat4& getProjectionMatrix();
void shaderPostprocessing(const unsigned int shaders, const glm::mat4& getProjectionMatrixNormal();
const Renderer::shaderParameters& parameters = shaderParameters(), void shaderPostprocessing(
unsigned char* textureRGBA = nullptr); const unsigned int shaders,
const Renderer::postProcessingParams& parameters = postProcessingParams(),
unsigned char* textureRGBA = nullptr);
void setupWindow(); void setupWindow();
bool createContext(); bool createContext();
@ -192,13 +208,9 @@ namespace Renderer
const Blend::Factor dstBlendFactor = Blend::ONE_MINUS_SRC_ALPHA); const Blend::Factor dstBlendFactor = Blend::ONE_MINUS_SRC_ALPHA);
void drawTriangleStrips(const Vertex* vertices, void drawTriangleStrips(const Vertex* vertices,
const unsigned int numVertices, const unsigned int numVertices,
const glm::mat4& trans = getIdentity(),
const Blend::Factor srcBlendFactor = Blend::SRC_ALPHA, const Blend::Factor srcBlendFactor = Blend::SRC_ALPHA,
const Blend::Factor dstBlendFactor = Blend::ONE_MINUS_SRC_ALPHA, const Blend::Factor dstBlendFactor = Blend::ONE_MINUS_SRC_ALPHA);
const shaderParameters& parameters = shaderParameters());
void setProjection(const glm::mat4& projection);
void setMatrix(const glm::mat4& matrix); void setMatrix(const glm::mat4& matrix);
void setViewport(const Rect& viewport);
void setScissor(const Rect& scissor); void setScissor(const Rect& scissor);
void setSwapInterval(); void setSwapInterval();
void swapBuffers(); void swapBuffers();

View file

@ -19,7 +19,9 @@
namespace Renderer namespace Renderer
{ {
static SDL_GLContext sdlContext = nullptr; static SDL_GLContext sdlContext = nullptr;
static GLuint whiteTexture = 0; static GLuint whiteTexture {0};
static GLuint postProcTexture1 {0};
static GLuint postProcTexture2 {0};
inline GLenum convertBlendFactor(const Blend::Factor _blendFactor) inline GLenum convertBlendFactor(const Blend::Factor _blendFactor)
{ {
@ -44,10 +46,10 @@ namespace Renderer
{ {
// clang-format off // clang-format off
switch (_type) { switch (_type) {
case Texture::RGBA: { return GL_RGBA; } break; case Texture::RGBA: { return GL_RGBA; } break;
case Texture::BGRA: { return GL_BGRA; } break; case Texture::BGRA: { return GL_BGRA; } break;
case Texture::ALPHA: { return GL_ALPHA; } break; case Texture::ALPHA: { return GL_LUMINANCE_ALPHA; } break;
default: { return GL_ZERO; } default: { return GL_ZERO; }
} }
// clang-format on // clang-format on
} }
@ -142,6 +144,14 @@ namespace Renderer
return false; return false;
} }
postProcTexture1 = createTexture(Texture::RGBA, Texture::RGBA, false, false, false,
static_cast<unsigned int>(getScreenWidth()),
static_cast<unsigned int>(getScreenHeight()), nullptr);
postProcTexture2 = createTexture(Texture::RGBA, Texture::RGBA, false, false, false,
static_cast<unsigned int>(getScreenWidth()),
static_cast<unsigned int>(getScreenHeight()), nullptr);
uint8_t data[4] = {255, 255, 255, 255}; uint8_t data[4] = {255, 255, 255, 255};
whiteTexture = createTexture(Texture::RGBA, Texture::RGBA, false, false, true, 1, 1, data); whiteTexture = createTexture(Texture::RGBA, Texture::RGBA, false, false, true, 1, 1, data);
@ -154,15 +164,20 @@ namespace Renderer
GL_CHECK_ERROR(glEnableClientState(GL_TEXTURE_COORD_ARRAY)); GL_CHECK_ERROR(glEnableClientState(GL_TEXTURE_COORD_ARRAY));
GL_CHECK_ERROR(glEnableClientState(GL_COLOR_ARRAY)); GL_CHECK_ERROR(glEnableClientState(GL_COLOR_ARRAY));
// This is the framebuffer that will be used for shader rendering. // These framebuffers are used for the shader post processing.
GL_CHECK_ERROR(glGenFramebuffers(1, &shaderFBO)); GL_CHECK_ERROR(glGenFramebuffers(1, &shaderFBO1));
GL_CHECK_ERROR(glGenFramebuffers(1, &shaderFBO2));
return true; return true;
} }
void destroyContext() void destroyContext()
{ {
GL_CHECK_ERROR(glDeleteFramebuffers(1, &shaderFBO)); GL_CHECK_ERROR(glDeleteFramebuffers(1, &shaderFBO1));
GL_CHECK_ERROR(glDeleteFramebuffers(1, &shaderFBO2));
destroyTexture(postProcTexture1);
destroyTexture(postProcTexture2);
destroyTexture(whiteTexture);
SDL_GL_DeleteContext(sdlContext); SDL_GL_DeleteContext(sdlContext);
sdlContext = nullptr; sdlContext = nullptr;
} }
@ -195,8 +210,23 @@ namespace Renderer
linearMagnify ? static_cast<GLfloat>(GL_LINEAR) : linearMagnify ? static_cast<GLfloat>(GL_LINEAR) :
static_cast<GLfloat>(GL_NEAREST))); static_cast<GLfloat>(GL_NEAREST)));
GL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, textureType, width, height, 0, textureType, if (textureType == GL_LUMINANCE_ALPHA) {
GL_UNSIGNED_BYTE, data)); uint8_t* a_data {reinterpret_cast<uint8_t*>(data)};
uint8_t* la_data {new uint8_t[width * height * 2]};
for (uint32_t i = 0; i < (width * height); ++i) {
la_data[(i * 2) + 0] = 255;
la_data[(i * 2) + 1] = a_data ? a_data[i] : 255;
}
GL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, textureType, width, height, 0,
textureType, GL_UNSIGNED_BYTE, la_data));
delete[] la_data;
}
else {
GL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, textureType, width, height, 0,
textureType, GL_UNSIGNED_BYTE, data));
}
return texture; return texture;
} }
@ -217,8 +247,27 @@ namespace Renderer
const GLenum textureType = convertTextureType(type); const GLenum textureType = convertTextureType(type);
GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture)); GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture));
GL_CHECK_ERROR(glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, textureType,
GL_UNSIGNED_BYTE, data)); // Regular GL_ALPHA textures are black + alpha when used in shaders, so create a
// GL_LUMINANCE_ALPHA texture instead so it's white + alpha.
if (textureType == GL_LUMINANCE_ALPHA) {
uint8_t* a_data {reinterpret_cast<uint8_t*>(data)};
uint8_t* la_data {new uint8_t[width * height * 2]};
for (uint32_t i = 0; i < (width * height); ++i) {
la_data[(i * 2) + 0] = 255;
la_data[(i * 2) + 1] = a_data ? a_data[i] : 255;
}
GL_CHECK_ERROR(glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, textureType,
GL_UNSIGNED_BYTE, la_data));
delete[] la_data;
}
else {
GL_CHECK_ERROR(glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, textureType,
GL_UNSIGNED_BYTE, data));
}
GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, whiteTexture)); GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, whiteTexture));
} }
@ -247,13 +296,11 @@ namespace Renderer
void drawTriangleStrips(const Vertex* vertices, void drawTriangleStrips(const Vertex* vertices,
const unsigned int numVertices, const unsigned int numVertices,
const glm::mat4& trans,
const Blend::Factor srcBlendFactor, const Blend::Factor srcBlendFactor,
const Blend::Factor dstBlendFactor, const Blend::Factor dstBlendFactor)
const shaderParameters& parameters)
{ {
const float width = vertices[3].pos[0]; const float width {vertices[3].pos[0]};
const float height = vertices[3].pos[1]; const float height {vertices[3].pos[1]};
GL_CHECK_ERROR(glVertexPointer(2, GL_FLOAT, sizeof(Vertex), &vertices[0].pos)); GL_CHECK_ERROR(glVertexPointer(2, GL_FLOAT, sizeof(Vertex), &vertices[0].pos));
GL_CHECK_ERROR(glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &vertices[0].tex)); GL_CHECK_ERROR(glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &vertices[0].tex));
@ -262,138 +309,78 @@ namespace Renderer
GL_CHECK_ERROR( GL_CHECK_ERROR(
glBlendFunc(convertBlendFactor(srcBlendFactor), convertBlendFactor(dstBlendFactor))); glBlendFunc(convertBlendFactor(srcBlendFactor), convertBlendFactor(dstBlendFactor)));
#if defined(USE_OPENGL_21) if (vertices->shaders == 0 || vertices->shaders & SHADER_CORE) {
if (vertices[0].shaders == 0) { Shader* runShader = getShaderProgram(SHADER_CORE);
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices)); if (runShader) {
} runShader->activateShaders();
else { runShader->setModelViewProjectionMatrix(mTrans);
// If saturation is set below the maximum (default) value, run the runShader->setOpacity(vertices->opacity);
// desaturation shader. runShader->setSaturation(vertices->saturation);
if (vertices->saturation < 1.0f || parameters.fragmentSaturation < 1.0f) { runShader->setDim(vertices->dim);
Shader* runShader = getShaderProgram(SHADER_DESATURATE); runShader->setBGRAToRGBA(vertices->convertBGRAToRGBA);
// Only try to use the shader if it has been loaded properly. GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices));
if (runShader) { runShader->deactivateShaders();
runShader->activateShaders(); }
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * trans); }
runShader->setSaturation(vertices->saturation); else if (vertices->shaders & SHADER_BLUR_HORIZONTAL) {
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices)); Shader* runShader = getShaderProgram(SHADER_BLUR_HORIZONTAL);
runShader->deactivateShaders(); if (runShader) {
} runShader->activateShaders();
} runShader->setModelViewProjectionMatrix(mTrans);
runShader->setTextureSize({width, height});
if (vertices->shaders & SHADER_OPACITY) { GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices));
Shader* runShader = getShaderProgram(SHADER_OPACITY); runShader->deactivateShaders();
if (runShader) { }
runShader->activateShaders(); return;
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * trans); }
vertices->opacity < 1.0f ? runShader->setOpacity(vertices->opacity) : else if (vertices->shaders & SHADER_BLUR_VERTICAL) {
runShader->setOpacity(parameters.fragmentOpacity); Shader* runShader = getShaderProgram(SHADER_BLUR_VERTICAL);
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices)); if (runShader) {
runShader->deactivateShaders(); runShader->activateShaders();
} runShader->setModelViewProjectionMatrix(mTrans);
} runShader->setTextureSize({width, height});
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices));
// Check if any other shaders are set to be used and if so, run them. runShader->deactivateShaders();
if (vertices->shaders & SHADER_DIM) { }
Shader* runShader = getShaderProgram(SHADER_DIM); return;
if (runShader) { }
runShader->activateShaders(); else if (vertices->shaders & SHADER_SCANLINES) {
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * trans); Shader* runShader {getShaderProgram(SHADER_SCANLINES)};
runShader->setDimValue(parameters.fragmentDimValue); float shaderWidth {width * 1.2f};
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices)); // Scale the scanlines relative to screen resolution.
runShader->deactivateShaders(); float screenHeightModifier {getScreenHeightModifier()};
} float relativeHeight {height / getScreenHeight()};
} float shaderHeight {0.0f};
if (relativeHeight == 1.0f) {
if (vertices->shaders & SHADER_BLUR_HORIZONTAL) { // Full screen.
Shader* runShader = getShaderProgram(SHADER_BLUR_HORIZONTAL); float modifier {1.30f - (0.1f * screenHeightModifier)};
if (runShader) { shaderHeight = height * modifier;
runShader->activateShaders(); }
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * trans); else {
runShader->setTextureSize({width, height}); // Portion of screen, e.g. gamelist view.
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices)); // Average the relative width and height to avoid applying exaggerated
runShader->deactivateShaders(); // scanlines to videos with non-standard aspect ratios.
} float relativeWidth {width / getScreenWidth()};
} float relativeAdjustment {(relativeWidth + relativeHeight) / 2.0f};
float modifier {1.41f + relativeAdjustment / 7.0f - (0.14f * screenHeightModifier)};
if (vertices->shaders & SHADER_BLUR_VERTICAL) { shaderHeight = height * modifier;
Shader* runShader = getShaderProgram(SHADER_BLUR_VERTICAL); }
if (runShader) { if (runShader) {
runShader->activateShaders(); runShader->activateShaders();
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * trans); runShader->setModelViewProjectionMatrix(mTrans);
runShader->setTextureSize({width, height}); runShader->setOpacity(vertices->opacity);
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices)); runShader->setTextureSize({shaderWidth, shaderHeight});
runShader->deactivateShaders(); GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices));
} runShader->deactivateShaders();
}
if (vertices->shaders & SHADER_SCANLINES) {
Shader* runShader = getShaderProgram(SHADER_SCANLINES);
float shaderWidth = width * 1.2f;
// Scale the scanlines relative to screen resolution.
float screenHeightModifier = getScreenHeightModifier();
float relativeHeight = height / getScreenHeight();
float shaderHeight = 0.0f;
if (relativeHeight == 1.0f) {
// Full screen.
float modifier = 1.30f - (0.1f * screenHeightModifier);
shaderHeight = height * modifier;
}
else {
// Portion of screen, e.g. gamelist view.
// Average the relative width and height to avoid applying exaggerated
// scanlines to videos with non-standard aspect ratios.
float relativeWidth = width / getScreenWidth();
float relativeAdjustment = (relativeWidth + relativeHeight) / 2.0f;
float modifier =
1.41f + relativeAdjustment / 7.0f - (0.14f * screenHeightModifier);
shaderHeight = height * modifier;
}
if (runShader) {
runShader->activateShaders();
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * trans);
runShader->setOpacity(vertices->opacity);
runShader->setTextureSize({shaderWidth, shaderHeight});
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices));
runShader->deactivateShaders();
}
}
if (vertices->shaders & SHADER_BGRA_TO_RGBA) {
Shader* runShader = getShaderProgram(SHADER_BGRA_TO_RGBA);
if (runShader) {
runShader->activateShaders();
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * trans);
runShader->setOpacity(vertices->opacity);
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices));
runShader->deactivateShaders();
}
} }
} }
#endif
}
void setProjection(const glm::mat4& projection)
{
GL_CHECK_ERROR(glMatrixMode(GL_PROJECTION));
GL_CHECK_ERROR(glLoadMatrixf(reinterpret_cast<const GLfloat*>(&projection)));
} }
void setMatrix(const glm::mat4& matrix) void setMatrix(const glm::mat4& matrix)
{ {
glm::mat4 newMatrix {matrix}; mTrans = matrix;
newMatrix[3] = glm::round(newMatrix[3]); mTrans[3] = glm::round(mTrans[3]);
mTrans = getProjectionMatrix() * mTrans;
GL_CHECK_ERROR(glMatrixMode(GL_MODELVIEW));
GL_CHECK_ERROR(glLoadMatrixf(reinterpret_cast<const GLfloat*>(&newMatrix)));
}
void setViewport(const Rect& viewport)
{
// glViewport starts at the bottom left of the window.
GL_CHECK_ERROR(glViewport(viewport.x,
static_cast<GLint>(getWindowHeight()) - viewport.y - viewport.h,
viewport.w, viewport.h));
} }
void setScissor(const Rect& scissor) void setScissor(const Rect& scissor)
@ -450,31 +437,31 @@ namespace Renderer
} }
void shaderPostprocessing(unsigned int shaders, void shaderPostprocessing(unsigned int shaders,
const Renderer::shaderParameters& parameters, const Renderer::postProcessingParams& parameters,
unsigned char* textureRGBA) unsigned char* textureRGBA)
{ {
Vertex vertices[4]; Vertex vertices[4];
std::vector<unsigned int> shaderList; std::vector<unsigned int> shaderList;
GLuint width {static_cast<GLuint>(getScreenWidth())}; float widthf {getScreenWidth()};
GLuint height {static_cast<GLuint>(getScreenHeight())}; float heightf {getScreenHeight()};
float widthf {static_cast<float>(width)}; GLuint width {static_cast<GLuint>(widthf)};
float heightf {static_cast<float>(height)}; GLuint height {static_cast<GLuint>(heightf)};
// Set vertex positions and texture coordinates to full screen as all // Set vertex positions and texture coordinates to full screen as all
// postprocessing is applied to the complete screen area. // postprocessing is applied to the complete screen area.
// clang-format off // clang-format off
vertices[0] = { { 0.0f , 0.0f }, { 0.0f, 1.0f }, 0 }; vertices[0] = {{0.0f, 0.0f }, {0.0f, 1.0f}, 0xFFFFFFFF};
vertices[1] = { { 0.0f , heightf }, { 0.0f, 0.0f }, 0 }; vertices[1] = {{0.0f, heightf}, {0.0f, 0.0f}, 0xFFFFFFFF};
vertices[2] = { { widthf, 0.0f }, { 1.0f, 1.0f }, 0 }; vertices[2] = {{widthf, 0.0f }, {1.0f, 1.0f}, 0xFFFFFFFF};
vertices[3] = { { widthf, heightf }, { 1.0f, 0.0f }, 0 }; vertices[3] = {{widthf, heightf}, {1.0f, 0.0f}, 0xFFFFFFFF};
// clang-format on // clang-format on
if (shaders & Renderer::SHADER_DESATURATE) vertices->opacity = parameters.opacity;
shaderList.push_back(Renderer::SHADER_DESATURATE); vertices->saturation = parameters.saturation;
if (shaders & Renderer::SHADER_OPACITY) vertices->dim = parameters.dim;
shaderList.push_back(Renderer::SHADER_OPACITY);
if (shaders & Renderer::SHADER_DIM) shaderList.emplace_back(Renderer::SHADER_CORE);
shaderList.push_back(Renderer::SHADER_DIM);
if (shaders & Renderer::SHADER_BLUR_HORIZONTAL) if (shaders & Renderer::SHADER_BLUR_HORIZONTAL)
shaderList.push_back(Renderer::SHADER_BLUR_HORIZONTAL); shaderList.push_back(Renderer::SHADER_BLUR_HORIZONTAL);
if (shaders & Renderer::SHADER_BLUR_VERTICAL) if (shaders & Renderer::SHADER_BLUR_VERTICAL)
@ -482,17 +469,30 @@ namespace Renderer
if (shaders & Renderer::SHADER_SCANLINES) if (shaders & Renderer::SHADER_SCANLINES)
shaderList.push_back(Renderer::SHADER_SCANLINES); shaderList.push_back(Renderer::SHADER_SCANLINES);
if (parameters.fragmentSaturation < 1.0)
vertices[0].saturation = parameters.fragmentSaturation;
setMatrix(getIdentity()); setMatrix(getIdentity());
GLuint screenTexture = createTexture(Texture::RGBA, Texture::RGBA, false, false, false, bindTexture(postProcTexture1);
width, height, nullptr);
GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, 0)); GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, shaderFBO2));
GL_CHECK_ERROR(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
postProcTexture2, 0));
GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, shaderFBO1));
// Attach texture to the shader framebuffer.
GL_CHECK_ERROR(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
postProcTexture1, 0));
// Blit the screen contents to postProcTexture.
GL_CHECK_ERROR(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height,
GL_COLOR_BUFFER_BIT, GL_NEAREST));
GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, shaderFBO2));
bool firstFBO {true};
int drawCalls {0};
for (size_t i = 0; i < shaderList.size(); ++i) { for (size_t i = 0; i < shaderList.size(); ++i) {
vertices[0].shaders = shaderList[i]; vertices->shaders = shaderList[i];
int shaderPasses = 1; int shaderPasses = 1;
// For the blur shaders there is an optional variable to set the number of passes // For the blur shaders there is an optional variable to set the number of passes
// to execute, which proportionally affects the blur amount. // to execute, which proportionally affects the blur amount.
@ -502,42 +502,55 @@ namespace Renderer
} }
for (int p = 0; p < shaderPasses; ++p) { for (int p = 0; p < shaderPasses; ++p) {
GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, shaderFBO)); if (textureRGBA == nullptr && i == shaderList.size() - 1 && p == shaderPasses - 1) {
// If the screen is rotated and we're at an even number of drawcalls, then
// Attach the texture to the shader framebuffer. // set the projection to a non-rotated state before making the last drawcall
GL_CHECK_ERROR(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, // as the image would otherwise get rendered upside down.
GL_TEXTURE_2D, screenTexture, 0)); if (getScreenRotated() && drawCalls % 2 == 0) {
mTrans = getIdentity();
// Blit the screen contents to screenTexture. mTrans[3] = glm::round(mTrans[3]);
GL_CHECK_ERROR(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, mTrans = getProjectionMatrixNormal() * mTrans;
GL_COLOR_BUFFER_BIT, GL_NEAREST)); }
// If it's the last shader pass, then render directly to the default framebuffer
// Apply/render the shaders. // to avoid having to make an expensive glBlitFramebuffer() call.
drawTriangleStrips(vertices, 4, getIdentity(), Blend::SRC_ALPHA,
Blend::ONE_MINUS_SRC_ALPHA, parameters);
// If textureRGBA has an address, it means that the output should go to this
// texture rather than to the screen. The glReadPixels() function is slow, but
// since this will typically only run every now and then to create a cached
// screen texture, it doesn't really matter.
if (textureRGBA) {
GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, shaderFBO));
GL_CHECK_ERROR(
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, textureRGBA));
GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)); GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0));
drawTriangleStrips(vertices, 4, Blend::SRC_ALPHA, Blend::ONE_MINUS_SRC_ALPHA);
break;
}
// Apply/render the shaders.
drawTriangleStrips(vertices, 4, Blend::SRC_ALPHA, Blend::ONE_MINUS_SRC_ALPHA);
++drawCalls;
if (firstFBO) {
bindTexture(postProcTexture2);
GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, shaderFBO2));
GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, shaderFBO1));
firstFBO = false;
} }
else { else {
// Blit the resulting postprocessed texture back to the primary framebuffer. bindTexture(postProcTexture1);
GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, shaderFBO)); GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, shaderFBO1));
GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)); GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, shaderFBO2));
GL_CHECK_ERROR(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, firstFBO = true;
GL_COLOR_BUFFER_BIT, GL_NEAREST));
} }
} }
} }
// If textureRGBA has an address, it means that the output should go to this
// texture rather than to the screen. The glReadPixels() function is slow, but
// since this will typically only run every now and then to create a cached
// screen texture, it doesn't really matter.
if (textureRGBA) {
if (firstFBO)
GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, shaderFBO1));
else
GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, shaderFBO2));
GL_CHECK_ERROR(
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, textureRGBA));
GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0));
}
GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, 0)); GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, 0));
destroyTexture(screenTexture);
} }
} // namespace Renderer } // namespace Renderer

View file

@ -21,10 +21,9 @@ namespace Renderer
, shaderMVPMatrix {0} , shaderMVPMatrix {0}
, shaderTextureSize {0} , shaderTextureSize {0}
, shaderTextureCoord {0} , shaderTextureCoord {0}
, shaderColor {0}
, shaderSaturation {0}
, shaderOpacity {0} , shaderOpacity {0}
, shaderDimValue {0} , shaderSaturation {0}
, shaderDim {0}
{ {
} }
@ -108,10 +107,10 @@ namespace Renderer
shaderMVPMatrix = glGetUniformLocation(mProgramID, "MVPMatrix"); shaderMVPMatrix = glGetUniformLocation(mProgramID, "MVPMatrix");
shaderTextureSize = glGetUniformLocation(mProgramID, "TextureSize"); shaderTextureSize = glGetUniformLocation(mProgramID, "TextureSize");
shaderTextureCoord = glGetAttribLocation(mProgramID, "TexCoord"); shaderTextureCoord = glGetAttribLocation(mProgramID, "TexCoord");
shaderColor = glGetAttribLocation(mProgramID, "COLOR");
shaderSaturation = glGetUniformLocation(mProgramID, "saturation");
shaderOpacity = glGetUniformLocation(mProgramID, "opacity"); shaderOpacity = glGetUniformLocation(mProgramID, "opacity");
shaderDimValue = glGetUniformLocation(mProgramID, "dimValue"); shaderSaturation = glGetUniformLocation(mProgramID, "saturation");
shaderDim = glGetUniformLocation(mProgramID, "dim");
shaderBGRAToRGBA = glGetUniformLocation(mProgramID, "BGRAToRGBA");
} }
void Renderer::Shader::setModelViewProjectionMatrix(glm::mat4 mvpMatrix) void Renderer::Shader::setModelViewProjectionMatrix(glm::mat4 mvpMatrix)
@ -135,11 +134,10 @@ namespace Renderer
} }
} }
void Renderer::Shader::setColor(std::array<GLfloat, 4> shaderVec4) void Renderer::Shader::setOpacity(GLfloat opacity)
{ {
if (shaderColor != GL_INVALID_OPERATION) if (shaderOpacity != GL_INVALID_VALUE && shaderOpacity != GL_INVALID_OPERATION)
GL_CHECK_ERROR(glUniform4f(shaderColor, shaderVec4[0], shaderVec4[1], shaderVec4[2], GL_CHECK_ERROR(glUniform1f(shaderOpacity, opacity));
shaderVec4[3]));
} }
void Renderer::Shader::setSaturation(GLfloat saturation) void Renderer::Shader::setSaturation(GLfloat saturation)
@ -148,16 +146,16 @@ namespace Renderer
GL_CHECK_ERROR(glUniform1f(shaderSaturation, saturation)); GL_CHECK_ERROR(glUniform1f(shaderSaturation, saturation));
} }
void Renderer::Shader::setOpacity(GLfloat opacity) void Renderer::Shader::setDim(GLfloat dim)
{ {
if (shaderOpacity != GL_INVALID_VALUE && shaderOpacity != GL_INVALID_OPERATION) if (shaderDim != GL_INVALID_VALUE && shaderDim != GL_INVALID_OPERATION)
GL_CHECK_ERROR(glUniform1f(shaderOpacity, opacity)); GL_CHECK_ERROR(glUniform1f(shaderDim, dim));
} }
void Renderer::Shader::setDimValue(GLfloat dimValue) void Renderer::Shader::setBGRAToRGBA(GLboolean BGRAToRGBA)
{ {
if (shaderDimValue != GL_INVALID_VALUE && shaderDimValue != GL_INVALID_OPERATION) if (shaderBGRAToRGBA != GL_INVALID_VALUE && shaderBGRAToRGBA != GL_INVALID_OPERATION)
GL_CHECK_ERROR(glUniform1f(shaderDimValue, dimValue)); GL_CHECK_ERROR(glUniform1i(shaderBGRAToRGBA, BGRAToRGBA ? 1 : 0));
} }
void Renderer::Shader::activateShaders() void Renderer::Shader::activateShaders()
@ -205,7 +203,7 @@ namespace Renderer
glGetShaderInfoLog(shaderID, maxLength, &logLength, &infoLog.front()); glGetShaderInfoLog(shaderID, maxLength, &logLength, &infoLog.front());
if (logLength > 0) { if (logLength > 0) {
LOG(LogDebug) << "Renderer_GL21::printShaderInfoLog(): Error in " LOG(LogDebug) << "Shader_GL21::printShaderInfoLog(): Error in "
<< (shaderType == GL_VERTEX_SHADER ? "VERTEX section:\n" : << (shaderType == GL_VERTEX_SHADER ? "VERTEX section:\n" :
"FRAGMENT section:\n") "FRAGMENT section:\n")
<< std::string(infoLog.begin(), infoLog.end()); << std::string(infoLog.begin(), infoLog.end());

View file

@ -49,10 +49,10 @@ namespace Renderer
void setTextureSize(std::array<GLfloat, 2> shaderVec2); void setTextureSize(std::array<GLfloat, 2> shaderVec2);
void setTextureCoordinates(std::array<GLfloat, 4> shaderVec4); void setTextureCoordinates(std::array<GLfloat, 4> shaderVec4);
void setColor(std::array<GLfloat, 4> shaderVec4);
void setSaturation(GLfloat saturation);
void setOpacity(GLfloat opacity); void setOpacity(GLfloat opacity);
void setDimValue(GLfloat dimValue); void setSaturation(GLfloat saturation);
void setDim(GLfloat dim);
void setBGRAToRGBA(GLboolean BGRAToRGBA);
// Sets the shader program to use the loaded shaders. // Sets the shader program to use the loaded shaders.
void activateShaders(); void activateShaders();
// Sets the shader program to 0 which reverts to the fixed function pipeline. // Sets the shader program to 0 which reverts to the fixed function pipeline.
@ -71,10 +71,10 @@ namespace Renderer
GLint shaderMVPMatrix; GLint shaderMVPMatrix;
GLint shaderTextureSize; GLint shaderTextureSize;
GLint shaderTextureCoord; GLint shaderTextureCoord;
GLint shaderColor;
GLint shaderSaturation;
GLint shaderOpacity; GLint shaderOpacity;
GLint shaderDimValue; GLint shaderSaturation;
GLint shaderDim;
GLint shaderBGRAToRGBA;
}; };
} // namespace Renderer } // namespace Renderer

View file

@ -654,21 +654,19 @@ TextCache* Font::buildTextCache(const std::string& text,
const float glyphStartX {x + glyph->bearing.x}; const float glyphStartX {x + glyph->bearing.x};
const glm::ivec2& textureSize {glyph->texture->textureSize}; const glm::ivec2& textureSize {glyph->texture->textureSize};
const unsigned int convertedColor = Renderer::convertRGBAToABGR(color);
vertices[1] = {{glyphStartX, y - glyph->bearing.y}, vertices[1] = {
{glyph->texPos.x, glyph->texPos.y}, {glyphStartX, y - glyph->bearing.y}, {glyph->texPos.x, glyph->texPos.y}, color};
convertedColor};
vertices[2] = {{glyphStartX, y - glyph->bearing.y + (glyph->texSize.y * textureSize.y)}, vertices[2] = {{glyphStartX, y - glyph->bearing.y + (glyph->texSize.y * textureSize.y)},
{glyph->texPos.x, glyph->texPos.y + glyph->texSize.y}, {glyph->texPos.x, glyph->texPos.y + glyph->texSize.y},
convertedColor}; color};
vertices[3] = {{glyphStartX + glyph->texSize.x * textureSize.x, y - glyph->bearing.y}, vertices[3] = {{glyphStartX + glyph->texSize.x * textureSize.x, y - glyph->bearing.y},
{glyph->texPos.x + glyph->texSize.x, glyph->texPos.y}, {glyph->texPos.x + glyph->texSize.x, glyph->texPos.y},
convertedColor}; color};
vertices[4] = {{glyphStartX + glyph->texSize.x * textureSize.x, vertices[4] = {{glyphStartX + glyph->texSize.x * textureSize.x,
y - glyph->bearing.y + (glyph->texSize.y * textureSize.y)}, y - glyph->bearing.y + (glyph->texSize.y * textureSize.y)},
{glyph->texPos.x + glyph->texSize.x, glyph->texPos.y + glyph->texSize.y}, {glyph->texPos.x + glyph->texSize.x, glyph->texPos.y + glyph->texSize.y},
convertedColor}; color};
// Round vertices. // Round vertices.
for (int i = 1; i < 5; ++i) for (int i = 1; i < 5; ++i)
@ -711,11 +709,25 @@ TextCache* Font::buildTextCache(const std::string& text,
void TextCache::setColor(unsigned int color) void TextCache::setColor(unsigned int color)
{ {
const unsigned int convertedColor = Renderer::convertRGBAToABGR(color);
for (auto it = vertexLists.begin(); it != vertexLists.end(); ++it) for (auto it = vertexLists.begin(); it != vertexLists.end(); ++it)
for (auto it2 = it->verts.begin(); it2 != it->verts.end(); ++it2) for (auto it2 = it->verts.begin(); it2 != it->verts.end(); ++it2)
it2->col = convertedColor; it2->col = color;
}
void TextCache::setOpacity(float opacity)
{
for (auto it = vertexLists.begin(); it != vertexLists.end(); ++it) {
for (auto it2 = it->verts.begin(); it2 != it->verts.end(); ++it2)
it2->opacity = opacity;
}
}
void TextCache::setDim(float dim)
{
for (auto it = vertexLists.begin(); it != vertexLists.end(); ++it) {
for (auto it2 = it->verts.begin(); it2 != it->verts.end(); ++it2)
it2->dim = dim;
}
} }
std::shared_ptr<Font> Font::getFromTheme(const ThemeData::ThemeElement* elem, std::shared_ptr<Font> Font::getFromTheme(const ThemeData::ThemeElement* elem,

View file

@ -206,6 +206,8 @@ public:
} metrics; } metrics;
void setColor(unsigned int color); void setColor(unsigned int color);
void setOpacity(float opacity);
void setDim(float dim);
friend Font; friend Font;
}; };

View file

@ -0,0 +1,74 @@
// SPDX-License-Identifier: MIT
//
// EmulationStation Desktop Edition
// core.glsl
//
// Core shader functionality:
// opacity, saturation, dimming and BGRA to RGBA conversion.
//
#if defined(VERTEX)
// Vertex section of code:
#if __VERSION__ >= 130
#define COMPAT_VARYING out
#else
#define COMPAT_VARYING varying
#endif
uniform mat4 MVPMatrix;
COMPAT_VARYING vec4 color;
COMPAT_VARYING vec2 texCoord;
void main(void)
{
texCoord = gl_MultiTexCoord0.xy;
color.rgba = gl_Color.abgr;
gl_Position = MVPMatrix * gl_Vertex;
}
#elif defined(FRAGMENT)
// Fragment section of code:
#if __VERSION__ >= 130
#define COMPAT_VARYING out
#define COMPAT_TEXTURE texture
#else
#define COMPAT_VARYING varying
#define COMPAT_TEXTURE texture2D
#endif
COMPAT_VARYING vec4 color;
COMPAT_VARYING vec2 texCoord;
uniform float opacity = 1.0f;
uniform float saturation = 1.0f;
uniform float dim = 1.0f;
uniform int BGRAToRGBA = 0;
uniform sampler2D myTexture;
void main()
{
vec4 color = COMPAT_TEXTURE(myTexture, texCoord) * color;
// Opacity.
if (opacity != 1.0f)
color.a = color.a * opacity;
// Saturation.
if (saturation != 1.0f) {
vec3 grayscale = vec3(dot(color.rgb, vec3(0.3f, 0.59f, 0.11f)));
vec3 blendedColor = mix(grayscale, color.rgb, saturation);
color = vec4(blendedColor, color.a);
}
// Dimming
vec4 dimColor = vec4(dim, dim, dim, 1.0f);
color = vec4(color.rgba) * dimColor;
// BGRA to RGBA conversion.
if (BGRAToRGBA == 1)
color = vec4(color.bgr, color.a);
gl_FragColor = color;
}
#endif