diff --git a/Common/Data/Collections/Hashmaps.h b/Common/Data/Collections/Hashmaps.h index ca5de0540477..939cb8eda994 100644 --- a/Common/Data/Collections/Hashmaps.h +++ b/Common/Data/Collections/Hashmaps.h @@ -29,7 +29,7 @@ enum class BucketState : uint8_t { // we always use very small values, so it's probably better to have them in the same // cache-line as the corresponding key. // Enforces that value are pointers to make sure that combined storage makes sense. -template +template class DenseHashMap { public: DenseHashMap(int initialCapacity) : capacity_(initialCapacity) { @@ -37,23 +37,44 @@ class DenseHashMap { state.resize(initialCapacity); } - // Returns nullptr if no entry was found. - Value Get(const Key &key) { + // Returns true if the entry was found, and writes the entry to *value. + // Returns false and does not write to value if no entry was found. + // Note that nulls can be stored. + bool Get(const Key &key, Value *value) const { uint32_t mask = capacity_ - 1; uint32_t pos = HashKey(key) & mask; // No? Let's go into search mode. Linear probing. uint32_t p = pos; while (true) { - if (state[p] == BucketState::TAKEN && KeyEquals(key, map[p].key)) - return map[p].value; - else if (state[p] == BucketState::FREE) - return NullValue; + if (state[p] == BucketState::TAKEN && KeyEquals(key, map[p].key)) { + *value = map[p].value; + return true; + } else if (state[p] == BucketState::FREE) { + return false; + } p = (p + 1) & mask; // If the state is REMOVED, we just keep on walking. if (p == pos) { + // We looped around the whole map. _assert_msg_(false, "DenseHashMap: Hit full on Get()"); } } - return NullValue; + return false; + } + + // Only works if Value can be nullptr + Value GetOrNull(const Key &key) const { + Value value; + if (Get(key, &value)) { + return value; + } else { + return (Value)nullptr; + } + } + + bool ContainsKey(const Key &key) const { + // Slightly wasteful. + Value value; + return Get(key, &value); } // Asserts if we already had the key! @@ -190,7 +211,7 @@ class DenseHashMap { // Like the above, uses linear probing for cache-friendliness. // Does not perform hashing at all so expects well-distributed keys. -template +template class PrehashMap { public: PrehashMap(int initialCapacity) : capacity_(initialCapacity) { @@ -199,22 +220,24 @@ class PrehashMap { } // Returns nullptr if no entry was found. - Value Get(uint32_t hash) { + bool Get(uint32_t hash, Value *value) { uint32_t mask = capacity_ - 1; uint32_t pos = hash & mask; // No? Let's go into search mode. Linear probing. uint32_t p = pos; while (true) { - if (state[p] == BucketState::TAKEN && hash == map[p].hash) - return map[p].value; - else if (state[p] == BucketState::FREE) - return NullValue; + if (state[p] == BucketState::TAKEN && hash == map[p].hash) { + *value = map[p].value; + return true; + } else if (state[p] == BucketState::FREE) { + return false; + } p = (p + 1) & mask; // If the state is REMOVED, we just keep on walking. if (p == pos) { _assert_msg_(false, "PrehashMap: Hit full on Get()"); } } - return NullValue; + return false; } // Returns false if we already had the key! Which is a bit different. diff --git a/Common/GPU/Vulkan/VulkanFrameData.h b/Common/GPU/Vulkan/VulkanFrameData.h index 8b014dfff5ce..9723c779bffe 100644 --- a/Common/GPU/Vulkan/VulkanFrameData.h +++ b/Common/GPU/Vulkan/VulkanFrameData.h @@ -99,13 +99,13 @@ struct FrameData { // Frames need unique IDs to wait for present on, let's keep them here. // Also used for indexing into the frame timing history buffer. - uint64_t frameId; + uint64_t frameId = 0; // Profiling. QueueProfileContext profile{}; // Async readback cache. - DenseHashMap readbacks_; + DenseHashMap readbacks_; FrameData() : readbacks_(8) {} diff --git a/Common/GPU/Vulkan/VulkanQueueRunner.cpp b/Common/GPU/Vulkan/VulkanQueueRunner.cpp index 774680d8f12b..99b82cc50a1e 100644 --- a/Common/GPU/Vulkan/VulkanQueueRunner.cpp +++ b/Common/GPU/Vulkan/VulkanQueueRunner.cpp @@ -251,8 +251,8 @@ void VulkanQueueRunner::DestroyBackBuffers() { // Self-dependency: https://github.com/gpuweb/gpuweb/issues/442#issuecomment-547604827 // Also see https://www.khronos.org/registry/vulkan/specs/1.3-extensions/html/vkspec.html#synchronization-pipeline-barriers-subpass-self-dependencies VKRRenderPass *VulkanQueueRunner::GetRenderPass(const RPKey &key) { - auto foundPass = renderPasses_.Get(key); - if (foundPass) { + VKRRenderPass *foundPass; + if (renderPasses_.Get(key, &foundPass)) { return foundPass; } @@ -1984,8 +1984,7 @@ void VulkanQueueRunner::PerformReadback(const VKRStep &step, VkCommandBuffer cmd key.height = step.readback.srcRect.extent.height; // See if there's already a buffer we can reuse - cached = frameData.readbacks_.Get(key); - if (!cached) { + if (!frameData.readbacks_.Get(key, &cached)) { cached = new CachedReadback(); cached->bufferSize = 0; frameData.readbacks_.Insert(key, cached); @@ -2065,8 +2064,8 @@ bool VulkanQueueRunner::CopyReadbackBuffer(FrameData &frameData, VKRFramebuffer key.framebuf = src; key.width = width; key.height = height; - CachedReadback *cached = frameData.readbacks_.Get(key); - if (cached) { + CachedReadback *cached; + if (frameData.readbacks_.Get(key, &cached)) { readback = cached; } else { // Didn't have a cached image ready yet diff --git a/Common/GPU/Vulkan/VulkanQueueRunner.h b/Common/GPU/Vulkan/VulkanQueueRunner.h index 161e4299823a..9c1bd765a4b1 100644 --- a/Common/GPU/Vulkan/VulkanQueueRunner.h +++ b/Common/GPU/Vulkan/VulkanQueueRunner.h @@ -322,7 +322,7 @@ class VulkanQueueRunner { // Renderpasses, all combinations of preserving or clearing or dont-care-ing fb contents. // Each VKRRenderPass contains all compatibility classes (which attachments they have, etc). - DenseHashMap renderPasses_; + DenseHashMap renderPasses_; // Readback buffer. Currently we only support synchronous readback, so we only really need one. // We size it generously. diff --git a/GPU/Common/DrawEngineCommon.cpp b/GPU/Common/DrawEngineCommon.cpp index 71439d028c27..663c0495fe91 100644 --- a/GPU/Common/DrawEngineCommon.cpp +++ b/GPU/Common/DrawEngineCommon.cpp @@ -62,8 +62,8 @@ void DrawEngineCommon::Init() { } VertexDecoder *DrawEngineCommon::GetVertexDecoder(u32 vtype) { - VertexDecoder *dec = decoderMap_.Get(vtype); - if (dec) + VertexDecoder *dec; + if (decoderMap_.Get(vtype, &dec)) return dec; dec = new VertexDecoder(); _assert_(dec); @@ -132,8 +132,12 @@ std::vector DrawEngineCommon::DebugGetVertexLoaderIDs() { std::string DrawEngineCommon::DebugGetVertexLoaderString(std::string id, DebugShaderStringType stringType) { u32 mapId; memcpy(&mapId, &id[0], sizeof(mapId)); - VertexDecoder *dec = decoderMap_.Get(mapId); - return dec ? dec->GetString(stringType) : "N/A"; + VertexDecoder *dec; + if (decoderMap_.Get(mapId, &dec)) { + return dec->GetString(stringType); + } else { + return "N/A"; + } } static Vec3f ClipToScreen(const Vec4f& coords) { diff --git a/GPU/Common/DrawEngineCommon.h b/GPU/Common/DrawEngineCommon.h index beb3e2775733..21c0476a4400 100644 --- a/GPU/Common/DrawEngineCommon.h +++ b/GPU/Common/DrawEngineCommon.h @@ -201,7 +201,7 @@ class DrawEngineCommon { // Cached vertex decoders u32 lastVType_ = -1; // corresponds to dec_. Could really just pick it out of dec_... - DenseHashMap decoderMap_; + DenseHashMap decoderMap_; VertexDecoder *dec_ = nullptr; VertexDecoderJitCache *decJitCache_ = nullptr; VertexDecoderOptions decOptions_{}; diff --git a/GPU/D3D11/DrawEngineD3D11.cpp b/GPU/D3D11/DrawEngineD3D11.cpp index 2068d57118ea..7780bfa28a26 100644 --- a/GPU/D3D11/DrawEngineD3D11.cpp +++ b/GPU/D3D11/DrawEngineD3D11.cpp @@ -199,8 +199,8 @@ ID3D11InputLayout *DrawEngineD3D11::SetupDecFmtForDraw(D3D11VertexShader *vshade // TODO: Instead of one for each vshader, we can reduce it to one for each type of shader // that reads TEXCOORD or not, etc. Not sure if worth it. const InputLayoutKey key{ vshader, decFmt.id }; - ID3D11InputLayout *inputLayout = inputLayoutMap_.Get(key); - if (inputLayout) { + ID3D11InputLayout *inputLayout; + if (inputLayoutMap_.Get(key, &inputLayout)) { return inputLayout; } else { D3D11_INPUT_ELEMENT_DESC VertexElements[8]; @@ -368,8 +368,8 @@ void DrawEngineD3D11::DoFlush() { // getUVGenMode can have an effect on which UV decoder we need to use! And hence what the decoded data will look like. See #9263 u32 dcid = (u32)XXH3_64bits(&drawCalls_, sizeof(DeferredDrawCall) * numDrawCalls_) ^ gstate.getUVGenMode(); - VertexArrayInfoD3D11 *vai = vai_.Get(dcid); - if (!vai) { + VertexArrayInfoD3D11 *vai; + if (!vai_.Get(dcid, &vai)) { vai = new VertexArrayInfoD3D11(); vai_.Insert(dcid, vai); } @@ -663,8 +663,8 @@ void DrawEngineD3D11::DoFlush() { // We really do need a vertex layout for each vertex shader (or at least check its ID bits for what inputs it uses)! // Some vertex shaders ignore one of the inputs, and then the layout created from it will lack it, which will be a problem for others. InputLayoutKey key{ vshader, 0xFFFFFFFF }; // Let's use 0xFFFFFFFF to signify TransformedVertex - ID3D11InputLayout *layout = inputLayoutMap_.Get(key); - if (!layout) { + ID3D11InputLayout *layout; + if (!inputLayoutMap_.Get(key, &layout)) { ASSERT_SUCCESS(device_->CreateInputLayout(TransformedVertexElements, ARRAY_SIZE(TransformedVertexElements), vshader->bytecode().data(), vshader->bytecode().size(), &layout)); inputLayoutMap_.Insert(key, layout); } diff --git a/GPU/D3D11/DrawEngineD3D11.h b/GPU/D3D11/DrawEngineD3D11.h index fcd28c873d50..763588e310f7 100644 --- a/GPU/D3D11/DrawEngineD3D11.h +++ b/GPU/D3D11/DrawEngineD3D11.h @@ -179,7 +179,7 @@ class DrawEngineD3D11 : public DrawEngineCommon { ID3D11DeviceContext *context_; ID3D11DeviceContext1 *context1_; - PrehashMap vai_; + PrehashMap vai_; struct InputLayoutKey { D3D11VertexShader *vshader; @@ -193,7 +193,7 @@ class DrawEngineD3D11 : public DrawEngineCommon { } }; - DenseHashMap inputLayoutMap_; + DenseHashMap inputLayoutMap_; // Other ShaderManagerD3D11 *shaderManager_ = nullptr; @@ -205,10 +205,10 @@ class DrawEngineD3D11 : public DrawEngineCommon { PushBufferD3D11 *pushInds_; // D3D11 state object caches. - DenseHashMap blendCache_; - DenseHashMap blendCache1_; - DenseHashMap depthStencilCache_; - DenseHashMap rasterCache_; + DenseHashMap blendCache_; + DenseHashMap blendCache1_; + DenseHashMap depthStencilCache_; + DenseHashMap rasterCache_; // Keep the depth state between ApplyDrawState and ApplyDrawStateLate ID3D11RasterizerState *rasterState_ = nullptr; diff --git a/GPU/D3D11/StateMappingD3D11.cpp b/GPU/D3D11/StateMappingD3D11.cpp index aa1ec6606600..aff432ba4225 100644 --- a/GPU/D3D11/StateMappingD3D11.cpp +++ b/GPU/D3D11/StateMappingD3D11.cpp @@ -356,8 +356,8 @@ void DrawEngineD3D11::ApplyDrawState(int prim) { // There might have been interactions between depth and blend above. if (gstate_c.IsDirty(DIRTY_BLEND_STATE)) { if (!device1_) { - ID3D11BlendState *bs = blendCache_.Get(keys_.blend.value); - if (bs == nullptr) { + ID3D11BlendState *bs = nullptr; + if (!blendCache_.Get(keys_.blend.value, &bs) || !bs) { D3D11_BLEND_DESC desc{}; D3D11_RENDER_TARGET_BLEND_DESC &rt = desc.RenderTarget[0]; rt.BlendEnable = keys_.blend.blendEnable; @@ -373,8 +373,8 @@ void DrawEngineD3D11::ApplyDrawState(int prim) { } blendState_ = bs; } else { - ID3D11BlendState1 *bs1 = blendCache1_.Get(keys_.blend.value); - if (bs1 == nullptr) { + ID3D11BlendState1 *bs1 = nullptr; + if (!blendCache1_.Get(keys_.blend.value, &bs1) || !bs1) { D3D11_BLEND_DESC1 desc1{}; D3D11_RENDER_TARGET_BLEND_DESC1 &rt = desc1.RenderTarget[0]; rt.BlendEnable = keys_.blend.blendEnable; @@ -395,8 +395,8 @@ void DrawEngineD3D11::ApplyDrawState(int prim) { } if (gstate_c.IsDirty(DIRTY_RASTER_STATE)) { - ID3D11RasterizerState *rs = rasterCache_.Get(keys_.raster.value); - if (rs == nullptr) { + ID3D11RasterizerState *rs; + if (!rasterCache_.Get(keys_.raster.value, &rs) || !rs) { D3D11_RASTERIZER_DESC desc{}; desc.CullMode = (D3D11_CULL_MODE)(keys_.raster.cullMode); desc.FillMode = D3D11_FILL_SOLID; @@ -410,8 +410,8 @@ void DrawEngineD3D11::ApplyDrawState(int prim) { } if (gstate_c.IsDirty(DIRTY_DEPTHSTENCIL_STATE)) { - ID3D11DepthStencilState *ds = depthStencilCache_.Get(keys_.depthStencil.value); - if (ds == nullptr) { + ID3D11DepthStencilState *ds; + if (!depthStencilCache_.Get(keys_.depthStencil.value, &ds) || !ds) { D3D11_DEPTH_STENCIL_DESC desc{}; desc.DepthEnable = keys_.depthStencil.depthTestEnable; desc.DepthWriteMask = keys_.depthStencil.depthWriteEnable ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO; diff --git a/GPU/Directx9/DrawEngineDX9.cpp b/GPU/Directx9/DrawEngineDX9.cpp index 388e05797f41..9efa233dd0b8 100644 --- a/GPU/Directx9/DrawEngineDX9.cpp +++ b/GPU/Directx9/DrawEngineDX9.cpp @@ -162,9 +162,8 @@ static void VertexAttribSetup(D3DVERTEXELEMENT9 * VertexElement, u8 fmt, u8 offs } IDirect3DVertexDeclaration9 *DrawEngineDX9::SetupDecFmtForDraw(const DecVtxFormat &decFmt, u32 pspFmt) { - IDirect3DVertexDeclaration9 *vertexDeclCached = vertexDeclMap_.Get(pspFmt); - - if (vertexDeclCached) { + IDirect3DVertexDeclaration9 *vertexDeclCached; + if (vertexDeclMap_.Get(pspFmt, &vertexDeclCached)) { return vertexDeclCached; } else { D3DVERTEXELEMENT9 VertexElements[8]; @@ -347,8 +346,8 @@ void DrawEngineDX9::DoFlush() { if (useCache) { // getUVGenMode can have an effect on which UV decoder we need to use! And hence what the decoded data will look like. See #9263 u32 dcid = (u32)XXH3_64bits(&drawCalls_, sizeof(DeferredDrawCall) * numDrawCalls_) ^ gstate.getUVGenMode(); - VertexArrayInfoDX9 *vai = vai_.Get(dcid); - if (!vai) { + VertexArrayInfoDX9 *vai; + if (!vai_.Get(dcid, &vai)) { vai = new VertexArrayInfoDX9(); vai_.Insert(dcid, vai); } diff --git a/GPU/Directx9/DrawEngineDX9.h b/GPU/Directx9/DrawEngineDX9.h index ca35da7b81d2..9ce7b11ec854 100644 --- a/GPU/Directx9/DrawEngineDX9.h +++ b/GPU/Directx9/DrawEngineDX9.h @@ -164,8 +164,8 @@ class DrawEngineDX9 : public DrawEngineCommon { LPDIRECT3DDEVICE9 device_ = nullptr; Draw::DrawContext *draw_; - PrehashMap vai_; - DenseHashMap vertexDeclMap_; + PrehashMap vai_; + DenseHashMap vertexDeclMap_; // SimpleVertex IDirect3DVertexDeclaration9* transformedVertexDecl_ = nullptr; diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index 5ff960b1bc9c..51798b32eb6d 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -206,8 +206,8 @@ static inline void VertexAttribSetup(int attrib, int fmt, int stride, int offset // TODO: Use VBO and get rid of the vertexData pointers - with that, we will supply only offsets GLRInputLayout *DrawEngineGLES::SetupDecFmtForDraw(const DecVtxFormat &decFmt) { uint32_t key = decFmt.id; - GLRInputLayout *inputLayout = inputLayoutMap_.Get(key); - if (inputLayout) { + GLRInputLayout *inputLayout; + if (inputLayoutMap_.Get(key, &inputLayout)) { return inputLayout; } diff --git a/GPU/GLES/DrawEngineGLES.h b/GPU/GLES/DrawEngineGLES.h index 4e7e8b14199d..8a11cbb5ea78 100644 --- a/GPU/GLES/DrawEngineGLES.h +++ b/GPU/GLES/DrawEngineGLES.h @@ -128,7 +128,7 @@ class DrawEngineGLES : public DrawEngineCommon { }; FrameData frameData_[GLRenderManager::MAX_INFLIGHT_FRAMES]; - DenseHashMap inputLayoutMap_; + DenseHashMap inputLayoutMap_; GLRInputLayout *softwareInputLayout_ = nullptr; GLRenderManager *render_; diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index 58216666f08c..7cbfa441af91 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -795,31 +795,33 @@ Shader *ShaderManagerGLES::ApplyVertexShader(bool useHWTransform, bool useHWTess } lastVSID_ = *VSID; - Shader *vs = vsCache_.Get(*VSID); - if (!vs) { - // Vertex shader not in cache. Let's compile it. - vs = CompileVertexShader(*VSID); - if (!vs || vs->Failed()) { - auto gr = GetI18NCategory(I18NCat::GRAPHICS); - ERROR_LOG(G3D, "Vertex shader generation failed, falling back to software transform"); - if (!g_Config.bHideSlowWarnings) { - g_OSD.Show(OSDType::MESSAGE_ERROR, gr->T("hardware transform error - falling back to software"), 2.5f); - } - delete vs; - - // TODO: Look for existing shader with the appropriate ID, use that instead of generating a new one - however, need to make sure - // that that shader ID is not used when computing the linked shader ID below, because then IDs won't match - // next time and we'll do this over and over... - - // Can still work with software transform. - VShaderID vsidTemp; - ComputeVertexShaderID(&vsidTemp, decoder, false, false, weightsAsFloat, true); - vs = CompileVertexShader(vsidTemp); + Shader *vs; + if (vsCache_.Get(*VSID, &vs)) { + return vs; + } + + // Vertex shader not in cache. Let's compile it. + vs = CompileVertexShader(*VSID); + if (!vs || vs->Failed()) { + auto gr = GetI18NCategory(I18NCat::GRAPHICS); + ERROR_LOG(G3D, "Vertex shader generation failed, falling back to software transform"); + if (!g_Config.bHideSlowWarnings) { + g_OSD.Show(OSDType::MESSAGE_ERROR, gr->T("hardware transform error - falling back to software"), 2.5f); } + delete vs; - vsCache_.Insert(*VSID, vs); - diskCacheDirty_ = true; + // TODO: Look for existing shader with the appropriate ID, use that instead of generating a new one - however, need to make sure + // that that shader ID is not used when computing the linked shader ID below, because then IDs won't match + // next time and we'll do this over and over... + + // Can still work with software transform. + VShaderID vsidTemp; + ComputeVertexShaderID(&vsidTemp, decoder, false, false, weightsAsFloat, true); + vs = CompileVertexShader(vsidTemp); } + + vsCache_.Insert(*VSID, vs); + diskCacheDirty_ = true; return vs; } @@ -847,12 +849,16 @@ LinkedShader *ShaderManagerGLES::ApplyFragmentShader(VShaderID VSID, Shader *vs, lastFSID_ = FSID; - Shader *fs = fsCache_.Get(FSID); - if (!fs) { + Shader *fs; + if (!fsCache_.Get(FSID, &fs)) { // Fragment shader not in cache. Let's compile it. // Can't really tell if we succeeded since the compile is on the GPU thread later. // Could fail to generate, in which case we're kinda screwed. fs = CompileFragmentShader(FSID); + if (!fs) { + ERROR_LOG(G3D, "Failed to generate fragment shader with ID %08x:%08x", FSID.d[0], FSID.d[1]); + // Still insert it so we don't end up spamming generation. + } fsCache_.Insert(FSID, fs); diskCacheDirty_ = true; } @@ -876,7 +882,7 @@ LinkedShader *ShaderManagerGLES::ApplyFragmentShader(VShaderID VSID, Shader *vs, _dbg_assert_(FSID.Bit(FS_BIT_FLATSHADE) == VSID.Bit(VS_BIT_FLATSHADE)); if (vs == nullptr || fs == nullptr) { - // Can't draw. This shouldn't really happen. + // Can't draw. This shouldn't really happen (but can happen if fragment shader generation fails) return nullptr; } @@ -939,14 +945,22 @@ std::string ShaderManagerGLES::DebugGetShaderString(std::string id, DebugShaderT switch (type) { case SHADER_TYPE_VERTEX: { - Shader *vs = vsCache_.Get(VShaderID(shaderId)); - return vs ? vs->GetShaderString(stringType, shaderId) : ""; + Shader *vs; + if (vsCache_.Get(VShaderID(shaderId), &vs) && vs) { + return vs->GetShaderString(stringType, shaderId); + } else { + return ""; + } } case SHADER_TYPE_FRAGMENT: { - Shader *fs = fsCache_.Get(FShaderID(shaderId)); - return fs->GetShaderString(stringType, shaderId); + Shader *fs; + if (fsCache_.Get(FShaderID(shaderId), &fs) && fs) { + return fs->GetShaderString(stringType, shaderId); + } else { + return ""; + } } default: return "N/A"; @@ -1076,7 +1090,7 @@ bool ShaderManagerGLES::ContinuePrecompile(float sliceTime) { } const VShaderID &id = pending.vert[i]; - if (!vsCache_.Get(id)) { + if (!vsCache_.ContainsKey(id)) { if (id.Bit(VS_BIT_IS_THROUGH) && id.Bit(VS_BIT_USE_HW_TRANSFORM)) { // Clearly corrupt, bailing. ERROR_LOG_REPORT(G3D, "Corrupt shader cache: Both IS_THROUGH and USE_HW_TRANSFORM set."); @@ -1106,7 +1120,7 @@ bool ShaderManagerGLES::ContinuePrecompile(float sliceTime) { } const FShaderID &id = pending.frag[i]; - if (!fsCache_.Get(id)) { + if (!fsCache_.ContainsKey(id)) { fsCache_.Insert(id, CompileFragmentShader(id)); } else { WARN_LOG(G3D, "Duplicate fragment shader found in GL shader cache, ignoring"); @@ -1121,8 +1135,10 @@ bool ShaderManagerGLES::ContinuePrecompile(float sliceTime) { const VShaderID &vsid = pending.link[i].first; const FShaderID &fsid = pending.link[i].second; - Shader *vs = vsCache_.Get(vsid); - Shader *fs = fsCache_.Get(fsid); + Shader *vs = nullptr; + Shader *fs = nullptr; + vsCache_.Get(vsid, &vs); + fsCache_.Get(fsid, &fs); if (vs && fs) { LinkedShader *ls = new LinkedShader(render_, vsid, vs, fsid, fs, vs->UseHWTransform(), true); LinkedShaderCacheEntry entry(vs, fs, ls); diff --git a/GPU/GLES/ShaderManagerGLES.h b/GPU/GLES/ShaderManagerGLES.h index cac2865cc3e5..4ae3db315403 100644 --- a/GPU/GLES/ShaderManagerGLES.h +++ b/GPU/GLES/ShaderManagerGLES.h @@ -217,10 +217,10 @@ class ShaderManagerGLES : public ShaderManagerCommon { u64 shaderSwitchDirtyUniforms_ = 0; char *codeBuffer_; - typedef DenseHashMap FSCache; + typedef DenseHashMap FSCache; FSCache fsCache_; - typedef DenseHashMap VSCache; + typedef DenseHashMap VSCache; VSCache vsCache_; bool diskCacheDirty_ = false; diff --git a/GPU/Software/DrawPixel.cpp b/GPU/Software/DrawPixel.cpp index eda04b0b4167..3569b7006743 100644 --- a/GPU/Software/DrawPixel.cpp +++ b/GPU/Software/DrawPixel.cpp @@ -844,7 +844,7 @@ void PixelJitCache::Flush() { for (const auto &queued : compileQueue_) { // Might've been compiled after enqueue, but before now. size_t queuedKey = std::hash()(queued); - if (!cache_.Get(queuedKey)) + if (!cache_.ContainsKey(queuedKey)) Compile(queued); } compileQueue_.clear(); @@ -859,10 +859,10 @@ SingleFunc PixelJitCache::GetSingle(const PixelFuncID &id, BinManager *binner) { return lastSingle_.func; std::unique_lock guard(jitCacheLock); - auto it = cache_.Get(key); - if (it != nullptr) { - lastSingle_.Set(key, it, clearGen_); - return it; + SingleFunc singleFunc; + if (cache_.Get(key, &singleFunc)) { + lastSingle_.Set(key, singleFunc, clearGen_); + return singleFunc; } if (!binner) { @@ -878,18 +878,19 @@ SingleFunc PixelJitCache::GetSingle(const PixelFuncID &id, BinManager *binner) { for (const auto &queued : compileQueue_) { // Might've been compiled after enqueue, but before now. size_t queuedKey = std::hash()(queued); - if (!cache_.Get(queuedKey)) + if (!cache_.ContainsKey(queuedKey)) Compile(queued); } compileQueue_.clear(); // Might've been in the queue. - if (!cache_.Get(key)) + if (!cache_.ContainsKey(key)) Compile(id); - it = cache_.Get(key); - lastSingle_.Set(key, it, clearGen_); - return it; + if (cache_.Get(key, &singleFunc)) { + lastSingle_.Set(key, singleFunc, clearGen_); + } + return singleFunc; } void PixelJitCache::Compile(const PixelFuncID &id) { diff --git a/GPU/Software/DrawPixel.h b/GPU/Software/DrawPixel.h index b0eae46ac672..e989e17e766b 100644 --- a/GPU/Software/DrawPixel.h +++ b/GPU/Software/DrawPixel.h @@ -125,7 +125,7 @@ class PixelJitCache : public Rasterizer::CodeBlock { } }; - DenseHashMap cache_; + DenseHashMap cache_; std::unordered_map addresses_; std::unordered_set compileQueue_; static int clearGen_; diff --git a/GPU/Software/Sampler.cpp b/GPU/Software/Sampler.cpp index f819aeddc7a2..60afbd39a9c5 100644 --- a/GPU/Software/Sampler.cpp +++ b/GPU/Software/Sampler.cpp @@ -166,7 +166,7 @@ void SamplerJitCache::Flush() { for (const auto &queued : compileQueue_) { // Might've been compiled after enqueue, but before now. size_t queuedKey = std::hash()(queued); - if (!cache_.Get(queuedKey)) + if (!cache_.ContainsKey(queuedKey)) Compile(queued); } compileQueue_.clear(); @@ -174,9 +174,11 @@ void SamplerJitCache::Flush() { NearestFunc SamplerJitCache::GetByID(const SamplerID &id, size_t key, BinManager *binner) { std::unique_lock guard(jitCacheLock); - auto it = cache_.Get(key); - if (it != nullptr) - return it; + + NearestFunc func; + if (cache_.Get(key, &func)) { + return func; + } if (!binner) { // Can't compile, let's try to do it later when there's an opportunity. @@ -191,16 +193,20 @@ NearestFunc SamplerJitCache::GetByID(const SamplerID &id, size_t key, BinManager for (const auto &queued : compileQueue_) { // Might've been compiled after enqueue, but before now. size_t queuedKey = std::hash()(queued); - if (!cache_.Get(queuedKey)) + if (!cache_.ContainsKey(queuedKey)) Compile(queued); } compileQueue_.clear(); - if (!cache_.Get(key)) + if (!cache_.ContainsKey(key)) Compile(id); // Okay, should be there now. - return cache_.Get(key); + if (cache_.Get(key, &func)) { + return func; + } else { + return nullptr; + } } NearestFunc SamplerJitCache::GetNearest(const SamplerID &id, BinManager *binner) { diff --git a/GPU/Software/Sampler.h b/GPU/Software/Sampler.h index ec8d4c263873..a07ce52a391c 100644 --- a/GPU/Software/Sampler.h +++ b/GPU/Software/Sampler.h @@ -144,7 +144,7 @@ class SamplerJitCache : public Rasterizer::CodeBlock { } }; - DenseHashMap cache_; + DenseHashMap cache_; std::unordered_map addresses_; std::unordered_set compileQueue_; static int clearGen_; diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 3bcc54be3d59..3104d5f33f3d 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -383,9 +383,10 @@ VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView FrameData &frame = GetCurFrame(); // See if we already have this descriptor set cached. if (!tess) { // Don't cache descriptors for HW tessellation. - VkDescriptorSet d = frame.descSets.Get(key); - if (d != VK_NULL_HANDLE) + VkDescriptorSet d; + if (frame.descSets.Get(key, &d)) { return d; + } } // Didn't find one in the frame descriptor set cache, let's make a new one. @@ -550,8 +551,8 @@ bool DrawEngineVulkan::VertexCacheLookup(int &vertexCount, GEPrimitiveType &prim u32 dcid = ComputeDrawcallsHash() ^ gstate.getUVGenMode(); PROFILE_THIS_SCOPE("vcache"); - VertexArrayInfoVulkan *vai = vai_.Get(dcid); - if (!vai) { + VertexArrayInfoVulkan *vai; + if (!vai_.Get(dcid, &vai)) { vai = new VertexArrayInfoVulkan(); vai_.Insert(dcid, vai); } diff --git a/GPU/Vulkan/DrawEngineVulkan.h b/GPU/Vulkan/DrawEngineVulkan.h index 0848028e5485..9f53638bcf93 100644 --- a/GPU/Vulkan/DrawEngineVulkan.h +++ b/GPU/Vulkan/DrawEngineVulkan.h @@ -251,7 +251,7 @@ class DrawEngineVulkan : public DrawEngineCommon { VkSampler samplerSecondaryLinear_ = VK_NULL_HANDLE; VkSampler samplerSecondaryNearest_ = VK_NULL_HANDLE; - PrehashMap vai_; + PrehashMap vai_; VulkanPushBuffer *vertexCache_; int descDecimationCounter_ = 0; @@ -273,7 +273,7 @@ class DrawEngineVulkan : public DrawEngineCommon { VulkanDescSetPool descPool; // We do rolling allocation and reset instead of caching across frames. That we might do later. - DenseHashMap descSets; + DenseHashMap descSets; void Destroy(VulkanContext *vulkan); }; diff --git a/GPU/Vulkan/PipelineManagerVulkan.cpp b/GPU/Vulkan/PipelineManagerVulkan.cpp index d7d31da8b6ef..1f96662bf5b9 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.cpp +++ b/GPU/Vulkan/PipelineManagerVulkan.cpp @@ -351,9 +351,10 @@ VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VulkanRenderManager * key.gShader = gs ? gs->GetModule() : VK_NULL_HANDLE; key.vtxFmtId = useHwTransform ? decFmt->id : 0; - auto iter = pipelines_.Get(key); - if (iter) - return iter; + VulkanPipeline *pipeline; + if (pipelines_.Get(key, &pipeline)) { + return pipeline; + } PipelineFlags pipelineFlags = (PipelineFlags)0; if (fs->Flags() & FragmentShaderFlags::USES_DISCARD) { @@ -365,7 +366,7 @@ VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VulkanRenderManager * VkSampleCountFlagBits sampleCount = MultiSampleLevelToFlagBits(multiSampleLevel); - VulkanPipeline *pipeline = CreateVulkanPipeline( + pipeline = CreateVulkanPipeline( renderManager, pipelineCache_, layout, pipelineFlags, sampleCount, rasterKey, decFmt, vs, fs, gs, useHwTransform, variantBitmask, cacheLoad); @@ -485,10 +486,11 @@ std::string PipelineManagerVulkan::DebugGetObjectString(std::string id, DebugSha VulkanPipelineKey pipelineKey; pipelineKey.FromString(id); - VulkanPipeline *pipeline = pipelines_.Get(pipelineKey); - if (!pipeline) { + VulkanPipeline *pipeline; + if (!pipelines_.Get(pipelineKey, &pipeline)) { return "N/A (missing)"; } + _assert_(pipeline != nullptr); u32 variants = pipeline->GetVariantsBitmask(); std::string keyDescription = pipelineKey.GetDescription(stringType, shaderManager); diff --git a/GPU/Vulkan/PipelineManagerVulkan.h b/GPU/Vulkan/PipelineManagerVulkan.h index 72f37813f932..0876c4cc2dcc 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.h +++ b/GPU/Vulkan/PipelineManagerVulkan.h @@ -104,7 +104,7 @@ class PipelineManagerVulkan { void CancelCache(); private: - DenseHashMap pipelines_; + DenseHashMap pipelines_; VkPipelineCache pipelineCache_ = VK_NULL_HANDLE; VulkanContext *vulkan_; bool cancelCache_ = false; diff --git a/GPU/Vulkan/ShaderManagerVulkan.cpp b/GPU/Vulkan/ShaderManagerVulkan.cpp index faaeb4182186..e1d30d87ea1d 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.cpp +++ b/GPU/Vulkan/ShaderManagerVulkan.cpp @@ -342,8 +342,8 @@ void ShaderManagerVulkan::GetShaders(int prim, VertexDecoder *decoder, VulkanVer } VulkanContext *vulkan = (VulkanContext *)draw_->GetNativeObject(Draw::NativeObject::CONTEXT); - VulkanVertexShader *vs = vsCache_.Get(VSID); - if (!vs) { + VulkanVertexShader *vs = nullptr; + if (!vsCache_.Get(VSID, &vs)) { // Vertex shader not in cache. Let's compile it. std::string genErrorString; uint64_t uniformMask = 0; // Not used @@ -354,15 +354,14 @@ void ShaderManagerVulkan::GetShaders(int prim, VertexDecoder *decoder, VulkanVer _assert_msg_(strlen(codeBuffer_) < CODE_BUFFER_SIZE, "VS length error: %d", (int)strlen(codeBuffer_)); std::lock_guard guard(cacheLock_); - vs = vsCache_.Get(VSID); - if (!vs) { + if (!vsCache_.Get(VSID, &vs)) { vs = new VulkanVertexShader(vulkan, VSID, flags, codeBuffer_, useHWTransform); vsCache_.Insert(VSID, vs); } } - VulkanFragmentShader *fs = fsCache_.Get(FSID); - if (!fs) { + VulkanFragmentShader *fs; + if (!fsCache_.Get(FSID, &fs)) { // Fragment shader not in cache. Let's compile it. std::string genErrorString; uint64_t uniformMask = 0; // Not used @@ -372,8 +371,7 @@ void ShaderManagerVulkan::GetShaders(int prim, VertexDecoder *decoder, VulkanVer _assert_msg_(strlen(codeBuffer_) < CODE_BUFFER_SIZE, "FS length error: %d", (int)strlen(codeBuffer_)); std::lock_guard guard(cacheLock_); - fs = fsCache_.Get(FSID); - if (!fs) { + if (!fsCache_.Get(FSID, &fs)) { fs = new VulkanFragmentShader(vulkan, FSID, flags, codeBuffer_); fsCache_.Insert(FSID, fs); } @@ -381,8 +379,7 @@ void ShaderManagerVulkan::GetShaders(int prim, VertexDecoder *decoder, VulkanVer VulkanGeometryShader *gs; if (GSID.Bit(GS_BIT_ENABLED)) { - gs = gsCache_.Get(GSID); - if (!gs) { + if (!gsCache_.Get(GSID, &gs)) { // Geometry shader not in cache. Let's compile it. std::string genErrorString; bool success = GenerateGeometryShader(GSID, codeBuffer_, compat_, draw_->GetBugs(), &genErrorString); @@ -390,8 +387,7 @@ void ShaderManagerVulkan::GetShaders(int prim, VertexDecoder *decoder, VulkanVer _assert_msg_(strlen(codeBuffer_) < CODE_BUFFER_SIZE, "GS length error: %d", (int)strlen(codeBuffer_)); std::lock_guard guard(cacheLock_); - gs = gsCache_.Get(GSID); - if (!gs) { + if (!gsCache_.Get(GSID, &gs)) { gs = new VulkanGeometryShader(vulkan, GSID, codeBuffer_); gsCache_.Insert(GSID, gs); } @@ -456,18 +452,30 @@ std::string ShaderManagerVulkan::DebugGetShaderString(std::string id, DebugShade switch (type) { case SHADER_TYPE_VERTEX: { - VulkanVertexShader *vs = vsCache_.Get(VShaderID(shaderId)); - return vs ? vs->GetShaderString(stringType) : ""; + VulkanVertexShader *vs; + if (vsCache_.Get(VShaderID(shaderId), &vs)) { + return vs ? vs->GetShaderString(stringType) : "null (bad)"; + } else { + return ""; + } } case SHADER_TYPE_FRAGMENT: { - VulkanFragmentShader *fs = fsCache_.Get(FShaderID(shaderId)); - return fs ? fs->GetShaderString(stringType) : ""; + VulkanFragmentShader *fs; + if (fsCache_.Get(FShaderID(shaderId), &fs)) { + return fs ? fs->GetShaderString(stringType) : "null (bad)"; + } else { + return ""; + } } case SHADER_TYPE_GEOMETRY: { - VulkanGeometryShader *gs = gsCache_.Get(GShaderID(shaderId)); - return gs ? gs->GetShaderString(stringType) : ""; + VulkanGeometryShader *gs; + if (gsCache_.Get(GShaderID(shaderId), &gs)) { + return gs ? gs->GetShaderString(stringType) : "null (bad)"; + } else { + return ""; + } } default: return "N/A"; @@ -592,8 +600,8 @@ bool ShaderManagerVulkan::LoadCache(FILE *f) { VulkanVertexShader *vs = new VulkanVertexShader(vulkan, id, flags, codeBuffer_, useHWTransform); // Remove first, just to be safe (we are loading on a background thread.) std::lock_guard guard(cacheLock_); - VulkanVertexShader *old = vsCache_.Get(id); - if (old) { + VulkanVertexShader *old; + if (vsCache_.Get(id, &old)) { vsCache_.Remove(id); delete old; } @@ -619,8 +627,8 @@ bool ShaderManagerVulkan::LoadCache(FILE *f) { _assert_msg_(strlen(codeBuffer_) < CODE_BUFFER_SIZE, "FS length error: %d", (int)strlen(codeBuffer_)); VulkanFragmentShader *fs = new VulkanFragmentShader(vulkan, id, flags, codeBuffer_); std::lock_guard guard(cacheLock_); - VulkanFragmentShader *old = fsCache_.Get(id); - if (old) { + VulkanFragmentShader *old; + if (fsCache_.Get(id, &old)) { fsCache_.Remove(id); delete old; } @@ -643,8 +651,8 @@ bool ShaderManagerVulkan::LoadCache(FILE *f) { _assert_msg_(strlen(codeBuffer_) < CODE_BUFFER_SIZE, "GS length error: %d", (int)strlen(codeBuffer_)); VulkanGeometryShader *gs = new VulkanGeometryShader(vulkan, id, codeBuffer_); std::lock_guard guard(cacheLock_); - VulkanGeometryShader *old = gsCache_.Get(id); - if (old) { + VulkanGeometryShader *old; + if (gsCache_.Get(id, &old)) { gsCache_.Remove(id); delete old; } diff --git a/GPU/Vulkan/ShaderManagerVulkan.h b/GPU/Vulkan/ShaderManagerVulkan.h index 13013fa498c4..80c1f39a131f 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.h +++ b/GPU/Vulkan/ShaderManagerVulkan.h @@ -127,9 +127,9 @@ class ShaderManagerVulkan : public ShaderManagerCommon { int GetNumGeometryShaders() const { return (int)gsCache_.size(); } // Used for saving/loading the cache. Don't need to be particularly fast. - VulkanVertexShader *GetVertexShaderFromID(VShaderID id) { return vsCache_.Get(id); } - VulkanFragmentShader *GetFragmentShaderFromID(FShaderID id) { return fsCache_.Get(id); } - VulkanGeometryShader *GetGeometryShaderFromID(GShaderID id) { return gsCache_.Get(id); } + VulkanVertexShader *GetVertexShaderFromID(VShaderID id) { return vsCache_.GetOrNull(id); } + VulkanFragmentShader *GetFragmentShaderFromID(FShaderID id) { return fsCache_.GetOrNull(id); } + VulkanGeometryShader *GetGeometryShaderFromID(GShaderID id) { return gsCache_.GetOrNull(id); } VulkanVertexShader *GetVertexShaderFromModule(VkShaderModule module); VulkanFragmentShader *GetFragmentShaderFromModule(VkShaderModule module); VulkanGeometryShader *GetGeometryShaderFromModule(VkShaderModule module); @@ -165,13 +165,13 @@ class ShaderManagerVulkan : public ShaderManagerCommon { ShaderLanguageDesc compat_; - typedef DenseHashMap FSCache; + typedef DenseHashMap FSCache; FSCache fsCache_; - typedef DenseHashMap VSCache; + typedef DenseHashMap VSCache; VSCache vsCache_; - typedef DenseHashMap GSCache; + typedef DenseHashMap GSCache; GSCache gsCache_; char *codeBuffer_; diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 100daf92abd1..6807446a3ebf 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -121,9 +121,10 @@ SamplerCache::~SamplerCache() { } VkSampler SamplerCache::GetOrCreateSampler(const SamplerCacheKey &key) { - VkSampler sampler = cache_.Get(key); - if (sampler != VK_NULL_HANDLE) + VkSampler sampler; + if (cache_.Get(key, &sampler)) { return sampler; + } VkSamplerCreateInfo samp = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; samp.addressModeU = key.sClamp ? VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE : VK_SAMPLER_ADDRESS_MODE_REPEAT; diff --git a/GPU/Vulkan/TextureCacheVulkan.h b/GPU/Vulkan/TextureCacheVulkan.h index 727bd04e3885..bb82e8c02f66 100644 --- a/GPU/Vulkan/TextureCacheVulkan.h +++ b/GPU/Vulkan/TextureCacheVulkan.h @@ -49,7 +49,7 @@ class SamplerCache { private: VulkanContext *vulkan_; - DenseHashMap cache_; + DenseHashMap cache_; }; class TextureCacheVulkan : public TextureCacheCommon { diff --git a/GPU/Vulkan/VulkanUtil.cpp b/GPU/Vulkan/VulkanUtil.cpp index d10749b1875a..b8dfe9e3d481 100644 --- a/GPU/Vulkan/VulkanUtil.cpp +++ b/GPU/Vulkan/VulkanUtil.cpp @@ -185,9 +185,10 @@ VkDescriptorSet VulkanComputeShaderManager::GetDescriptorSet(VkImageView image, VkPipeline VulkanComputeShaderManager::GetPipeline(VkShaderModule cs) { PipelineKey key{ cs }; - VkPipeline pipeline = pipelines_.Get(key); - if (pipeline) + VkPipeline pipeline; + if (pipelines_.Get(key, &pipeline)) { return pipeline; + } VkComputePipelineCreateInfo pci{ VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO }; pci.stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; diff --git a/GPU/Vulkan/VulkanUtil.h b/GPU/Vulkan/VulkanUtil.h index 75ed153ad354..68ff50b68b3c 100644 --- a/GPU/Vulkan/VulkanUtil.h +++ b/GPU/Vulkan/VulkanUtil.h @@ -85,7 +85,7 @@ class VulkanComputeShaderManager { VkShaderModule module; }; - DenseHashMap pipelines_; + DenseHashMap pipelines_; };