From 94c1271761c71e9d674257f97efb2784a683a1cf Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 24 Aug 2014 15:26:38 -0700 Subject: [PATCH] Add an experiment with using a texture for tests. Using an option for now so it's easy to test, if it works well we can maybe remove the option. --- CMakeLists.txt | 2 + Core/Config.cpp | 1 + Core/Config.h | 1 + GPU/GLES/FragmentShaderGenerator.cpp | 133 ++++++++++++--------- GPU/GLES/FragmentTestCache.cpp | 167 +++++++++++++++++++++++++++ GPU/GLES/FragmentTestCache.h | 83 +++++++++++++ GPU/GLES/GLES_GPU.cpp | 5 + GPU/GLES/GLES_GPU.h | 2 + GPU/GLES/ShaderManager.cpp | 2 + GPU/GLES/ShaderManager.h | 1 + GPU/GLES/StateMapping.cpp | 4 + GPU/GLES/TransformPipeline.cpp | 1 + GPU/GLES/TransformPipeline.h | 5 + GPU/GPU.vcxproj | 2 + GPU/GPU.vcxproj.filters | 6 + Qt/GPU.pro | 1 + android/jni/Android.mk | 1 + 17 files changed, 362 insertions(+), 55 deletions(-) create mode 100644 GPU/GLES/FragmentTestCache.cpp create mode 100644 GPU/GLES/FragmentTestCache.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 95d9464074..d74aa541ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1327,6 +1327,8 @@ add_library(GPU OBJECT GPU/GLES/GLES_GPU.h GPU/GLES/FragmentShaderGenerator.cpp GPU/GLES/FragmentShaderGenerator.h + GPU/GLES/FragmentTestCache.cpp + GPU/GLES/FragmentTestCache.h GPU/GLES/Framebuffer.cpp GPU/GLES/Framebuffer.h GPU/GLES/ShaderManager.cpp diff --git a/Core/Config.cpp b/Core/Config.cpp index 15f8352baa..3e215b285d 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -441,6 +441,7 @@ static ConfigSetting graphicsSettings[] = { ReportedConfigSetting("MemBlockTransferGPU", &g_Config.bBlockTransferGPU, true), ReportedConfigSetting("DisableSlowFramebufEffects", &g_Config.bDisableSlowFramebufEffects, false), + ReportedConfigSetting("FragmentTestCache", &g_Config.bFragmentTestCache, true), ConfigSetting(false), }; diff --git a/Core/Config.h b/Core/Config.h index 216886e42c..b6a543669a 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -161,6 +161,7 @@ public: bool bAlphaMaskHack; bool bBlockTransferGPU; bool bDisableSlowFramebufEffects; + bool bFragmentTestCache; int iSplineBezierQuality; // 0 = low , 1 = Intermediate , 2 = High std::string sPostShaderName; // Off for off. diff --git a/GPU/GLES/FragmentShaderGenerator.cpp b/GPU/GLES/FragmentShaderGenerator.cpp index a07266fce3..72481830e6 100644 --- a/GPU/GLES/FragmentShaderGenerator.cpp +++ b/GPU/GLES/FragmentShaderGenerator.cpp @@ -567,9 +567,13 @@ void GenerateFragmentShader(char *buffer) { } if (enableAlphaTest || enableColorTest) { - WRITE(p, "uniform vec4 u_alphacolorref;\n"); - if (bitwiseOps && (enableColorTest || !alphaTestAgainstZero)) { - WRITE(p, "uniform ivec4 u_alphacolormask;\n"); + if (g_Config.bFragmentTestCache) { + WRITE(p, "uniform sampler2D testtex;\n"); + } else { + WRITE(p, "uniform vec4 u_alphacolorref;\n"); + if (bitwiseOps && (enableColorTest || !alphaTestAgainstZero)) { + WRITE(p, "uniform ivec4 u_alphacolormask;\n"); + } } } if (stencilToAlpha && ReplaceAlphaWithStencilType() == STENCIL_VALUE_UNIFORM) { @@ -592,22 +596,24 @@ void GenerateFragmentShader(char *buffer) { WRITE(p, "%s mediump vec2 v_texcoord;\n", varying); } - if (enableAlphaTest && !alphaTestAgainstZero) { - if (bitwiseOps) { - WRITE(p, "int roundAndScaleTo255i(in float x) { return int(floor(x * 255.0 + 0.5)); }\n"); - } else if (gl_extensions.gpuVendor == GPU_VENDOR_POWERVR) { - WRITE(p, "float roundTo255thf(in mediump float x) { mediump float y = x + (0.5/255.0); return y - fract(y * 255.0) * (1.0 / 255.0); }\n"); - } else { - WRITE(p, "float roundAndScaleTo255f(in float x) { return floor(x * 255.0 + 0.5); }\n"); + if (!g_Config.bFragmentTestCache) { + if (enableAlphaTest && !alphaTestAgainstZero) { + if (bitwiseOps) { + WRITE(p, "int roundAndScaleTo255i(in float x) { return int(floor(x * 255.0 + 0.5)); }\n"); + } else if (gl_extensions.gpuVendor == GPU_VENDOR_POWERVR) { + WRITE(p, "float roundTo255thf(in mediump float x) { mediump float y = x + (0.5/255.0); return y - fract(y * 255.0) * (1.0 / 255.0); }\n"); + } else { + WRITE(p, "float roundAndScaleTo255f(in float x) { return floor(x * 255.0 + 0.5); }\n"); + } } - } - if (enableColorTest) { - if (bitwiseOps) { - WRITE(p, "ivec3 roundAndScaleTo255iv(in vec3 x) { return ivec3(floor(x * 255.0 + 0.5)); }\n"); - } else if (gl_extensions.gpuVendor == GPU_VENDOR_POWERVR) { - WRITE(p, "vec3 roundTo255thv(in vec3 x) { vec3 y = x + (0.5/255.0); return y - fract(y * 255.0) * (1.0 / 255.0); }\n"); - } else { - WRITE(p, "vec3 roundAndScaleTo255v(in vec3 x) { return floor(x * 255.0 + 0.5); }\n"); + if (enableColorTest) { + if (bitwiseOps) { + WRITE(p, "ivec3 roundAndScaleTo255iv(in vec3 x) { return ivec3(floor(x * 255.0 + 0.5)); }\n"); + } else if (gl_extensions.gpuVendor == GPU_VENDOR_POWERVR) { + WRITE(p, "vec3 roundTo255thv(in vec3 x) { vec3 y = x + (0.5/255.0); return y - fract(y * 255.0) * (1.0 / 255.0); }\n"); + } else { + WRITE(p, "vec3 roundAndScaleTo255v(in vec3 x) { return floor(x * 255.0 + 0.5); }\n"); + } } } @@ -748,51 +754,68 @@ void GenerateFragmentShader(char *buffer) { } if (enableAlphaTest) { - GEComparison alphaTestFunc = gstate.getAlphaTestFunction(); - const char *alphaTestFuncs[] = { "#", "#", " != ", " == ", " >= ", " > ", " <= ", " < " }; - if (alphaTestFuncs[alphaTestFunc][0] != '#') { - if (alphaTestAgainstZero) { - // When testing against 0 (extremely common), we can avoid some math. - // 0.002 is approximately half of 1.0 / 255.0. - if (alphaTestFunc == GE_COMP_NOTEQUAL || alphaTestFunc == GE_COMP_GREATER) { - WRITE(p, " if (v.a < 0.002) discard;\n"); - } else { - // Anything else is a test for == 0. Happens sometimes, actually... - WRITE(p, " if (v.a > 0.002) discard;\n"); - } - } else if (bitwiseOps) { - WRITE(p, " if ((roundAndScaleTo255i(v.a) & u_alphacolormask.a) %s int(u_alphacolorref.a)) discard;\n", alphaTestFuncs[alphaTestFunc]); - } else if (gl_extensions.gpuVendor == GPU_VENDOR_POWERVR) { - // Work around bad PVR driver problem where equality check + discard just doesn't work. - if (alphaTestFunc != GE_COMP_NOTEQUAL) - WRITE(p, " if (roundTo255thf(v.a) %s u_alphacolorref.a) discard;\n", alphaTestFuncs[alphaTestFunc]); + if (alphaTestAgainstZero) { + GEComparison alphaTestFunc = gstate.getAlphaTestFunction(); + // When testing against 0 (extremely common), we can avoid some math. + // 0.002 is approximately half of 1.0 / 255.0. + if (alphaTestFunc == GE_COMP_NOTEQUAL || alphaTestFunc == GE_COMP_GREATER) { + WRITE(p, " if (v.a < 0.002) discard;\n"); + } else if (alphaTestFunc != GE_COMP_NEVER) { + // Anything else is a test for == 0. Happens sometimes, actually... + WRITE(p, " if (v.a > 0.002) discard;\n"); } else { - WRITE(p, " if (roundAndScaleTo255f(v.a) %s u_alphacolorref.a) discard;\n", alphaTestFuncs[alphaTestFunc]); + // NEVER has been logged as used by games, although it makes little sense - statically failing. + // Maybe we could discard the drawcall, but it's pretty rare. Let's just statically discard here. + WRITE(p, " discard;\n"); } + } else if (g_Config.bFragmentTestCache) { + WRITE(p, " float aResult = %s(testtex, vec2(v.a, 0)).a;\n", texture); + WRITE(p, " if (aResult < 0.5) discard;\n", texture); } else { - // NEVER has been logged as used by games, although it makes little sense - statically failing. - // Maybe we could discard the drawcall, but it's pretty rare. Let's just statically discard here. - WRITE(p, " discard;\n"); + GEComparison alphaTestFunc = gstate.getAlphaTestFunction(); + const char *alphaTestFuncs[] = { "#", "#", " != ", " == ", " >= ", " > ", " <= ", " < " }; + if (alphaTestFuncs[alphaTestFunc][0] != '#') { + if (bitwiseOps) { + WRITE(p, " if ((roundAndScaleTo255i(v.a) & u_alphacolormask.a) %s int(u_alphacolorref.a)) discard;\n", alphaTestFuncs[alphaTestFunc]); + } else if (gl_extensions.gpuVendor == GPU_VENDOR_POWERVR) { + // Work around bad PVR driver problem where equality check + discard just doesn't work. + if (alphaTestFunc != GE_COMP_NOTEQUAL) { + WRITE(p, " if (roundTo255thf(v.a) %s u_alphacolorref.a) discard;\n", alphaTestFuncs[alphaTestFunc]); + } + } else { + WRITE(p, " if (roundAndScaleTo255f(v.a) %s u_alphacolorref.a) discard;\n", alphaTestFuncs[alphaTestFunc]); + } + } else { + // This means NEVER. See above. + WRITE(p, " discard;\n"); + } } } if (enableColorTest) { - GEComparison colorTestFunc = gstate.getColorTestFunction(); - const char *colorTestFuncs[] = { "#", "#", " != ", " == " }; - if (colorTestFuncs[colorTestFunc][0] != '#') { - if (bitwiseOps) { - // Apparently GLES3 does not support vector bitwise ops. - WRITE(p, " ivec3 v_scaled = roundAndScaleTo255iv(v.rgb);\n"); - const char *maskedFragColor = "ivec3(v_scaled.r & u_alphacolormask.r, v_scaled.g & u_alphacolormask.g, v_scaled.b & u_alphacolormask.b)"; - const char *maskedColorRef = "ivec3(int(u_alphacolorref.r) & u_alphacolormask.r, int(u_alphacolorref.g) & u_alphacolormask.g, int(u_alphacolorref.b) & u_alphacolormask.b)"; - WRITE(p, " if (%s %s %s) discard;\n", maskedFragColor, colorTestFuncs[colorTestFunc], maskedColorRef); - } else if (gl_extensions.gpuVendor == GPU_VENDOR_POWERVR) { - WRITE(p, " if (roundTo255thv(v.rgb) %s u_alphacolorref.rgb) discard;\n", colorTestFuncs[colorTestFunc]); - } else { - WRITE(p, " if (roundAndScaleTo255v(v.rgb) %s u_alphacolorref.rgb) discard;\n", colorTestFuncs[colorTestFunc]); - } + if (g_Config.bFragmentTestCache) { + WRITE(p, " float rResult = %s(testtex, vec2(v.r, 0)).r;\n", texture); + WRITE(p, " float gResult = %s(testtex, vec2(v.g, 0)).g;\n", texture); + WRITE(p, " float bResult = %s(testtex, vec2(v.b, 0)).b;\n", texture); + WRITE(p, " if (rResult < 0.5 || gResult < 0.5 || bResult < 0.5) discard;\n", texture); } else { - WRITE(p, " discard;\n"); + GEComparison colorTestFunc = gstate.getColorTestFunction(); + const char *colorTestFuncs[] = { "#", "#", " != ", " == " }; + if (colorTestFuncs[colorTestFunc][0] != '#') { + if (bitwiseOps) { + // Apparently GLES3 does not support vector bitwise ops. + WRITE(p, " ivec3 v_scaled = roundAndScaleTo255iv(v.rgb);\n"); + const char *maskedFragColor = "ivec3(v_scaled.r & u_alphacolormask.r, v_scaled.g & u_alphacolormask.g, v_scaled.b & u_alphacolormask.b)"; + const char *maskedColorRef = "ivec3(int(u_alphacolorref.r) & u_alphacolormask.r, int(u_alphacolorref.g) & u_alphacolormask.g, int(u_alphacolorref.b) & u_alphacolormask.b)"; + WRITE(p, " if (%s %s %s) discard;\n", maskedFragColor, colorTestFuncs[colorTestFunc], maskedColorRef); + } else if (gl_extensions.gpuVendor == GPU_VENDOR_POWERVR) { + WRITE(p, " if (roundTo255thv(v.rgb) %s u_alphacolorref.rgb) discard;\n", colorTestFuncs[colorTestFunc]); + } else { + WRITE(p, " if (roundAndScaleTo255v(v.rgb) %s u_alphacolorref.rgb) discard;\n", colorTestFuncs[colorTestFunc]); + } + } else { + WRITE(p, " discard;\n"); + } } } diff --git a/GPU/GLES/FragmentTestCache.cpp b/GPU/GLES/FragmentTestCache.cpp new file mode 100644 index 0000000000..f562f65fed --- /dev/null +++ b/GPU/GLES/FragmentTestCache.cpp @@ -0,0 +1,167 @@ +// Copyright (c) 2014- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "Core/Config.h" +#include "GPU/GLES/FragmentTestCache.h" +#include "GPU/GPUState.h" + +static const int FRAGTEST_TEXTURE_OLD_AGE = 123; + +FragmentTestCache::FragmentTestCache() : textureCache_(NULL), lastTexture_(0) { + scratchpad_ = new u8[256 * 4]; +} + +FragmentTestCache::~FragmentTestCache() { + Clear(); + delete [] scratchpad_; +} + +void FragmentTestCache::BindTestTexture(GLenum unit) { + if (!g_Config.bFragmentTestCache) { + return; + } + + const FragmentTestID id = GenerateTestID(); + const auto cached = cache_.find(id); + if (cached != cache_.end()) { + GLuint tex = cached->second.texture; + if (tex == lastTexture_) { + // Already bound, hurray. + return; + } + glActiveTexture(unit); + glBindTexture(GL_TEXTURE_2D, tex); + // Always return to the default. + glActiveTexture(GL_TEXTURE0); + return; + } + + const u8 rRef = (gstate.getColorTestRef() >> 0) & 0xFF; + const u8 rMask = (gstate.getColorTestMask() >> 0) & 0xFF; + const u8 gRef = (gstate.getColorTestRef() >> 8) & 0xFF; + const u8 gMask = (gstate.getColorTestMask() >> 8) & 0xFF; + const u8 bRef = (gstate.getColorTestRef() >> 16) & 0xFF; + const u8 bMask = (gstate.getColorTestMask() >> 16) & 0xFF; + const u8 aRef = gstate.getAlphaTestRef(); + const u8 aMask = gstate.getAlphaTestMask(); + const u8 refs[4] = {rRef, gRef, bRef, aRef}; + const u8 masks[4] = {rMask, gMask, bMask, aMask}; + const GEComparison funcs[4] = {gstate.getColorTestFunction(), gstate.getColorTestFunction(), gstate.getColorTestFunction(), gstate.getAlphaTestFunction()}; + const bool valid[4] = {gstate.isColorTestEnabled(), gstate.isColorTestEnabled(), gstate.isColorTestEnabled(), gstate.isAlphaTestEnabled()}; + + glActiveTexture(unit); + // This will necessarily bind the texture. + const GLuint tex = CreateCache(funcs, refs, masks, valid); + // Always return to the default. + glActiveTexture(GL_TEXTURE0); + + FragmentTestTexture item; + item.lastFrame = gpuStats.numFlips; + item.texture = tex; + cache_[id] = item; +} + +FragmentTestID FragmentTestCache::GenerateTestID() const { + FragmentTestID id; + // Let's just keep it simple, it's all in here. + id.alpha = gstate.isAlphaTestEnabled() ? gstate.alphatest : 0; + if (gstate.isColorTestEnabled()) { + id.colorRefFunc = gstate.getColorTestFunction() | (gstate.getColorTestRef() << 8); + id.colorMask = gstate.getColorTestMask(); + } else { + id.colorRefFunc = 0; + id.colorMask = 0; + } + return id; +} + +GLuint FragmentTestCache::CreateCache(const GEComparison funcs[4], const u8 refs[4], const u8 masks[4], const bool valid[4]) { + // TODO: Might it be better to use GL_ALPHA for simple textures? + // TODO: Experiment with 4-bit/etc. textures. + + GLuint tex = textureCache_->AllocTextureName(); + glBindTexture(GL_TEXTURE_2D, tex); + + // Build the logic map. + for (int color = 0; color < 256; ++color) { + for (int i = 0; i < 4; ++i) { + bool res; + if (valid[i]) { + switch (funcs[i]) { + case GE_COMP_NEVER: + res = false; + break; + case GE_COMP_ALWAYS: + res = true; + break; + case GE_COMP_EQUAL: + res = (color & masks[i]) == (refs[i] & masks[i]); + break; + case GE_COMP_NOTEQUAL: + res = (color & masks[i]) != (refs[i] & masks[i]); + break; + case GE_COMP_LESS: + res = (color & masks[i]) < (refs[i] & masks[i]); + break; + case GE_COMP_LEQUAL: + res = (color & masks[i]) <= (refs[i] & masks[i]); + break; + case GE_COMP_GREATER: + res = (color & masks[i]) > (refs[i] & masks[i]); + break; + case GE_COMP_GEQUAL: + res = (color & masks[i]) >= (refs[i] & masks[i]); + break; + } + } else { + res = true; + } + scratchpad_[color * 4 + i] = res ? 0xFF : 0; + } + } + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, scratchpad_); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + return tex; +} + +void FragmentTestCache::Clear(bool deleteThem) { + if (deleteThem) { + for (auto tex = cache_.begin(); tex != cache_.end(); ++tex) { + glDeleteTextures(1, &tex->second.texture); + } + } + cache_.clear(); + lastTexture_ = 0; +} + +void FragmentTestCache::Decimate() { + for (auto tex = cache_.begin(); tex != cache_.end(); ) { + if (tex->second.lastFrame + FRAGTEST_TEXTURE_OLD_AGE < gpuStats.numFlips) { + glDeleteTextures(1, &tex->second.texture); + cache_.erase(tex++); + } else { + ++tex; + } + } + lastTexture_ = 0; +} diff --git a/GPU/GLES/FragmentTestCache.h b/GPU/GLES/FragmentTestCache.h new file mode 100644 index 0000000000..5dc7cc3bd4 --- /dev/null +++ b/GPU/GLES/FragmentTestCache.h @@ -0,0 +1,83 @@ +// Copyright (c) 2014- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include +#include "Common/CommonTypes.h" +#include "gfx_es2/gl_state.h" +#include "GPU/ge_constants.h" +#include "GPU/GLES/TextureCache.h" + +struct FragmentTestID { + union { + struct { + u32 alpha; + u32 colorRefFunc; + u32 colorMask; + }; + u32 d[3]; + }; + + bool operator < (const FragmentTestID &other) const { + for (size_t i = 0; i < sizeof(d) / sizeof(u32); i++) { + if (d[i] < other.d[i]) + return true; + if (d[i] > other.d[i]) + return false; + } + return false; + } + bool operator == (const FragmentTestID &other) const { + for (size_t i = 0; i < sizeof(d) / sizeof(u32); i++) { + if (d[i] != other.d[i]) + return false; + } + return true; + } +}; + +struct FragmentTestTexture { + GLuint texture; + int lastFrame; +}; + +class FragmentTestCache { +public: + FragmentTestCache(); + ~FragmentTestCache(); + + void SetTextureCache(TextureCache *tc) { + textureCache_ = tc; + } + + void BindTestTexture(GLenum unit); + + void Clear(bool deleteThem = true); + void Decimate(); + +private: + + GLuint CreateCache(const GEComparison funcs[4], const u8 refs[4], const u8 masks[4], const bool valid[4]); + FragmentTestID GenerateTestID() const; + + TextureCache *textureCache_; + + std::map cache_; + u8 *scratchpad_; + GLuint lastTexture_; +}; diff --git a/GPU/GLES/GLES_GPU.cpp b/GPU/GLES/GLES_GPU.cpp index 573f1bd3ea..b8bfea374d 100644 --- a/GPU/GLES/GLES_GPU.cpp +++ b/GPU/GLES/GLES_GPU.cpp @@ -397,6 +397,7 @@ GLES_GPU::GLES_GPU() transformDraw_.SetShaderManager(shaderManager_); transformDraw_.SetTextureCache(&textureCache_); transformDraw_.SetFramebufferManager(&framebufferManager_); + transformDraw_.SetFragmentTestCache(&fragmentTestCache_); framebufferManager_.Init(); framebufferManager_.SetTextureCache(&textureCache_); framebufferManager_.SetShaderManager(shaderManager_); @@ -404,6 +405,7 @@ GLES_GPU::GLES_GPU() textureCache_.SetFramebufferManager(&framebufferManager_); textureCache_.SetDepalShaderCache(&depalShaderCache_); textureCache_.SetShaderManager(shaderManager_); + fragmentTestCache_.SetTextureCache(&textureCache_); // Sanity check gstate if ((int *)&gstate.transferstart - (int *)&gstate != 0xEA) { @@ -447,6 +449,7 @@ GLES_GPU::~GLES_GPU() { framebufferManager_.DestroyAllFBOs(); shaderManager_->ClearCache(true); depalShaderCache_.Clear(); + fragmentTestCache_.Clear(); delete shaderManager_; glstate.SetVSyncInterval(0); } @@ -484,6 +487,7 @@ void GLES_GPU::DeviceLost() { // TransformDraw has registered as a GfxResourceHolder. shaderManager_->ClearCache(false); textureCache_.Clear(false); + fragmentTestCache_.Clear(false); depalShaderCache_.Clear(); framebufferManager_.DeviceLost(); @@ -585,6 +589,7 @@ void GLES_GPU::BeginFrameInternal() { textureCache_.StartFrame(); transformDraw_.DecimateTrackedVertexArrays(); depalShaderCache_.Decimate(); + fragmentTestCache_.Decimate(); if (dumpNextFrame_) { NOTICE_LOG(G3D, "DUMPING THIS FRAME"); diff --git a/GPU/GLES/GLES_GPU.h b/GPU/GLES/GLES_GPU.h index 10e5476069..d5b1f45e6e 100644 --- a/GPU/GLES/GLES_GPU.h +++ b/GPU/GLES/GLES_GPU.h @@ -27,6 +27,7 @@ #include "GPU/GLES/TransformPipeline.h" #include "GPU/GLES/TextureCache.h" #include "GPU/GLES/DepalettizeShader.h" +#include "GPU/GLES/FragmentTestCache.h" class ShaderManager; class LinkedShader; @@ -173,6 +174,7 @@ private: TextureCache textureCache_; DepalShaderCache depalShaderCache_; TransformDrawEngine transformDraw_; + FragmentTestCache fragmentTestCache_; ShaderManager *shaderManager_; bool resized_; diff --git a/GPU/GLES/ShaderManager.cpp b/GPU/GLES/ShaderManager.cpp index 95ec2fa1da..a03d4d30bc 100644 --- a/GPU/GLES/ShaderManager.cpp +++ b/GPU/GLES/ShaderManager.cpp @@ -147,6 +147,7 @@ LinkedShader::LinkedShader(Shader *vs, Shader *fs, u32 vertType, bool useHWTrans u_alphacolorref = glGetUniformLocation(program, "u_alphacolorref"); u_alphacolormask = glGetUniformLocation(program, "u_alphacolormask"); u_stencilReplaceValue = glGetUniformLocation(program, "u_stencilReplaceValue"); + u_testtex = glGetUniformLocation(program, "testtex"); u_fbotex = glGetUniformLocation(program, "fbotex"); u_blendFixA = glGetUniformLocation(program, "u_blendFixA"); @@ -258,6 +259,7 @@ LinkedShader::LinkedShader(Shader *vs, Shader *fs, u32 vertType, bool useHWTrans // Default uniform values glUniform1i(u_tex, 0); glUniform1i(u_fbotex, 1); + glUniform1i(u_testtex, 2); // The rest, use the "dirty" mechanism. dirtyUniforms = DIRTY_ALL; use(vertType, previous); diff --git a/GPU/GLES/ShaderManager.h b/GPU/GLES/ShaderManager.h index 5102c94b1a..b043af883b 100644 --- a/GPU/GLES/ShaderManager.h +++ b/GPU/GLES/ShaderManager.h @@ -82,6 +82,7 @@ public: // Fragment processing inputs int u_alphacolorref; int u_alphacolormask; + int u_testtex; int u_fogcolor; int u_fogcoef; diff --git a/GPU/GLES/StateMapping.cpp b/GPU/GLES/StateMapping.cpp index 17a93fa682..0904c1d9cd 100644 --- a/GPU/GLES/StateMapping.cpp +++ b/GPU/GLES/StateMapping.cpp @@ -639,6 +639,10 @@ void TransformDrawEngine::ApplyDrawState(int prim) { } else { glstate.stencilTest.disable(); } + + if (gstate.isAlphaTestEnabled() || gstate.isColorTestEnabled()) { + fragmentTestCache_->BindTestTexture(GL_TEXTURE2); + } } bool throughmode = gstate.isModeThrough(); diff --git a/GPU/GLES/TransformPipeline.cpp b/GPU/GLES/TransformPipeline.cpp index 06d5bcaf9a..2365ec9d5f 100644 --- a/GPU/GLES/TransformPipeline.cpp +++ b/GPU/GLES/TransformPipeline.cpp @@ -80,6 +80,7 @@ #include "GPU/Common/TextureDecoder.h" #include "GPU/Common/SplineCommon.h" +#include "GPU/GLES/FragmentTestCache.h" #include "GPU/GLES/StateMapping.h" #include "GPU/GLES/TextureCache.h" #include "GPU/GLES/TransformPipeline.h" diff --git a/GPU/GLES/TransformPipeline.h b/GPU/GLES/TransformPipeline.h index 732cbd12cd..514a0f1b7f 100644 --- a/GPU/GLES/TransformPipeline.h +++ b/GPU/GLES/TransformPipeline.h @@ -29,6 +29,7 @@ class LinkedShader; class ShaderManager; class TextureCache; class FramebufferManager; +class FragmentTestCache; struct TransformedVertex; struct DecVtxFormat; @@ -116,6 +117,9 @@ public: void SetFramebufferManager(FramebufferManager *fbManager) { framebufferManager_ = fbManager; } + void SetFragmentTestCache(FragmentTestCache *testCache) { + fragmentTestCache_ = testCache; + } void InitDeviceObjects(); void DestroyDeviceObjects(); void GLLost(); @@ -236,6 +240,7 @@ private: ShaderManager *shaderManager_; TextureCache *textureCache_; FramebufferManager *framebufferManager_; + FragmentTestCache *fragmentTestCache_; enum { MAX_DEFERRED_DRAW_CALLS = 128 }; DeferredDrawCall drawCalls[MAX_DEFERRED_DRAW_CALLS]; diff --git a/GPU/GPU.vcxproj b/GPU/GPU.vcxproj index cbea9ef634..a556418a3d 100644 --- a/GPU/GPU.vcxproj +++ b/GPU/GPU.vcxproj @@ -192,6 +192,7 @@ + @@ -245,6 +246,7 @@ + diff --git a/GPU/GPU.vcxproj.filters b/GPU/GPU.vcxproj.filters index e3ba8997c2..4b1b880a03 100644 --- a/GPU/GPU.vcxproj.filters +++ b/GPU/GPU.vcxproj.filters @@ -168,6 +168,9 @@ GLES + + GLES + @@ -317,6 +320,9 @@ GLES + + GLES + diff --git a/Qt/GPU.pro b/Qt/GPU.pro index 4ba5580252..717fc56d78 100644 --- a/Qt/GPU.pro +++ b/Qt/GPU.pro @@ -28,6 +28,7 @@ SOURCES += $$P/GPU/GeDisasm.cpp \ # GPU $$P/GPU/Null/NullGpu.cpp \ $$P/GPU/GLES/DepalettizeShader.cpp \ $$P/GPU/GLES/FragmentShaderGenerator.cpp \ + $$P/GPU/GLES/FragmentTestCache.cpp \ $$P/GPU/GLES/Framebuffer.cpp \ $$P/GPU/GLES/GLES_GPU.cpp \ $$P/GPU/GLES/ShaderManager.cpp \ diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 976eb95e05..675d02d0db 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -155,6 +155,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/GPU/GLES/ShaderManager.cpp.arm \ $(SRC)/GPU/GLES/VertexShaderGenerator.cpp.arm \ $(SRC)/GPU/GLES/FragmentShaderGenerator.cpp.arm \ + $(SRC)/GPU/GLES/FragmentTestCache.cpp.arm \ $(SRC)/GPU/GLES/TextureScaler.cpp \ $(SRC)/GPU/GLES/Spline.cpp \ $(SRC)/GPU/Null/NullGpu.cpp \