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);
#if defined(USE_OPENGL_21)
Renderer::shaderParameters videoParameters;
Renderer::postProcessingParams videoParameters;
unsigned int shaders {0};
if (Settings::getInstance()->getBool("MediaViewerVideoScanlines"))
shaders = Renderer::SHADER_SCANLINES;

View file

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

View file

@ -20,7 +20,6 @@ GuiComponent::GuiComponent()
: mWindow {Window::getInstance()}
, mParent {nullptr}
, mColor {0}
, mSaturation {1.0f}
, mColorShift {0}
, mColorShiftEnd {0}
, mColorOriginalValue {0}
@ -31,6 +30,8 @@ GuiComponent::GuiComponent()
, mRotationOrigin {0.5f, 0.5f}
, mSize {0.0f, 0.0f}
, mOpacity {1.0f}
, mSaturation {1.0f}
, mDim {1.0f}
, mThemeOpacity {1.0f}
, mRotation {0.0f}
, mScale {1.0f}
@ -191,6 +192,16 @@ void GuiComponent::setOpacity(float 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()
{
mTransform = Renderer::getIdentity();

View file

@ -194,12 +194,14 @@ public:
virtual void stopListScrolling() {}
virtual const float getOpacity() const { return mOpacity; }
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 getColorShift() const { return mColorShift; }
virtual float getLineSpacing() { return 0.0f; }
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)
{
mColorShift = color;
@ -286,7 +288,6 @@ protected:
std::string mThemeGameSelector;
unsigned int mColor;
float mSaturation;
unsigned int mColorShift;
unsigned int mColorShiftEnd;
unsigned int mColorOriginalValue;
@ -299,6 +300,8 @@ protected:
glm::vec2 mSize;
float mOpacity;
float mSaturation;
float mDim;
float mThemeOpacity;
float mRotation;
float mScale;

View file

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

View file

@ -3,7 +3,7 @@
// EmulationStation Desktop Edition
// LambdaAnimation.h
//
// Basic animation controls, to be used in lambda expressions.
// Custom animations, expressed as lambdas.
//
#ifndef ES_CORE_ANIMATIONS_LAMBDA_ANIMATION_H
@ -13,8 +13,6 @@
#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
{
public:

View file

@ -382,11 +382,11 @@ void ComponentList::render(const glm::mat4& parentTrans)
if (mOpacity == 1.0f) {
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::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);
}
@ -402,12 +402,12 @@ void ComponentList::render(const glm::mat4& parentTrans)
float y = 0;
for (unsigned int i = 0; i < mEntries.size(); ++i) {
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);
}
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();
}

View file

