From 245ed61c0ffcde70f172596307563ae18b54d4c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 3 Sep 2026 10:08:51 -0600 Subject: [PATCH 1/7] GLES: Actually apply the stencil write mask in ApplyDrawStateLate 53aa2cc596 changed the first argument from "true" to stencilState_.writeMask, but that slot is "bool enabled" - the writeMask argument stayed hardcoded to 0xFF, so the mask still never reached GL. The clear-mode call just above gets the slots right. Reachable because SoftwareTransformCommon refuses the fast clear path when the stencil write mask is partial, so exactly those clears end up here. --- GPU/GLES/StateMappingGLES.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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. } From ad131e522f420c63fef6666bf6d49f07246c5c46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 3 Sep 2026 10:10:16 -0600 Subject: [PATCH 2/7] GLES: Handle shader compilation failure in the hardware transform path ApplyVertexShader can return null - if the requested shader fails to compile it retries with a software transform ID, and if that fails too it returns (and caches) null. We then called UseHWTransform() on it. ApplyFragmentShader can likewise return null, and the hardware path ignored it, unlike the software path. Without a linked shader nothing binds a program for this render pass, so the draw would have gone through with whatever program a previous pass left bound. --- GPU/GLES/DrawEngineGLES.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index 88a6d0b962..c9b7e0c1a1 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -258,15 +258,20 @@ 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. + 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. @@ -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, From 388eef9d880171d2821c1322a838c05735ba1d54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 3 Sep 2026 10:10:56 -0600 Subject: [PATCH 3/7] GLES: Harden the shader disk cache loader and the 3D texture upload path The cache loader indexed &vec[0] on vectors that can legitimately be empty (a header-sized file with zero counts passes both sanity checks), and the counts are signed ints where only the upper bound was checked - a negative count would reach resize() as a huge size_t. The 3D texture branch had the out-of-memory assert but not the bail-out the 2D branch has, so an ignored assert fell straight into memset(nullptr). --- GPU/GLES/ShaderManagerGLES.cpp | 12 ++++++++---- GPU/GLES/TextureCacheGLES.cpp | 5 +++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index 8301770348..9823b4507d 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -942,8 +942,10 @@ bool ShaderManagerGLES::LoadCache(File::IOFile &f) { diskCachePending_.start = time_now_d(); diskCachePending_.Clear(); - // Sanity check the file contents - if (header.numFragmentShaders > 1000 || header.numVertexShaders > 1000 || header.numLinkedPrograms > 1000) { + // Sanity check the file contents. Note that the counts are signed, so check for negative too - + // otherwise they'd turn into huge sizes in the resize() calls below. + if (header.numFragmentShaders > 1000 || header.numVertexShaders > 1000 || header.numLinkedPrograms > 1000 || + header.numFragmentShaders < 0 || header.numVertexShaders < 0 || header.numLinkedPrograms < 0) { ERROR_LOG(Log::G3D, "Corrupt shader cache file header, aborting."); return false; } @@ -958,14 +960,16 @@ bool ShaderManagerGLES::LoadCache(File::IOFile &f) { return false; } + // Note: ReadArray gets .data(), not &v[0] - the counts can legitimately be zero, + // and indexing an empty vector is UB (and asserts in the debug STL). 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/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index 0e1ebb587e..b5d96717ae 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -314,6 +314,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; From 89ebb8acc64fcbb48c795e7b90d7357acd05dc8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 3 Sep 2026 10:14:07 -0600 Subject: [PATCH 4/7] GLES: Actually apply anisotropic filtering TextureCacheGLES passed a hardcoded 0.0f instead of key.aniso, so the Anisotropic Filtering setting did nothing at all on the OpenGL backend, even though GPU_USE_ANISOTROPY was advertised and D3D11/Vulkan both honor it. Looks like it was left behind by the 2017 render manager refactor. The queue runner now clamps to the device maximum it already queried into maxAnisotropyLevel_ (until now unused), and only touches the parameter when the extension is actually supported - the anisotropy branch there has been dead since every caller passed 0.0f, so this is the first time it runs. 0.0f keeps its meaning of "don't care" for the CLUT/fragment-test/thin3d callers; the texture cache now passes 1.0f when the setting is off, so turning it off takes effect on already-uploaded textures instead of only new ones. TexCache: Never use anisotropic filtering for CLUT8-indexed textures What gets sampled for those is palette indices, depalettized by the shader afterwards - averaging indices across an anisotropic footprint produces garbage colors. Affects all backends, not just the GL one that just started honoring key.aniso. TexCache: Clear key.aniso wherever filtering is forced to nearest It was only cleared in the two places inside the AUTO_MAX_QUALITY branch, so the TEX_FILTER_AUTO path (pixel-mapped textures, the ugly color test heuristic), the FORCE_NEAREST setting and the replacement-texture override could all end up requesting nearest filtering with anisotropy still on. Doing it in the switch that applies forceFiltering covers every path, so it can't drift apart again. GLES: Only record the applied anisotropy, and log skipped draws The queue runner updated tex->anisotropy even when it skipped the call because the value was 0.0f ("don't care") - harmless while nothing ever set anisotropy, but now it would make the tracked state disagree with GL, so a later request for the value it thinks is set would be wrongly skipped. Also log when a draw is skipped for a missing vertex shader. The failure is cached per shader ID, so without it geometry silently disappears for the rest of the session after the one-shot OSD message. --- Common/GPU/OpenGL/GLQueueRunner.cpp | 10 ++++++---- GPU/Common/TextureCacheCommon.cpp | 17 ++++++++++++----- GPU/GLES/DrawEngineGLES.cpp | 5 ++++- GPU/GLES/TextureCacheGLES.cpp | 5 ++++- 4 files changed, 26 insertions(+), 11 deletions(-) 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 c9b7e0c1a1..eb8220c2b0 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -266,7 +266,10 @@ void DrawEngineGLES::Flush() { 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. + // Not much we can do here, let's skip drawing. Note that the failure is cached, so this + // will keep happening for this shader ID - hence the log, or geometry would just silently + // disappear for the rest of the session. + WARN_LOG_N_TIMES(novshader, 5, Log::G3D, "Skipping draw, no vertex shader"); goto bail; } diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index b5d96717ae..9e137ac5d2 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, From 78692deca2a0f1248a09d536e14a172ce1ce58f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 3 Sep 2026 10:37:55 -0600 Subject: [PATCH 5/7] GLES: Set IS_3D when creating the 3D texture, not after uploading it The out-of-memory bail-out added in the previous commit returned before the status flag was set, leaving a GL_TEXTURE_3D object bound while ApplyTexture told the shader generator it was a 2D texture. The entry stays cached, so it would repeat every frame, not just the one that failed to allocate. --- GPU/GLES/TextureCacheGLES.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index 9e137ac5d2..8cd71a4dec 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -233,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. @@ -332,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); } From ac016201dc118d506dcd2ad83e2bd6c7c154a263 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 3 Sep 2026 10:57:33 -0600 Subject: [PATCH 6/7] GLES: Small cleanups Scissor the stencil readback to the region actually being read back - latent, every caller passes a zero origin today. Remove a DecodeVerts call that can never do anything: both branches above it have already advanced decodeVertsCounter_ to numDrawVerts_. Worse than useless, since in the non-skinning branch the vertices went to the push buffer, so decoded_ doesn't hold them. --- GPU/GLES/DrawEngineGLES.cpp | 1 - GPU/GLES/StencilBufferGLES.cpp | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index eb8220c2b0..8364ca30ef 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -292,7 +292,6 @@ void DrawEngineGLES::Flush() { int vertexCount; int maxIndex; bool useElements; - DecodeVerts(dec_, decoded_); DecodeIndsAndGetData(&prim, &vertexCount, &maxIndex, &useElements, false); gpuStats.perFrame.numVertsDrawn += vertexCount; diff --git a/GPU/GLES/StencilBufferGLES.cpp b/GPU/GLES/StencilBufferGLES.cpp index 40ca65cb35..19b94d21bb 100644 --- a/GPU/GLES/StencilBufferGLES.cpp +++ b/GPU/GLES/StencilBufferGLES.cpp @@ -136,7 +136,9 @@ 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); + // Note: scissor to the region we're about to read back, not to (0, 0, w, h) - every caller + // passes (0, 0) today, but with a nonzero origin the copy below would read scissored-away pixels. + draw_->SetScissorRect(x, y, w, h); draw_->BindPipeline(stencilReadbackPipeline_); // Fullscreen triangle coordinates. From 256984d5613b26b3ead78b341f0518e703601a0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 4 Sep 2026 10:19:13 -0600 Subject: [PATCH 7/7] Fix some Claude-isms --- GPU/GLES/DrawEngineGLES.cpp | 4 +--- GPU/GLES/ShaderManagerGLES.cpp | 14 +++++--------- GPU/GLES/StencilBufferGLES.cpp | 2 -- 3 files changed, 6 insertions(+), 14 deletions(-) diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index 8364ca30ef..f778b8d451 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -266,9 +266,7 @@ void DrawEngineGLES::Flush() { 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, so this - // will keep happening for this shader ID - hence the log, or geometry would just silently - // disappear for the rest of the session. + // 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; } diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index 9823b4507d..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,10 +942,8 @@ bool ShaderManagerGLES::LoadCache(File::IOFile &f) { diskCachePending_.start = time_now_d(); diskCachePending_.Clear(); - // Sanity check the file contents. Note that the counts are signed, so check for negative too - - // otherwise they'd turn into huge sizes in the resize() calls below. - if (header.numFragmentShaders > 1000 || header.numVertexShaders > 1000 || header.numLinkedPrograms > 1000 || - header.numFragmentShaders < 0 || header.numVertexShaders < 0 || header.numLinkedPrograms < 0) { + // 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; } @@ -960,8 +958,6 @@ bool ShaderManagerGLES::LoadCache(File::IOFile &f) { return false; } - // Note: ReadArray gets .data(), not &v[0] - the counts can legitimately be zero, - // and indexing an empty vector is UB (and asserts in the debug STL). diskCachePending_.vert.resize(header.numVertexShaders); if (!f.ReadArray(diskCachePending_.vert.data(), header.numVertexShaders)) { diskCachePending_.vert.clear(); diff --git a/GPU/GLES/StencilBufferGLES.cpp b/GPU/GLES/StencilBufferGLES.cpp index 19b94d21bb..be3d713424 100644 --- a/GPU/GLES/StencilBufferGLES.cpp +++ b/GPU/GLES/StencilBufferGLES.cpp @@ -136,8 +136,6 @@ 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. - // Note: scissor to the region we're about to read back, not to (0, 0, w, h) - every caller - // passes (0, 0) today, but with a nonzero origin the copy below would read scissored-away pixels. draw_->SetScissorRect(x, y, w, h); draw_->BindPipeline(stencilReadbackPipeline_);