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 "Core/Reporting.h"
35 #include "GPU/GLES/GPU_GLES.h"
36 #include "GPU/GLES/ShaderManagerGLES.h"
37 #include "GPU/GLES/TextureCacheGLES.h"
38 #include "GPU/GLES/FramebufferManagerGLES.h"
39 #include "GPU/Common/FragmentShaderGenerator.h"
40
41 static const GLushort glBlendFactorLookup[(size_t)BlendFactor::COUNT] = {
42 GL_ZERO,
43 GL_ONE,
44 GL_SRC_COLOR,
45 GL_ONE_MINUS_SRC_COLOR,
46 GL_DST_COLOR,
47 GL_ONE_MINUS_DST_COLOR,
48 GL_SRC_ALPHA,
49 GL_ONE_MINUS_SRC_ALPHA,
50 GL_DST_ALPHA,
51 GL_ONE_MINUS_DST_ALPHA,
52 GL_CONSTANT_COLOR,
53 GL_ONE_MINUS_CONSTANT_COLOR,
54 GL_CONSTANT_ALPHA,
55 GL_ONE_MINUS_CONSTANT_ALPHA,
56 #if !defined(USING_GLES2) // TODO: Remove when we have better headers
57 GL_SRC1_COLOR,
58 GL_ONE_MINUS_SRC1_COLOR,
59 GL_SRC1_ALPHA,
60 GL_ONE_MINUS_SRC1_ALPHA,
61 #elif !PPSSPP_PLATFORM(IOS)
62 GL_SRC1_COLOR_EXT,
63 GL_ONE_MINUS_SRC1_COLOR_EXT,
64 GL_SRC1_ALPHA_EXT,
65 GL_ONE_MINUS_SRC1_ALPHA_EXT,
66 #else
67 GL_INVALID_ENUM,
68 GL_INVALID_ENUM,
69 GL_INVALID_ENUM,
70 GL_INVALID_ENUM,
71 #endif
72 GL_INVALID_ENUM,
73 };
74
75 static const GLushort glBlendEqLookup[(size_t)BlendEq::COUNT] = {
76 GL_FUNC_ADD,
77 GL_FUNC_SUBTRACT,
78 GL_FUNC_REVERSE_SUBTRACT,
79 GL_MIN,
80 GL_MAX,
81 };
82
83 static const GLushort cullingMode[] = {
84 GL_FRONT,
85 GL_BACK,
86 };
87
88 static const GLushort compareOps[] = {
89 GL_NEVER, GL_ALWAYS, GL_EQUAL, GL_NOTEQUAL,
90 GL_LESS, GL_LEQUAL, GL_GREATER, GL_GEQUAL,
91 };
92
93 static const GLushort stencilOps[] = {
94 GL_KEEP,
95 GL_ZERO,
96 GL_REPLACE,
97 GL_INVERT,
98 GL_INCR,
99 GL_DECR,
100 GL_KEEP, // reserved
101 GL_KEEP, // reserved
102 };
103
104 #if !defined(USING_GLES2)
105 static const GLushort logicOps[] = {
106 GL_CLEAR,
107 GL_AND,
108 GL_AND_REVERSE,
109 GL_COPY,
110 GL_AND_INVERTED,
111 GL_NOOP,
112 GL_XOR,
113 GL_OR,
114 GL_NOR,
115 GL_EQUIV,
116 GL_INVERT,
117 GL_OR_REVERSE,
118 GL_COPY_INVERTED,
119 GL_OR_INVERTED,
120 GL_NAND,
121 GL_SET,
122 };
123 #endif
124
ResetFramebufferRead()125 inline void DrawEngineGLES::ResetFramebufferRead() {
126 if (fboTexBound_) {
127 GLRenderManager *renderManager = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
128 renderManager->BindTexture(TEX_SLOT_SHADERBLEND_SRC, nullptr);
129 fboTexBound_ = false;
130 }
131 }
132
ApplyDrawState(int prim)133 void DrawEngineGLES::ApplyDrawState(int prim) {
134 GLRenderManager *renderManager = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
135
136 if (!gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE)) {
137 // Nothing to do, let's early-out
138 return;
139 }
140
141 // Start profiling here to skip SetTexture which is already accounted for
142 PROFILE_THIS_SCOPE("applydrawstate");
143
144 bool useBufferedRendering = framebufferManager_->UseBufferedRendering();
145
146 if (gstate_c.IsDirty(DIRTY_BLEND_STATE)) {
147 gstate_c.Clean(DIRTY_BLEND_STATE);
148 gstate_c.SetAllowFramebufferRead(!g_Config.bDisableSlowFramebufEffects);
149
150 if (gstate.isModeClear()) {
151 // Color Test
152 bool colorMask = gstate.isClearModeColorMask();
153 bool alphaMask = gstate.isClearModeAlphaMask();
154 renderManager->SetNoBlendAndMask((colorMask ? 7 : 0) | (alphaMask ? 8 : 0));
155 } else {
156 // Do the large chunks of state conversion. We might be able to hide these two behind a dirty-flag each,
157 // to avoid recomputing heavy stuff unnecessarily every draw call.
158 GenericBlendState blendState;
159 ConvertBlendState(blendState, gstate_c.allowFramebufferRead);
160
161 GenericMaskState maskState;
162 ConvertMaskState(maskState, gstate_c.allowFramebufferRead);
163
164 if (blendState.applyFramebufferRead || maskState.applyFramebufferRead) {
165 if (ApplyFramebufferRead(&fboTexNeedsBind_)) {
166 // The shader takes over the responsibility for blending, so recompute.
167 ApplyStencilReplaceAndLogicOpIgnoreBlend(blendState.replaceAlphaWithStencil, blendState);
168
169 // We copy the framebuffer here, as doing so will wipe any blend state if we do it later.
170 if (fboTexNeedsBind_) {
171 // Note that this is positions, not UVs, that we need the copy from.
172 framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY);
173 // If we are rendering at a higher resolution, linear is probably best for the dest color.
174 renderManager->SetTextureSampler(1, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_LINEAR, GL_LINEAR, 0.0f);
175 fboTexBound_ = true;
176 fboTexNeedsBind_ = false;
177
178 framebufferManager_->RebindFramebuffer("RebindFramebuffer - ApplyDrawState");
179 // Must dirty blend state here so we re-copy next time. Example: Lunar's spell effects.
180 gstate_c.Dirty(DIRTY_BLEND_STATE);
181 }
182 } else {
183 // Until next time, force it off.
184 ResetFramebufferRead();
185 gstate_c.SetAllowFramebufferRead(false);
186 }
187 gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE);
188 } else if (blendState.resetFramebufferRead) {
189 ResetFramebufferRead();
190 }
191
192 if (blendState.enabled) {
193 if (blendState.dirtyShaderBlendFixValues) {
194 // Not quite sure how necessary this is.
195 gstate_c.Dirty(DIRTY_SHADERBLEND);
196 }
197 if (blendState.useBlendColor) {
198 uint32_t color = blendState.blendColor;
199 float col[4];
200 Uint8x4ToFloat4(col, color);
201 renderManager->SetBlendFactor(col);
202 }
203 }
204
205 int mask = (int)maskState.rgba[0] | ((int)maskState.rgba[1] << 1) | ((int)maskState.rgba[2] << 2) | ((int)maskState.rgba[3] << 3);
206 if (blendState.enabled) {
207 renderManager->SetBlendAndMask(mask, blendState.enabled,
208 glBlendFactorLookup[(size_t)blendState.srcColor], glBlendFactorLookup[(size_t)blendState.dstColor],
209 glBlendFactorLookup[(size_t)blendState.srcAlpha], glBlendFactorLookup[(size_t)blendState.dstAlpha],
210 glBlendEqLookup[(size_t)blendState.eqColor], glBlendEqLookup[(size_t)blendState.eqAlpha]);
211 } else {
212 renderManager->SetNoBlendAndMask(mask);
213 }
214
215 #ifndef USING_GLES2
216 if (gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP)) {
217 renderManager->SetLogicOp(gstate.isLogicOpEnabled() && gstate.getLogicOp() != GE_LOGIC_COPY,
218 logicOps[gstate.getLogicOp()]);
219 }
220 #endif
221 }
222 }
223
224 if (gstate_c.IsDirty(DIRTY_RASTER_STATE)) {
225 gstate_c.Clean(DIRTY_RASTER_STATE);
226
227 // Dither
228 bool dither = gstate.isDitherEnabled();
229 bool cullEnable;
230 GLenum cullMode = cullingMode[gstate.getCullMode() ^ !useBufferedRendering];
231
232 cullEnable = !gstate.isModeClear() && prim != GE_PRIM_RECTANGLES && gstate.isCullEnabled();
233
234 bool depthClampEnable = false;
235 if (gstate.isModeClear() || gstate.isModeThrough()) {
236 // TODO: Might happen in clear mode if not through...
237 depthClampEnable = false;
238 } else {
239 if (gstate.getDepthRangeMin() == 0 || gstate.getDepthRangeMax() == 65535) {
240 // TODO: Still has a bug where we clamp to depth range if one is not the full range.
241 // But the alternate is not clamping in either direction...
242 depthClampEnable = gstate.isDepthClampEnabled() && gstate_c.Supports(GPU_SUPPORTS_DEPTH_CLAMP);
243 } else {
244 // We just want to clip in this case, the clamp would be clipped anyway.
245 depthClampEnable = false;
246 }
247 }
248
249 renderManager->SetRaster(cullEnable, GL_CCW, cullMode, dither, depthClampEnable);
250 }
251
252 if (gstate_c.IsDirty(DIRTY_DEPTHSTENCIL_STATE)) {
253 gstate_c.Clean(DIRTY_DEPTHSTENCIL_STATE);
254 GenericStencilFuncState stencilState;
255 ConvertStencilFuncState(stencilState);
256
257 if (gstate.isModeClear()) {
258 // Depth Test
259 if (gstate.isClearModeDepthMask()) {
260 framebufferManager_->SetDepthUpdated();
261 }
262 renderManager->SetStencilFunc(gstate.isClearModeAlphaMask(), GL_ALWAYS, 0xFF, 0xFF);
263 renderManager->SetStencilOp(stencilState.writeMask, GL_REPLACE, GL_REPLACE, GL_REPLACE);
264 renderManager->SetDepth(true, gstate.isClearModeDepthMask() ? true : false, GL_ALWAYS);
265 } else {
266 // Depth Test
267 renderManager->SetDepth(gstate.isDepthTestEnabled(), gstate.isDepthWriteEnabled(), compareOps[gstate.getDepthTestFunction()]);
268 if (gstate.isDepthTestEnabled() && gstate.isDepthWriteEnabled()) {
269 framebufferManager_->SetDepthUpdated();
270 }
271
272 // Stencil Test
273 if (stencilState.enabled) {
274 renderManager->SetStencilFunc(stencilState.enabled, compareOps[stencilState.testFunc], stencilState.testRef, stencilState.testMask);
275 renderManager->SetStencilOp(stencilState.writeMask, stencilOps[stencilState.sFail], stencilOps[stencilState.zFail], stencilOps[stencilState.zPass]);
276 } else {
277 renderManager->SetStencilDisabled();
278 }
279 }
280 }
281
282 if (gstate_c.IsDirty(DIRTY_VIEWPORTSCISSOR_STATE)) {
283 gstate_c.Clean(DIRTY_VIEWPORTSCISSOR_STATE);
284 ConvertViewportAndScissor(useBufferedRendering,
285 framebufferManager_->GetRenderWidth(), framebufferManager_->GetRenderHeight(),
286 framebufferManager_->GetTargetBufferWidth(), framebufferManager_->GetTargetBufferHeight(),
287 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 if (vpAndScissor.dirtyProj) {
296 gstate_c.Dirty(DIRTY_PROJMATRIX);
297 }
298 if (vpAndScissor.dirtyDepth) {
299 gstate_c.Dirty(DIRTY_DEPTHRANGE);
300 }
301 }
302 }
303
ApplyDrawStateLate(bool setStencilValue,int stencilValue)304 void DrawEngineGLES::ApplyDrawStateLate(bool setStencilValue, int stencilValue) {
305 if (setStencilValue) {
306 render_->SetStencilFunc(GL_TRUE, GL_ALWAYS, stencilValue, 255);
307 }
308
309 // At this point, we know if the vertices are full alpha or not.
310 // TODO: Set the nearest/linear here (since we correctly know if alpha/color tests are needed)?
311 if (!gstate.isModeClear()) {
312 // Apply last, once we know the alpha params of the texture.
313 if (gstate.isAlphaTestEnabled() || gstate.isColorTestEnabled()) {
314 fragmentTestCache_->BindTestTexture(TEX_SLOT_ALPHATEST);
315 }
316 }
317 }
318