@ -487,14 +487,13 @@ void GIFAnimComponent::render(const glm::mat4& parentTrans)
for (int i = 0; i < 4; ++i)
vertices[i].pos = glm::round(vertices[i].pos);
#if defined(USE_OPENGL_21)
// Perform color space conversion from BGRA to RGBA.
vertices[0].opacity = mThemeOpacity;
vertices[0].shaders = Renderer::SHADER_BGRA_TO_RGBA;
#endif
vertices->saturation = mSaturation;
vertices->opacity = mOpacity * mThemeOpacity;
vertices->dim = mDim;
vertices->convertBGRAToRGBA = true;
// Render it.
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();
}
void ImageComponent::setDim(float dim)
{
// Set dim value.
mDim = dim;
}
void ImageComponent::updateVertices()
{
if (!mTexture)
@ -371,12 +377,11 @@ void ImageComponent::updateVertices()
void ImageComponent::updateColors()
{
const float opacity = (mOpacity * mThemeOpacity * (mFading ? mFadeOpacity : 1.0f));
const unsigned int color = Renderer::convertRGBAToABGR(
(mColorShift & 0xFFFFFF00) | static_cast<unsigned char>((mColorShift & 0xFF) * opacity));
const unsigned int colorEnd =
Renderer::convertRGBAToABGR((mColorShiftEnd & 0xFFFFFF00) |
static_cast<unsigned char>((mColorShiftEnd & 0xFF) * opacity));
const float opacity = (mOpacity * (mFading ? mFadeOpacity : 1.0f));
const unsigned int color {(mColorShift & 0xFFFFFF00) |
static_cast<unsigned char>((mColorShift & 0xFF) * opacity)};
const unsigned int colorEnd {(mColorShiftEnd & 0xFFFFFF00) |
static_cast<unsigned char>((mColorShiftEnd & 0xFF) * opacity)};
mVertices[0].col = color;
mVertices[1].col = mColorGradientHorizontal ? color : colorEnd;
@ -413,13 +418,11 @@ void ImageComponent::render(const glm::mat4& parentTrans)
else
fadeIn(mTexture->bind());
#if defined(USE_OPENGL_21)
if (mSaturation < 1.0) {
mVertices[0].shaders = Renderer::SHADER_DESATURATE;
mVertices[0].saturation = mSaturation;
}
#endif
Renderer::drawTriangleStrips(&mVertices[0], 4, trans);
mVertices->saturation = mSaturation;
mVertices->opacity = mThemeOpacity;
mVertices->dim = mDim;
Renderer::drawTriangleStrips(&mVertices[0], 4);
}
else {
if (!mTexture) {

View file

@ -73,6 +73,7 @@ public:
void setOpacity(float opacity) override;
void setSaturation(float saturation) override;
void setDim(float dim) override;
void setFlipX(bool flip); // Mirror on the X 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)
vertices[i].pos = glm::round(vertices[i].pos);
#if defined(USE_OPENGL_21)
// Perform color space conversion from BGRA to RGBA.
vertices[0].opacity = mThemeOpacity;
vertices[0].shaders = Renderer::SHADER_BGRA_TO_RGBA;
#endif
vertices->saturation = mSaturation;
vertices->opacity = mOpacity * mThemeOpacity;
vertices->dim = mDim;
vertices->convertBGRAToRGBA = true;
// Render it.
Renderer::drawTriangleStrips(&vertices[0], 4, trans);
Renderer::drawTriangleStrips(&vertices[0], 4);
}
}

View file

@ -35,14 +35,11 @@ NinePatchComponent::~NinePatchComponent()
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)
mVertices[i].col = edgeColor;
mVertices[i].col = mEdgeColor;
for (int i = 6 * 4; i < 6; ++i)
mVertices[(6 * 4) + i].col = centerColor;
mVertices[(6 * 4) + i].col = mCenterColor;
}
void NinePatchComponent::buildVertices()
@ -135,18 +132,9 @@ void NinePatchComponent::render(const glm::mat4& parentTrans)
if (mTexture && mVertices != nullptr) {
Renderer::setMatrix(trans);
if (mOpacity < 1.0f) {
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;
}
mVertices->opacity = mOpacity;
mTexture->bind();
Renderer::drawTriangleStrips(&mVertices[0], 6 * 9, trans);
Renderer::drawTriangleStrips(&mVertices[0], 6 * 9);
}
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 w {getSize().y * mValue * numStars};
const float fw {getSize().y * numStars};
const unsigned int color {Renderer::convertRGBAToABGR(mColorShift)};
// clang-format off
mVertices[0] = {{0.0f, 0.0f}, {0.0f, 1.0f}, color};
mVertices[1] = {{0.0f, h }, {0.0f, 0.0f}, color};
mVertices[2] = {{w, 0.0f}, {mValue * numStars, 1.0f}, color};
mVertices[3] = {{w, h }, {mValue * numStars, 0.0f}, color};
mVertices[0] = {{0.0f, 0.0f}, {0.0f, 1.0f}, mColorShift};
mVertices[1] = {{0.0f, h }, {0.0f, 0.0f}, mColorShift};
mVertices[2] = {{w, 0.0f}, {mValue * numStars, 1.0f}, mColorShift};
mVertices[3] = {{w, h }, {mValue * numStars, 0.0f}, mColorShift};
mVertices[4] = {{0.0f, 0.0f}, {0.0f, 1.0f}, color};
mVertices[5] = {{0.0f, h }, {0.0f, 0.0f}, color};
mVertices[6] = {{fw, 0.0f}, {numStars, 1.0f}, color};
mVertices[7] = {{fw, h }, {numStars, 0.0f}, color};
mVertices[4] = {{0.0f, 0.0f}, {0.0f, 1.0f}, mColorShift};
mVertices[5] = {{0.0f, h }, {0.0f, 0.0f}, mColorShift};
mVertices[6] = {{fw, 0.0f}, {numStars, 1.0f}, mColorShift};
mVertices[7] = {{fw, h }, {numStars, 0.0f}, mColorShift};
// clang-format on
}
void RatingComponent::updateColors()
{
const unsigned int color {Renderer::convertRGBAToABGR(mColorShift)};
for (int i = 0; i < 8; ++i)
mVertices[i].col = color;
mVertices[i].col = mColorShift;
}
void RatingComponent::render(const glm::mat4& parentTrans)
@ -165,9 +162,9 @@ void RatingComponent::render(const glm::mat4& parentTrans)
if (mUnfilledTexture->bind()) {
if (mUnfilledColor != mColorShift) {
const unsigned int color = Renderer::convertRGBAToABGR(mUnfilledColor);
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);

View file

@ -92,17 +92,26 @@ void TextComponent::setBackgroundColor(unsigned int color)
mBgColorOpacity = static_cast<float>(mBgColor & 0x000000FF) / 255.0f;
}
// Scale the 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);
float textBackgroundOpacity {opacity * mBgColorOpacity * mThemeOpacity};
float textBackgroundOpacity {opacity * mBgColorOpacity};
mBgColor = (mBgColor & 0xFFFFFF00) | static_cast<unsigned char>(textBackgroundOpacity * 255.0f);
onColorChanged();
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)
@ -152,11 +161,11 @@ void TextComponent::render(const glm::mat4& parentTrans)
return;
glm::mat4 trans {parentTrans * getTransform()};
if (mRenderBackground) {
Renderer::setMatrix(trans);
Renderer::drawRect(0.0f, 0.0f, mSize.x, mSize.y, mBgColor, mBgColor);
}
if (mRenderBackground)
Renderer::drawRect(0.0f, 0.0f, mSize.x, mSize.y, mBgColor, mBgColor, false,
mOpacity * mThemeOpacity, mDim);
if (mTextCache) {
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};
if (Settings::getInstance()->getBool("DebugText")) {
// Draw the "textbox" area, what we are aligned within.
Renderer::setMatrix(trans);
if (Settings::getInstance()->getBool("DebugText"))
Renderer::drawRect(0.0f, 0.0f, mSize.x, mSize.y, 0x0000FF33, 0x0000FF33);
}
trans = glm::translate(trans, off);
Renderer::setMatrix(trans);

