Fix the semantics of DenseHashMap to be consistent even when inserting nulls
This commit is contained in:
parent
4d58bb801c
commit
10f93875c6
29 changed files with 220 additions and 163 deletions
|
@ -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.
|
||||
|
|
|
@ -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) {}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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_{};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -49,7 +49,7 @@ public:
|
|||
|
||||
private:
|
||||
VulkanContext *vulkan_;
|
||||
DenseHashMap<SamplerCacheKey, VkSampler, (VkSampler)VK_NULL_HANDLE> cache_;
|
||||
DenseHashMap<SamplerCacheKey, VkSampler> cache_;
|
||||
};
|
||||
|
||||
class TextureCacheVulkan : public TextureCacheCommon {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -85,7 +85,7 @@ private:
|
|||
VkShaderModule module;
|
||||
};
|
||||
|
||||
DenseHashMap<PipelineKey, VkPipeline, (VkPipeline)VK_NULL_HANDLE> pipelines_;
|
||||
DenseHashMap<PipelineKey, VkPipeline> pipelines_;
|
||||
};
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue