mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-09-07 21:23:29 +02:00
Merge pull request #22214 from hrydgard/gles-fixes
Claude code review: OpenGL backend
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user