View file

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

View file

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

View file

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

View file

@ -26,13 +26,14 @@ namespace Renderer
static std::stack<Rect> clipStack;
static SDL_Window* sdlWindow {nullptr};
static glm::mat4 mProjectionMatrix {};
static glm::mat4 mProjectionMatrixRotated {};
static int windowWidth {0};
static int windowHeight {0};
static int screenWidth {0};
static int screenHeight {0};
static int screenOffsetX {0};
static int screenOffsetY {0};
static int screenRotate {0};
static bool screenRotated {0};
static bool initialCursorState {1};
// Screen resolution modifiers relative to the 1920x1080 reference.
static float screenHeightModifier {0.0f};
@ -141,9 +142,7 @@ namespace Renderer
screenOffsetY = Settings::getInstance()->getInt("ScreenOffsetY") ?
Settings::getInstance()->getInt("ScreenOffsetY") :
0;
screenRotate = Settings::getInstance()->getInt("ScreenRotate") ?
Settings::getInstance()->getInt("ScreenRotate") :
0;
screenRotated = Settings::getInstance()->getInt("ScreenRotate") == 2;
// Prevent the application window from minimizing when switching windows (when launching
// games or when manually switching windows using the task switcher).
@ -265,17 +264,13 @@ namespace Renderer
swapBuffers();
#endif
#if defined(USE_OPENGL_21)
LOG(LogInfo) << "Loading shaders...";
std::vector<std::string> shaderFiles;
shaderFiles.push_back(":/shaders/glsl/desaturate.glsl");
shaderFiles.push_back(":/shaders/glsl/opacity.glsl");
shaderFiles.push_back(":/shaders/glsl/dim.glsl");
shaderFiles.push_back(":/shaders/glsl/core.glsl");
shaderFiles.push_back(":/shaders/glsl/blur_horizontal.glsl");
shaderFiles.push_back(":/shaders/glsl/blur_vertical.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) {
Shader* loadShader = new Shader();
@ -290,17 +285,14 @@ namespace Renderer
sShaderProgramVector.push_back(loadShader);
}
#endif
return true;
}
static void destroyWindow()
{
#if defined(USE_OPENGL_21)
for (auto it = sShaderProgramVector.cbegin(); it != sShaderProgramVector.cend(); ++it)
delete *it;
#endif
destroyContext();
SDL_DestroyWindow(sdlWindow);
@ -319,19 +311,6 @@ namespace Renderer
glm::mat4 projection {getIdentity()};
Rect viewport {0, 0, 0, 0};
switch (screenRotate) {
case 1: {
viewport.x = windowWidth - screenOffsetY - screenHeight;
viewport.y = screenOffsetX;
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(90.0f), {0.0f, 0.0f, 1.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;
@ -339,36 +318,15 @@ namespace Renderer
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 =
mProjectionMatrixRotated =
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),
mProjectionMatrix = glm::ortho(0.0f, static_cast<float>(screenWidth),
static_cast<float>(screenHeight), 0.0f, -1.0f, 1.0f);
break;
}
}
mProjectionMatrix = projection;
setViewport(viewport);
setProjection(projection);
// 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()),
@ -393,22 +351,12 @@ namespace Renderer
if (box.h == 0)
box.h = screenHeight - box.y;
switch (screenRotate) {
case 0:
box = Rect(screenOffsetX + box.x, screenOffsetY + box.y, box.w, box.h);
break;
case 1:
box = Rect(windowWidth - screenOffsetY - box.y - box.h, screenOffsetX + box.x,
box.h, box.w);
break;
case 2:
if (screenRotated) {
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;
}
else {
box = Rect(screenOffsetX + box.x, screenOffsetY + box.y, box.w, box.h);
}
// Make sure the box fits within clipStack.top(), and clip further accordingly.
@ -453,20 +401,18 @@ namespace Renderer
const float y,
const float w,
const float h,
const unsigned int _color,
const unsigned int _colorEnd,
const unsigned int color,
const unsigned int colorEnd,
bool horizontalGradient,
const float opacity,
const glm::mat4& trans,
const float dim,
const Blend::Factor srcBlendFactor,
const Blend::Factor dstBlendFactor)
{
const unsigned int rColor = convertRGBAToABGR(_color);
const unsigned int rColorEnd = convertRGBAToABGR(_colorEnd);
Vertex vertices[4];
float wL = w;
float hL = h;
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.
@ -476,44 +422,21 @@ namespace Renderer
hL = 1.0f;
// clang-format off
vertices[0] = {{x, y }, {0.0f, 0.0f}, rColor};
vertices[1] = {{x, y + hL}, {0.0f, 0.0f}, horizontalGradient ? rColor : rColorEnd};
vertices[2] = {{x + wL, y }, {0.0f, 0.0f}, horizontalGradient ? rColorEnd : rColor};
vertices[3] = {{x + wL, y + hL}, {0.0f, 0.0f}, rColorEnd};
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].pos = glm::round(vertices[i].pos);
if (opacity < 1.0) {
vertices[0].shaders = SHADER_OPACITY;
vertices[0].opacity = opacity;
}
else {
vertices->opacity = opacity;
vertices->dim = dim;
bindTexture(0);
}
drawTriangleStrips(vertices, 4, trans, srcBlendFactor, dstBlendFactor);
}
const unsigned int convertRGBAToABGR(const unsigned int _color)
{
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;
drawTriangleStrips(vertices, 4, srcBlendFactor, dstBlendFactor);
}
Shader* getShaderProgram(unsigned int shaderID)
@ -533,7 +456,14 @@ namespace Renderer
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; }
const float getWindowWidth() { return static_cast<float>(windowWidth); }
const float getWindowHeight() { return static_cast<float>(windowHeight); }
@ -541,7 +471,7 @@ namespace Renderer
const float getScreenHeight() { return static_cast<float>(screenHeight); }
const float getScreenOffsetX() { return static_cast<float>(screenOffsetX); }
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 getScreenHeightModifier() { return screenHeightModifier; }
const float getScreenAspectRatio() { return screenAspectRatio; }

