Diligent Engine API Reference: D:/GitHub/DiligentEngine/DiligentCore/Graphics/GraphicsEngine/include/StateObjectsRegistry.h Source File

Diligent Engine API Reference

StateObjectsRegistry.h
1 /* Copyright 2015-2018 Egor Yusov
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
10 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
11 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS.
12 *
13 * In no event and under no legal theory, whether in tort (including negligence),
14 * contract, or otherwise, unless required by applicable law (such as deliberate
15 * and grossly negligent acts) or agreed to in writing, shall any Contributor be
16 * liable for any damages, including any direct, indirect, special, incidental,
17 * or consequential damages of any character arising as a result of this License or
18 * out of the use or inability to use the software (including but not limited to damages
19 * for loss of goodwill, work stoppage, computer failure or malfunction, or any and
20 * all other commercial damages or losses), even if such Contributor has been advised
21 * of the possibility of such damages.
22 */
23
24 #pragma once
25
28
29 #include "DeviceObject.h"
30 #include <unordered_map>
31 #include "STDAllocator.h"
32
33 namespace Diligent
34 {
36
57 template<typename ResourceDescType>
58 class StateObjectsRegistry
59 {
60 public:
62 static constexpr int DeletedObjectsToPurge = 32;
63
64 StateObjectsRegistry(IMemoryAllocator &RawAllocator, const Char* RegistryName) :
65 m_RegistryName( RegistryName ),
66 m_DescToObjHashMap(STD_ALLOCATOR_RAW_MEM(HashMapElem, RawAllocator, "Allocator for unordered_map<ResourceDescType, RefCntWeakPtr<IDeviceObject> >") )
67 {}
68
69 ~StateObjectsRegistry()
70 {
71 // Object registry is part of the device, and every device
72 // object holds a strong reference to the device. So device
73 // is destroyed after all device objects are destroyed, and there
74 // may only be expired references in the registry. After we
75 // purge it, the registry must be empty.
76 Purge();
77 VERIFY( m_DescToObjHashMap.empty(), "DescToObjHashMap is not empty" );
78 }
79
81
91 void Add( const ResourceDescType& ObjectDesc, IDeviceObject *pObject )
92 {
93 ThreadingTools::LockHelper Lock( m_LockFlag );
94
95 // If the number of outstanding deleted objects reached the threshold value,
96 // purge the registry. Since we have exclusive access now, it is safe
97 // to do.
98 if( m_NumDeletedObjects >= DeletedObjectsToPurge )
99 {
100 Purge();
101 m_NumDeletedObjects = 0;
102 }
103
104 // Try to construct the new element in place
105 auto Elems = m_DescToObjHashMap.emplace( std::make_pair( ObjectDesc, Diligent::RefCntWeakPtr<IDeviceObject>(pObject) ) );
106 // It is theorertically possible that the same object can be found
107 // in the registry. This might happen if two threads try to create
108 // the same object at the same time. They both will not find the
109 // object and then will create and try to add it.
110 //
111 // If the object already exists, we replace the existing reference.
112 // This is safer as there might be scenarios where existing reference
113 // might be expired. For instance, two threads try to create the same
114 // object which is not in the registry. The first thread creates
115 // the object, adds it to the registry and then releases it. After that
116 // the second thread creates the same object and tries to add it to
117 // the registry. It will find an existing expired reference to the
118 // object.
119 if( !Elems.second )
120 {
121 VERIFY( Elems.first->first == ObjectDesc, "Incorrect object description" );
122 LOG_WARNING_MESSAGE( "Object named \"", Elems.first->first.Name, "\" with the same description already exists in the registry."
123 "Replacing with the new object named \"", ObjectDesc.Name ? ObjectDesc.Name : "", "\".");
124 Elems.first->second = pObject;
125 }
126 }
127
129 void Find( const ResourceDescType &Desc, IDeviceObject **ppObject )
130 {
131 VERIFY( *ppObject == nullptr, "Overwriting reference to existing object may cause memory leaks" );
132 *ppObject = nullptr;
133 ThreadingTools::LockHelper Lock( m_LockFlag );
134
135 auto It = m_DescToObjHashMap.find( Desc );
136 if( It != m_DescToObjHashMap.end() )
137 {
138 // Try to obtain strong reference to the object.
139 // This is an atomic operation and we either get
140 // a new strong reference or object has been destroyed
141 // and we get null.
142 auto pObject = It->second.Lock();
143 if( pObject )
144 {
145 *ppObject = pObject.Detach();
146 LOG_INFO_MESSAGE( "Equivalent of the requested state object named \"", Desc.Name ? Desc.Name : "", "\" found in the ", m_RegistryName, " registry. Reusing existing object.");
147 }
148 else
149 {
150 // Expired object found: remove it from the map
151 m_DescToObjHashMap.erase(It);
152 Atomics::AtomicDecrement(m_NumDeletedObjects);
153 }
154 }
155 }
156
158 void Purge()
159 {
160 Uint32 NumPurgedObjects = 0;
161 auto It = m_DescToObjHashMap.begin();
162 while( It != m_DescToObjHashMap.end() )
163 {
164 auto NextIt = It;
165 ++NextIt;
166 // Note that IsValid() is not a thread-safe function in the sense that it
167 // can give false positive results. The only thread-safe way to check if the
168 // object is alive is to lock the weak pointer, but that requires thread
169 // synchronization. We will immediately unlock the pointer anyway, so we
170 // want to detect 100% expired pointers. IsValid() does provide that information
171 // because once a weak pointer becomes invalid, it will be invalid
172 // until it is destroyed. It is not a problem if we miss an expired weak
173 // pointer as it will definitiely be removed next time.
174 if( !It->second.IsValid() )
175 {
176 m_DescToObjHashMap.erase( It );
177 ++NumPurgedObjects;
178 }
179
180 It = NextIt;
181 }
182 LOG_INFO_MESSAGE( "Purged ", NumPurgedObjects, " deleted objects from the ", m_RegistryName, " registry" );
183 }
184
188 void ReportDeletedObject()
189 {
190 Atomics::AtomicIncrement(m_NumDeletedObjects);
191 }
192
193 private:
195 ThreadingTools::LockFlag m_LockFlag;
196
198 Atomics::AtomicLong m_NumDeletedObjects;
199
201 typedef std::pair< const ResourceDescType, RefCntWeakPtr<IDeviceObject> > HashMapElem;
202 std::unordered_map<ResourceDescType, RefCntWeakPtr<IDeviceObject>, std::hash<ResourceDescType>, std::equal_to<ResourceDescType>, STDAllocatorRawMem<HashMapElem> > m_DescToObjHashMap;
203
205 const String m_RegistryName;
206 };
207 }
Diligent::IDeviceObject
Base interface for all objects created by the render device Diligent::IRenderDevice.
Definition: DeviceObject.h:40
Diligent::StateObjectsRegistry::DeletedObjectsToPurge
static constexpr int DeletedObjectsToPurge
Number of outstanding deleted objects to purge the registry.
Definition: StateObjectsRegistry.h:62
Diligent
Namespace for the OpenGL implementation of the graphics engine.
Definition: BufferD3D11Impl.h:34
Diligent::StateObjectsRegistry
Template class implementing state object registry.
Definition: StateObjectsRegistry.h:58
Diligent::RefCntWeakPtr
Implementation of weak pointers.
Definition: RefCntAutoPtr.h:38
Diligent::StateObjectsRegistry::Find
void Find(const ResourceDescType &Desc, IDeviceObject **ppObject)
Finds the object in the registry.
Definition: StateObjectsRegistry.h:129
Diligent::StateObjectsRegistry::Add
void Add(const ResourceDescType &ObjectDesc, IDeviceObject *pObject)
Adds a new object to the registry.
Definition: StateObjectsRegistry.h:91
Diligent::StateObjectsRegistry::ReportDeletedObject
void ReportDeletedObject()
Increments the number of outstanding deleted objects. When this number reaches DeletedObjectsToPurge...
Definition: StateObjectsRegistry.h:188
Diligent::StateObjectsRegistry::Purge
void Purge()
Purges outstanding deleted objects from the registry.
Definition: StateObjectsRegistry.h:158