Fix the semantics of DenseHashMap to be consistent even when inserting nulls

This commit is contained in:
Henrik Rydgård 2023-09-11 12:02:56 +02:00
parent 4d58bb801c
commit 10f93875c6
29 changed files with 220 additions and 163 deletions

View file

@ -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 <class Key, class Value, Value NullValue>
template <class Key, class Value>
class DenseHashMap {
public:
DenseHashMap(int initialCapacity) : capacity_(initialCapacity) {
@ -37,23 +37,44 @@ public:
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 @@ private:
// Like the above, uses linear probing for cache-friendliness.
// Does not perform hashing at all so expects well-distributed keys.
template <class Value, Value NullValue>
template <class Value>
class PrehashMap {
public:
PrehashMap(int initialCapacity) : capacity_(initialCapacity) {
@ -199,22 +220,24 @@ public:
}
// 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.

View file

@ -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<ReadbackKey, CachedReadback*, nullptr> readbacks_;
DenseHashMap<ReadbackKey, CachedReadback *> readbacks_;
FrameData() : readbacks_(8) {}

View file

@ -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

View file

@ -322,7 +322,7 @@ private:
// 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<RPKey, VKRRenderPass *, nullptr> renderPasses_;
DenseHashMap<RPKey, VKRRenderPass *> renderPasses_;
// Readback buffer. Currently we only support synchronous readback, so we only really need one.
// We size it generously.

View file

@ -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<std::string> 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) {

View file

@ -201,7 +201,7 @@ protected:
// Cached vertex decoders
u32 lastVType_ = -1; // corresponds to dec_. Could really just pick it out of dec_...
DenseHashMap<u32, VertexDecoder *, nullptr> decoderMap_;
DenseHashMap<u32, VertexDecoder *> decoderMap_;
VertexDecoder *dec_ = nullptr;
VertexDecoderJitCache *decJitCache_ = nullptr;
VertexDecoderOptions decOptions_{};

View file

@ -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 @@ rotateVBO:
// 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);
}

View file

@ -179,7 +179,7 @@ private:
ID3D11DeviceContext *context_;
ID3D11DeviceContext1 *context1_;
PrehashMap<VertexArrayInfoD3D11 *, nullptr> vai_;
PrehashMap<VertexArrayInfoD3D11 *> vai_;
struct InputLayoutKey {
D3D11VertexShader *vshader;
@ -193,7 +193,7 @@ private:
}
};
DenseHashMap<InputLayoutKey, ID3D11InputLayout *, nullptr> inputLayoutMap_;
DenseHashMap<InputLayoutKey, ID3D11InputLayout *> inputLayoutMap_;
// Other
ShaderManagerD3D11 *shaderManager_ = nullptr;
@ -205,10 +205,10 @@ private:
PushBufferD3D11 *pushInds_;
// D3D11 state object caches.
DenseHashMap<uint64_t, ID3D11BlendState *, nullptr> blendCache_;
DenseHashMap<uint64_t, ID3D11BlendState1 *, nullptr> blendCache1_;
DenseHashMap<uint64_t, ID3D11DepthStencilState *, nullptr> depthStencilCache_;
DenseHashMap<uint32_t, ID3D11RasterizerState *, nullptr> rasterCache_;
DenseHashMap<uint64_t, ID3D11BlendState *> blendCache_;
DenseHashMap<uint64_t, ID3D11BlendState1 *> blendCache1_;
DenseHashMap<uint64_t, ID3D11DepthStencilState *> depthStencilCache_;
DenseHashMap<uint32_t, ID3D11RasterizerState *> rasterCache_;
// Keep the depth state between ApplyDrawState and ApplyDrawStateLate
ID3D11RasterizerState *rasterState_ = nullptr;

View file

@ -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;

View file

@ -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);
}

View file

@ -164,8 +164,8 @@ private:
LPDIRECT3DDEVICE9 device_ = nullptr;
Draw::DrawContext *draw_;
PrehashMap<VertexArrayInfoDX9 *, nullptr> vai_;
DenseHashMap<u32, IDirect3DVertexDeclaration9 *, nullptr> vertexDeclMap_;
PrehashMap<VertexArrayInfoDX9 *> vai_;
DenseHashMap<u32, IDirect3DVertexDeclaration9 *> vertexDeclMap_;
// SimpleVertex
IDirect3DVertexDeclaration9* transformedVertexDecl_ = nullptr;

View file

@ -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;
}

View file

@ -128,7 +128,7 @@ private:
};
FrameData frameData_[GLRenderManager::MAX_INFLIGHT_FRAMES];
DenseHashMap<uint32_t, GLRInputLayout *, nullptr> inputLayoutMap_;
DenseHashMap<uint32_t, GLRInputLayout *> inputLayoutMap_;
GLRInputLayout *softwareInputLayout_ = nullptr;
GLRenderManager *render_;

