From 373b90af003fe5f9b765f2a1ec8c0f803bd808e2 Mon Sep 17 00:00:00 2001 From: Jonas Kulla Date: Tue, 23 Dec 2014 18:56:00 +0100 Subject: [PATCH] Graphics: Optimize Viewport effect rendering Using the kitchen sink plane shader for viewport effects, even if only a small part of them are active, incurs great performance loss on mobile, so split the rendering into multiple optional passes which additionally use the blending hardware for faster mixing (lerping). Also, don't mirror the PingPong textures if the viewport effect covers the entire screen area anyway. --- CMakeLists.txt | 3 ++ mkxp.pro | 3 ++ shader/flatColor.frag | 7 +++ shader/gray.frag | 19 +++++++ shader/minimal.vert | 8 +++ src/etc-internal.h | 13 +++++ src/graphics.cpp | 116 ++++++++++++++++++++++++++++++++++-------- src/shader.cpp | 33 ++++++++++++ src/shader.h | 24 +++++++++ 9 files changed, 206 insertions(+), 20 deletions(-) create mode 100644 shader/flatColor.frag create mode 100644 shader/gray.frag create mode 100644 shader/minimal.vert diff --git a/CMakeLists.txt b/CMakeLists.txt index 04f3c06..d875826 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -216,12 +216,15 @@ set(EMBEDDED_INPUT shader/hue.frag shader/sprite.frag shader/plane.frag + shader/gray.frag shader/bitmapBlit.frag + shader/flatColor.frag shader/simple.frag shader/simpleColor.frag shader/simpleAlpha.frag shader/simpleAlphaUni.frag shader/flashMap.frag + shader/minimal.vert shader/simple.vert shader/simpleColor.vert shader/sprite.vert diff --git a/mkxp.pro b/mkxp.pro index f223482..558727a 100644 --- a/mkxp.pro +++ b/mkxp.pro @@ -189,12 +189,15 @@ EMBED = \ shader/hue.frag \ shader/sprite.frag \ shader/plane.frag \ + shader/gray.frag \ shader/bitmapBlit.frag \ + shader/flatColor.frag \ shader/simple.frag \ shader/simpleColor.frag \ shader/simpleAlpha.frag \ shader/simpleAlphaUni.frag \ shader/flashMap.frag \ + shader/minimal.vert \ shader/simple.vert \ shader/simpleColor.vert \ shader/sprite.vert \ diff --git a/shader/flatColor.frag b/shader/flatColor.frag new file mode 100644 index 0000000..985f5dc --- /dev/null +++ b/shader/flatColor.frag @@ -0,0 +1,7 @@ + +uniform lowp vec4 color; + +void main() +{ + gl_FragColor = color; +} diff --git a/shader/gray.frag b/shader/gray.frag new file mode 100644 index 0000000..c4cb982 --- /dev/null +++ b/shader/gray.frag @@ -0,0 +1,19 @@ + +uniform sampler2D texture; +uniform lowp float gray; + +varying vec2 v_texCoord; + +const vec3 lumaF = vec3(.299, .587, .114); + +void main() +{ + /* Sample source color */ + vec4 frag = texture2D(texture, v_texCoord); + + /* Apply gray */ + float luma = dot(frag.rgb, lumaF); + frag.rgb = mix(frag.rgb, vec3(luma), gray); + + gl_FragColor = frag; +} diff --git a/shader/minimal.vert b/shader/minimal.vert new file mode 100644 index 0000000..cccadc1 --- /dev/null +++ b/shader/minimal.vert @@ -0,0 +1,8 @@ + +uniform mat4 projMat; +attribute vec2 position; + +void main() +{ + gl_Position = projMat * vec4(position, 0, 1); +} diff --git a/src/etc-internal.h b/src/etc-internal.h index 2d3e547..0b4624c 100644 --- a/src/etc-internal.h +++ b/src/etc-internal.h @@ -60,6 +60,11 @@ struct Vec4 { return (x == other.x && y == other.y && z == other.z && w == other.w); } + + bool xyzHasEffect() const + { + return (x != 0.0 || y != 0.0 || z != 0.0); + } }; struct Vec2i @@ -129,6 +134,14 @@ struct IntRect : SDL_Rect SDL_Rect r = { x, y, w, h }; return r; } + + bool encloses(const IntRect &o) const + { + return (x <= o.x && + y <= o.y && + x+w >= o.x+o.w && + y+h >= o.y+o.h); + } }; struct StaticRect { float x, y, w, h; }; diff --git a/src/graphics.cpp b/src/graphics.cpp index ecf850f..5537b03 100644 --- a/src/graphics.cpp +++ b/src/graphics.cpp @@ -171,36 +171,112 @@ public: void requestViewportRender(Vec4 &c, Vec4 &f, Vec4 &t) { - pp.swapRender(); + const IntRect &viewpRect = glState.scissorBox.get(); + const IntRect &screenRect = geometry.rect; - /* Scissor test _does_ affect FBO blit operations, - * and since we're inside the draw cycle, it will - * be turned on, so turn it off temporarily */ - glState.scissorTest.pushSet(false); + if (t.w != 0.0) + { + pp.swapRender(); - GLMeta::blitBegin(pp.frontBuffer()); - GLMeta::blitSource(pp.backBuffer()); - GLMeta::blitRectangle(geometry.rect, Vec2i()); - GLMeta::blitEnd(); + if (!viewpRect.encloses(screenRect)) + { + /* Scissor test _does_ affect FBO blit operations, + * and since we're inside the draw cycle, it will + * be turned on, so turn it off temporarily */ + glState.scissorTest.pushSet(false); - glState.scissorTest.pop(); + GLMeta::blitBegin(pp.frontBuffer()); + GLMeta::blitSource(pp.backBuffer()); + GLMeta::blitRectangle(geometry.rect, Vec2i()); + GLMeta::blitEnd(); - PlaneShader &shader = shState->shaders().plane; + glState.scissorTest.pop(); + } + + GrayShader &shader = shState->shaders().gray; + shader.bind(); + shader.setGray(t.w); + shader.applyViewportProj(); + shader.setTexSize(screenRect.size()); + + TEX::bind(pp.backBuffer().tex); + + glState.blend.pushSet(false); + screenQuad.draw(); + glState.blend.pop(); + } + + bool toneEffect = t.xyzHasEffect(); + bool colorEffect = c.xyzHasEffect(); + bool flashEffect = f.xyzHasEffect(); + + if (!toneEffect && !colorEffect && !flashEffect) + return; + + FlatColorShader &shader = shState->shaders().flatColor; shader.bind(); - shader.setColor(c); - shader.setFlash(f); - shader.setTone(t); - shader.setOpacity(1.0); shader.applyViewportProj(); - shader.setTexSize(geometry.rect.size()); - TEX::bind(pp.backBuffer().tex); + /* Apply tone */ + if (toneEffect) + { + /* First split up additive / substractive components */ + Vec4 add, sub; - glState.blend.pushSet(false); + if (t.x > 0) + add.x = t.x; + if (t.y > 0) + add.y = t.y; + if (t.z > 0) + add.z = t.z; - screenQuad.draw(); + if (t.x < 0) + sub.x = -t.x; + if (t.y < 0) + sub.y = -t.y; + if (t.z < 0) + sub.z = -t.z; - glState.blend.pop(); + /* Then apply them using hardware blending */ + gl.BlendFuncSeparate(GL_ONE, GL_ONE, GL_ZERO, GL_ONE); + + if (add.xyzHasEffect()) + { + gl.BlendEquation(GL_FUNC_ADD); + shader.setColor(add); + + screenQuad.draw(); + } + + if (sub.xyzHasEffect()) + { + gl.BlendEquation(GL_FUNC_REVERSE_SUBTRACT); + shader.setColor(sub); + + screenQuad.draw(); + } + } + + if (colorEffect || flashEffect) + { + gl.BlendEquation(GL_FUNC_ADD); + gl.BlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, + GL_ZERO, GL_ONE); + } + + if (colorEffect) + { + shader.setColor(c); + screenQuad.draw(); + } + + if (flashEffect) + { + shader.setColor(f); + screenQuad.draw(); + } + + glState.blendMode.refresh(); } void setBrightness(float norm) diff --git a/src/shader.cpp b/src/shader.cpp index 4f891f2..3b22483 100644 --- a/src/shader.cpp +++ b/src/shader.cpp @@ -35,11 +35,14 @@ #include "transSimple.frag.xxd" #include "bitmapBlit.frag.xxd" #include "plane.frag.xxd" +#include "gray.frag.xxd" +#include "flatColor.frag.xxd" #include "simple.frag.xxd" #include "simpleColor.frag.xxd" #include "simpleAlpha.frag.xxd" #include "simpleAlphaUni.frag.xxd" #include "flashMap.frag.xxd" +#include "minimal.vert.xxd" #include "simple.vert.xxd" #include "simpleColor.vert.xxd" #include "sprite.vert.xxd" @@ -269,6 +272,21 @@ void ShaderBase::setTranslation(const Vec2i &value) } +FlatColorShader::FlatColorShader() +{ + INIT_SHADER(minimal, flatColor, FlatColorShader); + + ShaderBase::init(); + + GET_U(color); +} + +void FlatColorShader::setColor(const Vec4 &value) +{ + setVec4Uniform(u_color, value); +} + + SimpleShader::SimpleShader() { INIT_SHADER(simple, simple, SimpleShader); @@ -480,6 +498,21 @@ void PlaneShader::setOpacity(float value) } +GrayShader::GrayShader() +{ + INIT_SHADER(simple, gray, GrayShader); + + ShaderBase::init(); + + GET_U(gray); +} + +void GrayShader::setGray(float value) +{ + gl.Uniform1f(u_gray, value); +} + + TilemapShader::TilemapShader() { INIT_SHADER(tilemap, simple, TilemapShader); diff --git a/src/shader.h b/src/shader.h index 96a6e3b..323b10a 100644 --- a/src/shader.h +++ b/src/shader.h @@ -87,6 +87,17 @@ protected: GLint u_texSizeInv, u_translation; }; +class FlatColorShader : public ShaderBase +{ +public: + FlatColorShader(); + + void setColor(const Vec4 &value); + +private: + GLint u_color; +}; + class SimpleShader : public ShaderBase { public: @@ -191,6 +202,17 @@ private: GLint u_tone, u_color, u_flash, u_opacity; }; +class GrayShader : public ShaderBase +{ +public: + GrayShader(); + + void setGray(float value); + +private: + GLint u_gray; +}; + class TilemapShader : public ShaderBase { public: @@ -285,6 +307,7 @@ private: /* Global object containing all available shaders */ struct ShaderSet { + FlatColorShader flatColor; SimpleShader simple; SimpleColorShader simpleColor; SimpleAlphaShader simpleAlpha; @@ -292,6 +315,7 @@ struct ShaderSet AlphaSpriteShader alphaSprite; SpriteShader sprite; PlaneShader plane; + GrayShader gray; TilemapShader tilemap; FlashMapShader flashMap; TransShader trans;