Skip to content

Commit

Permalink
Merge branch 'master' into fix_build
Browse files Browse the repository at this point in the history
  • Loading branch information
TheMostDiligent authored Sep 12, 2023
2 parents 074fe49 + aebd4e3 commit d79e21f
Show file tree
Hide file tree
Showing 47 changed files with 1,030 additions and 418 deletions.
2 changes: 1 addition & 1 deletion BuildTools/Android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ buildscript {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.2.2'
classpath 'com.android.tools.build:gradle:8.1.1'
}
}

Expand Down
3 changes: 3 additions & 0 deletions BuildTools/Android/gradle.properties
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
android.defaults.buildfeatures.buildconfig=true
android.native.buildOutput=verbose
android.nonFinalResIds=false
android.nonTransitiveRClass=false
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-all.zip
9 changes: 7 additions & 2 deletions BuildTools/Android/tests/build.gradle
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
apply plugin: 'com.android.application'

def android_arch = System.getenv("ANDROID_ARCH")
def abi_filters = android_arch != null ? [android_arch] : ['armeabi-v7a', 'arm64-v8a']

android {
compileSdkVersion 33
compileSdkVersion 34

defaultConfig {
applicationId 'DE.Android.Tests'
targetSdkVersion 32
minSdkVersion 21

ndk {
abiFilters "$System.env.ANDROID_ARCH"
abiFilters = []
abiFilters.addAll(abi_filters)
}
externalNativeBuild {
cmake {
Expand All @@ -35,4 +39,5 @@ android {
path 'src/main/jni/CMakeLists.txt'
}
}
namespace 'DE.Android.Tests'
}
4 changes: 2 additions & 2 deletions BuildTools/Android/tests/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="DE.Android.Tests"
android:versionCode="1"
android:versionName="1.0">
<uses-feature android:glEsVersion="0x00030000" android:required="true"></uses-feature>
<application
android:label="DE"
android:hasCode="false">
<activity android:name="android.app.NativeActivity"
android:configChanges="orientation|keyboardHidden">
android:configChanges="orientation|keyboardHidden"
android:exported="true">
<meta-data android:name="android.app.lib_name" android:value="DETestLauncher"/>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Expand Down
1 change: 1 addition & 0 deletions Common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ set(INTERFACE
interface/DynamicLinearAllocator.hpp
interface/MemoryFileStream.hpp
interface/ObjectBase.hpp
interface/ObjectsRegistry.hpp
interface/ParsingTools.hpp
interface/RefCntAutoPtr.hpp
interface/RefCountedObjectImpl.hpp
Expand Down
316 changes: 316 additions & 0 deletions Common/interface/ObjectsRegistry.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,316 @@
/* Copyright 2023 Diligent Graphics LLC
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS.
*
* In no event and under no legal theory, whether in tort (including negligence),
* contract, or otherwise, unless required by applicable law (such as deliberate
* and grossly negligent acts) or agreed to in writing, shall any Contributor be
* liable for any damages, including any direct, indirect, special, incidental,
* or consequential damages of any character arising as a result of this License or
* out of the use or inability to use the software (including but not limited to damages
* for loss of goodwill, work stoppage, computer failure or malfunction, or any and
* all other commercial damages or losses), even if such Contributor has been advised
* of the possibility of such damages.
*/

#pragma once

#include <unordered_map>
#include <mutex>
#include <memory>
#include <algorithm>
#include <atomic>

#include "../../../DiligentCore/Platforms/Basic/interface/DebugUtilities.hpp"
#include "RefCntAutoPtr.hpp"

namespace Diligent
{

template <typename StongPtrType>
struct _StrongPtrHelper;

// _StrongPtrHelper specialization for RefCntAutoPtr<T>
template <typename T>
struct _StrongPtrHelper<typename Diligent::RefCntAutoPtr<T>>
{
using WeakPtrType = RefCntWeakPtr<T>;
};

// _StrongPtrHelper specialization for std::shared_ptr<T>
template <typename T>
struct _StrongPtrHelper<std::shared_ptr<T>>
{
using WeakPtrType = std::weak_ptr<T>;
};

template <typename T>
auto _LockWeakPtr(RefCntWeakPtr<T>& pWeakPtr)
{
return pWeakPtr.Lock();
}

template <typename T>
auto _LockWeakPtr(std::weak_ptr<T>& pWeakPtr)
{
return pWeakPtr.lock();
}


template <typename T>
auto _IsWeakPtrExpired(RefCntWeakPtr<T>& pWeakPtr)
{
return !pWeakPtr.IsValid();
}

template <typename T>
auto _IsWeakPtrExpired(std::weak_ptr<T>& pWeakPtr)
{
return pWeakPtr.expired();
}

/// A thread-safe and exception-safe object registry that works with std::shared_ptr or RefCntAutoPtr.
/// The registry keeps weak pointers to the objects and returns strong pointers if the requested object exits.
/// An application should keep strong pointers to the objects to keep them alive.
///
/// Usage example for RefCntAutoPtr:
///
/// ObjectsRegistry<std::string, RefCntAutoPtr<IObject>> Registry;
///
/// auto pObj = Registry.Get("Key",
/// []()
/// {
/// RefCntAutoPtr<IObject> pObj;
/// // Create object
/// return pObj;
/// });
///
/// Usage example for shared_ptr:
///
/// struct RegistryData
/// {
/// // ...
/// };
/// ObjectsRegistry<std::string, std::shared_ptr<RegistryData>> Registry;
///
/// auto pObj = Registry.Get("Key",
/// []()
/// {
/// return std::make_shared<RegistryData>();
/// });
///
///
/// \note If the object is not found in the registry, it is atomically created by the provided initializer function.
/// If the object is found, the initializer function is not called.
///
/// It is guaranteed, that the Object will only be initialized once, even if multiple threads call Get() simultaneously.
///
template <typename KeyType,
typename StrongPtrType,
typename KeyHasher = std::hash<KeyType>,
typename KeyEqual = std::equal_to<KeyType>>
class ObjectsRegistry
{
public:
using WeakPtrType = typename _StrongPtrHelper<StrongPtrType>::WeakPtrType;

explicit ObjectsRegistry(Uint32 NumRequestsToPurge = 1024) noexcept :
m_NumRequestsToPurge{NumRequestsToPurge}
{}

/// Finds the object in the registry and returns strong pointer to it (std::shared_ptr or RefCntAutoPtr).
/// If the object is not found, it is atomically created using the provided initializer.
///
/// \param [in] Key - The object key.
/// \param [in] CreateObject - Initializer function that is called if the object is not found in the registry.
///
/// \return Strong pointer to the object with the specified key, either retrieved from the registry or initialized
/// with the CreateObject function.
///
/// \remarks CreateObject function may throw in case of an error.
///
/// It is guaranteed, that the Object will only be initialized once, even if multiple threads call Get() simultaneously.
/// However, if another thread runs an overloaded Get() without the initializer function with the same key, it may
/// remove the entry from the registry, and the object will be initialized multiple times.
/// This is OK as only one object will be added to the registry.
template <typename CreateObjectType>
StrongPtrType Get(const KeyType& Key,
CreateObjectType&& CreateObject // May throw
) noexcept(false)
{
// Get the Object wrapper. Since this is a shared pointer, it may not be destroyed
// while we keep one, even if it is popped from the registry by another thread.
std::shared_ptr<ObjectWrapper> pObjectWrpr;
{
std::lock_guard<std::mutex> Guard{m_CacheMtx};

auto it = m_Cache.find(Key);
if (it == m_Cache.end())
{
it = m_Cache.emplace(Key, std::make_shared<ObjectWrapper>()).first;
}
pObjectWrpr = it->second;
}

StrongPtrType pObject;
try
{
pObject = pObjectWrpr->Get(std::forward<CreateObjectType>(CreateObject));
}
catch (...)
{
std::lock_guard<std::mutex> Guard{m_CacheMtx};

auto it = m_Cache.find(Key);
if (it != m_Cache.end())
{
pObject = it->second->Lock();
if (pObject)
{
// The object was created by another thread while we were waiting for the lock
return pObject;
}
else
{
m_Cache.erase(it);
}
}

throw;
}

{
std::lock_guard<std::mutex> Guard{m_CacheMtx};

auto it = m_Cache.find(Key);
if (pObject)
{
if (it == m_Cache.end())
{
// The wrapper was removed from the cache by another thread while we were waiting
// for the lock - add it back.
m_Cache.emplace(Key, pObjectWrpr);
}
}
else
{
if (it != m_Cache.end())
{
pObject = it->second->Lock();
// Note that the object may have been created by another thread while we were waiting for the lock
if (!pObject)
m_Cache.erase(it);
}
}

if (m_NumRequestsSinceLastPurge.fetch_add(1) + 1 >= m_NumRequestsToPurge)
PurgeUnguarded();
}

return pObject;
}

/// Finds the object in the registry and returns a strong pointer to it (std::shared_ptr or RefCntAutoPtr).
/// If the object is not found, returns empty pointer.
///
/// \param [in] Key - The object key.
///
/// \return Strong pointer to the object with the specified key, if the object is found in the registry,
/// or empty pointer otherwise.
StrongPtrType Get(const KeyType& Key)
{
std::lock_guard<std::mutex> Guard{m_CacheMtx};

if (m_NumRequestsSinceLastPurge.fetch_add(1) + 1 >= m_NumRequestsToPurge)
PurgeUnguarded();

auto it = m_Cache.find(Key);
if (it != m_Cache.end())
{
auto pObject = it->second->Lock();
if (!pObject)
{
// Note that we may remove the entry from the cache while another thread is creating the object.
// This is OK as it will be added back to the cache.
m_Cache.erase(it);
}

return pObject;
}

return {};
}

/// Removes all expired pointers from the cache
void Purge()
{
std::lock_guard<std::mutex> Guard{m_CacheMtx};
PurgeUnguarded();
}

private:
class ObjectWrapper
{
public:
template <typename CreateObjectType>
const StrongPtrType Get(CreateObjectType&& CreateObject) noexcept(false)
{
StrongPtrType pObject;

std::lock_guard<std::mutex> Guard{m_CreateObjectMtx};
pObject = _LockWeakPtr(m_wpObject);
if (!pObject)
{
pObject = CreateObject(); // May throw
m_wpObject = pObject;
}

return pObject;
}

StrongPtrType Lock()
{
return _LockWeakPtr(m_wpObject);
}

bool IsExpired()
{
return _IsWeakPtrExpired(m_wpObject);
}

private:
std::mutex m_CreateObjectMtx;
WeakPtrType m_wpObject;
};

void PurgeUnguarded()
{
for (auto it = m_Cache.begin(); it != m_Cache.end();)
{
if (it->second->IsExpired())
{
it = m_Cache.erase(it);
}
else
{
++it;
}
}

m_NumRequestsSinceLastPurge.store(0);
}

private:
using CacheType = std::unordered_map<KeyType, std::shared_ptr<ObjectWrapper>, KeyHasher, KeyEqual>;

const Uint32 m_NumRequestsToPurge;

std::atomic<Uint32> m_NumRequestsSinceLastPurge{0};

std::mutex m_CacheMtx;
CacheType m_Cache;
};

} // namespace Diligent
1 change: 0 additions & 1 deletion Graphics/GraphicsEngine/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ set(INCLUDE
include/ShaderResourceCacheCommon.hpp
include/ShaderResourceVariableBase.hpp
include/ShaderBindingTableBase.hpp
include/StateObjectsRegistry.hpp
include/SwapChainBase.hpp
include/TextureBase.hpp
include/TextureViewBase.hpp
Expand Down
Loading

0 comments on commit d79e21f

Please sign in to comment.