View file

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

View file

@ -19,7 +19,9 @@
namespace Renderer
{
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)
{
@ -46,7 +48,7 @@ namespace Renderer
switch (_type) {
case Texture::RGBA: { return GL_RGBA; } 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; }
}
// clang-format on
@ -142,6 +144,14 @@ namespace Renderer
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};
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_COLOR_ARRAY));
// This is the framebuffer that will be used for shader rendering.
GL_CHECK_ERROR(glGenFramebuffers(1, &shaderFBO));
// These framebuffers are used for the shader post processing.
GL_CHECK_ERROR(glGenFramebuffers(1, &shaderFBO1));
GL_CHECK_ERROR(glGenFramebuffers(1, &shaderFBO2));
return true;
}
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);
sdlContext = nullptr;
}
@ -195,8 +210,23 @@ namespace Renderer
linearMagnify ? static_cast<GLfloat>(GL_LINEAR) :
static_cast<GLfloat>(GL_NEAREST)));
GL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, textureType, width, height, 0, textureType,
GL_UNSIGNED_BYTE, data));
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(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;
}
@ -217,8 +247,27 @@ namespace Renderer
const GLenum textureType = convertTextureType(type);
GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture));
// 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));
}
@ -247,13 +296,11 @@ namespace Renderer
void drawTriangleStrips(const Vertex* vertices,
const unsigned int numVertices,
const glm::mat4& trans,
const Blend::Factor srcBlendFactor,
const Blend::Factor dstBlendFactor,
const shaderParameters& parameters)
const Blend::Factor dstBlendFactor)
{
const float width = vertices[3].pos[0];
const float height = vertices[3].pos[1];
const float width {vertices[3].pos[0]};
const float height {vertices[3].pos[1]};
GL_CHECK_ERROR(glVertexPointer(2, GL_FLOAT, sizeof(Vertex), &vertices[0].pos));
GL_CHECK_ERROR(glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &vertices[0].tex));
@ -262,138 +309,78 @@ namespace Renderer
GL_CHECK_ERROR(
glBlendFunc(convertBlendFactor(srcBlendFactor), convertBlendFactor(dstBlendFactor)));
#if defined(USE_OPENGL_21)
if (vertices[0].shaders == 0) {
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices));
}
else {
// If saturation is set below the maximum (default) value, run the
// desaturation shader.
if (vertices->saturation < 1.0f || parameters.fragmentSaturation < 1.0f) {
Shader* runShader = getShaderProgram(SHADER_DESATURATE);
// Only try to use the shader if it has been loaded properly.
if (vertices->shaders == 0 || vertices->shaders & SHADER_CORE) {
Shader* runShader = getShaderProgram(SHADER_CORE);
if (runShader) {
runShader->activateShaders();
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * trans);
runShader->setModelViewProjectionMatrix(mTrans);
runShader->setOpacity(vertices->opacity);
runShader->setSaturation(vertices->saturation);
runShader->setDim(vertices->dim);
runShader->setBGRAToRGBA(vertices->convertBGRAToRGBA);
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices));
runShader->deactivateShaders();
}
}
if (vertices->shaders & SHADER_OPACITY) {
Shader* runShader = getShaderProgram(SHADER_OPACITY);
if (runShader) {
runShader->activateShaders();
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * trans);
vertices->opacity < 1.0f ? runShader->setOpacity(vertices->opacity) :
runShader->setOpacity(parameters.fragmentOpacity);
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices));
runShader->deactivateShaders();
}
}
// Check if any other shaders are set to be used and if so, run them.
if (vertices->shaders & SHADER_DIM) {
Shader* runShader = getShaderProgram(SHADER_DIM);
if (runShader) {
runShader->activateShaders();
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * trans);
runShader->setDimValue(parameters.fragmentDimValue);
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices));
runShader->deactivateShaders();
}
}
if (vertices->shaders & SHADER_BLUR_HORIZONTAL) {
else if (vertices->shaders & SHADER_BLUR_HORIZONTAL) {
Shader* runShader = getShaderProgram(SHADER_BLUR_HORIZONTAL);
if (runShader) {
runShader->activateShaders();
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * trans);
runShader->setModelViewProjectionMatrix(mTrans);
runShader->setTextureSize({width, height});
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices));
runShader->deactivateShaders();
}
return;
}
if (vertices->shaders & SHADER_BLUR_VERTICAL) {
else if (vertices->shaders & SHADER_BLUR_VERTICAL) {
Shader* runShader = getShaderProgram(SHADER_BLUR_VERTICAL);
if (runShader) {
runShader->activateShaders();
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * trans);
runShader->setModelViewProjectionMatrix(mTrans);
runShader->setTextureSize({width, height});
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices));
runShader->deactivateShaders();
}
return;
}
if (vertices->shaders & SHADER_SCANLINES) {
Shader* runShader = getShaderProgram(SHADER_SCANLINES);
float shaderWidth = width * 1.2f;
else 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;
float screenHeightModifier {getScreenHeightModifier()};
float relativeHeight {height / getScreenHeight()};
float shaderHeight {0.0f};
if (relativeHeight == 1.0f) {
// Full screen.
float modifier = 1.30f - (0.1f * screenHeightModifier);
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);
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->setModelViewProjectionMatrix(mTrans);
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)
{
glm::mat4 newMatrix {matrix};
newMatrix[3] = glm::round(newMatrix[3]);
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));
mTrans = matrix;
mTrans[3] = glm::round(mTrans[3]);
mTrans = getProjectionMatrix() * mTrans;
}
void setScissor(const Rect& scissor)
@ -450,31 +437,31 @@ namespace Renderer
}
void shaderPostprocessing(unsigned int shaders,
const Renderer::shaderParameters& parameters,
const Renderer::postProcessingParams& parameters,
unsigned char* textureRGBA)
{
Vertex vertices[4];
std::vector<unsigned int> shaderList;
GLuint width {static_cast<GLuint>(getScreenWidth())};
GLuint height {static_cast<GLuint>(getScreenHeight())};
float widthf {static_cast<float>(width)};
float heightf {static_cast<float>(height)};
float widthf {getScreenWidth()};
float heightf {getScreenHeight()};
GLuint width {static_cast<GLuint>(widthf)};
GLuint height {static_cast<GLuint>(heightf)};
// Set vertex positions and texture coordinates to full screen as all
// postprocessing is applied to the complete screen area.
// clang-format off
vertices[0] = { { 0.0f , 0.0f }, { 0.0f, 1.0f }, 0 };
vertices[1] = { { 0.0f , heightf }, { 0.0f, 0.0f }, 0 };
vertices[2] = { { widthf, 0.0f }, { 1.0f, 1.0f }, 0 };
vertices[3] = { { widthf, heightf }, { 1.0f, 0.0f }, 0 };
vertices[0] = {{0.0f, 0.0f }, {0.0f, 1.0f}, 0xFFFFFFFF};
vertices[1] = {{0.0f, heightf}, {0.0f, 0.0f}, 0xFFFFFFFF};
vertices[2] = {{widthf, 0.0f }, {1.0f, 1.0f}, 0xFFFFFFFF};
vertices[3] = {{widthf, heightf}, {1.0f, 0.0f}, 0xFFFFFFFF};
// clang-format on
if (shaders & Renderer::SHADER_DESATURATE)
shaderList.push_back(Renderer::SHADER_DESATURATE);
if (shaders & Renderer::SHADER_OPACITY)
shaderList.push_back(Renderer::SHADER_OPACITY);
if (shaders & Renderer::SHADER_DIM)
shaderList.push_back(Renderer::SHADER_DIM);
vertices->opacity = parameters.opacity;
vertices->saturation = parameters.saturation;
vertices->dim = parameters.dim;
shaderList.emplace_back(Renderer::SHADER_CORE);
if (shaders & Renderer::SHADER_BLUR_HORIZONTAL)
shaderList.push_back(Renderer::SHADER_BLUR_HORIZONTAL);
if (shaders & Renderer::SHADER_BLUR_VERTICAL)
@ -482,17 +469,30 @@ namespace Renderer
if (shaders & Renderer::SHADER_SCANLINES)
shaderList.push_back(Renderer::SHADER_SCANLINES);
if (parameters.fragmentSaturation < 1.0)
vertices[0].saturation = parameters.fragmentSaturation;
setMatrix(getIdentity());
GLuint screenTexture = createTexture(Texture::RGBA, Texture::RGBA, false, false, false,
width, height, nullptr);
bindTexture(postProcTexture1);
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) {
vertices[0].shaders = shaderList[i];
vertices->shaders = shaderList[i];
int shaderPasses = 1;
// For the blur shaders there is an optional variable to set the number of passes
// to execute, which proportionally affects the blur amount.
@ -502,42 +502,55 @@ namespace Renderer
}
for (int p = 0; p < shaderPasses; ++p) {
GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, shaderFBO));
// Attach the texture to the shader framebuffer.
GL_CHECK_ERROR(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, screenTexture, 0));
// Blit the screen contents to screenTexture.
GL_CHECK_ERROR(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height,
GL_COLOR_BUFFER_BIT, GL_NEAREST));
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
// set the projection to a non-rotated state before making the last drawcall
// as the image would otherwise get rendered upside down.
if (getScreenRotated() && drawCalls % 2 == 0) {
mTrans = getIdentity();
mTrans[3] = glm::round(mTrans[3]);
mTrans = getProjectionMatrixNormal() * mTrans;
}
// If it's the last shader pass, then render directly to the default framebuffer
// to avoid having to make an expensive glBlitFramebuffer() call.
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, getIdentity(), Blend::SRC_ALPHA,
Blend::ONE_MINUS_SRC_ALPHA, parameters);
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 {
bindTexture(postProcTexture1);
GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, shaderFBO1));
GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, shaderFBO2));
firstFBO = true;
}
}
}
// 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));
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));
}
else {
// Blit the resulting postprocessed texture back to the primary framebuffer.
GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, shaderFBO));
GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0));
GL_CHECK_ERROR(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height,
GL_COLOR_BUFFER_BIT, GL_NEAREST));
}
}
}
GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, 0));
destroyTexture(screenTexture);
}
} // namespace Renderer

View file

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

View file

@ -49,10 +49,10 @@ namespace Renderer
void setTextureSize(std::array<GLfloat, 2> shaderVec2);
void setTextureCoordinates(std::array<GLfloat, 4> shaderVec4);
void setColor(std::array<GLfloat, 4> shaderVec4);
void setSaturation(GLfloat saturation);
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.
void activateShaders();
// Sets the shader program to 0 which reverts to the fixed function pipeline.
@ -71,10 +71,10 @@ namespace Renderer
GLint shaderMVPMatrix;
GLint shaderTextureSize;
GLint shaderTextureCoord;
GLint shaderColor;
GLint shaderSaturation;
GLint shaderOpacity;
GLint shaderDimValue;
GLint shaderSaturation;
GLint shaderDim;
GLint shaderBGRAToRGBA;
};
} // namespace Renderer

View file

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

View file

@ -206,6 +206,8 @@ public:
} metrics;
void setColor(unsigned int color);
void setOpacity(float opacity);
void setDim(float dim);
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