Nvrhi的RefCounter和侵入式计数 | Blurred code

Nvrhi的RefCounter和侵入式计数

2023/11/04

LastMod:2023/11/04

Categories: nvrhi

笔记栏文章声明

Warning

笔记栏所记录文章往往未经校对,或包含错误认识或偏颇观点,亦或采用只有自身能够理解的记录。

RefCountPtr

介绍可以参考:

参考:DirectX11--ComPtr智能指针 - X_Jun - 博客园

DX的类都继承自IUnknown,除掉可以查询接口并转型以外,比较大特点是自带引用计数。 vulkan的资源需要手动管理引用计数,不用的时候归还回去。

统一这两种方式的方式是用侵入式Ptr。 仿造实现一个ComPtr。

另外一个好处是侵入式指针是zero-space,由于它不包含任何成员。

https://github.com/NVIDIAGameWorks/nvrhi/blob/556eb6e22e5c5a61f09b84ad26945d76b0172dfa/include/nvrhi/common/resource.h#L127

微软也有一个不包含Windows SDK专属功能的实现在DirectX12 for WSL的头文件里,https://github.com/microsoft/DirectX-Headers/blob/48f23952bc08a6dce0727339c07cedbc4797356c/include/wsl/wrladapter.h#L101 可以跨平台使用。

IResource,RefCounter,RefCountPtr

情况来到nvrhi管理的资源,由于没有IUnknown可以用,所以需要自己造这么一套机制。

以D3D11Device为例,他的继承关系为

graph TD A[IResource] B[IDevice] C[RefCounter_IDevice_] D[D3D11Device] E[RefCountPtr_IDevice_] A-->B B-->C C-->D E-.->D

其中IResource类似于IUnknown,主要的作用有三个

class IResource {
protected:
  IResource() = default;
  virtual ~IResource() = default;

public:
  virtual uint32_t AddRef() = 0;
  virtual uint32_t Release() = 0;

private:
  // neither movable nor copyable
  IResource(const IResource &) = delete;
  IResource(const IResource &&) = delete;
  IResource &operator==(const IResource &) = delete;
  IResource &operator==(const IResource &&) = delete;
};

其中比较trick的部分是RefCounter的实现,它嵌入在整个继承链中

他的声明为

template <class T> class RefCounter : public T {...}

而使用方法是

class Device : public RefCounter<IDevice> {...}

一个不可行的思路

我一开始不太理解为什么要把RefCounter做进继承链里,认为这里使用多重继承的方式可能更好,RefCounter不需要是一个模板类

但是后来发现这个思路会构成菱形继承,不太好

class Device : public RefCounter,IDevice  {...}

这样继承的类图为

graph TD A[IResource] B[IDevice] C[RefCounter] D[D3D11Device] E[RefCountPtr_IDevice_] A-->B A-->C B-->D C-->D E-.->D