ppsspp/GPU/GLES/FragmentTestCacheGLES.cpp
Henrik Rydgård 0e3a84b4a8 Move most GPU things to Common.
It works after the move, on Windows and Android at least.

Deletes the D3DX9 shader compiler loader, which was not used.
2020-10-04 23:39:02 +02:00

178 lines
5.8 KiB
C++

// 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 "Common/GPU/thin3d.h"
#include "Common/GPU/OpenGL/GLDebugLog.h"
#include "Core/Config.h"
#include "GPU/GLES/FragmentTestCacheGLES.h"
#include "GPU/GPUState.h"
#include "GPU/Common/GPUStateUtils.h"
#include "GPU/Common/ShaderId.h"
// These are small, let's give them plenty of frames.
static const int FRAGTEST_TEXTURE_OLD_AGE = 307;
static const int FRAGTEST_DECIMATION_INTERVAL = 113;
FragmentTestCacheGLES::FragmentTestCacheGLES(Draw::DrawContext *draw) {
render_ = (GLRenderManager *)draw->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
}
FragmentTestCacheGLES::~FragmentTestCacheGLES() {
Clear();
}
void FragmentTestCacheGLES::DeviceRestore(Draw::DrawContext *draw) {
render_ = (GLRenderManager *)draw->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
}
void FragmentTestCacheGLES::BindTestTexture(int slot) {
if (!g_Config.bFragmentTestCache) {
return;
}
bool alphaNeedsTexture = gstate.isAlphaTestEnabled() && !IsAlphaTestAgainstZero() && !IsAlphaTestTriviallyTrue();
bool colorNeedsTexture = gstate.isColorTestEnabled() && !IsColorTestAgainstZero() && !IsColorTestTriviallyTrue();
if (!alphaNeedsTexture && !colorNeedsTexture) {
// Common case: testing against zero. Just skip it, faster not to bind anything.
return;
}
const FragmentTestID id = GenerateTestID();
const auto cached = cache_.find(id);
if (cached != cache_.end()) {
cached->second.lastFrame = gpuStats.numFlips;
GLRTexture *tex = cached->second.texture;
if (tex == lastTexture_) {
// Already bound, hurray.
return;
}
render_->BindTexture(slot, tex);
lastTexture_ = tex;
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()};
GLRTexture *tex = CreateTestTexture(funcs, refs, masks, valid);
lastTexture_ = tex;
render_->BindTexture(slot, tex);
// We only need to do this once for the texture.
render_->SetTextureSampler(slot, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_NEAREST, GL_NEAREST, 0.0f);
FragmentTestTexture item;
item.lastFrame = gpuStats.numFlips;
item.texture = tex;
cache_[id] = item;
}
FragmentTestID FragmentTestCacheGLES::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;
}
GLRTexture *FragmentTestCacheGLES::CreateTestTexture(const GEComparison funcs[4], const u8 refs[4], const u8 masks[4], const bool valid[4]) {
u8 *data = new u8[256 * 4];
// TODO: Might it be better to use GL_ALPHA for simple textures?
// TODO: Experiment with 4-bit/etc. textures.
// Build the logic map.
for (int color = 0; color < 256; ++color) {
for (int i = 0; i < 4; ++i) {
bool res = true;
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;
}
}
data[color * 4 + i] = res ? 0xFF : 0;
}
}
GLRTexture *tex = render_->CreateTexture(GL_TEXTURE_2D);
render_->TextureImage(tex, 0, 256, 1, Draw::DataFormat::R8G8B8A8_UNORM, data);
return tex;
}
void FragmentTestCacheGLES::Clear(bool deleteThem) {
if (deleteThem) {
for (auto tex = cache_.begin(); tex != cache_.end(); ++tex) {
render_->DeleteTexture(tex->second.texture);
}
}
cache_.clear();
lastTexture_ = 0;
}
void FragmentTestCacheGLES::Decimate() {
if (--decimationCounter_ <= 0) {
for (auto tex = cache_.begin(); tex != cache_.end(); ) {
if (tex->second.lastFrame + FRAGTEST_TEXTURE_OLD_AGE < gpuStats.numFlips) {
render_->DeleteTexture(tex->second.texture);
cache_.erase(tex++);
} else {
++tex;
}
}
decimationCounter_ = FRAGTEST_DECIMATION_INTERVAL;
}
lastTexture_ = 0;
}