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 // we always use very small values, so it's probably better to have them in the same
// cache-line as the corresponding key. // cache-line as the corresponding key.
// Enforces that value are pointers to make sure that combined storage makes sense. // 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 { class DenseHashMap {
public: public:
DenseHashMap(int initialCapacity) : capacity_(initialCapacity) { DenseHashMap(int initialCapacity) : capacity_(initialCapacity) {
@ -37,23 +37,44 @@ public:
state.resize(initialCapacity); state.resize(initialCapacity);
} }
// Returns nullptr if no entry was found. // Returns true if the entry was found, and writes the entry to *value.
Value Get(const Key &key) { // 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 mask = capacity_ - 1;
uint32_t pos = HashKey(key) & mask; uint32_t pos = HashKey(key) & mask;
// No? Let's go into search mode. Linear probing. // No? Let's go into search mode. Linear probing.
uint32_t p = pos; uint32_t p = pos;
while (true) { while (true) {
if (state[p] == BucketState::TAKEN && KeyEquals(key, map[p].key)) if (state[p] == BucketState::TAKEN && KeyEquals(key, map[p].key)) {
return map[p].value; *value = map[p].value;
else if (state[p] == BucketState::FREE) return true;
return NullValue; } else if (state[p] == BucketState::FREE) {
return false;
}
p = (p + 1) & mask; // If the state is REMOVED, we just keep on walking. p = (p + 1) & mask; // If the state is REMOVED, we just keep on walking.
if (p == pos) { if (p == pos) {
// We looped around the whole map.
_assert_msg_(false, "DenseHashMap: Hit full on Get()"); _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! // Asserts if we already had the key!
@ -190,7 +211,7 @@ private:
// Like the above, uses linear probing for cache-friendliness. // Like the above, uses linear probing for cache-friendliness.
// Does not perform hashing at all so expects well-distributed keys. // Does not perform hashing at all so expects well-distributed keys.
template <class Value, Value NullValue> template <class Value>
class PrehashMap { class PrehashMap {
public: public:
PrehashMap(int initialCapacity) : capacity_(initialCapacity) { PrehashMap(int initialCapacity) : capacity_(initialCapacity) {
@ -199,22 +220,24 @@ public:
} }
// Returns nullptr if no entry was found. // 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 mask = capacity_ - 1;
uint32_t pos = hash & mask; uint32_t pos = hash & mask;
// No? Let's go into search mode. Linear probing. // No? Let's go into search mode. Linear probing.
uint32_t p = pos; uint32_t p = pos;
while (true) { while (true) {
if (state[p] == BucketState::TAKEN && hash == map[p].hash) if (state[p] == BucketState::TAKEN && hash == map[p].hash) {
return map[p].value; *value = map[p].value;
else if (state[p] == BucketState::FREE) return true;
return NullValue; } else if (state[p] == BucketState::FREE) {
return false;
}
p = (p + 1) & mask; // If the state is REMOVED, we just keep on walking. p = (p + 1) & mask; // If the state is REMOVED, we just keep on walking.
if (p == pos) { if (p == pos) {
_assert_msg_(false, "PrehashMap: Hit full on Get()"); _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. // 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. // Frames need unique IDs to wait for present on, let's keep them here.
// Also used for indexing into the frame timing history buffer. // Also used for indexing into the frame timing history buffer.
uint64_t frameId; uint64_t frameId = 0;
// Profiling. // Profiling.
QueueProfileContext profile{}; QueueProfileContext profile{};
// Async readback cache. // Async readback cache.
DenseHashMap<ReadbackKey, CachedReadback*, nullptr> readbacks_; DenseHashMap<ReadbackKey, CachedReadback *> readbacks_;
FrameData() : readbacks_(8) {} FrameData() : readbacks_(8) {}

View file

@ -251,8 +251,8 @@ void VulkanQueueRunner::DestroyBackBuffers() {
// Self-dependency: https://github.com/gpuweb/gpuweb/issues/442#issuecomment-547604827 // 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 // 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) { VKRRenderPass *VulkanQueueRunner::GetRenderPass(const RPKey &key) {
auto foundPass = renderPasses_.Get(key); VKRRenderPass *foundPass;
if (foundPass) { if (renderPasses_.Get(key, &foundPass)) {
return foundPass; return foundPass;
} }
@ -1984,8 +1984,7 @@ void VulkanQueueRunner::PerformReadback(const VKRStep &step, VkCommandBuffer cmd
key.height = step.readback.srcRect.extent.height; key.height = step.readback.srcRect.extent.height;
// See if there's already a buffer we can reuse // See if there's already a buffer we can reuse
cached = frameData.readbacks_.Get(key); if (!frameData.readbacks_.Get(key, &cached)) {
if (!cached) {
cached = new CachedReadback(); cached = new CachedReadback();
cached->bufferSize = 0; cached->bufferSize = 0;
frameData.readbacks_.Insert(key, cached); frameData.readbacks_.Insert(key, cached);
@ -2065,8 +2064,8 @@ bool VulkanQueueRunner::CopyReadbackBuffer(FrameData &frameData, VKRFramebuffer
key.framebuf = src; key.framebuf = src;
key.width = width; key.width = width;
key.height = height; key.height = height;
CachedReadback *cached = frameData.readbacks_.Get(key); CachedReadback *cached;
if (cached) { if (frameData.readbacks_.Get(key, &cached)) {
readback = cached; readback = cached;
} else { } else {
// Didn't have a cached image ready yet // 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. // Renderpasses, all combinations of preserving or clearing or dont-care-ing fb contents.
// Each VKRRenderPass contains all compatibility classes (which attachments they have, etc). // 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. // Readback buffer. Currently we only support synchronous readback, so we only really need one.
// We size it generously. // We size it generously.

View file

@ -62,8 +62,8 @@ void DrawEngineCommon::Init() {
} }
VertexDecoder *DrawEngineCommon::GetVertexDecoder(u32 vtype) { VertexDecoder *DrawEngineCommon::GetVertexDecoder(u32 vtype) {
VertexDecoder *dec = decoderMap_.Get(vtype); VertexDecoder *dec;
if (dec) if (decoderMap_.Get(vtype, &dec))
return dec; return dec;
dec = new VertexDecoder(); dec = new VertexDecoder();
_assert_(dec); _assert_(dec);
@ -132,8 +132,12 @@ std::vector<std::string> DrawEngineCommon::DebugGetVertexLoaderIDs() {
std::string DrawEngineCommon::DebugGetVertexLoaderString(std::string id, DebugShaderStringType stringType) { std::string DrawEngineCommon::DebugGetVertexLoaderString(std::string id, DebugShaderStringType stringType) {
u32 mapId; u32 mapId;
memcpy(&mapId, &id[0], sizeof(mapId)); memcpy(&mapId, &id[0], sizeof(mapId));
VertexDecoder *dec = decoderMap_.Get(mapId); VertexDecoder *dec;
return dec ? dec->GetString(stringType) : "N/A"; if (decoderMap_.Get(mapId, &dec)) {
return dec->GetString(stringType);
} else {
return "N/A";
}
} }
static Vec3f ClipToScreen(const Vec4f& coords) { static Vec3f ClipToScreen(const Vec4f& coords) {

View file

@ -201,7 +201,7 @@ protected:
// Cached vertex decoders // Cached vertex decoders
u32 lastVType_ = -1; // corresponds to dec_. Could really just pick it out of dec_... 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; VertexDecoder *dec_ = nullptr;
VertexDecoderJitCache *decJitCache_ = nullptr; VertexDecoderJitCache *decJitCache_ = nullptr;
VertexDecoderOptions decOptions_{}; 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 // 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. // that reads TEXCOORD or not, etc. Not sure if worth it.
const InputLayoutKey key{ vshader, decFmt.id }; const InputLayoutKey key{ vshader, decFmt.id };
ID3D11InputLayout *inputLayout = inputLayoutMap_.Get(key); ID3D11InputLayout *inputLayout;
if (inputLayout) { if (inputLayoutMap_.Get(key, &inputLayout)) {
return inputLayout; return inputLayout;
} else { } else {
D3D11_INPUT_ELEMENT_DESC VertexElements[8]; 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 // 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(); u32 dcid = (u32)XXH3_64bits(&drawCalls_, sizeof(DeferredDrawCall) * numDrawCalls_) ^ gstate.getUVGenMode();
VertexArrayInfoD3D11 *vai = vai_.Get(dcid); VertexArrayInfoD3D11 *vai;
if (!vai) { if (!vai_.Get(dcid, &vai)) {
vai = new VertexArrayInfoD3D11(); vai = new VertexArrayInfoD3D11();
vai_.Insert(dcid, vai); 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)! // 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. // 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 InputLayoutKey key{ vshader, 0xFFFFFFFF }; // Let's use 0xFFFFFFFF to signify TransformedVertex
ID3D11InputLayout *layout = inputLayoutMap_.Get(key); ID3D11InputLayout *layout;
if (!layout) { if (!inputLayoutMap_.Get(key, &layout)) {
ASSERT_SUCCESS(device_->CreateInputLayout(TransformedVertexElements, ARRAY_SIZE(TransformedVertexElements), vshader->bytecode().data(), vshader->bytecode().size(), &layout)); ASSERT_SUCCESS(device_->CreateInputLayout(TransformedVertexElements, ARRAY_SIZE(TransformedVertexElements), vshader->bytecode().data(), vshader->bytecode().size(), &layout));
inputLayoutMap_.Insert(key, layout); inputLayoutMap_.Insert(key, layout);
} }

View file

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

View file

@ -356,8 +356,8 @@ void DrawEngineD3D11::ApplyDrawState(int prim) {
// There might have been interactions between depth and blend above. // There might have been interactions between depth and blend above.
if (gstate_c.IsDirty(DIRTY_BLEND_STATE)) { if (gstate_c.IsDirty(DIRTY_BLEND_STATE)) {
if (!device1_) { if (!device1_) {
ID3D11BlendState *bs = blendCache_.Get(keys_.blend.value); ID3D11BlendState *bs = nullptr;
if (bs == nullptr) { if (!blendCache_.Get(keys_.blend.value, &bs) || !bs) {
D3D11_BLEND_DESC desc{}; D3D11_BLEND_DESC desc{};
D3D11_RENDER_TARGET_BLEND_DESC &rt = desc.RenderTarget[0]; D3D11_RENDER_TARGET_BLEND_DESC &rt = desc.RenderTarget[0];
rt.BlendEnable = keys_.blend.blendEnable; rt.BlendEnable = keys_.blend.blendEnable;
@ -373,8 +373,8 @@ void DrawEngineD3D11::ApplyDrawState(int prim) {
} }
blendState_ = bs; blendState_ = bs;
} else { } else {
ID3D11BlendState1 *bs1 = blendCache1_.Get(keys_.blend.value); ID3D11BlendState1 *bs1 = nullptr;
if (bs1 == nullptr) { if (!blendCache1_.Get(keys_.blend.value, &bs1) || !bs1) {
D3D11_BLEND_DESC1 desc1{}; D3D11_BLEND_DESC1 desc1{};
D3D11_RENDER_TARGET_BLEND_DESC1 &rt = desc1.RenderTarget[0]; D3D11_RENDER_TARGET_BLEND_DESC1 &rt = desc1.RenderTarget[0];
rt.BlendEnable = keys_.blend.blendEnable; rt.BlendEnable = keys_.blend.blendEnable;
@ -395,8 +395,8 @@ void DrawEngineD3D11::ApplyDrawState(int prim) {
} }
if (gstate_c.IsDirty(DIRTY_RASTER_STATE)) { if (gstate_c.IsDirty(DIRTY_RASTER_STATE)) {
ID3D11RasterizerState *rs = rasterCache_.Get(keys_.raster.value); ID3D11RasterizerState *rs;
if (rs == nullptr) { if (!rasterCache_.Get(keys_.raster.value, &rs) || !rs) {
D3D11_RASTERIZER_DESC desc{}; D3D11_RASTERIZER_DESC desc{};
desc.CullMode = (D3D11_CULL_MODE)(keys_.raster.cullMode); desc.CullMode = (D3D11_CULL_MODE)(keys_.raster.cullMode);
desc.FillMode = D3D11_FILL_SOLID; desc.FillMode = D3D11_FILL_SOLID;
@ -410,8 +410,8 @@ void DrawEngineD3D11::ApplyDrawState(int prim) {
} }
if (gstate_c.IsDirty(DIRTY_DEPTHSTENCIL_STATE)) { if (gstate_c.IsDirty(DIRTY_DEPTHSTENCIL_STATE)) {
ID3D11DepthStencilState *ds = depthStencilCache_.Get(keys_.depthStencil.value); ID3D11DepthStencilState *ds;
if (ds == nullptr) { if (!depthStencilCache_.Get(keys_.depthStencil.value, &ds) || !ds) {
D3D11_DEPTH_STENCIL_DESC desc{}; D3D11_DEPTH_STENCIL_DESC desc{};
desc.DepthEnable = keys_.depthStencil.depthTestEnable; desc.DepthEnable = keys_.depthStencil.depthTestEnable;
desc.DepthWriteMask = keys_.depthStencil.depthWriteEnable ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO; 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 *DrawEngineDX9::SetupDecFmtForDraw(const DecVtxFormat &decFmt, u32 pspFmt) {
IDirect3DVertexDeclaration9 *vertexDeclCached = vertexDeclMap_.Get(pspFmt); IDirect3DVertexDeclaration9 *vertexDeclCached;
if (vertexDeclMap_.Get(pspFmt, &vertexDeclCached)) {
if (vertexDeclCached) {
return vertexDeclCached; return vertexDeclCached;
} else { } else {
D3DVERTEXELEMENT9 VertexElements[8]; D3DVERTEXELEMENT9 VertexElements[8];
@ -347,8 +346,8 @@ void DrawEngineDX9::DoFlush() {
if (useCache) { 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 // 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(); u32 dcid = (u32)XXH3_64bits(&drawCalls_, sizeof(DeferredDrawCall) * numDrawCalls_) ^ gstate.getUVGenMode();
VertexArrayInfoDX9 *vai = vai_.Get(dcid); VertexArrayInfoDX9 *vai;
if (!vai) { if (!vai_.Get(dcid, &vai)) {
vai = new VertexArrayInfoDX9(); vai = new VertexArrayInfoDX9();
vai_.Insert(dcid, vai); vai_.Insert(dcid, vai);
} }

View file

@ -164,8 +164,8 @@ private:
LPDIRECT3DDEVICE9 device_ = nullptr; LPDIRECT3DDEVICE9 device_ = nullptr;
Draw::DrawContext *draw_; Draw::DrawContext *draw_;
PrehashMap<VertexArrayInfoDX9 *, nullptr> vai_; PrehashMap<VertexArrayInfoDX9 *> vai_;
DenseHashMap<u32, IDirect3DVertexDeclaration9 *, nullptr> vertexDeclMap_; DenseHashMap<u32, IDirect3DVertexDeclaration9 *> vertexDeclMap_;
// SimpleVertex // SimpleVertex
IDirect3DVertexDeclaration9* transformedVertexDecl_ = nullptr; 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 // TODO: Use VBO and get rid of the vertexData pointers - with that, we will supply only offsets
GLRInputLayout *DrawEngineGLES::SetupDecFmtForDraw(const DecVtxFormat &decFmt) { GLRInputLayout *DrawEngineGLES::SetupDecFmtForDraw(const DecVtxFormat &decFmt) {
uint32_t key = decFmt.id; uint32_t key = decFmt.id;
GLRInputLayout *inputLayout = inputLayoutMap_.Get(key); GLRInputLayout *inputLayout;
if (inputLayout) { if (inputLayoutMap_.Get(key, &inputLayout)) {
return inputLayout; return inputLayout;
} }

View file

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

View file

@ -795,8 +795,11 @@ Shader *ShaderManagerGLES::ApplyVertexShader(bool useHWTransform, bool useHWTess
} }
lastVSID_ = *VSID; lastVSID_ = *VSID;
Shader *vs = vsCache_.Get(*VSID); Shader *vs;
if (!vs) { if (vsCache_.Get(*VSID, &vs)) {
return vs;
}
// Vertex shader not in cache. Let's compile it. // Vertex shader not in cache. Let's compile it.
vs = CompileVertexShader(*VSID); vs = CompileVertexShader(*VSID);
if (!vs || vs->Failed()) { if (!vs || vs->Failed()) {
@ -819,7 +822,6 @@ Shader *ShaderManagerGLES::ApplyVertexShader(bool useHWTransform, bool useHWTess
vsCache_.Insert(*VSID, vs); vsCache_.Insert(*VSID, vs);
diskCacheDirty_ = true; diskCacheDirty_ = true;
}
return vs; return vs;
} }
@ -847,8 +849,8 @@ LinkedShader *ShaderManagerGLES::ApplyFragmentShader(VShaderID VSID, Shader *vs,
lastFSID_ = FSID; lastFSID_ = FSID;
Shader *fs = fsCache_.Get(FSID); Shader *fs;
if (!fs) { if (!fsCache_.Get(FSID, &fs)) {
// Fragment shader not in cache. Let's compile it. // 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. // 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. // 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) { switch (type) {
case SHADER_TYPE_VERTEX: case SHADER_TYPE_VERTEX:
{ {
Shader *vs = vsCache_.Get(VShaderID(shaderId)); Shader *vs;
return vs ? vs->GetShaderString(stringType, shaderId) : ""; if (vsCache_.Get(VShaderID(shaderId), &vs) && vs) {
return vs->GetShaderString(stringType, shaderId);
} else {
return "";
}
} }
case SHADER_TYPE_FRAGMENT: case SHADER_TYPE_FRAGMENT:
{ {
Shader *fs = fsCache_.Get(FShaderID(shaderId)); Shader *fs;
if (fsCache_.Get(FShaderID(shaderId), &fs) && fs) {
return fs->GetShaderString(stringType, shaderId); return fs->GetShaderString(stringType, shaderId);
} else {
return "";
}
} }
default: default:
return "N/A"; return "N/A";
@ -1076,7 +1086,7 @@ bool ShaderManagerGLES::ContinuePrecompile(float sliceTime) {
} }
const VShaderID &id = pending.vert[i]; 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)) { if (id.Bit(VS_BIT_IS_THROUGH) && id.Bit(VS_BIT_USE_HW_TRANSFORM)) {
// Clearly corrupt, bailing. // Clearly corrupt, bailing.
ERROR_LOG_REPORT(G3D, "Corrupt shader cache: Both IS_THROUGH and USE_HW_TRANSFORM set."); 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]; const FShaderID &id = pending.frag[i];
if (!fsCache_.Get(id)) { if (!fsCache_.ContainsKey(id)) {
fsCache_.Insert(id, CompileFragmentShader(id)); fsCache_.Insert(id, CompileFragmentShader(id));
} else { } else {
WARN_LOG(G3D, "Duplicate fragment shader found in GL shader cache, ignoring"); 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 VShaderID &vsid = pending.link[i].first;
const FShaderID &fsid = pending.link[i].second; const FShaderID &fsid = pending.link[i].second;
Shader *vs = vsCache_.Get(vsid); Shader *vs = nullptr;
Shader *fs = fsCache_.Get(fsid); Shader *fs = nullptr;
vsCache_.Get(vsid, &vs);
fsCache_.Get(fsid, &fs);
if (vs && fs) { if (vs && fs) {
LinkedShader *ls = new LinkedShader(render_, vsid, vs, fsid, fs, vs->UseHWTransform(), true); LinkedShader *ls = new LinkedShader(render_, vsid, vs, fsid, fs, vs->UseHWTransform(), true);
LinkedShaderCacheEntry entry(vs, fs, ls); LinkedShaderCacheEntry entry(vs, fs, ls);

View file

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

View file

@ -844,7 +844,7 @@ void PixelJitCache::Flush() {
for (const auto &queued : compileQueue_) { for (const auto &queued : compileQueue_) {
// Might've been compiled after enqueue, but before now. // Might've been compiled after enqueue, but before now.
size_t queuedKey = std::hash<PixelFuncID>()(queued); size_t queuedKey = std::hash<PixelFuncID>()(queued);
if (!cache_.Get(queuedKey)) if (!cache_.ContainsKey(queuedKey))
Compile(queued); Compile(queued);
} }
compileQueue_.clear(); compileQueue_.clear();
@ -859,10 +859,10 @@ SingleFunc PixelJitCache::GetSingle(const PixelFuncID &id, BinManager *binner) {
return lastSingle_.func; return lastSingle_.func;
std::unique_lock<std::mutex> guard(jitCacheLock); std::unique_lock<std::mutex> guard(jitCacheLock);
auto it = cache_.Get(key); SingleFunc singleFunc;
if (it != nullptr) { if (cache_.Get(key, &singleFunc)) {
lastSingle_.Set(key, it, clearGen_); lastSingle_.Set(key, singleFunc, clearGen_);
return it; return singleFunc;
} }
if (!binner) { if (!binner) {
@ -878,18 +878,19 @@ SingleFunc PixelJitCache::GetSingle(const PixelFuncID &id, BinManager *binner) {
for (const auto &queued : compileQueue_) { for (const auto &queued : compileQueue_) {
// Might've been compiled after enqueue, but before now. // Might've been compiled after enqueue, but before now.
size_t queuedKey = std::hash<PixelFuncID>()(queued); size_t queuedKey = std::hash<PixelFuncID>()(queued);
if (!cache_.Get(queuedKey)) if (!cache_.ContainsKey(queuedKey))
Compile(queued); Compile(queued);
} }
compileQueue_.clear(); compileQueue_.clear();
// Might've been in the queue. // Might've been in the queue.
if (!cache_.Get(key)) if (!cache_.ContainsKey(key))
Compile(id); Compile(id);
it = cache_.Get(key); if (cache_.Get(key, &singleFunc)) {
lastSingle_.Set(key, it, clearGen_); lastSingle_.Set(key, singleFunc, clearGen_);
return it; }
return singleFunc;
} }
void PixelJitCache::Compile(const PixelFuncID &id) { 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_map<PixelFuncID, const u8 *> addresses_;
std::unordered_set<PixelFuncID> compileQueue_; std::unordered_set<PixelFuncID> compileQueue_;
static int clearGen_; static int clearGen_;

View file

@ -166,7 +166,7 @@ void SamplerJitCache::Flush() {
for (const auto &queued : compileQueue_) { for (const auto &queued : compileQueue_) {
// Might've been compiled after enqueue, but before now. // Might've been compiled after enqueue, but before now.
size_t queuedKey = std::hash<SamplerID>()(queued); size_t queuedKey = std::hash<SamplerID>()(queued);
if (!cache_.Get(queuedKey)) if (!cache_.ContainsKey(queuedKey))
Compile(queued); Compile(queued);
} }
compileQueue_.clear(); compileQueue_.clear();
@ -174,9 +174,11 @@ void SamplerJitCache::Flush() {
NearestFunc SamplerJitCache::GetByID(const SamplerID &id, size_t key, BinManager *binner) { NearestFunc SamplerJitCache::GetByID(const SamplerID &id, size_t key, BinManager *binner) {
std::unique_lock<std::mutex> guard(jitCacheLock); std::unique_lock<std::mutex> guard(jitCacheLock);
auto it = cache_.Get(key);
if (it != nullptr) NearestFunc func;
return it; if (cache_.Get(key, &func)) {
return func;
}
if (!binner) { if (!binner) {
// Can't compile, let's try to do it later when there's an opportunity. // 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_) { for (const auto &queued : compileQueue_) {
// Might've been compiled after enqueue, but before now. // Might've been compiled after enqueue, but before now.
size_t queuedKey = std::hash<SamplerID>()(queued); size_t queuedKey = std::hash<SamplerID>()(queued);
if (!cache_.Get(queuedKey)) if (!cache_.ContainsKey(queuedKey))
Compile(queued); Compile(queued);
} }
compileQueue_.clear(); compileQueue_.clear();
if (!cache_.Get(key)) if (!cache_.ContainsKey(key))
Compile(id); Compile(id);
// Okay, should be there now. // 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) { 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_map<SamplerID, const u8 *> addresses_;
std::unordered_set<SamplerID> compileQueue_; std::unordered_set<SamplerID> compileQueue_;
static int clearGen_; static int clearGen_;

View file

@ -383,10 +383,11 @@ VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView
FrameData &frame = GetCurFrame(); FrameData &frame = GetCurFrame();
// See if we already have this descriptor set cached. // See if we already have this descriptor set cached.
if (!tess) { // Don't cache descriptors for HW tessellation. if (!tess) { // Don't cache descriptors for HW tessellation.
VkDescriptorSet d = frame.descSets.Get(key); VkDescriptorSet d;
if (d != VK_NULL_HANDLE) if (frame.descSets.Get(key, &d)) {
return d; return d;
} }
}
// Didn't find one in the frame descriptor set cache, let's make a new one. // Didn't find one in the frame descriptor set cache, let's make a new one.
// We wipe the cache on every frame. // We wipe the cache on every frame.
@ -550,8 +551,8 @@ bool DrawEngineVulkan::VertexCacheLookup(int &vertexCount, GEPrimitiveType &prim
u32 dcid = ComputeDrawcallsHash() ^ gstate.getUVGenMode(); u32 dcid = ComputeDrawcallsHash() ^ gstate.getUVGenMode();
PROFILE_THIS_SCOPE("vcache"); PROFILE_THIS_SCOPE("vcache");
VertexArrayInfoVulkan *vai = vai_.Get(dcid); VertexArrayInfoVulkan *vai;
if (!vai) { if (!vai_.Get(dcid, &vai)) {
vai = new VertexArrayInfoVulkan(); vai = new VertexArrayInfoVulkan();
vai_.Insert(dcid, vai); vai_.Insert(dcid, vai);
} }

View file

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

View file

@ -351,9 +351,10 @@ VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VulkanRenderManager *
key.gShader = gs ? gs->GetModule() : VK_NULL_HANDLE; key.gShader = gs ? gs->GetModule() : VK_NULL_HANDLE;
key.vtxFmtId = useHwTransform ? decFmt->id : 0; key.vtxFmtId = useHwTransform ? decFmt->id : 0;
auto iter = pipelines_.Get(key); VulkanPipeline *pipeline;
if (iter) if (pipelines_.Get(key, &pipeline)) {
return iter; return pipeline;
}
PipelineFlags pipelineFlags = (PipelineFlags)0; PipelineFlags pipelineFlags = (PipelineFlags)0;
if (fs->Flags() & FragmentShaderFlags::USES_DISCARD) { if (fs->Flags() & FragmentShaderFlags::USES_DISCARD) {
@ -365,7 +366,7 @@ VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VulkanRenderManager *
VkSampleCountFlagBits sampleCount = MultiSampleLevelToFlagBits(multiSampleLevel); VkSampleCountFlagBits sampleCount = MultiSampleLevelToFlagBits(multiSampleLevel);
VulkanPipeline *pipeline = CreateVulkanPipeline( pipeline = CreateVulkanPipeline(
renderManager, pipelineCache_, layout, pipelineFlags, sampleCount, renderManager, pipelineCache_, layout, pipelineFlags, sampleCount,
rasterKey, decFmt, vs, fs, gs, useHwTransform, variantBitmask, cacheLoad); rasterKey, decFmt, vs, fs, gs, useHwTransform, variantBitmask, cacheLoad);
@ -485,10 +486,11 @@ std::string PipelineManagerVulkan::DebugGetObjectString(std::string id, DebugSha
VulkanPipelineKey pipelineKey; VulkanPipelineKey pipelineKey;
pipelineKey.FromString(id); pipelineKey.FromString(id);
VulkanPipeline *pipeline = pipelines_.Get(pipelineKey); VulkanPipeline *pipeline;
if (!pipeline) { if (!pipelines_.Get(pipelineKey, &pipeline)) {
return "N/A (missing)"; return "N/A (missing)";
} }
_assert_(pipeline != nullptr);
u32 variants = pipeline->GetVariantsBitmask(); u32 variants = pipeline->GetVariantsBitmask();
std::string keyDescription = pipelineKey.GetDescription(stringType, shaderManager); std::string keyDescription = pipelineKey.GetDescription(stringType, shaderManager);

View file

@ -104,7 +104,7 @@ public:
void CancelCache(); void CancelCache();
private: private:
DenseHashMap<VulkanPipelineKey, VulkanPipeline *, nullptr> pipelines_; DenseHashMap<VulkanPipelineKey, VulkanPipeline *> pipelines_;
VkPipelineCache pipelineCache_ = VK_NULL_HANDLE; VkPipelineCache pipelineCache_ = VK_NULL_HANDLE;
VulkanContext *vulkan_; VulkanContext *vulkan_;
bool cancelCache_ = false; 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); VulkanContext *vulkan = (VulkanContext *)draw_->GetNativeObject(Draw::NativeObject::CONTEXT);
VulkanVertexShader *vs = vsCache_.Get(VSID); VulkanVertexShader *vs = nullptr;
if (!vs) { if (!vsCache_.Get(VSID, &vs)) {
// Vertex shader not in cache. Let's compile it. // Vertex shader not in cache. Let's compile it.
std::string genErrorString; std::string genErrorString;
uint64_t uniformMask = 0; // Not used 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_)); _assert_msg_(strlen(codeBuffer_) < CODE_BUFFER_SIZE, "VS length error: %d", (int)strlen(codeBuffer_));
std::lock_guard<std::mutex> guard(cacheLock_); std::lock_guard<std::mutex> guard(cacheLock_);
vs = vsCache_.Get(VSID); if (!vsCache_.Get(VSID, &vs)) {
if (!vs) {
vs = new VulkanVertexShader(vulkan, VSID, flags, codeBuffer_, useHWTransform); vs = new VulkanVertexShader(vulkan, VSID, flags, codeBuffer_, useHWTransform);
vsCache_.Insert(VSID, vs); vsCache_.Insert(VSID, vs);
} }
} }
VulkanFragmentShader *fs = fsCache_.Get(FSID); VulkanFragmentShader *fs;
if (!fs) { if (!fsCache_.Get(FSID, &fs)) {
// Fragment shader not in cache. Let's compile it. // Fragment shader not in cache. Let's compile it.
std::string genErrorString; std::string genErrorString;
uint64_t uniformMask = 0; // Not used 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_)); _assert_msg_(strlen(codeBuffer_) < CODE_BUFFER_SIZE, "FS length error: %d", (int)strlen(codeBuffer_));
std::lock_guard<std::mutex> guard(cacheLock_); std::lock_guard<std::mutex> guard(cacheLock_);
fs = fsCache_.Get(FSID); if (!fsCache_.Get(FSID, &fs)) {
if (!fs) {
fs = new VulkanFragmentShader(vulkan, FSID, flags, codeBuffer_); fs = new VulkanFragmentShader(vulkan, FSID, flags, codeBuffer_);
fsCache_.Insert(FSID, fs); fsCache_.Insert(FSID, fs);
} }
@ -381,8 +379,7 @@ void ShaderManagerVulkan::GetShaders(int prim, VertexDecoder *decoder, VulkanVer
VulkanGeometryShader *gs; VulkanGeometryShader *gs;
if (GSID.Bit(GS_BIT_ENABLED)) { if (GSID.Bit(GS_BIT_ENABLED)) {
gs = gsCache_.Get(GSID); if (!gsCache_.Get(GSID, &gs)) {
if (!gs) {
// Geometry shader not in cache. Let's compile it. // Geometry shader not in cache. Let's compile it.
std::string genErrorString; std::string genErrorString;
bool success = GenerateGeometryShader(GSID, codeBuffer_, compat_, draw_->GetBugs(), &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_)); _assert_msg_(strlen(codeBuffer_) < CODE_BUFFER_SIZE, "GS length error: %d", (int)strlen(codeBuffer_));
std::lock_guard<std::mutex> guard(cacheLock_); std::lock_guard<std::mutex> guard(cacheLock_);
gs = gsCache_.Get(GSID); if (!gsCache_.Get(GSID, &gs)) {
if (!gs) {
gs = new VulkanGeometryShader(vulkan, GSID, codeBuffer_); gs = new VulkanGeometryShader(vulkan, GSID, codeBuffer_);
gsCache_.Insert(GSID, gs); gsCache_.Insert(GSID, gs);
} }
@ -456,18 +452,30 @@ std::string ShaderManagerVulkan::DebugGetShaderString(std::string id, DebugShade
switch (type) { switch (type) {
case SHADER_TYPE_VERTEX: case SHADER_TYPE_VERTEX:
{ {
VulkanVertexShader *vs = vsCache_.Get(VShaderID(shaderId)); VulkanVertexShader *vs;
return vs ? vs->GetShaderString(stringType) : ""; if (vsCache_.Get(VShaderID(shaderId), &vs)) {
return vs ? vs->GetShaderString(stringType) : "null (bad)";
} else {
return "";
}
} }
case SHADER_TYPE_FRAGMENT: case SHADER_TYPE_FRAGMENT:
{ {
VulkanFragmentShader *fs = fsCache_.Get(FShaderID(shaderId)); VulkanFragmentShader *fs;
return fs ? fs->GetShaderString(stringType) : ""; if (fsCache_.Get(FShaderID(shaderId), &fs)) {
return fs ? fs->GetShaderString(stringType) : "null (bad)";
} else {
return "";
}
} }
case SHADER_TYPE_GEOMETRY: case SHADER_TYPE_GEOMETRY:
{ {
VulkanGeometryShader *gs = gsCache_.Get(GShaderID(shaderId)); VulkanGeometryShader *gs;
return gs ? gs->GetShaderString(stringType) : ""; if (gsCache_.Get(GShaderID(shaderId), &gs)) {
return gs ? gs->GetShaderString(stringType) : "null (bad)";
} else {
return "";
}
} }
default: default:
return "N/A"; return "N/A";
@ -592,8 +600,8 @@ bool ShaderManagerVulkan::LoadCache(FILE *f) {
VulkanVertexShader *vs = new VulkanVertexShader(vulkan, id, flags, codeBuffer_, useHWTransform); VulkanVertexShader *vs = new VulkanVertexShader(vulkan, id, flags, codeBuffer_, useHWTransform);
// Remove first, just to be safe (we are loading on a background thread.) // Remove first, just to be safe (we are loading on a background thread.)
std::lock_guard<std::mutex> guard(cacheLock_); std::lock_guard<std::mutex> guard(cacheLock_);
VulkanVertexShader *old = vsCache_.Get(id); VulkanVertexShader *old;
if (old) { if (vsCache_.Get(id, &old)) {
vsCache_.Remove(id); vsCache_.Remove(id);
delete old; 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_)); _assert_msg_(strlen(codeBuffer_) < CODE_BUFFER_SIZE, "FS length error: %d", (int)strlen(codeBuffer_));
VulkanFragmentShader *fs = new VulkanFragmentShader(vulkan, id, flags, codeBuffer_); VulkanFragmentShader *fs = new VulkanFragmentShader(vulkan, id, flags, codeBuffer_);
std::lock_guard<std::mutex> guard(cacheLock_); std::lock_guard<std::mutex> guard(cacheLock_);
VulkanFragmentShader *old = fsCache_.Get(id); VulkanFragmentShader *old;
if (old) { if (fsCache_.Get(id, &old)) {
fsCache_.Remove(id); fsCache_.Remove(id);
delete old; 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_)); _assert_msg_(strlen(codeBuffer_) < CODE_BUFFER_SIZE, "GS length error: %d", (int)strlen(codeBuffer_));
VulkanGeometryShader *gs = new VulkanGeometryShader(vulkan, id, codeBuffer_); VulkanGeometryShader *gs = new VulkanGeometryShader(vulkan, id, codeBuffer_);
std::lock_guard<std::mutex> guard(cacheLock_); std::lock_guard<std::mutex> guard(cacheLock_);
VulkanGeometryShader *old = gsCache_.Get(id); VulkanGeometryShader *old;
if (old) { if (gsCache_.Get(id, &old)) {
gsCache_.Remove(id); gsCache_.Remove(id);
delete old; delete old;
} }

View file

@ -127,9 +127,9 @@ public:
int GetNumGeometryShaders() const { return (int)gsCache_.size(); } int GetNumGeometryShaders() const { return (int)gsCache_.size(); }
// Used for saving/loading the cache. Don't need to be particularly fast. // Used for saving/loading the cache. Don't need to be particularly fast.
VulkanVertexShader *GetVertexShaderFromID(VShaderID id) { return vsCache_.Get(id); } VulkanVertexShader *GetVertexShaderFromID(VShaderID id) { return vsCache_.GetOrNull(id); }
VulkanFragmentShader *GetFragmentShaderFromID(FShaderID id) { return fsCache_.Get(id); } VulkanFragmentShader *GetFragmentShaderFromID(FShaderID id) { return fsCache_.GetOrNull(id); }
VulkanGeometryShader *GetGeometryShaderFromID(GShaderID id) { return gsCache_.Get(id); } VulkanGeometryShader *GetGeometryShaderFromID(GShaderID id) { return gsCache_.GetOrNull(id); }
VulkanVertexShader *GetVertexShaderFromModule(VkShaderModule module); VulkanVertexShader *GetVertexShaderFromModule(VkShaderModule module);
VulkanFragmentShader *GetFragmentShaderFromModule(VkShaderModule module); VulkanFragmentShader *GetFragmentShaderFromModule(VkShaderModule module);
VulkanGeometryShader *GetGeometryShaderFromModule(VkShaderModule module); VulkanGeometryShader *GetGeometryShaderFromModule(VkShaderModule module);
@ -165,13 +165,13 @@ private:
ShaderLanguageDesc compat_; ShaderLanguageDesc compat_;
typedef DenseHashMap<FShaderID, VulkanFragmentShader *, nullptr> FSCache; typedef DenseHashMap<FShaderID, VulkanFragmentShader *> FSCache;
FSCache fsCache_; FSCache fsCache_;
typedef DenseHashMap<VShaderID, VulkanVertexShader *, nullptr> VSCache; typedef DenseHashMap<VShaderID, VulkanVertexShader *> VSCache;
VSCache vsCache_; VSCache vsCache_;
typedef DenseHashMap<GShaderID, VulkanGeometryShader *, nullptr> GSCache; typedef DenseHashMap<GShaderID, VulkanGeometryShader *> GSCache;
GSCache gsCache_; GSCache gsCache_;
char *codeBuffer_; char *codeBuffer_;

View file

@ -121,9 +121,10 @@ SamplerCache::~SamplerCache() {
} }
VkSampler SamplerCache::GetOrCreateSampler(const SamplerCacheKey &key) { VkSampler SamplerCache::GetOrCreateSampler(const SamplerCacheKey &key) {
VkSampler sampler = cache_.Get(key); VkSampler sampler;
if (sampler != VK_NULL_HANDLE) if (cache_.Get(key, &sampler)) {
return sampler; return sampler;
}
VkSamplerCreateInfo samp = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; VkSamplerCreateInfo samp = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };
samp.addressModeU = key.sClamp ? VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE : VK_SAMPLER_ADDRESS_MODE_REPEAT; samp.addressModeU = key.sClamp ? VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE : VK_SAMPLER_ADDRESS_MODE_REPEAT;

View file

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

View file

@ -185,9 +185,10 @@ VkDescriptorSet VulkanComputeShaderManager::GetDescriptorSet(VkImageView image,
VkPipeline VulkanComputeShaderManager::GetPipeline(VkShaderModule cs) { VkPipeline VulkanComputeShaderManager::GetPipeline(VkShaderModule cs) {
PipelineKey key{ cs }; PipelineKey key{ cs };
VkPipeline pipeline = pipelines_.Get(key); VkPipeline pipeline;
if (pipeline) if (pipelines_.Get(key, &pipeline)) {
return pipeline; return pipeline;
}
VkComputePipelineCreateInfo pci{ VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO }; VkComputePipelineCreateInfo pci{ VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO };
pci.stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; pci.stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;

View file

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