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/StateMappingGLES.cpp
Views: 1401
1
// Copyright (c) 2012- 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
19
// Alpha/stencil is a convoluted mess. Some good comments are here:
20
// https://github.com/hrydgard/ppsspp/issues/3768
21
22
#include "ppsspp_config.h"
23
#include "StateMappingGLES.h"
24
#include "Common/Profiler/Profiler.h"
25
#include "Common/GPU/OpenGL/GLDebugLog.h"
26
#include "Common/GPU/OpenGL/GLRenderManager.h"
27
#include "Common/Data/Convert/SmallDataConvert.h"
28
29
#include "GPU/Math3D.h"
30
#include "GPU/GPUState.h"
31
#include "GPU/ge_constants.h"
32
#include "Core/System.h"
33
#include "Core/Config.h"
34
#include "GPU/GLES/GPU_GLES.h"
35
#include "GPU/GLES/ShaderManagerGLES.h"
36
#include "GPU/GLES/TextureCacheGLES.h"
37
#include "GPU/GLES/FramebufferManagerGLES.h"
38
#include "GPU/Common/FragmentShaderGenerator.h"
39
40
static const GLushort glBlendFactorLookup[(size_t)BlendFactor::COUNT] = {
41
GL_ZERO,
42
GL_ONE,
43
GL_SRC_COLOR,
44
GL_ONE_MINUS_SRC_COLOR,
45
GL_DST_COLOR,
46
GL_ONE_MINUS_DST_COLOR,
47
GL_SRC_ALPHA,
48
GL_ONE_MINUS_SRC_ALPHA,
49
GL_DST_ALPHA,
50
GL_ONE_MINUS_DST_ALPHA,
51
GL_CONSTANT_COLOR,
52
GL_ONE_MINUS_CONSTANT_COLOR,
53
GL_CONSTANT_ALPHA,
54
GL_ONE_MINUS_CONSTANT_ALPHA,
55
#if !defined(USING_GLES2) // TODO: Remove when we have better headers
56
GL_SRC1_COLOR,
57
GL_ONE_MINUS_SRC1_COLOR,
58
GL_SRC1_ALPHA,
59
GL_ONE_MINUS_SRC1_ALPHA,
60
#elif !PPSSPP_PLATFORM(IOS)
61
GL_SRC1_COLOR_EXT,
62
GL_ONE_MINUS_SRC1_COLOR_EXT,
63
GL_SRC1_ALPHA_EXT,
64
GL_ONE_MINUS_SRC1_ALPHA_EXT,
65
#else
66
GL_INVALID_ENUM,
67
GL_INVALID_ENUM,
68
GL_INVALID_ENUM,
69
GL_INVALID_ENUM,
70
#endif
71
GL_INVALID_ENUM,
72
};
73
74
static const GLushort glBlendEqLookup[(size_t)BlendEq::COUNT] = {
75
GL_FUNC_ADD,
76
GL_FUNC_SUBTRACT,
77
GL_FUNC_REVERSE_SUBTRACT,
78
GL_MIN,
79
GL_MAX,
80
};
81
82
static const GLushort cullingMode[] = {
83
GL_FRONT,
84
GL_BACK,
85
};
86
87
static const GLushort compareOps[] = {
88
GL_NEVER, GL_ALWAYS, GL_EQUAL, GL_NOTEQUAL,
89
GL_LESS, GL_LEQUAL, GL_GREATER, GL_GEQUAL,
90
};
91
92
static const GLushort stencilOps[] = {
93
GL_KEEP,
94
GL_ZERO,
95
GL_REPLACE,
96
GL_INVERT,
97
GL_INCR,
98
GL_DECR,
99
GL_KEEP, // reserved
100
GL_KEEP, // reserved
101
};
102
103
#if !defined(USING_GLES2)
104
static const GLushort logicOps[] = {
105
GL_CLEAR,
106
GL_AND,
107
GL_AND_REVERSE,
108
GL_COPY,
109
GL_AND_INVERTED,
110
GL_NOOP,
111
GL_XOR,
112
GL_OR,
113
GL_NOR,
114
GL_EQUIV,
115
GL_INVERT,
116
GL_OR_REVERSE,
117
GL_COPY_INVERTED,
118
GL_OR_INVERTED,
119
GL_NAND,
120
GL_SET,
121
};
122
#endif
123
124
void DrawEngineGLES::ApplyDrawState(int prim) {
125
GLRenderManager *renderManager = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
126
127
if (!gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE)) {
128
// Nothing to do, let's early-out
129
return;
130
}
131
132
// Start profiling here to skip SetTexture which is already accounted for
133
PROFILE_THIS_SCOPE("applydrawstate");
134
135
uint64_t dirtyRequiresRecheck_ = 0;
136
bool useBufferedRendering = framebufferManager_->UseBufferedRendering();
137
138
if (gstate_c.IsDirty(DIRTY_BLEND_STATE)) {
139
if (gstate.isModeClear()) {
140
// Color Test
141
bool colorMask = gstate.isClearModeColorMask();
142
bool alphaMask = gstate.isClearModeAlphaMask();
143
renderManager->SetNoBlendAndMask((colorMask ? 7 : 0) | (alphaMask ? 8 : 0));
144
} else {
145
pipelineState_.Convert(draw_->GetShaderLanguageDesc().bitwiseOps);
146
GenericMaskState &maskState = pipelineState_.maskState;
147
GenericBlendState &blendState = pipelineState_.blendState;
148
GenericLogicState &logicState = pipelineState_.logicState;
149
150
if (pipelineState_.FramebufferRead()) {
151
FBOTexState fboTexBindState = FBO_TEX_NONE;
152
ApplyFramebufferRead(&fboTexBindState);
153
// The shader takes over the responsibility for blending, so recompute.
154
ApplyStencilReplaceAndLogicOpIgnoreBlend(blendState.replaceAlphaWithStencil, blendState);
155
156
// We copy the framebuffer here, as doing so will wipe any blend state if we do it later.
157
// fboTexNeedsBind_ won't be set if we can read directly from the target.
158
if (fboTexBindState == FBO_TEX_COPY_BIND_TEX) {
159
// Note that this is positions, not UVs, that we need the copy from.
160
framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY | BINDFBCOLOR_UNCACHED, 0);
161
// If we are rendering at a higher resolution, linear is probably best for the dest color.
162
renderManager->SetTextureSampler(1, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_LINEAR, GL_LINEAR, 0.0f);
163
fboTexBound_ = true;
164
165
framebufferManager_->RebindFramebuffer("RebindFramebuffer - ApplyDrawState");
166
// Must dirty blend state here so we re-copy next time. Example: Lunar's spell effects.
167
dirtyRequiresRecheck_ |= DIRTY_BLEND_STATE;
168
gstate_c.Dirty(DIRTY_BLEND_STATE);
169
} else if (fboTexBindState == FBO_TEX_READ_FRAMEBUFFER) {
170
// No action needed here.
171
fboTexBindState = FBO_TEX_NONE;
172
}
173
dirtyRequiresRecheck_ |= DIRTY_FRAGMENTSHADER_STATE;
174
gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE);
175
} else {
176
if (fboTexBound_) {
177
GLRenderManager *renderManager = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
178
renderManager->BindTexture(TEX_SLOT_SHADERBLEND_SRC, nullptr);
179
fboTexBound_ = false;
180
dirtyRequiresRecheck_ |= DIRTY_FRAGMENTSHADER_STATE;
181
gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE);
182
}
183
}
184
185
if (blendState.blendEnabled) {
186
if (blendState.dirtyShaderBlendFixValues) {
187
// Not quite sure how necessary this is.
188
dirtyRequiresRecheck_ |= DIRTY_SHADERBLEND;
189
gstate_c.Dirty(DIRTY_SHADERBLEND);
190
}
191
if (blendState.useBlendColor) {
192
uint32_t color = blendState.blendColor;
193
float col[4];
194
Uint8x4ToFloat4(col, color);
195
renderManager->SetBlendFactor(col);
196
}
197
}
198
199
int mask = (int)maskState.channelMask;
200
if (blendState.blendEnabled) {
201
renderManager->SetBlendAndMask(mask, blendState.blendEnabled,
202
glBlendFactorLookup[(size_t)blendState.srcColor], glBlendFactorLookup[(size_t)blendState.dstColor],
203
glBlendFactorLookup[(size_t)blendState.srcAlpha], glBlendFactorLookup[(size_t)blendState.dstAlpha],
204
glBlendEqLookup[(size_t)blendState.eqColor], glBlendEqLookup[(size_t)blendState.eqAlpha]);
205
} else {
206
renderManager->SetNoBlendAndMask(mask);
207
}
208
209
// TODO: Get rid of the ifdef
210
#ifndef USING_GLES2
211
if (gstate_c.Use(GPU_USE_LOGIC_OP)) {
212
renderManager->SetLogicOp(logicState.logicOpEnabled, logicOps[(int)logicState.logicOp]);
213
}
214
#endif
215
}
216
}
217
218
if (gstate_c.IsDirty(DIRTY_RASTER_STATE)) {
219
// Dither
220
bool dither = gstate.isDitherEnabled();
221
bool cullEnable;
222
GLenum cullMode = cullingMode[gstate.getCullMode() ^ !useBufferedRendering];
223
224
cullEnable = !gstate.isModeClear() && prim != GE_PRIM_RECTANGLES && prim > GE_PRIM_LINE_STRIP && gstate.isCullEnabled();
225
226
bool depthClampEnable = false;
227
if (gstate.isModeClear() || gstate.isModeThrough()) {
228
// TODO: Might happen in clear mode if not through...
229
depthClampEnable = false;
230
} else {
231
if (gstate.getDepthRangeMin() == 0 || gstate.getDepthRangeMax() == 65535) {
232
// TODO: Still has a bug where we clamp to depth range if one is not the full range.
233
// But the alternate is not clamping in either direction...
234
depthClampEnable = gstate.isDepthClampEnabled() && gstate_c.Use(GPU_USE_DEPTH_CLAMP);
235
} else {
236
// We just want to clip in this case, the clamp would be clipped anyway.
237
depthClampEnable = false;
238
}
239
}
240
241
renderManager->SetRaster(cullEnable, GL_CCW, cullMode, dither, depthClampEnable);
242
}
243
244
if (gstate_c.IsDirty(DIRTY_DEPTHSTENCIL_STATE)) {
245
ConvertStencilFuncState(stencilState_);
246
247
if (gstate.isModeClear()) {
248
renderManager->SetStencil(
249
gstate.isClearModeAlphaMask(), GL_ALWAYS, 0xFF, 0xFF,
250
stencilState_.writeMask, GL_REPLACE, GL_REPLACE, GL_REPLACE);
251
renderManager->SetDepth(true, gstate.isClearModeDepthMask() ? true : false, GL_ALWAYS);
252
} else {
253
// Depth Test
254
bool depthTestUsed = !IsDepthTestEffectivelyDisabled();
255
renderManager->SetDepth(depthTestUsed, gstate.isDepthWriteEnabled(), compareOps[gstate.getDepthTestFunction()]);
256
if (depthTestUsed)
257
UpdateEverUsedEqualDepth(gstate.getDepthTestFunction());
258
259
// Stencil Test
260
if (stencilState_.enabled) {
261
renderManager->SetStencil(
262
stencilState_.enabled, compareOps[stencilState_.testFunc], stencilState_.testRef, stencilState_.testMask,
263
stencilState_.writeMask, stencilOps[stencilState_.sFail], stencilOps[stencilState_.zFail], stencilOps[stencilState_.zPass]);
264
265
// Nasty special case for Spongebob and similar where it tries to write zeros to alpha/stencil during
266
// depth-fail. We can't write to alpha then because the pixel is killed. However, we can invert the depth
267
// test and modify the alpha function...
268
if (SpongebobDepthInverseConditions(stencilState_)) {
269
renderManager->SetBlendAndMask(0x8, true, GL_ZERO, GL_ZERO, GL_ZERO, GL_ZERO, GL_FUNC_ADD, GL_FUNC_ADD);
270
renderManager->SetDepth(true, false, GL_LESS);
271
renderManager->SetStencil(true, GL_ALWAYS, 0xFF, 0xFF, 0xFF, GL_ZERO, GL_KEEP, GL_ZERO);
272
273
dirtyRequiresRecheck_ |= DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE;
274
gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE);
275
}
276
} else {
277
renderManager->SetStencilDisabled();
278
}
279
}
280
}
281
282
if (gstate_c.IsDirty(DIRTY_VIEWPORTSCISSOR_STATE)) {
283
ConvertViewportAndScissor(useBufferedRendering,
284
framebufferManager_->GetRenderWidth(), framebufferManager_->GetRenderHeight(),
285
framebufferManager_->GetTargetBufferWidth(), framebufferManager_->GetTargetBufferHeight(),
286
vpAndScissor_);
287
UpdateCachedViewportState(vpAndScissor_);
288
289
renderManager->SetScissor(GLRect2D{ vpAndScissor_.scissorX, vpAndScissor_.scissorY, vpAndScissor_.scissorW, vpAndScissor_.scissorH });
290
renderManager->SetViewport({
291
vpAndScissor_.viewportX, vpAndScissor_.viewportY,
292
vpAndScissor_.viewportW, vpAndScissor_.viewportH,
293
vpAndScissor_.depthRangeMin, vpAndScissor_.depthRangeMax });
294
}
295
296
gstate_c.Clean(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_BLEND_STATE);
297
gstate_c.Dirty(dirtyRequiresRecheck_);
298
dirtyRequiresRecheck_ = 0;
299
}
300
301
void DrawEngineGLES::ApplyDrawStateLate(bool setStencilValue, int stencilValue) {
302
if (setStencilValue) {
303
render_->SetStencil(stencilState_.writeMask, GL_ALWAYS, stencilValue, 255, 0xFF, GL_REPLACE, GL_REPLACE, GL_REPLACE);
304
gstate_c.Dirty(DIRTY_DEPTHSTENCIL_STATE); // For the next time.
305
}
306
307
// At this point, we know if the vertices are full alpha or not.
308
// TODO: Set the nearest/linear here (since we correctly know if alpha/color tests are needed)?
309
if (!gstate.isModeClear() && gstate_c.Use(GPU_USE_FRAGMENT_TEST_CACHE)) {
310
// Apply last, once we know the alpha params of the texture.
311
if (gstate.isAlphaTestEnabled() || gstate.isColorTestEnabled()) {
312
fragmentTestCache_->BindTestTexture(TEX_SLOT_ALPHATEST);
313
}
314
}
315
}
316
317