CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/GPU/GLES/FragmentTestCacheGLES.cpp
Views: 1401
1
// Copyright (c) 2014- PPSSPP Project.
2
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
6
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
11
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
14
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18
#include "Common/GPU/thin3d.h"
19
#include "Common/GPU/OpenGL/GLDebugLog.h"
20
#include "Core/Config.h"
21
#include "GPU/GLES/FragmentTestCacheGLES.h"
22
#include "GPU/GPUState.h"
23
#include "GPU/Common/GPUStateUtils.h"
24
#include "GPU/Common/ShaderId.h"
25
26
// These are small, let's give them plenty of frames.
27
static const int FRAGTEST_TEXTURE_OLD_AGE = 307;
28
static const int FRAGTEST_DECIMATION_INTERVAL = 113;
29
30
FragmentTestCacheGLES::FragmentTestCacheGLES(Draw::DrawContext *draw) {
31
render_ = (GLRenderManager *)draw->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
32
}
33
34
FragmentTestCacheGLES::~FragmentTestCacheGLES() {
35
Clear();
36
}
37
38
void FragmentTestCacheGLES::DeviceRestore(Draw::DrawContext *draw) {
39
render_ = (GLRenderManager *)draw->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
40
}
41
42
void FragmentTestCacheGLES::BindTestTexture(int slot) {
43
bool alphaNeedsTexture = gstate.isAlphaTestEnabled() && !IsAlphaTestAgainstZero() && !IsAlphaTestTriviallyTrue();
44
bool colorNeedsTexture = gstate.isColorTestEnabled() && !IsColorTestAgainstZero() && !IsColorTestTriviallyTrue();
45
if (!alphaNeedsTexture && !colorNeedsTexture) {
46
// Common case: testing against zero. Just skip it, faster not to bind anything.
47
return;
48
}
49
50
const FragmentTestID id = GenerateTestID();
51
const auto cached = cache_.find(id);
52
if (cached != cache_.end()) {
53
cached->second.lastFrame = gpuStats.numFlips;
54
GLRTexture *tex = cached->second.texture;
55
if (tex == lastTexture_) {
56
// Already bound, hurray.
57
return;
58
}
59
render_->BindTexture(slot, tex);
60
lastTexture_ = tex;
61
return;
62
}
63
64
const u8 rRef = (gstate.getColorTestRef() >> 0) & 0xFF;
65
const u8 rMask = (gstate.getColorTestMask() >> 0) & 0xFF;
66
const u8 gRef = (gstate.getColorTestRef() >> 8) & 0xFF;
67
const u8 gMask = (gstate.getColorTestMask() >> 8) & 0xFF;
68
const u8 bRef = (gstate.getColorTestRef() >> 16) & 0xFF;
69
const u8 bMask = (gstate.getColorTestMask() >> 16) & 0xFF;
70
const u8 aRef = gstate.getAlphaTestRef();
71
const u8 aMask = gstate.getAlphaTestMask();
72
const u8 refs[4] = {rRef, gRef, bRef, aRef};
73
const u8 masks[4] = {rMask, gMask, bMask, aMask};
74
const GEComparison funcs[4] = {gstate.getColorTestFunction(), gstate.getColorTestFunction(), gstate.getColorTestFunction(), gstate.getAlphaTestFunction()};
75
const bool valid[4] = {gstate.isColorTestEnabled(), gstate.isColorTestEnabled(), gstate.isColorTestEnabled(), gstate.isAlphaTestEnabled()};
76
77
GLRTexture *tex = CreateTestTexture(funcs, refs, masks, valid);
78
lastTexture_ = tex;
79
render_->BindTexture(slot, tex);
80
// We only need to do this once for the texture.
81
render_->SetTextureSampler(slot, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_NEAREST, GL_NEAREST, 0.0f);
82
FragmentTestTexture item;
83
item.lastFrame = gpuStats.numFlips;
84
item.texture = tex;
85
cache_[id] = item;
86
}
87
88
FragmentTestID FragmentTestCacheGLES::GenerateTestID() {
89
FragmentTestID id;
90
// Let's just keep it simple, it's all in here.
91
id.alpha = gstate.isAlphaTestEnabled() ? gstate.alphatest : 0;
92
if (gstate.isColorTestEnabled()) {
93
id.colorRefFunc = gstate.getColorTestFunction() | (gstate.getColorTestRef() << 8);
94
id.colorMask = gstate.getColorTestMask();
95
} else {
96
id.colorRefFunc = 0;
97
id.colorMask = 0;
98
}
99
return id;
100
}
101
102
GLRTexture *FragmentTestCacheGLES::CreateTestTexture(const GEComparison funcs[4], const u8 refs[4], const u8 masks[4], const bool valid[4]) {
103
u8 *data = new u8[256 * 4];
104
// TODO: Might it be better to use GL_ALPHA for simple textures?
105
// TODO: Experiment with 4-bit/etc. textures.
106
107
// Build the logic map.
108
for (int color = 0; color < 256; ++color) {
109
for (int i = 0; i < 4; ++i) {
110
bool res = true;
111
if (valid[i]) {
112
switch (funcs[i]) {
113
case GE_COMP_NEVER:
114
res = false;
115
break;
116
case GE_COMP_ALWAYS:
117
res = true;
118
break;
119
case GE_COMP_EQUAL:
120
res = (color & masks[i]) == (refs[i] & masks[i]);
121
break;
122
case GE_COMP_NOTEQUAL:
123
res = (color & masks[i]) != (refs[i] & masks[i]);
124
break;
125
case GE_COMP_LESS:
126
res = (color & masks[i]) < (refs[i] & masks[i]);
127
break;
128
case GE_COMP_LEQUAL:
129
res = (color & masks[i]) <= (refs[i] & masks[i]);
130
break;
131
case GE_COMP_GREATER:
132
res = (color & masks[i]) > (refs[i] & masks[i]);
133
break;
134
case GE_COMP_GEQUAL:
135
res = (color & masks[i]) >= (refs[i] & masks[i]);
136
break;
137
}
138
}
139
data[color * 4 + i] = res ? 0xFF : 0;
140
}
141
}
142
143
GLRTexture *tex = render_->CreateTexture(GL_TEXTURE_2D, 256, 1, 1, 1);
144
render_->TextureImage(tex, 0, 256, 1, 1, Draw::DataFormat::R8G8B8A8_UNORM, data);
145
return tex;
146
}
147
148
void FragmentTestCacheGLES::Clear(bool deleteThem) {
149
if (deleteThem) {
150
for (const auto &[_, v] : cache_) {
151
render_->DeleteTexture(v.texture);
152
}
153
}
154
cache_.clear();
155
lastTexture_ = nullptr;
156
}
157
158
void FragmentTestCacheGLES::Decimate() {
159
if (--decimationCounter_ <= 0) {
160
for (auto tex = cache_.begin(); tex != cache_.end(); ) {
161
if (tex->second.lastFrame + FRAGTEST_TEXTURE_OLD_AGE < gpuStats.numFlips) {
162
render_->DeleteTexture(tex->second.texture);
163
cache_.erase(tex++);
164
} else {
165
++tex;
166
}
167
}
168
169
decimationCounter_ = FRAGTEST_DECIMATION_INTERVAL;
170
}
171
172
lastTexture_ = nullptr;
173
}
174
175