DirectXTK12/Src/SharedResourcePool.h

106 строки
3.5 KiB
C++

//--------------------------------------------------------------------------------------
// File: SharedResourcePool.h
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// http://go.microsoft.com/fwlink/?LinkId=248929
//--------------------------------------------------------------------------------------
#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:
SharedResourcePool()
: mResourceMap(std::make_shared<ResourceMap>())
{ }
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);
}
// Allocate a new instance.
auto newValue = std::make_shared<WrappedData>(key, mResourceMap, args...);
mResourceMap->insert(std::make_pair(key, newValue));
return newValue;
}
private:
// Keep track of all allocated TData instances.
struct ResourceMap : public std::map<TKey, std::weak_ptr<TData>>
{
std::mutex mutex;
};
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)
: mKey(key),
mResourceMap(resourceMap),
TData(key, args...)
{ }
~WrappedData()
{
std::lock_guard<std::mutex> lock(mResourceMap->mutex);
auto pos = mResourceMap->find(mKey);
// 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;
};
};
}