1
0
mirror of https://github.com/doitsujin/dxvk.git synced 2025-01-19 05:52:11 +01:00

[dxvk] Rework DxvkResource lifetime tracking

Reduces the number of atomic operations required for lifetime tracking by
using a single 64-bit integer for usage tracking and reference counting.
This commit is contained in:
Philip Rebohle 2022-06-27 15:10:23 +02:00
parent 8d1d3d66e0
commit 0e38b11569
No known key found for this signature in database
GPG Key ID: C8CC613427A31C99
4 changed files with 148 additions and 55 deletions

View File

@ -146,9 +146,9 @@ namespace dxvk {
* the device can guarantee that the submission has
* completed.
*/
template<DxvkAccess Access>
void trackResource(Rc<DxvkResource> rc) {
m_resources.trackResource<Access>(std::move(rc));
template<DxvkAccess Access, typename T>
void trackResource(const Rc<T>& rc) {
m_resources.trackResource<Access>(rc.ptr());
}
/**

View File

@ -7,19 +7,11 @@ namespace dxvk {
void DxvkLifetimeTracker::notify() {
for (const auto& resource : m_resources)
resource.first->release(resource.second);
m_notified = true;
m_resources.clear();
}
void DxvkLifetimeTracker::reset() {
// If this gets called without ever being submitted then
// we should at least report the resources as unused
if (!m_notified)
this->notify();
m_resources.clear();
}

View File

@ -7,7 +7,81 @@
namespace dxvk {
/**
* \brief DXVK lifetime tracker
* \brief Resource pointer
*
* Keeps a resource alive and stores access information.
*/
class DxvkLifetime {
public:
DxvkLifetime()
: m_resource(nullptr), m_access(DxvkAccess::None) { }
DxvkLifetime(
DxvkResource* resource,
DxvkAccess access)
: m_resource(resource), m_access(access) {
acquire();
}
DxvkLifetime(DxvkLifetime&& other)
: m_resource(other.m_resource), m_access(other.m_access) {
other.m_resource = nullptr;
other.m_access = DxvkAccess::None;
}
DxvkLifetime(const DxvkLifetime& other)
: m_resource(other.m_resource), m_access(other.m_access) {
acquire();
}
DxvkLifetime& operator = (DxvkLifetime&& other) {
release();
m_resource = other.m_resource;
m_access = other.m_access;
other.m_resource = nullptr;
other.m_access = DxvkAccess::None;
return *this;
}
DxvkLifetime& operator = (const DxvkLifetime& other) {
other.acquire();
release();
m_resource = other.m_resource;
m_access = other.m_access;
return *this;
}
~DxvkLifetime() {
release();
}
private:
DxvkResource* m_resource;
DxvkAccess m_access;
void acquire() const {
if (m_resource)
m_resource->acquire(m_access);
}
void release() const {
if (m_resource) {
if (!m_resource->release(m_access))
delete m_resource;
}
}
};
/**
* \brief Lifetime tracker
*
* Maintains references to a set of resources. This is
* used to guarantee that resources are not destroyed
@ -26,9 +100,8 @@ namespace dxvk {
* \param [in] rc The resource to track
*/
template<DxvkAccess Access>
void trackResource(Rc<DxvkResource>&& rc) {
rc->acquire(Access);
m_resources.emplace_back(std::move(rc), Access);
void trackResource(DxvkResource* rc) {
m_resources.emplace_back(rc, Access);
}
/**
@ -48,8 +121,7 @@ namespace dxvk {
private:
std::vector<std::pair<Rc<DxvkResource>, DxvkAccess>> m_resources;
bool m_notified = false;
std::vector<DxvkLifetime> m_resources;
};

View File

@ -4,7 +4,7 @@
namespace dxvk {
enum class DxvkAccess {
enum class DxvkAccess : uint32_t {
Read = 0,
Write = 1,
None = 2,
@ -19,12 +19,59 @@ namespace dxvk {
* by the GPU. As soon as a command that uses the resource
* is recorded, it will be marked as 'in use'.
*/
class DxvkResource : public RcObject {
class DxvkResource {
static constexpr uint64_t RdAccessShift = 24;
static constexpr uint64_t WrAccessShift = 44;
static constexpr uint64_t RefcountMask = (1ull << RdAccessShift) - 1;
static constexpr uint64_t RdAccessMask = ((1ull << (WrAccessShift - RdAccessShift)) - 1) << RdAccessShift;
static constexpr uint64_t WrAccessMask = ((1ull << (64 - WrAccessShift)) - 1) << WrAccessShift;
static constexpr uint64_t RefcountInc = 1ull;
static constexpr uint64_t RdAccessInc = 1ull << RdAccessShift;
static constexpr uint64_t WrAccessInc = 1ull << WrAccessShift;
public:
virtual ~DxvkResource();
/**
* \brief Increments reference count
* \returns New reference count
*/
uint32_t incRef() {
return acquire(DxvkAccess::None);
}
/**
* \brief Decrements reference count
* \returns New reference count
*/
uint32_t decRef() {
return release(DxvkAccess::None);
}
/**
* \brief Acquires resource with given access
*
* Atomically increments both the reference count
* as well as the use count for the given access.
* \returns New reference count
*/
uint32_t acquire(DxvkAccess access) {
return uint32_t((m_useCount += getIncrement(access)) & RefcountMask);
}
/**
* \brief Releases resource with given access
*
* Atomically decrements both the reference count
* as well as the use count for the given access.
* \returns New reference count
*/
uint32_t release(DxvkAccess access) {
return uint32_t((m_useCount -= getIncrement(access)) & RefcountMask);
}
/**
* \brief Checks whether resource is in use
*
@ -36,40 +83,12 @@ namespace dxvk {
* \returns \c true if the resource is in use
*/
bool isInUse(DxvkAccess access = DxvkAccess::Read) const {
bool result = m_useCountW.load() != 0;
uint64_t mask = WrAccessMask;
if (access == DxvkAccess::Read)
result |= m_useCountR.load() != 0;
return result;
mask |= RdAccessMask;
return bool(m_useCount.load() & mask);
}
/**
* \brief Acquires resource
*
* Increments use count for the given access type.
* \param Access Resource access type
*/
void acquire(DxvkAccess access) {
if (access != DxvkAccess::None) {
(access == DxvkAccess::Read
? m_useCountR
: m_useCountW) += 1;
}
}
/**
* \brief Releases resource
*
* Decrements use count for the given access type.
* \param Access Resource access type
*/
void release(DxvkAccess access) {
if (access != DxvkAccess::None) {
(access == DxvkAccess::Read
? m_useCountR
: m_useCountW) -= 1;
}
}
/**
* \brief Waits for resource to become unused
*
@ -85,9 +104,19 @@ namespace dxvk {
private:
std::atomic<uint32_t> m_useCountR = { 0u };
std::atomic<uint32_t> m_useCountW = { 0u };
std::atomic<uint64_t> m_useCount = { 0ull };
static constexpr uint64_t getIncrement(DxvkAccess access) {
uint64_t increment = RefcountInc;
if (access != DxvkAccess::None) {
increment |= (access == DxvkAccess::Read)
? RdAccessInc : WrAccessInc;
}
return increment;
}
};
}