CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/GPU/GLES/StateMappingGLES.cpp
Views: 1401
// Copyright (c) 2012- PPSSPP Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.161718// Alpha/stencil is a convoluted mess. Some good comments are here:19// https://github.com/hrydgard/ppsspp/issues/37682021#include "ppsspp_config.h"22#include "StateMappingGLES.h"23#include "Common/Profiler/Profiler.h"24#include "Common/GPU/OpenGL/GLDebugLog.h"25#include "Common/GPU/OpenGL/GLRenderManager.h"26#include "Common/Data/Convert/SmallDataConvert.h"2728#include "GPU/Math3D.h"29#include "GPU/GPUState.h"30#include "GPU/ge_constants.h"31#include "Core/System.h"32#include "Core/Config.h"33#include "GPU/GLES/GPU_GLES.h"34#include "GPU/GLES/ShaderManagerGLES.h"35#include "GPU/GLES/TextureCacheGLES.h"36#include "GPU/GLES/FramebufferManagerGLES.h"37#include "GPU/Common/FragmentShaderGenerator.h"3839static const GLushort glBlendFactorLookup[(size_t)BlendFactor::COUNT] = {40GL_ZERO,41GL_ONE,42GL_SRC_COLOR,43GL_ONE_MINUS_SRC_COLOR,44GL_DST_COLOR,45GL_ONE_MINUS_DST_COLOR,46GL_SRC_ALPHA,47GL_ONE_MINUS_SRC_ALPHA,48GL_DST_ALPHA,49GL_ONE_MINUS_DST_ALPHA,50GL_CONSTANT_COLOR,51GL_ONE_MINUS_CONSTANT_COLOR,52GL_CONSTANT_ALPHA,53GL_ONE_MINUS_CONSTANT_ALPHA,54#if !defined(USING_GLES2) // TODO: Remove when we have better headers55GL_SRC1_COLOR,56GL_ONE_MINUS_SRC1_COLOR,57GL_SRC1_ALPHA,58GL_ONE_MINUS_SRC1_ALPHA,59#elif !PPSSPP_PLATFORM(IOS)60GL_SRC1_COLOR_EXT,61GL_ONE_MINUS_SRC1_COLOR_EXT,62GL_SRC1_ALPHA_EXT,63GL_ONE_MINUS_SRC1_ALPHA_EXT,64#else65GL_INVALID_ENUM,66GL_INVALID_ENUM,67GL_INVALID_ENUM,68GL_INVALID_ENUM,69#endif70GL_INVALID_ENUM,71};7273static const GLushort glBlendEqLookup[(size_t)BlendEq::COUNT] = {74GL_FUNC_ADD,75GL_FUNC_SUBTRACT,76GL_FUNC_REVERSE_SUBTRACT,77GL_MIN,78GL_MAX,79};8081static const GLushort cullingMode[] = {82GL_FRONT,83GL_BACK,84};8586static const GLushort compareOps[] = {87GL_NEVER, GL_ALWAYS, GL_EQUAL, GL_NOTEQUAL,88GL_LESS, GL_LEQUAL, GL_GREATER, GL_GEQUAL,89};9091static const GLushort stencilOps[] = {92GL_KEEP,93GL_ZERO,94GL_REPLACE,95GL_INVERT,96GL_INCR,97GL_DECR,98GL_KEEP, // reserved99GL_KEEP, // reserved100};101102#if !defined(USING_GLES2)103static const GLushort logicOps[] = {104GL_CLEAR,105GL_AND,106GL_AND_REVERSE,107GL_COPY,108GL_AND_INVERTED,109GL_NOOP,110GL_XOR,111GL_OR,112GL_NOR,113GL_EQUIV,114GL_INVERT,115GL_OR_REVERSE,116GL_COPY_INVERTED,117GL_OR_INVERTED,118GL_NAND,119GL_SET,120};121#endif122123void DrawEngineGLES::ApplyDrawState(int prim) {124GLRenderManager *renderManager = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);125126if (!gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE)) {127// Nothing to do, let's early-out128return;129}130131// Start profiling here to skip SetTexture which is already accounted for132PROFILE_THIS_SCOPE("applydrawstate");133134uint64_t dirtyRequiresRecheck_ = 0;135bool useBufferedRendering = framebufferManager_->UseBufferedRendering();136137if (gstate_c.IsDirty(DIRTY_BLEND_STATE)) {138if (gstate.isModeClear()) {139// Color Test140bool colorMask = gstate.isClearModeColorMask();141bool alphaMask = gstate.isClearModeAlphaMask();142renderManager->SetNoBlendAndMask((colorMask ? 7 : 0) | (alphaMask ? 8 : 0));143} else {144pipelineState_.Convert(draw_->GetShaderLanguageDesc().bitwiseOps);145GenericMaskState &maskState = pipelineState_.maskState;146GenericBlendState &blendState = pipelineState_.blendState;147GenericLogicState &logicState = pipelineState_.logicState;148149if (pipelineState_.FramebufferRead()) {150FBOTexState fboTexBindState = FBO_TEX_NONE;151ApplyFramebufferRead(&fboTexBindState);152// The shader takes over the responsibility for blending, so recompute.153ApplyStencilReplaceAndLogicOpIgnoreBlend(blendState.replaceAlphaWithStencil, blendState);154155// We copy the framebuffer here, as doing so will wipe any blend state if we do it later.156// fboTexNeedsBind_ won't be set if we can read directly from the target.157if (fboTexBindState == FBO_TEX_COPY_BIND_TEX) {158// Note that this is positions, not UVs, that we need the copy from.159framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY | BINDFBCOLOR_UNCACHED, 0);160// If we are rendering at a higher resolution, linear is probably best for the dest color.161renderManager->SetTextureSampler(1, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_LINEAR, GL_LINEAR, 0.0f);162fboTexBound_ = true;163164framebufferManager_->RebindFramebuffer("RebindFramebuffer - ApplyDrawState");165// Must dirty blend state here so we re-copy next time. Example: Lunar's spell effects.166dirtyRequiresRecheck_ |= DIRTY_BLEND_STATE;167gstate_c.Dirty(DIRTY_BLEND_STATE);168} else if (fboTexBindState == FBO_TEX_READ_FRAMEBUFFER) {169// No action needed here.170fboTexBindState = FBO_TEX_NONE;171}172dirtyRequiresRecheck_ |= DIRTY_FRAGMENTSHADER_STATE;173gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE);174} else {175if (fboTexBound_) {176GLRenderManager *renderManager = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);177renderManager->BindTexture(TEX_SLOT_SHADERBLEND_SRC, nullptr);178fboTexBound_ = false;179dirtyRequiresRecheck_ |= DIRTY_FRAGMENTSHADER_STATE;180gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE);181}182}183184if (blendState.blendEnabled) {185if (blendState.dirtyShaderBlendFixValues) {186// Not quite sure how necessary this is.187dirtyRequiresRecheck_ |= DIRTY_SHADERBLEND;188gstate_c.Dirty(DIRTY_SHADERBLEND);189}190if (blendState.useBlendColor) {191uint32_t color = blendState.blendColor;192float col[4];193Uint8x4ToFloat4(col, color);194renderManager->SetBlendFactor(col);195}196}197198int mask = (int)maskState.channelMask;199if (blendState.blendEnabled) {200renderManager->SetBlendAndMask(mask, blendState.blendEnabled,201glBlendFactorLookup[(size_t)blendState.srcColor], glBlendFactorLookup[(size_t)blendState.dstColor],202glBlendFactorLookup[(size_t)blendState.srcAlpha], glBlendFactorLookup[(size_t)blendState.dstAlpha],203glBlendEqLookup[(size_t)blendState.eqColor], glBlendEqLookup[(size_t)blendState.eqAlpha]);204} else {205renderManager->SetNoBlendAndMask(mask);206}207208// TODO: Get rid of the ifdef209#ifndef USING_GLES2210if (gstate_c.Use(GPU_USE_LOGIC_OP)) {211renderManager->SetLogicOp(logicState.logicOpEnabled, logicOps[(int)logicState.logicOp]);212}213#endif214}215}216217if (gstate_c.IsDirty(DIRTY_RASTER_STATE)) {218// Dither219bool dither = gstate.isDitherEnabled();220bool cullEnable;221GLenum cullMode = cullingMode[gstate.getCullMode() ^ !useBufferedRendering];222223cullEnable = !gstate.isModeClear() && prim != GE_PRIM_RECTANGLES && prim > GE_PRIM_LINE_STRIP && gstate.isCullEnabled();224225bool depthClampEnable = false;226if (gstate.isModeClear() || gstate.isModeThrough()) {227// TODO: Might happen in clear mode if not through...228depthClampEnable = false;229} else {230if (gstate.getDepthRangeMin() == 0 || gstate.getDepthRangeMax() == 65535) {231// TODO: Still has a bug where we clamp to depth range if one is not the full range.232// But the alternate is not clamping in either direction...233depthClampEnable = gstate.isDepthClampEnabled() && gstate_c.Use(GPU_USE_DEPTH_CLAMP);234} else {235// We just want to clip in this case, the clamp would be clipped anyway.236depthClampEnable = false;237}238}239240renderManager->SetRaster(cullEnable, GL_CCW, cullMode, dither, depthClampEnable);241}242243if (gstate_c.IsDirty(DIRTY_DEPTHSTENCIL_STATE)) {244ConvertStencilFuncState(stencilState_);245246if (gstate.isModeClear()) {247renderManager->SetStencil(248gstate.isClearModeAlphaMask(), GL_ALWAYS, 0xFF, 0xFF,249stencilState_.writeMask, GL_REPLACE, GL_REPLACE, GL_REPLACE);250renderManager->SetDepth(true, gstate.isClearModeDepthMask() ? true : false, GL_ALWAYS);251} else {252// Depth Test253bool depthTestUsed = !IsDepthTestEffectivelyDisabled();254renderManager->SetDepth(depthTestUsed, gstate.isDepthWriteEnabled(), compareOps[gstate.getDepthTestFunction()]);255if (depthTestUsed)256UpdateEverUsedEqualDepth(gstate.getDepthTestFunction());257258// Stencil Test259if (stencilState_.enabled) {260renderManager->SetStencil(261stencilState_.enabled, compareOps[stencilState_.testFunc], stencilState_.testRef, stencilState_.testMask,262stencilState_.writeMask, stencilOps[stencilState_.sFail], stencilOps[stencilState_.zFail], stencilOps[stencilState_.zPass]);263264// Nasty special case for Spongebob and similar where it tries to write zeros to alpha/stencil during265// depth-fail. We can't write to alpha then because the pixel is killed. However, we can invert the depth266// test and modify the alpha function...267if (SpongebobDepthInverseConditions(stencilState_)) {268renderManager->SetBlendAndMask(0x8, true, GL_ZERO, GL_ZERO, GL_ZERO, GL_ZERO, GL_FUNC_ADD, GL_FUNC_ADD);269renderManager->SetDepth(true, false, GL_LESS);270renderManager->SetStencil(true, GL_ALWAYS, 0xFF, 0xFF, 0xFF, GL_ZERO, GL_KEEP, GL_ZERO);271272dirtyRequiresRecheck_ |= DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE;273gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE);274}275} else {276renderManager->SetStencilDisabled();277}278}279}280281if (gstate_c.IsDirty(DIRTY_VIEWPORTSCISSOR_STATE)) {282ConvertViewportAndScissor(useBufferedRendering,283framebufferManager_->GetRenderWidth(), framebufferManager_->GetRenderHeight(),284framebufferManager_->GetTargetBufferWidth(), framebufferManager_->GetTargetBufferHeight(),285vpAndScissor_);286UpdateCachedViewportState(vpAndScissor_);287288renderManager->SetScissor(GLRect2D{ vpAndScissor_.scissorX, vpAndScissor_.scissorY, vpAndScissor_.scissorW, vpAndScissor_.scissorH });289renderManager->SetViewport({290vpAndScissor_.viewportX, vpAndScissor_.viewportY,291vpAndScissor_.viewportW, vpAndScissor_.viewportH,292vpAndScissor_.depthRangeMin, vpAndScissor_.depthRangeMax });293}294295gstate_c.Clean(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_BLEND_STATE);296gstate_c.Dirty(dirtyRequiresRecheck_);297dirtyRequiresRecheck_ = 0;298}299300void DrawEngineGLES::ApplyDrawStateLate(bool setStencilValue, int stencilValue) {301if (setStencilValue) {302render_->SetStencil(stencilState_.writeMask, GL_ALWAYS, stencilValue, 255, 0xFF, GL_REPLACE, GL_REPLACE, GL_REPLACE);303gstate_c.Dirty(DIRTY_DEPTHSTENCIL_STATE); // For the next time.304}305306// At this point, we know if the vertices are full alpha or not.307// TODO: Set the nearest/linear here (since we correctly know if alpha/color tests are needed)?308if (!gstate.isModeClear() && gstate_c.Use(GPU_USE_FRAGMENT_TEST_CACHE)) {309// Apply last, once we know the alpha params of the texture.310if (gstate.isAlphaTestEnabled() || gstate.isColorTestEnabled()) {311fragmentTestCache_->BindTestTexture(TEX_SLOT_ALPHATEST);312}313}314}315316317