#include "Common/GPU/thin3d.h"
#include "Common/GPU/OpenGL/GLDebugLog.h"
#include "GPU/GLES/FragmentTestCacheGLES.h"
#include "GPU/GPUState.h"
#include "GPU/Common/GPUStateUtils.h"
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) {
bool alphaNeedsTexture = gstate.isAlphaTestEnabled() && !IsAlphaTestAgainstZero() && !IsAlphaTestTriviallyTrue();
bool colorNeedsTexture = gstate.isColorTestEnabled() && !IsColorTestAgainstZero() && !IsColorTestTriviallyTrue();
if (!alphaNeedsTexture && !colorNeedsTexture) {
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_) {
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);
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() {
FragmentTestID id;
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];
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, 256, 1, 1, 1);
render_->TextureImage(tex, 0, 256, 1, 1, Draw::DataFormat::R8G8B8A8_UNORM, data);
return tex;
}
void FragmentTestCacheGLES::Clear(bool deleteThem) {
if (deleteThem) {
for (const auto &[_, v] : cache_) {
render_->DeleteTexture(v.texture);
}
}
cache_.clear();
lastTexture_ = nullptr;
}
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_ = nullptr;
}