DirectXTK/Src/SharedResourcePool.h

110 строки
3.7 KiB
C
Исходник Обычный вид История

2016-08-22 21:17:20 +03:00
//--------------------------------------------------------------------------------------
// File: SharedResourcePool.h
//
2021-02-27 10:00:12 +03:00
// Copyright (c) Microsoft Corporation.
2018-02-23 22:49:48 +03:00
// Licensed under the MIT License.
2016-08-22 21:17:20 +03:00
//
// http://go.microsoft.com/fwlink/?LinkId=248929
2018-02-23 22:49:48 +03:00
// http://go.microsoft.com/fwlink/?LinkID=615561
2016-08-22 21:17:20 +03:00
//--------------------------------------------------------------------------------------
#pragma once
#include <map>
#include <memory>
#include "PlatformHelpers.h"
namespace DirectX
{
// Pool manager ensures that only a single TData instance is created for each unique TKey.
// This is used to avoid duplicate resource creation, so that for instance a caller can
// create any number of SpriteBatch instances, but these can internally share shaders and
// vertex buffer if more than one SpriteBatch uses the same underlying D3D device.
template<typename TKey, typename TData, typename... TConstructorArgs>
class SharedResourcePool
{
public:
2018-05-05 04:16:09 +03:00
SharedResourcePool() noexcept(false)
2018-03-16 21:33:06 +03:00
: mResourceMap(std::make_shared<ResourceMap>())
2016-08-22 21:17:20 +03:00
{ }
SharedResourcePool(SharedResourcePool const&) = delete;
SharedResourcePool& operator= (SharedResourcePool const&) = delete;
// Allocates or looks up the shared TData instance for the specified key.
std::shared_ptr<TData> DemandCreate(TKey key, TConstructorArgs... args)
{
std::lock_guard<std::mutex> lock(mResourceMap->mutex);
// Return an existing instance?
auto pos = mResourceMap->find(key);
if (pos != mResourceMap->end())
{
auto existingValue = pos->second.lock();
if (existingValue)
return existingValue;
else
mResourceMap->erase(pos);
}
2018-03-16 21:33:06 +03:00
2016-08-22 21:17:20 +03:00
// Allocate a new instance.
auto newValue = std::make_shared<WrappedData>(key, mResourceMap, args...);
2018-05-05 04:16:09 +03:00
auto entry = std::make_pair(key, newValue);
mResourceMap->insert(entry);
2016-08-22 21:17:20 +03:00
return std::move(newValue);
2016-08-22 21:17:20 +03:00
}
private:
// Keep track of all allocated TData instances.
struct ResourceMap : public std::map<TKey, std::weak_ptr<TData>>
{
std::mutex mutex;
};
2018-03-16 21:33:06 +03:00
2016-08-22 21:17:20 +03:00
std::shared_ptr<ResourceMap> mResourceMap;
// Wrap TData with our own subclass, so we can hook the destructor
// to remove instances from our pool before they are freed.
struct WrappedData : public TData
{
WrappedData(TKey key, std::shared_ptr<ResourceMap> const& resourceMap, TConstructorArgs... args)
2018-03-16 21:33:06 +03:00
: TData(key, args...),
2017-07-11 06:54:54 +03:00
mKey(key),
mResourceMap(resourceMap)
2016-08-22 21:17:20 +03:00
{ }
WrappedData(WrappedData&&) = default;
WrappedData& operator= (WrappedData&&) = default;
WrappedData(WrappedData const&) = delete;
WrappedData& operator= (WrappedData const&) = delete;
2016-08-22 21:17:20 +03:00
~WrappedData()
{
2022-02-21 13:02:17 +03:00
const std::lock_guard<std::mutex> lock(mResourceMap->mutex);
2016-08-22 21:17:20 +03:00
2022-02-21 13:02:17 +03:00
auto const pos = mResourceMap->find(mKey);
2016-08-22 21:17:20 +03:00
// Check for weak reference expiry before erasing, in case DemandCreate runs on
// a different thread at the same time as a previous instance is being destroyed.
// We mustn't erase replacement objects that have just been added!
if (pos != mResourceMap->end() && pos->second.expired())
{
mResourceMap->erase(pos);
}
}
TKey mKey;
std::shared_ptr<ResourceMap> mResourceMap;
};
};
}