diff options
author | Shahbaz Youssefi <syoussefi@chromium.org> | 2024-05-10 00:15:31 -0400 |
---|---|---|
committer | Angle LUCI CQ <angle-scoped@luci-project-accounts.iam.gserviceaccount.com> | 2024-05-14 03:59:10 +0000 |
commit | a1665d2fc335ef1481c76d4066cb2e22bab83ba7 (patch) | |
tree | 47d614a76bfd09b316f0570bf9fa5e7508bc4979 | |
parent | df2e7bd7c99e27868947f887355ad11979f6d492 (diff) | |
download | angle-a1665d2fc335ef1481c76d4066cb2e22bab83ba7.tar.gz |
Reland "Document thread-unsafe iterator access to resource maps"
This is a reland of commit d1bb6ed8399dd12e79484f30f9e9ded95c25625a
The crash was due to another issue (disabling EGL validation in
Chrome)
Original change's description:
> Document thread-unsafe iterator access to resource maps
>
> By using a proxy type, everywhere resource maps are iterated are clearly
> marked as not being thread safe. In most cases, only destruction and
> capture/replay iterate over these maps, which means thread safety is not
> an issue (or is externally enforced).
>
> The only case where iterators are used in the presence of other contexts
> is with ANGLE_request_extension, which is changed to explicitly require
> the application to ensure thread safety. In practice, the user is
> Chrome which already guarantees this.
>
> Bug: angleproject:8667
> Change-Id: I7af13c6433b6955d9c36f9088b3aa4c065e1cfc1
> Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/5526428
> Reviewed-by: Charlie Lao <cclao@google.com>
> Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
> Reviewed-by: Geoff Lang <geofflang@chromium.org>
Bug: angleproject:8667
Change-Id: Id539cabac01df5f242150f6684222577003eef3f
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/5531278
Reviewed-by: Charlie Lao <cclao@google.com>
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
-rw-r--r-- | extensions/ANGLE_request_extension.txt | 4 | ||||
-rw-r--r-- | src/libANGLE/Context.cpp | 14 | ||||
-rw-r--r-- | src/libANGLE/ResourceManager.cpp | 60 | ||||
-rw-r--r-- | src/libANGLE/ResourceManager.h | 6 | ||||
-rw-r--r-- | src/libANGLE/ResourceMap.h | 42 | ||||
-rw-r--r-- | src/libANGLE/ResourceMap_unittest.cpp | 10 | ||||
-rw-r--r-- | src/libANGLE/capture/FrameCapture.cpp | 33 | ||||
-rw-r--r-- | src/libANGLE/capture/serialize.cpp | 20 |
8 files changed, 124 insertions, 65 deletions
diff --git a/extensions/ANGLE_request_extension.txt b/extensions/ANGLE_request_extension.txt index 88d3422e3b..56317e6d6a 100644 --- a/extensions/ANGLE_request_extension.txt +++ b/extensions/ANGLE_request_extension.txt @@ -85,7 +85,9 @@ Additions to the OpenGL ES 3.0 Specification enable or disable the requestable OpenGL ES extension named <name>. If the requested extension was not requestable or disablable, INVALID_OPERATION is generated. Not all requestable extensions can be disabled. There is - currently no query for disablable extensions. + currently no query for disablable extensions. This operation is not thread + safe, and the application is responsible for ensuring no other context in + the share group is accessed by another thread during this operation. New State diff --git a/src/libANGLE/Context.cpp b/src/libANGLE/Context.cpp index 1b23bd94a8..5eda734ca6 100644 --- a/src/libANGLE/Context.cpp +++ b/src/libANGLE/Context.cpp @@ -850,7 +850,7 @@ egl::Error Context::onDestroy(const egl::Display *display) mDefaultFramebuffer->onDestroy(this); mDefaultFramebuffer.reset(); - for (auto fence : mFenceNVMap) + for (auto fence : UnsafeResourceMapIter(mFenceNVMap)) { if (fence.second) { @@ -860,7 +860,7 @@ egl::Error Context::onDestroy(const egl::Display *display) } mFenceNVMap.clear(); - for (auto query : mQueryMap) + for (auto query : UnsafeResourceMapIter(mQueryMap)) { if (query.second != nullptr) { @@ -869,7 +869,7 @@ egl::Error Context::onDestroy(const egl::Display *display) } mQueryMap.clear(); - for (auto vertexArray : mVertexArrayMap) + for (auto vertexArray : UnsafeResourceMapIter(mVertexArrayMap)) { if (vertexArray.second) { @@ -878,7 +878,7 @@ egl::Error Context::onDestroy(const egl::Display *display) } mVertexArrayMap.clear(); - for (auto transformFeedback : mTransformFeedbackMap) + for (auto transformFeedback : UnsafeResourceMapIter(mTransformFeedbackMap)) { if (transformFeedback.second != nullptr) { @@ -3618,7 +3618,8 @@ void Context::beginTransformFeedback(PrimitiveMode primitiveMode) bool Context::hasActiveTransformFeedback(ShaderProgramID program) const { - for (auto pair : mTransformFeedbackMap) + // Note: transform feedback objects are private to context and so the map doesn't need locking + for (auto pair : UnsafeResourceMapIter(mTransformFeedbackMap)) { if (pair.second != nullptr && pair.second->hasBoundProgram(program)) { @@ -4498,7 +4499,8 @@ void Context::updateCaps() (mState.isWebGL() || mState.hasRobustAccess())); // Cache this in the VertexArrays. They need to check it in state change notifications. - for (auto vaoIter : mVertexArrayMap) + // Note: vertex array objects are private to context and so the map doesn't need locking + for (auto vaoIter : UnsafeResourceMapIter(mVertexArrayMap)) { VertexArray *vao = vaoIter.second; vao->setBufferAccessValidationEnabled(mBufferAccessValidationEnabled); diff --git a/src/libANGLE/ResourceManager.cpp b/src/libANGLE/ResourceManager.cpp index 8c63342b12..bf0c93d59e 100644 --- a/src/libANGLE/ResourceManager.cpp +++ b/src/libANGLE/ResourceManager.cpp @@ -61,14 +61,16 @@ void ResourceManagerBase::release(const Context *context) template <typename ResourceType, typename ImplT, typename IDType> TypedResourceManager<ResourceType, ImplT, IDType>::~TypedResourceManager() { - ASSERT(mObjectMap.empty()); + ASSERT(UnsafeResourceMapIter(mObjectMap).empty()); } template <typename ResourceType, typename ImplT, typename IDType> void TypedResourceManager<ResourceType, ImplT, IDType>::reset(const Context *context) { + // Note: this function is called when the last context in the share group is destroyed. Thus + // there are no thread safety concerns. this->mHandleAllocator.reset(); - for (const auto &resource : mObjectMap) + for (const auto &resource : UnsafeResourceMapIter(mObjectMap)) { if (resource.second) { @@ -138,21 +140,30 @@ ShaderProgramManager::ShaderProgramManager() {} ShaderProgramManager::~ShaderProgramManager() { - ASSERT(mPrograms.empty()); - ASSERT(mShaders.empty()); + ASSERT(UnsafeResourceMapIter(mPrograms).empty()); + ASSERT(UnsafeResourceMapIter(mShaders).empty()); } void ShaderProgramManager::reset(const Context *context) { - while (!mPrograms.empty()) + // Note: this function is called when the last context in the share group is destroyed. Thus + // there are no thread safety concerns. + mHandleAllocator.reset(); + for (const auto &program : UnsafeResourceMapIter(mPrograms)) { - deleteProgram(context, {mPrograms.begin()->first}); + if (program.second) + { + program.second->onDestroy(context); + } } - mPrograms.clear(); - while (!mShaders.empty()) + for (const auto &shader : UnsafeResourceMapIter(mShaders)) { - deleteShader(context, {mShaders.begin()->first}); + if (shader.second) + { + shader.second->onDestroy(context); + } } + mPrograms.clear(); mShaders.clear(); } @@ -238,7 +249,9 @@ TextureID TextureManager::createTexture() void TextureManager::signalAllTexturesDirty() const { - for (const auto &texture : mObjectMap) + // Note: this function is called with glRequestExtensionANGLE and glDisableExtensionANGLE. The + // GL_ANGLE_request_extension explicitly requires the application to ensure thread safety. + for (const auto &texture : UnsafeResourceMapIter(mObjectMap)) { if (texture.second) { @@ -384,7 +397,8 @@ Framebuffer *FramebufferManager::getDefaultFramebuffer() const void FramebufferManager::invalidateFramebufferCompletenessCache() const { - for (const auto &framebuffer : mObjectMap) + // Note: framebuffer objects are private to context and so the map doesn't need locking + for (const auto &framebuffer : UnsafeResourceMapIter(mObjectMap)) { if (framebuffer.second) { @@ -428,14 +442,20 @@ MemoryObjectManager::MemoryObjectManager() {} MemoryObjectManager::~MemoryObjectManager() { - ASSERT(mMemoryObjects.empty()); + ASSERT(UnsafeResourceMapIter(mMemoryObjects).empty()); } void MemoryObjectManager::reset(const Context *context) { - while (!mMemoryObjects.empty()) + // Note: this function is called when the last context in the share group is destroyed. Thus + // there are no thread safety concerns. + mHandleAllocator.reset(); + for (const auto &memoryObject : UnsafeResourceMapIter(mMemoryObjects)) { - deleteMemoryObject(context, {mMemoryObjects.begin()->first}); + if (memoryObject.second) + { + memoryObject.second->release(context); + } } mMemoryObjects.clear(); } @@ -477,14 +497,20 @@ SemaphoreManager::SemaphoreManager() {} SemaphoreManager::~SemaphoreManager() { - ASSERT(mSemaphores.empty()); + ASSERT(UnsafeResourceMapIter(mSemaphores).empty()); } void SemaphoreManager::reset(const Context *context) { - while (!mSemaphores.empty()) + // Note: this function is called when the last context in the share group is destroyed. Thus + // there are no thread safety concerns. + mHandleAllocator.reset(); + for (const auto &semaphore : UnsafeResourceMapIter(mSemaphores)) { - deleteSemaphore(context, {mSemaphores.begin()->first}); + if (semaphore.second) + { + semaphore.second->release(context); + } } mSemaphores.clear(); } diff --git a/src/libANGLE/ResourceManager.h b/src/libANGLE/ResourceManager.h index 710a88dd44..372b76b4b5 100644 --- a/src/libANGLE/ResourceManager.h +++ b/src/libANGLE/ResourceManager.h @@ -75,11 +75,7 @@ class TypedResourceManager : public ResourceManagerBase return GetIDValue(handle) == 0 || mObjectMap.contains(handle); } - typename ResourceMap<ResourceType, IDType>::Iterator begin() const - { - return mObjectMap.begin(); - } - typename ResourceMap<ResourceType, IDType>::Iterator end() const { return mObjectMap.end(); } + const ResourceMap<ResourceType, IDType> &getResourcesForCapture() const { return mObjectMap; } protected: ~TypedResourceManager() override; diff --git a/src/libANGLE/ResourceMap.h b/src/libANGLE/ResourceMap.h index b454e261a2..92d6b6251d 100644 --- a/src/libANGLE/ResourceMap.h +++ b/src/libANGLE/ResourceMap.h @@ -73,6 +73,11 @@ class ResourceMap final : angle::NonCopyable bool mSkipNulls; }; + private: + friend class Iterator; + template <typename SameResourceType, typename SameIDType> + friend class UnsafeResourceMapIter; + // null values represent reserved handles. Iterator begin() const; Iterator end() const; @@ -80,12 +85,7 @@ class ResourceMap final : angle::NonCopyable Iterator beginWithNull() const; Iterator endWithNull() const; - // Not a constant-time operation, should only be used for verification. - bool empty() const; - - private: - friend class Iterator; - + // Used by iterators and related functions only (due to lack of thread safety). GLuint nextResource(size_t flatIndex, bool skipNulls) const; // constexpr methods cannot contain reinterpret_cast, so we need a static method. @@ -108,6 +108,30 @@ class ResourceMap final : angle::NonCopyable HashMap mHashedResources; }; +// A helper to retrieve the resource map iterators while being explicit that this is not thread +// safe. Usage of iterators are limited to clean up on destruction and capture/replay, neither of +// which can race with other threads in their access to the resource map. +template <typename ResourceType, typename IDType> +class UnsafeResourceMapIter +{ + public: + using ResMap = ResourceMap<ResourceType, IDType>; + + UnsafeResourceMapIter(const ResMap &resourceMap) : mResourceMap(resourceMap) {} + + typename ResMap::Iterator begin() const { return mResourceMap.begin(); } + typename ResMap::Iterator end() const { return mResourceMap.end(); } + + typename ResMap::Iterator beginWithNull() const { return mResourceMap.beginWithNull(); } + typename ResMap::Iterator endWithNull() const { return mResourceMap.endWithNull(); } + + // Not a constant-time operation, should only be used for verification. + bool empty() const; + + private: + const ResMap &mResourceMap; +}; + template <typename ResourceType, typename IDType> ResourceMap<ResourceType, IDType>::ResourceMap() : mFlatResourcesSize(kInitialFlatResourcesSize), @@ -119,7 +143,7 @@ ResourceMap<ResourceType, IDType>::ResourceMap() template <typename ResourceType, typename IDType> ResourceMap<ResourceType, IDType>::~ResourceMap() { - ASSERT(empty()); + ASSERT(UnsafeResourceMapIter(*this).empty()); delete[] mFlatResources; } @@ -222,9 +246,9 @@ ResourceMap<ResourceType, IDType>::endWithNull() const } template <typename ResourceType, typename IDType> -bool ResourceMap<ResourceType, IDType>::empty() const +bool UnsafeResourceMapIter<ResourceType, IDType>::empty() const { - return (begin() == end()); + return begin() == end(); } template <typename ResourceType, typename IDType> diff --git a/src/libANGLE/ResourceMap_unittest.cpp b/src/libANGLE/ResourceMap_unittest.cpp index 056dde518a..01d2d706ba 100644 --- a/src/libANGLE/ResourceMap_unittest.cpp +++ b/src/libANGLE/ResourceMap_unittest.cpp @@ -33,7 +33,7 @@ TEST(ResourceMapTest, AssignAndErase) ASSERT_EQ(&objects[index], found); } - ASSERT_TRUE(resourceMap.empty()); + ASSERT_TRUE(UnsafeResourceMapIter(resourceMap).empty()); } // Tests assigning slots in the map and then using clear() to free it. @@ -48,7 +48,7 @@ TEST(ResourceMapTest, AssignAndClear) } resourceMap.clear(); - ASSERT_TRUE(resourceMap.empty()); + ASSERT_TRUE(UnsafeResourceMapIter(resourceMap).empty()); } // Tests growing a map more than double the size. @@ -80,7 +80,7 @@ TEST(ResourceMapTest, BigGrowth) ASSERT_EQ(object, *found); } - ASSERT_TRUE(resourceMap.empty()); + ASSERT_TRUE(UnsafeResourceMapIter(resourceMap).empty()); } // Tests querying unassigned or erased values. @@ -106,7 +106,7 @@ TEST(ResourceMapTest, QueryUnassigned) resourceMap.assign(object, &object); } - ASSERT_FALSE(resourceMap.empty()); + ASSERT_FALSE(UnsafeResourceMapIter(resourceMap).empty()); for (size_t &object : objects) { @@ -126,7 +126,7 @@ TEST(ResourceMapTest, QueryUnassigned) ASSERT_EQ(object, *found); } - ASSERT_TRUE(resourceMap.empty()); + ASSERT_TRUE(UnsafeResourceMapIter(resourceMap).empty()); ASSERT_FALSE(resourceMap.contains(0)); ASSERT_EQ(nullptr, resourceMap.query(0)); diff --git a/src/libANGLE/capture/FrameCapture.cpp b/src/libANGLE/capture/FrameCapture.cpp index 22daf186f7..3d9680aa94 100644 --- a/src/libANGLE/capture/FrameCapture.cpp +++ b/src/libANGLE/capture/FrameCapture.cpp @@ -4125,7 +4125,7 @@ void CaptureShareGroupMidExecutionSetup( // Capture Buffer data. const gl::BufferManager &buffers = apiState.getBufferManagerForCapture(); - for (const auto &bufferIter : buffers) + for (const auto &bufferIter : gl::UnsafeResourceMapIter(buffers.getResourcesForCapture())) { gl::BufferID id = {bufferIter.first}; gl::Buffer *buffer = bufferIter.second; @@ -4267,7 +4267,7 @@ void CaptureShareGroupMidExecutionSetup( // Capture Texture setup and data. const gl::TextureManager &textures = apiState.getTextureManagerForCapture(); - for (const auto &textureIter : textures) + for (const auto &textureIter : gl::UnsafeResourceMapIter(textures.getResourcesForCapture())) { gl::TextureID id = {textureIter.first}; gl::Texture *texture = textureIter.second; @@ -4593,7 +4593,8 @@ void CaptureShareGroupMidExecutionSetup( const gl::RenderbufferManager &renderbuffers = apiState.getRenderbufferManagerForCapture(); FramebufferCaptureFuncs framebufferFuncs(context->isGLES1()); - for (const auto &renderbufIter : renderbuffers) + for (const auto &renderbufIter : + gl::UnsafeResourceMapIter(renderbuffers.getResourcesForCapture())) { gl::RenderbufferID id = {renderbufIter.first}; const gl::Renderbuffer *renderbuffer = renderbufIter.second; @@ -4657,7 +4658,7 @@ void CaptureShareGroupMidExecutionSetup( // Capture Program binary state. gl::ShaderProgramID tempShaderStartID = {resourceTracker->getMaxShaderPrograms()}; std::map<gl::ShaderProgramID, std::vector<gl::ShaderProgramID>> deferredAttachCalls; - for (const auto &programIter : programs) + for (const auto &programIter : gl::UnsafeResourceMapIter(programs)) { gl::ShaderProgramID id = {programIter.first}; gl::Program *program = programIter.second; @@ -4738,7 +4739,7 @@ void CaptureShareGroupMidExecutionSetup( } // Handle shaders. - for (const auto &shaderIter : shaders) + for (const auto &shaderIter : gl::UnsafeResourceMapIter(shaders)) { gl::ShaderProgramID id = {shaderIter.first}; gl::Shader *shader = shaderIter.second; @@ -4827,7 +4828,7 @@ void CaptureShareGroupMidExecutionSetup( // Capture Sampler Objects const gl::SamplerManager &samplers = apiState.getSamplerManagerForCapture(); - for (const auto &samplerIter : samplers) + for (const auto &samplerIter : gl::UnsafeResourceMapIter(samplers.getResourcesForCapture())) { gl::SamplerID samplerID = {samplerIter.first}; @@ -4892,7 +4893,7 @@ void CaptureShareGroupMidExecutionSetup( // Capture Sync Objects const gl::SyncManager &syncs = apiState.getSyncManagerForCapture(); - for (const auto &syncIter : syncs) + for (const auto &syncIter : gl::UnsafeResourceMapIter(syncs.getResourcesForCapture())) { gl::SyncID syncID = {syncIter.first}; const gl::Sync *sync = syncIter.second; @@ -4967,7 +4968,7 @@ void CaptureMidExecutionSetup(const gl::Context *context, const gl::VertexArrayMap &vertexArrayMap = context->getVertexArraysForCapture(); gl::VertexArrayID boundVertexArrayID = {0}; - for (const auto &vertexArrayIter : vertexArrayMap) + for (const auto &vertexArrayIter : gl::UnsafeResourceMapIter(vertexArrayMap)) { TrackedResource &trackedVertexArrays = resourceTracker->getTrackedResource(context->id(), ResourceIDType::VertexArray); @@ -5145,7 +5146,8 @@ void CaptureMidExecutionSetup(const gl::Context *context, const gl::RenderbufferManager &renderbuffers = apiState.getRenderbufferManagerForCapture(); gl::RenderbufferID currentRenderbuffer = {0}; - for (const auto &renderbufIter : renderbuffers) + for (const auto &renderbufIter : + gl::UnsafeResourceMapIter(renderbuffers.getResourcesForCapture())) { currentRenderbuffer = renderbufIter.second->id(); } @@ -5162,7 +5164,8 @@ void CaptureMidExecutionSetup(const gl::Context *context, gl::FramebufferID currentDrawFramebuffer = {0}; gl::FramebufferID currentReadFramebuffer = {0}; - for (const auto &framebufferIter : framebuffers) + for (const auto &framebufferIter : + gl::UnsafeResourceMapIter(framebuffers.getResourcesForCapture())) { gl::FramebufferID id = {framebufferIter.first}; const gl::Framebuffer *framebuffer = framebufferIter.second; @@ -5297,7 +5300,8 @@ void CaptureMidExecutionSetup(const gl::Context *context, const gl::ProgramPipelineManager *programPipelineManager = apiState.getProgramPipelineManagerForCapture(); - for (const auto &ppoIterator : *programPipelineManager) + for (const auto &ppoIterator : + gl::UnsafeResourceMapIter(programPipelineManager->getResourcesForCapture())) { gl::ProgramPipeline *pipeline = ppoIterator.second; gl::ProgramPipelineID id = {ppoIterator.first}; @@ -5380,8 +5384,9 @@ void CaptureMidExecutionSetup(const gl::Context *context, // Create existing queries. Note that queries may be genned and not yet started. In that // case the queries will exist in the query map as nullptr entries. const gl::QueryMap &queryMap = context->getQueriesForCapture(); - for (gl::QueryMap::Iterator queryIter = queryMap.beginWithNull(); - queryIter != queryMap.endWithNull(); ++queryIter) + for (gl::QueryMap::Iterator queryIter = gl::UnsafeResourceMapIter(queryMap).beginWithNull(), + endIter = gl::UnsafeResourceMapIter(queryMap).endWithNull(); + queryIter != endIter; ++queryIter) { ASSERT(queryIter->first); gl::QueryID queryID = {queryIter->first}; @@ -5407,7 +5412,7 @@ void CaptureMidExecutionSetup(const gl::Context *context, // Transform Feedback const gl::TransformFeedbackMap &xfbMap = context->getTransformFeedbacksForCapture(); - for (const auto &xfbIter : xfbMap) + for (const auto &xfbIter : gl::UnsafeResourceMapIter(xfbMap)) { gl::TransformFeedbackID xfbID = {xfbIter.first}; diff --git a/src/libANGLE/capture/serialize.cpp b/src/libANGLE/capture/serialize.cpp index c1138a7f34..61655020a6 100644 --- a/src/libANGLE/capture/serialize.cpp +++ b/src/libANGLE/capture/serialize.cpp @@ -1404,7 +1404,8 @@ Result SerializeContextToString(const gl::Context *context, std::string *stringO const gl::FramebufferManager &framebufferManager = context->getState().getFramebufferManagerForCapture(); GroupScope framebufferGroup(&json, "FramebufferManager"); - for (const auto &framebuffer : framebufferManager) + for (const auto &framebuffer : + gl::UnsafeResourceMapIter(framebufferManager.getResourcesForCapture())) { gl::Framebuffer *framebufferPtr = framebuffer.second; ANGLE_TRY(SerializeFramebuffer(context, &json, &scratchBuffer, framebufferPtr)); @@ -1413,7 +1414,7 @@ Result SerializeContextToString(const gl::Context *context, std::string *stringO { const gl::BufferManager &bufferManager = context->getState().getBufferManagerForCapture(); GroupScope framebufferGroup(&json, "BufferManager"); - for (const auto &buffer : bufferManager) + for (const auto &buffer : gl::UnsafeResourceMapIter(bufferManager.getResourcesForCapture())) { gl::Buffer *bufferPtr = buffer.second; ANGLE_TRY(SerializeBuffer(context, &json, &scratchBuffer, bufferPtr)); @@ -1423,7 +1424,8 @@ Result SerializeContextToString(const gl::Context *context, std::string *stringO const gl::SamplerManager &samplerManager = context->getState().getSamplerManagerForCapture(); GroupScope samplerGroup(&json, "SamplerManager"); - for (const auto &sampler : samplerManager) + for (const auto &sampler : + gl::UnsafeResourceMapIter(samplerManager.getResourcesForCapture())) { gl::Sampler *samplerPtr = sampler.second; SerializeSampler(&json, samplerPtr); @@ -1433,7 +1435,8 @@ Result SerializeContextToString(const gl::Context *context, std::string *stringO const gl::RenderbufferManager &renderbufferManager = context->getState().getRenderbufferManagerForCapture(); GroupScope renderbufferGroup(&json, "RenderbufferManager"); - for (const auto &renderbuffer : renderbufferManager) + for (const auto &renderbuffer : + gl::UnsafeResourceMapIter(renderbufferManager.getResourcesForCapture())) { gl::Renderbuffer *renderbufferPtr = renderbuffer.second; ANGLE_TRY(SerializeRenderbuffer(context, &json, &scratchBuffer, renderbufferPtr)); @@ -1445,7 +1448,7 @@ Result SerializeContextToString(const gl::Context *context, std::string *stringO const gl::ResourceMap<gl::Shader, gl::ShaderProgramID> &shaderManager = shaderProgramManager.getShadersForCapture(); GroupScope shaderGroup(&json, "ShaderManager"); - for (const auto &shader : shaderManager) + for (const auto &shader : gl::UnsafeResourceMapIter(shaderManager)) { GLuint id = shader.first; gl::Shader *shaderPtr = shader.second; @@ -1456,7 +1459,7 @@ Result SerializeContextToString(const gl::Context *context, std::string *stringO const gl::ResourceMap<gl::Program, gl::ShaderProgramID> &programManager = shaderProgramManager.getProgramsForCaptureAndPerf(); GroupScope shaderGroup(&json, "ProgramManager"); - for (const auto &program : programManager) + for (const auto &program : gl::UnsafeResourceMapIter(programManager)) { GLuint id = program.first; gl::Program *programPtr = program.second; @@ -1467,7 +1470,8 @@ Result SerializeContextToString(const gl::Context *context, std::string *stringO const gl::TextureManager &textureManager = context->getState().getTextureManagerForCapture(); GroupScope shaderGroup(&json, "TextureManager"); - for (const auto &texture : textureManager) + for (const auto &texture : + gl::UnsafeResourceMapIter(textureManager.getResourcesForCapture())) { gl::Texture *texturePtr = texture.second; ANGLE_TRY(SerializeTexture(context, &json, &scratchBuffer, texturePtr)); @@ -1476,7 +1480,7 @@ Result SerializeContextToString(const gl::Context *context, std::string *stringO { const gl::VertexArrayMap &vertexArrayMap = context->getVertexArraysForCapture(); GroupScope shaderGroup(&json, "VertexArrayMap"); - for (const auto &vertexArray : vertexArrayMap) + for (const auto &vertexArray : gl::UnsafeResourceMapIter(vertexArrayMap)) { gl::VertexArray *vertexArrayPtr = vertexArray.second; SerializeVertexArray(&json, vertexArrayPtr); |