View file

@ -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);
}
vsCache_.Insert(*VSID, vs);
diskCacheDirty_ = true;
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;
// 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,8 +849,8 @@ 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.
@ -939,14 +941,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 +1086,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 +1116,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 +1131,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);

View file

@ -217,10 +217,10 @@ private:
u64 shaderSwitchDirtyUniforms_ = 0;
char *codeBuffer_;
typedef DenseHashMap<FShaderID, Shader *, nullptr> FSCache;
typedef DenseHashMap<FShaderID, Shader *> FSCache;
FSCache fsCache_;
typedef DenseHashMap<VShaderID, Shader *, nullptr> VSCache;
typedef DenseHashMap<VShaderID, Shader *> VSCache;
VSCache vsCache_;
bool diskCacheDirty_ = false;

View file

@ -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<PixelFuncID>()(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<std::mutex> 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<PixelFuncID>()(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) {

View file

@ -125,7 +125,7 @@ private:
}
};
DenseHashMap<size_t, SingleFunc, nullptr> cache_;
DenseHashMap<size_t, SingleFunc> cache_;
std::unordered_map<PixelFuncID, const u8 *> addresses_;
std::unordered_set<PixelFuncID> compileQueue_;
static int clearGen_;

View file

@ -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<SamplerID>()(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<std::mutex> 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<SamplerID>()(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) {

View file

@ -144,7 +144,7 @@ private:
}
};
DenseHashMap<size_t, NearestFunc, nullptr> cache_;
DenseHashMap<size_t, NearestFunc> cache_;
std::unordered_map<SamplerID, const u8 *> addresses_;
std::unordered_set<SamplerID> compileQueue_;
static int clearGen_;

View file

@ -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);
}

View file

@ -251,7 +251,7 @@ private:
VkSampler samplerSecondaryLinear_ = VK_NULL_HANDLE;
VkSampler samplerSecondaryNearest_ = VK_NULL_HANDLE;
PrehashMap<VertexArrayInfoVulkan *, nullptr> vai_;
PrehashMap<VertexArrayInfoVulkan *> vai_;
VulkanPushBuffer *vertexCache_;
int descDecimationCounter_ = 0;
@ -273,7 +273,7 @@ private:
VulkanDescSetPool descPool;
// We do rolling allocation and reset instead of caching across frames. That we might do later.
DenseHashMap<DescriptorSetKey, VkDescriptorSet, (VkDescriptorSet)VK_NULL_HANDLE> descSets;
DenseHashMap<DescriptorSetKey, VkDescriptorSet> descSets;
void Destroy(VulkanContext *vulkan);
};

View file

@ -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);

View file

@ -104,7 +104,7 @@ public:
void CancelCache();
private:
DenseHashMap<VulkanPipelineKey, VulkanPipeline *, nullptr> pipelines_;
DenseHashMap<VulkanPipelineKey, VulkanPipeline *> pipelines_;
VkPipelineCache pipelineCache_ = VK_NULL_HANDLE;
VulkanContext *vulkan_;
bool cancelCache_ = false;

View file

@ -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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> guard(cacheLock_);
VulkanGeometryShader *old = gsCache_.Get(id);
if (old) {
VulkanGeometryShader *old;
if (gsCache_.Get(id, &old)) {
gsCache_.Remove(id);
delete old;
}

View file

@ -127,9 +127,9 @@ public:
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 @@ private:
ShaderLanguageDesc compat_;
typedef DenseHashMap<FShaderID, VulkanFragmentShader *, nullptr> FSCache;
typedef DenseHashMap<FShaderID, VulkanFragmentShader *> FSCache;
FSCache fsCache_;
typedef DenseHashMap<VShaderID, VulkanVertexShader *, nullptr> VSCache;
typedef DenseHashMap<VShaderID, VulkanVertexShader *> VSCache;
VSCache vsCache_;
typedef DenseHashMap<GShaderID, VulkanGeometryShader *, nullptr> GSCache;
typedef DenseHashMap<GShaderID, VulkanGeometryShader *> GSCache;
GSCache gsCache_;
char *codeBuffer_;

View file

@ -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;

View file

@ -49,7 +49,7 @@ public:
private:
VulkanContext *vulkan_;
DenseHashMap<SamplerCacheKey, VkSampler, (VkSampler)VK_NULL_HANDLE> cache_;
DenseHashMap<SamplerCacheKey, VkSampler> cache_;
};
class TextureCacheVulkan : public TextureCacheCommon {

View file

@ -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;

View file

@ -85,7 +85,7 @@ private:
VkShaderModule module;
};
DenseHashMap<PipelineKey, VkPipeline, (VkPipeline)VK_NULL_HANDLE> pipelines_;
DenseHashMap<PipelineKey, VkPipeline> pipelines_;
};