diff --git a/Common/GPU/OpenGL/GLQueueRunner.cpp b/Common/GPU/OpenGL/GLQueueRunner.cpp index 5a622c881f..1c7043475d 100644 --- a/Common/GPU/OpenGL/GLQueueRunner.cpp +++ b/Common/GPU/OpenGL/GLQueueRunner.cpp @@ -1281,10 +1281,12 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last tex->minFilter = c.textureSampler.minFilter; } CHECK_GL_ERROR_IF_DEBUG(); - if (tex->anisotropy != c.textureSampler.anisotropy) { - if (c.textureSampler.anisotropy != 0.0f) { - glTexParameterf(tex->target, GL_TEXTURE_MAX_ANISOTROPY_EXT, c.textureSampler.anisotropy); - } + // 0.0f means "don't care", used by callers that never want anisotropy. Note that we must + // only record the value when we actually set it, or we'd think we had reset the texture + // to something we never applied. + if (tex->anisotropy != c.textureSampler.anisotropy && c.textureSampler.anisotropy != 0.0f && caps_.anisoSupported) { + // Values above the device maximum are not allowed, and the minimum is 1.0. + glTexParameterf(tex->target, GL_TEXTURE_MAX_ANISOTROPY_EXT, std::max(1.0f, std::min(c.textureSampler.anisotropy, maxAnisotropyLevel_))); tex->anisotropy = c.textureSampler.anisotropy; } CHECK_GL_ERROR_IF_DEBUG(); diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 27919e9d5c..820c0244e0 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -195,6 +195,11 @@ SamplerCacheKey TextureCacheCommon::GetSamplingParams(int maxLevel, const TexCac key.aniso = false; key.texture3d = gstate_c.curTextureIs3D; + // Anisotropic filtering must stay off for CLUT8-indexed textures - what gets sampled there are + // palette indices that the shader depalettizes afterwards, and averaging indices gives garbage. + const bool canUseAniso = gstate_c.Use(GPU_USE_ANISOTROPY) && !flatZ && + !(entry && (entry->status & TexStatus::CLUT8_INDEXED)); + GETexLevelMode mipMode = gstate.getTexLevelMode(); bool autoMip = mipMode == GE_TEXLEVEL_MODE_AUTO; @@ -224,7 +229,7 @@ SamplerCacheKey TextureCacheCommon::GetSamplingParams(int maxLevel, const TexCac key.maxLevel = maxLevel * 256; key.minLevel = 0; key.lodBias = (int)(lodBias * 256.0f); - if (gstate_c.Use(GPU_USE_ANISOTROPY) && !flatZ) { + if (canUseAniso) { key.aniso = true; } break; @@ -260,7 +265,7 @@ SamplerCacheKey TextureCacheCommon::GetSamplingParams(int maxLevel, const TexCac key.mipEnable = true; key.mipFilt = 1; key.maxLevel = 9 * 256; - if (gstate_c.Use(GPU_USE_ANISOTROPY) && !flatZ) { + if (canUseAniso) { key.aniso = true; } } @@ -293,19 +298,17 @@ SamplerCacheKey TextureCacheCommon::GetSamplingParams(int maxLevel, const TexCac case TEX_FILTER_AUTO_MAX_QUALITY: default: forceFiltering = TEX_FILTER_AUTO_MAX_QUALITY; - if (gstate_c.Use(GPU_USE_ANISOTROPY) && !flatZ) { + if (canUseAniso) { key.aniso = true; } if (gstate.isModeThrough() && g_Config.iInternalResolution != 1) { bool uglyColorTest = gstate.isColorTestEnabled() && !IsColorTestTriviallyTrue() && gstate.getColorTestRef() != 0; if (uglyColorTest) { forceFiltering = TEX_FILTER_FORCE_NEAREST; - key.aniso = false; } } if (pixelMapped) { forceFiltering = TEX_FILTER_FORCE_NEAREST; - key.aniso = false; } break; } @@ -322,6 +325,10 @@ SamplerCacheKey TextureCacheCommon::GetSamplingParams(int maxLevel, const TexCac case TEX_FILTER_FORCE_NEAREST: key.magFilt = 0; key.minFilt = 0; + // Anisotropic filtering is meaningless without minification filtering, and every path that + // forces nearest does so to keep the texels exact - so clear it here rather than at each + // of the places that can set forceFiltering to nearest. + key.aniso = false; break; case TEX_FILTER_AUTO_MAX_QUALITY: // NOTE: We do not override magfilt here. If a game should have pixellated filtering, diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index 88a6d0b962..f778b8d451 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -258,15 +258,21 @@ void DrawEngineGLES::Flush() { lastUseHwTransform_ = useHWTransform; } - Shader *vshader = shaderManager_->ApplyVertexShader(useHWTransform, dec_->VertexType(), clipInfoFlags_, &vsid); - - useHWTransform = vshader->UseHWTransform(); // In case shader compilation failed and it fell back. However, this can no longer really happen... Need to fix this. - GLRBuffer *vertexBuffer = nullptr; GLRBuffer *indexBuffer = nullptr; uint32_t vertexBufferOffset = 0; uint32_t indexBufferOffset = 0; + Shader *vshader = shaderManager_->ApplyVertexShader(useHWTransform, dec_->VertexType(), clipInfoFlags_, &vsid); + if (!vshader) { + // Both the requested shader and the software transform fallback failed to compile. + // Not much we can do here, let's skip drawing. Note that the failure is cached. + WARN_LOG_N_TIMES(novshader, 5, Log::G3D, "Skipping draw, no vertex shader"); + goto bail; + } + + useHWTransform = vshader->UseHWTransform(); // In case shader compilation failed and it fell back. + if (useHWTransform) { if (lastVType_ & GE_VTYPE_WEIGHT_MASK) { // If software skinning, we're predecoding into "decoded". So make sure we're done, then push that content. @@ -284,7 +290,6 @@ void DrawEngineGLES::Flush() { int vertexCount; int maxIndex; bool useElements; - DecodeVerts(dec_, decoded_); DecodeIndsAndGetData(&prim, &vertexCount, &maxIndex, &useElements, false); gpuStats.perFrame.numVertsDrawn += vertexCount; @@ -317,6 +322,11 @@ void DrawEngineGLES::Flush() { ApplyDrawStateLate(false, 0); LinkedShader *program = shaderManager_->ApplyFragmentShader(vsid, vshader, pipelineState_, clipInfoFlags_, false); + if (!program) { + // Failed to link. No program is bound, so drawing would use whatever was bound before. + goto bail; + } + GLRInputLayout *inputLayout = SetupDecFmtForDraw(dec_->GetDecVtxFmt()); if (useElements) { render_->DrawIndexed(inputLayout, diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index 8301770348..d1a41862d4 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -886,9 +886,9 @@ struct CacheHeader { uint32_t version; uint32_t useFlags; uint32_t detectFlags; - int numVertexShaders; - int numFragmentShaders; - int numLinkedPrograms; + uint32_t numVertexShaders; + uint32_t numFragmentShaders; + uint32_t numLinkedPrograms; }; bool ShaderManagerGLES::LoadCacheFlags(File::IOFile &f, DrawEngineGLES *drawEngine) { @@ -942,7 +942,7 @@ bool ShaderManagerGLES::LoadCache(File::IOFile &f) { diskCachePending_.start = time_now_d(); diskCachePending_.Clear(); - // Sanity check the file contents + // Sanity check the file contents. Counts are now unsigned so this is enough. if (header.numFragmentShaders > 1000 || header.numVertexShaders > 1000 || header.numLinkedPrograms > 1000) { ERROR_LOG(Log::G3D, "Corrupt shader cache file header, aborting."); return false; @@ -959,13 +959,13 @@ bool ShaderManagerGLES::LoadCache(File::IOFile &f) { } diskCachePending_.vert.resize(header.numVertexShaders); - if (!f.ReadArray(&diskCachePending_.vert[0], header.numVertexShaders)) { + if (!f.ReadArray(diskCachePending_.vert.data(), header.numVertexShaders)) { diskCachePending_.vert.clear(); return false; } diskCachePending_.frag.resize(header.numFragmentShaders); - if (!f.ReadArray(&diskCachePending_.frag[0], header.numFragmentShaders)) { + if (!f.ReadArray(diskCachePending_.frag.data(), header.numFragmentShaders)) { diskCachePending_.vert.clear(); diskCachePending_.frag.clear(); return false; diff --git a/GPU/GLES/StateMappingGLES.cpp b/GPU/GLES/StateMappingGLES.cpp index 4b954a65a2..e16c208e81 100644 --- a/GPU/GLES/StateMappingGLES.cpp +++ b/GPU/GLES/StateMappingGLES.cpp @@ -293,7 +293,8 @@ void DrawEngineGLES::ApplyDrawState(int prim) { void DrawEngineGLES::ApplyDrawStateLate(bool setStencilValue, int stencilValue) { if (setStencilValue) { - render_->SetStencil(stencilState_.writeMask, GL_ALWAYS, stencilValue, 255, 0xFF, GL_REPLACE, GL_REPLACE, GL_REPLACE); + // NOTE: The write mask goes in the writeMask slot, not the enabled slot - see the clear-mode call above. + render_->SetStencil(true, GL_ALWAYS, stencilValue, 255, stencilState_.writeMask, GL_REPLACE, GL_REPLACE, GL_REPLACE); gstate_c.Dirty(DIRTY_DEPTHSTENCIL_STATE); // For the next time. } diff --git a/GPU/GLES/StencilBufferGLES.cpp b/GPU/GLES/StencilBufferGLES.cpp index 40ca65cb35..be3d713424 100644 --- a/GPU/GLES/StencilBufferGLES.cpp +++ b/GPU/GLES/StencilBufferGLES.cpp @@ -136,7 +136,7 @@ bool FramebufferManagerGLES::ReadbackStencilbuffer(Draw::Framebuffer *fbo, int x draw_->BindSamplerStates(TEX_SLOT_PSP_TEXTURE, 1, &stencilReadbackSampler_); // We must bind the program after starting the render pass. - draw_->SetScissorRect(0, 0, w, h); + draw_->SetScissorRect(x, y, w, h); draw_->BindPipeline(stencilReadbackPipeline_); // Fullscreen triangle coordinates. diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index 0e1ebb587e..8cd71a4dec 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -26,6 +26,8 @@ #include "Common/GPU/OpenGL/GLRenderManager.h" #include "Common/TimeUtil.h" +#include "Core/Config.h" + #include "GPU/ge_constants.h" #include "GPU/GPUState.h" #include "GPU/GPUDefinitions.h" @@ -99,7 +101,8 @@ void TextureCacheGLES::ApplySamplerByKey(const SamplerCacheKey &key) { render_->SetTextureLod(0, minLod, maxLod, lodBias); } - float aniso = 0.0f; + // 1.0 means no anisotropic filtering. The queue runner clamps to the device maximum. + float aniso = key.aniso ? (float)(1 << g_Config.iAnisotropyLevel) : 1.0f; int minKey = ((int)key.mipEnable << 2) | ((int)key.mipFilt << 1) | ((int)key.minFilt); render_->SetTextureSampler(0, key.sClamp ? GL_CLAMP_TO_EDGE : GL_REPEAT, key.tClamp ? GL_CLAMP_TO_EDGE : GL_REPEAT, @@ -230,6 +233,10 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry) { } else { _dbg_assert_(draw_->GetDeviceCaps().texture3DSupported); entry->textureName = render_->CreateTexture(GL_TEXTURE_3D, tw, th, plan.depth, 1); + // Set this together with creating the texture - it has to match the target of the object we + // just created even if we bail out below, or the shader gets generated with a 2D sampler + // for a 3D texture. + entry->status |= TexStatus::IS_3D; } // Apply some additional compatibility checks. @@ -314,6 +321,11 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry) { size_t dataSize = levelStride * plan.depth; u8 *data = (u8 *)AllocateAlignedMemory(dataSize, 16); _assert_msg_(data != nullptr, "Failed to allocate aligned memory for 3d texture: %d bytes", (int)dataSize); + if (!data) { + ERROR_LOG(Log::G3D, "Ran out of RAM trying to allocate a temporary 3D texture upload buffer (%dx%dx%d)", plan.w, plan.h, plan.depth); + return; + } + memset(data, 0, levelStride * plan.depth); u8 *p = data; @@ -324,9 +336,6 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry) { render_->TextureImage(entry->textureName, 0, plan.w * plan.scaleFactor, plan.h * plan.scaleFactor, plan.depth, dstFmt, data, GLRAllocType::ALIGNED); - // Signal that we support depth textures so use it as one. - entry->status |= TexStatus::IS_3D; - render_->FinalizeTexture(entry->textureName, 1, false); }