游戏引擎开发实践(基础架构)

渲染架构

RHI

RHI相关的资源全部被封装位RHIResource

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
enum RHIResourceType : uint32_t
{
    // 基础资源
    RHI_BUFFER = 0,
    RHI_TEXTURE,
    RHI_TEXTURE_VIEW,
    RHI_SAMPLER,
    RHI_SHADER,

    // 光追
    RHI_SHADER_BINDING_TABLE,
    RHI_TOP_LEVEL_ACCELERATION_STRUCTURE,
    RHI_BOTTOM_LEVEL_ACCELERATION_STRUCTURE,

    // 资源描述符
    RHI_ROOT_SIGNATURE,
    RHI_DESCRIPTOR_SET,

    // 渲染Pass
    RHI_RENDER_PASS,
    RHI_GRAPHICS_PIPELINE,
    RHI_COMPUTE_PIPELINE,
    RHI_RAY_TRACING_PIPELINE,

    // 底层相关
    RHI_QUEUE,
    RHI_SURFACE,
    RHI_SWAPCHAIN,
    RHI_COMMAND_POOL,

    RHI_COMMAND_CONTEXT,
    RHI_COMMAND_CONTEXT_IMMEDIATE,

    // 同步
    RHI_FENCE,
    RHI_SEMAPHORE,

    RHI_RESOURCE_TYPE_MAX_CNT,	//
};

RHIResource基类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
class RHIResource
{
public:
    RHIResource() = delete;
    RHIResource(RHIResourceType resourceType) : resourceType(resourceType) {};
    virtual ~RHIResource() {};

    inline RHIResourceType GetType() { return resourceType; }

    virtual void* RawHandle() { return nullptr; };		// 底层资源的裸指针,仅debug时使用
    void printRawHandle();
private:
    RHIResourceType resourceType;
    uint32_t lastUseTick = 0;

    virtual void Destroy() {};

    friend class DynamicRHI;
};

每个RHI资源都拥有自己的类来管理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class RHITexture : public RHIResource
{
public:
	RHITexture(const RHITextureInfo& info): RHIResource(RHI_TEXTURE), info(info){}

	Extent3D MipExtent(uint32_t mipLevel);

	inline const TextureSubresourceRange& GetDefaultSubresourceRange() const { return defaultRange; }
	inline const TextureSubresourceLayers& GetDefaultSubresourceLayers() const { return defaultLayers; }

	inline const RHITextureInfo& GetInfo() const { return info; }

protected:
	RHITextureInfo info;

	TextureSubresourceRange defaultRange = {};
	TextureSubresourceLayers defaultLayers = {};
};

class RHIBuffer : public RHIResource
{
public:
    RHIBuffer(const RHIBufferInfo& info)
        : RHIResource(RHI_BUFFER)
        , info(info)
    {
    }

    virtual void* Map() = 0;
    virtual void UnMap() = 0;

    inline const RHIBufferInfo& GetInfo() const { return info; }

protected:
    RHIBufferInfo info;
};

DynamicRHI

  1. 负责不需要Context的RHI命令,比如创建纹理、Buffer、Fence等

  2. 负责API Context的创建

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class DynamicRHI {
private:
	static DynamicRHIRef s_DynamicRHI;
public:
    static DynamicRHIRef Init(RHIConfig config);
	static DynamicRHIRef Get(){return s_DynamicRHI;}

	virtual void Tick();    // 更新资源计数,清理无引用且长时间未使用资源
	virtual void InitImGui(GLFWwindow* window) = 0;
	virtual RHIDescriptorSetRef GetImGuiTextId(RHITextureViewRef textureView) = 0;
	virtual void Destroy();
	virtual RHIComputePipelineRef CreateComputePipeline(const RHIComputePipelineInfo& info) = 0;
	virtual RHITopLevelAccelerationStructureRef CreateTopLevelAccelerationStructure(const RHITopLevelAccelerationStructureInfo& info) = 0;
	virtual RHIShaderBindingTableRef CreateShaderBindingTable(const RHIShaderBindingTableInfo& info) = 0;

	virtual RHIRayTracingPipelineRef CreateRayTracingPipeline(const RHIRayTracingPipelineInfo& info) = 0;

	virtual RHIBottomLevelAccelerationStructureRef CreateBottomLevelAccelerationStructure(const RHIBottomLevelAccelerationStructureInfo& info) = 0;
	virtual RHIQueueRef GetQueue(const RHIQueueInfo& info) = 0;
	virtual RHISurfaceRef CreateSurface(GLFWwindow* window) = 0;
	virtual RHISwapchainRef CreateSwapChain(const RHISwapchainInfo& info) = 0;
	virtual RHICommandPoolRef CreateCommandPool(const RHICommandPoolInfo& info) = 0;
	virtual RHICommandContextRef CreateCommandContext(RHICommandPoolRef pool) = 0;
	virtual RHITextureRef CreateTexture(const RHITextureInfo& info) = 0;
	virtual RHITextureViewRef CreateTextureView(const RHITextureViewInfo& info) = 0;
	virtual RHISamplerRef CreateSampler(const RHISamplerInfo& info) = 0;
	virtual RHIShaderRef CreateShader(const RHIShaderInfo& info) = 0;
	virtual RHIBufferRef CreateBuffer(const RHIBufferInfo& info) = 0;
	virtual RHIGraphicsPipelineRef CreateGraphicsPipeline(const RHIGraphicsPipelineInfo& info) = 0;
	virtual RHIRenderPassRef CreateRenderPass(const RHIRenderPassInfo& info) = 0;
	virtual RHIRootSignatureRef CreateRootSignature(const RHIRootSignatureInfo& info) = 0;

	// 同步
	virtual RHIFenceRef CreateFence(bool signaled) = 0;
	virtual RHISemaphoreRef CreateSemaphore() = 0;
	virtual RHICommandListImmediateRef GetImmediateCommandList() = 0;
	void RegisterResource(RHIResourceRef resource) { resourceMap[resource->GetType()].push_back(resource); }
	RHIConfig& GetConfig() { return m_Config; }
	bool isEnableRayTracing() { return m_Config.enableRayTracing; }
protected:
	DynamicRHI(const RHIConfig& config) : m_Config(config) {};

	std::array<std::vector<RHIResourceRef>, RHI_RESOURCE_TYPE_MAX_CNT> resourceMap;
    RHIConfig m_Config;
};

另外他还维护一个resourceMap(每种资源一个Vector),每帧Tick

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
void DynamicRHI::Tick()
{
    for (auto& resources : resourceMap)
    {
        bool needClean = false;
        for (RHIResourceRef& resource : resources)
        {
            if (resource)
            {
                if (resource.use_count() == 1)   resource->lastUseTick++;
                else                            resource->lastUseTick = 0;

                if (resource->lastUseTick > 6)  //析构资源6帧后销毁
                {
                    needClean = true;
                     //if(resource->GetType() != RHI_RENDER_PASS) 
                         // std::cout << "RHI resource [" << resource.get() << "] of type [" << resource->GetType() << "] destroied" << std::endl;
                    resource->Destroy();
                    resource = nullptr;
                }
            }
        }

        // 删除空指针
        if (needClean) {
            resources.erase(std::remove_if(resources.begin(), resources.end(), [](RHIResourceRef x) {
                return x == nullptr;
                }), resources.end());
        }
    }
}

RHICommandContext,用于定义需要Commandbuffer的命令,内部持有RHICommandBuffer对象

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
	class RHICommandContext : public RHIResource {
	public:
		RHICommandContext(RHICommandPoolRef pool): RHIResource(RHI_COMMAND_CONTEXT), pool(pool){}
		virtual void BeginCommand() = 0;
		virtual void EndCommand() = 0;
		virtual void Execute(RHIFenceRef waitFence, RHISemaphoreRef waitSemaphore, RHISemaphoreRef signalSemaphore) = 0;
		virtual void TextureBarrier(const RHITextureBarrier& barrier) = 0;
		virtual void BufferBarrier(const RHIBufferBarrier& barrier) = 0;
		virtual void BeginRenderPass(RHIRenderPassRef renderPass) = 0;   //也可以运行时FindOrCreate相应的renderpass和framebuffer等,很多东西可以做中心化的查找表统一管理状态
		virtual void CopyTexture(RHITextureRef src, TextureSubresourceLayers srcSubresource, RHITextureRef dst, TextureSubresourceLayers dstSubresource) = 0;
		virtual void GenerateMips(RHITextureRef src) = 0;
		virtual void SetViewport(Offset2D min, Offset2D max) = 0;

		virtual void SetScissor(Offset2D min, Offset2D max) = 0;

		virtual void SetDepthBias(float constantBias, float slopeBias, float clampBias) = 0;
		virtual void SetLineWidth(float width) = 0;

		virtual void SetGraphicsPipeline(RHIGraphicsPipelineRef graphicsPipeline) = 0;

		virtual void SetComputePipeline(RHIComputePipelineRef computePipeline) = 0;
		virtual void PushLabel(const std::string& name, Color3 color = { 1.0f, 1.0f, 1.0f }) = 0;

		virtual void PopLabel() = 0;
		virtual void SetRayTracingPipeline(RHIRayTracingPipelineRef rayTracingPipeline) = 0;

		virtual void PushConstants(void* data, uint16_t size, ShaderFrequency frequency) = 0;

		virtual void BindDescriptorSet(RHIDescriptorSetRef descriptor, uint32_t set) = 0;

		virtual void BindVertexBuffer(RHIBufferRef vertexBuffer, uint32_t streamIndex, uint32_t offset) = 0;

		virtual void BindIndexBuffer(RHIBufferRef indexBuffer, uint32_t offset) = 0;

		virtual void Dispatch(uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ) = 0;

		virtual void DispatchIndirect(RHIBufferRef argumentBuffer, uint32_t argumentOffset) = 0;

		virtual void TraceRays(uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ) = 0;

		virtual void Draw(uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance) = 0;

		virtual void DrawIndexed(uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, uint32_t vertexOffset, uint32_t firstInstance) = 0;

		virtual void DrawIndirect(RHIBufferRef argumentBuffer, uint32_t offset, uint32_t drawCount) = 0;

		virtual void DrawIndexedIndirect(RHIBufferRef argumentBuffer, uint32_t offset, uint32_t drawCount) = 0;
		virtual void EndRenderPass() = 0;
		virtual std::vector<RHIGPUTimeInfo> GetGPUTime() = 0;
		virtual void ImGuiRenderDrawData() = 0;

	protected:
		RHICommandPoolRef pool;
	};

VulkanRHICommandContextImmediate负责临时命令执行和提交

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class RHICommandContextImmediate : public RHIResource
{
public:
    RHICommandContextImmediate(): RHIResource(RHI_COMMAND_CONTEXT_IMMEDIATE){}

    virtual void Flush() = 0;
    virtual void GenerateMips(RHITextureRef src) = 0;
    virtual void TextureBarrier(const RHITextureBarrier& barrier) = 0;
    virtual void CopyBufferToTexture(RHIBufferRef src, uint64_t srcOffset, RHITextureRef dst, TextureSubresourceLayers dstSubresource) = 0;
    virtual void ImGuiUploadFonts() = 0;
};

RHICommandList负责为上层(RDG)提供服务

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
class RHICommandList {
public:
	RHICommandList(const CommandListInfo& info) : info(info) {}
	void BeginCommand();
	void EndCommand();
	void TextureBarrier(const RHITextureBarrier& barrier);
	void BeginRenderPass(RHIRenderPassRef renderPass);
	void CopyTexture(RHITextureRef src, TextureSubresourceLayers srcSubresource, RHITextureRef dst, TextureSubresourceLayers dstSubresource);

	void GenerateMips(RHITextureRef src);
	void EndRenderPass();
	void BufferBarrier(const RHIBufferBarrier& barrier);
	void Execute(RHIFenceRef fence = nullptr, RHISemaphoreRef waitSemaphore = nullptr, RHISemaphoreRef signalSemaphore = nullptr);
	void SetViewport(Offset2D min, Offset2D max);

	void SetScissor(Offset2D min, Offset2D max);

	void SetDepthBias(float constantBias, float slopeBias, float clampBias);
	void PushLabel(const std::string& name, Color3 color = { 1.0f, 1.0f, 1.0f });
	void PopLabel();
	void SetLineWidth(float width);

	void SetGraphicsPipeline(RHIGraphicsPipelineRef graphicsPipeline);
	void SetRayTracingPipeline(RHIRayTracingPipelineRef rayTracingPipeline);
	void SetComputePipeline(RHIComputePipelineRef computePipeline);
	void PushConstants(void* data, uint16_t size, ShaderFrequency frequency);

	void BindDescriptorSet(RHIDescriptorSetRef descriptor, uint32_t set = 0);

	void BindVertexBuffer(RHIBufferRef vertexBuffer, uint32_t streamIndex = 0, uint32_t offset = 0);

	void BindIndexBuffer(RHIBufferRef indexBuffer, uint32_t offset = 0);

	void Dispatch(uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ);

	void DispatchIndirect(RHIBufferRef argumentBuffer, uint32_t argumentOffset = 0);

	void TraceRays(uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ);

	void Draw(uint32_t vertexCount, uint32_t instanceCount = 1, uint32_t firstVertex = 0, uint32_t firstInstance = 0);

	void DrawIndexed(uint32_t indexCount, uint32_t instanceCount = 1, uint32_t firstIndex = 0, uint32_t vertexOffset = 0, uint32_t firstInstance = 0);

	void DrawIndirect(RHIBufferRef argumentBuffer, uint32_t offset, uint32_t drawCount);

	void DrawIndexedIndirect(RHIBufferRef argumentBuffer, uint32_t offset, uint32_t drawCount);
	void ImGuiRenderDrawData();

public:
	std::vector<RHIGPUTimeInfo> GetGPUTime();
	uint32_t GetDrawCallCount() { return drawCallCount; }
	void ClearDrawCallCount() { drawCallCount = 0; }
protected:
	inline void AddCommand(RHICommand* command) { commands.push_back(command); }
private:
	std::vector<RHICommand*> commands;
	CommandListInfo info;
	uint32_t drawCallCount = 0;
};

他的各个函数其实和RHICommandContext是一致的,但是实现上是可以直接执行底层函数,也可以缓存

1
2
3
4
5
6
void RHICommandList::BeginCommand()
{
	COMMANDLIST_DEBUG_OUTPUT();
	if (info.byPass) info.context->BeginCommand();
	else ADD_COMMAND(BeginCommand);
}

命令的缓存是通过ADD_COMMAND

1
2
3
4
5
6
7
#define ADD_COMMAND(commandName, ...) do { \
    RHICommand* command = new RHICommand##commandName(__VA_ARGS__);  \
    AddCommand(command); \
} while (0)

// commands的指针存在一个Vector,最终执行依次执行即可
    std::vector<RHICommandImmediate*> commands;

所以说每个命令都是一个类

1
2
3
4
5
struct RHICommandBeginCommand : public RHICommand
{
    RHICommandBeginCommand() {}
    virtual void Execute(RHICommandContextRef context) override final;
};

执行CommandList

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
void RHICommandList::Execute(RHIFenceRef fence, RHISemaphoreRef waitSemaphore, RHISemaphoreRef signalSemaphore)
{
    if (!info.byPass)
    {
        // LOG_DEBUG("Recording command list in delay mode.");
        for (int32_t i = 0; i < commands.size(); i++)
        {
            commands[i]->Execute(info.context);
            delete commands[i];
        }
        commands.clear();
    }

    info.context->Execute(fence, waitSemaphore, signalSemaphore);
}

RDG

RDFNode: 分为资源Node和PassNode

资源Node分为BufferNode和TextureNode,PassNode有 图形、计算、光追、Copy、呈现五种

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
enum RDGPassNodeType
{
    RDG_PASS_NODE_TYPE_RENDER = 0,
    RDG_PASS_NODE_TYPE_COMPUTE,
    RDG_PASS_NODE_TYPE_RAY_TRACING,
    RDG_PASS_NODE_TYPE_PRESENT,
    RDG_PASS_NODE_TYPE_COPY,

    RDG_PASS_NODE_TYPE_MAX_ENUM,    //
};

enum RDGResourceNodeType
{
    RDG_RESOURCE_NODE_TYPE_TEXTURE = 0,
    RDG_RESOURCE_NODE_TYPE_BUFFER,

    RDG_RESOURCE_NODE_TYPE_MAX_ENUM,    //
};

// PassNode需要的Context
typedef struct RDGPassContext
{
    RHICommandListRef command;
    RDGBuilder* builder;
    std::array<RHIDescriptorSetRef, MAX_DESCRIPTOR_SETS> descriptors;
    uint32_t passIndex[3] = { 0, 0, 0 };
} RDGPassContext;

// Node基类
class RDGNode : public DependencyGraph::Node
{
public:
    RDGNode(std::string name)
        : name(name)
    {
    }

    const std::string& Name() { return name; }

private:
    std::string name;
};
typedef RDGNode* RDGNodeRef;


 // 资源节点/////////////////////////////////////////////////////////////////////////////////////
 class RDGResourceNode : public RDGNode
 {
 public:
     RDGResourceNode(std::string name, RDGResourceNodeType nodeType)
         : RDGNode(name)
         , nodeType(nodeType)
     {
     }

     inline bool IsImported() { return isImported; }

     RDGResourceNodeType NodeType() { return nodeType; }

 protected:
     RDGResourceNodeType nodeType;
     bool isImported = false;
 };
 typedef RDGResourceNode* RDGResourceNodeRef;

 class RDGTextureNode : public RDGResourceNode
 {
 public:
     RDGTextureNode(std::string name): RDGResourceNode(name, RDG_RESOURCE_NODE_TYPE_TEXTURE){}

     void ForEachPass(const std::function<void(RDGTextureEdgeRef, class RDGPassNode*)>& func);

     RDGTextureHandle GetHandle() { return RDGTextureHandle(ID()); }

     const RHITextureInfo& GetInfo() { return info; }
     RHITextureRef GetRHITexture() { return texture; }
 private:
     RHITextureInfo info;
     RHIResourceState initState; // 从池中/外部引用时的最初状态

     RHITextureRef texture;      // 执行时分配和绑定,会动态更新,在最后一个依赖pass完成后返回资源池

     friend class RDGTextureBuilder;
     friend class RDGBuilder;
 };
 typedef RDGTextureNode* RDGTextureNodeRef;

 class RDGBufferNode : public RDGResourceNode
 {
 public:
     RDGBufferNode(std::string name): RDGResourceNode(name, RDG_RESOURCE_NODE_TYPE_BUFFER){}

     void ForEachPass(const std::function<void(RDGBufferEdgeRef, class RDGPassNode*)>& func);

     RDGBufferHandle GetHandle() { return RDGBufferHandle(ID()); }

     const RHIBufferInfo& GetInfo() { return info; }

 private:
     RHIBufferInfo info;
     RHIResourceState initState; // 从池中/外部引用时的最初状态

     RHIBufferRef buffer;        // 执行时分配和绑定,会动态更新,在最后一个依赖pass完成后返回资源池

     friend class RDGBufferBuilder;
     friend class RDGBuilder;
 };
 typedef RDGBufferNode* RDGBufferNodeRef;


// pass节点/////////////////////////////////////////////////////////////////////////////////////

class RDGPassNode : public RDGNode
{
public:
    RDGPassNode(std::string name, RDGPassNodeType nodeType)
        : RDGNode(name)
        , nodeType(nodeType)
    {
    }

    inline bool Before(RDGPassNode* other) { return ID() < other->ID(); }    // 假定所有pass的添加顺序就是执行顺序
    inline bool After(RDGPassNode* other) { return ID() > other->ID(); }

    void ForEachTexture(const std::function<void(RDGTextureEdgeRef, RDGTextureNodeRef)>& func);
    void ForEachBuffer(const std::function<void(RDGBufferEdgeRef, RDGBufferNodeRef)>& func);
    Color3 GetLabelColor() { return LabelColor; }
    void SetLabelColor(Color3 color) { LabelColor = color; }
    RDGPassNodeType NodeType() { return nodeType; }

protected:
    RDGPassNodeType nodeType;
    bool isCulled = false;
    Color3 LabelColor = { 1.0f, 1.0f, 1.0f };

    RHIRootSignatureRef rootSignature;
    std::array<RHIDescriptorSetRef, MAX_DESCRIPTOR_SETS> descriptorSets;

    std::vector<RHITextureViewRef> pooledViews;             // 动态分配的池化资源,执行完毕后返回资源池
    std::vector<std::pair<RHIDescriptorSetRef, uint32_t>> pooledDescriptorSets;

    friend class RDGBuilder;
};
typedef RDGPassNode* RDGPassNodeRef;


class RDGRenderPassNode : public RDGPassNode
{
public:
    RDGRenderPassNode(std::string name)
        : RDGPassNode(name, RDG_PASS_NODE_TYPE_RENDER)
    {
    }

    RDGRenderPassHandle GetHandle() { return RDGRenderPassHandle(ID()); }
private:
    uint32_t passIndex[3] = { 0, 0, 0 };
    RDGPassExecuteFunc execute;
    friend class RDGRenderPassBuilder;
    friend class RDGBuilder;
};
typedef RDGRenderPassNode* RDGRenderPassNodeRef;

class RDGComputePassNode : public RDGPassNode
{
public:
    RDGComputePassNode(std::string name)
        : RDGPassNode(name, RDG_PASS_NODE_TYPE_COMPUTE)
    {
    }

    RDGComputePassHandle GetHandle() { return RDGComputePassHandle(ID()); }
private:
    uint32_t passIndex[3] = { 0, 0, 0 };
    RDGPassExecuteFunc execute;

    friend class RDGComputePassBuilder;
    friend class RDGBuilder;
};
typedef RDGComputePassNode* RDGComputePassNodeRef;

class RDGRayTracingPassNode : public RDGPassNode
{
public:
    RDGRayTracingPassNode(std::string name)
        : RDGPassNode(name, RDG_PASS_NODE_TYPE_RAY_TRACING)
    {
    }

    RDGRayTracingPassHandle GetHandle() { return RDGRayTracingPassHandle(ID()); }
private:
    uint32_t passIndex[3] = { 0, 0, 0 };
    RDGPassExecuteFunc execute;

    friend class RDGRayTracingPassBuilder;
    friend class RDGBuilder;
};
typedef RDGRayTracingPassNode* RDGRayTracingPassNodeRef;

class RDGPresentPassNode : public RDGPassNode
{
public:
    RDGPresentPassNode(std::string name)
        : RDGPassNode(name, RDG_PASS_NODE_TYPE_PRESENT)
    {
    }

    RDGPresentPassHandle GetHandle() { return RDGPresentPassHandle(ID()); }
private:
    friend class RDGPresentPassBuilder;
    friend class RDGBuilder;
};
typedef RDGPresentPassNode* RDGPresentPassNodeRef;

class RDGCopyPassNode : public RDGPassNode
{
public:
    RDGCopyPassNode(std::string name)
        : RDGPassNode(name, RDG_PASS_NODE_TYPE_COPY)
    {
    }

    RDGCopyPassHandle GetHandle() { return RDGCopyPassHandle(ID()); }
private:
    bool generateMip = false;

    friend class RDGCopyPassBuilder;
    friend class RDGBuilder;
};
typedef RDGCopyPassNode* RDGCopyPassNodeRef;

RDGEdge 定义Pass如何访问某个资源,分为TextureEdge和BufferEdge

边用于定义一个Pass如何访问一个资源,并且标识了资源在资源描述符集的位置(Set Binding),另外需要知道Pass访问这个资源需要这个资源处于哪种状态

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
 enum RDGEdgeType
 {
     RDG_EDGE_TYPE_TEXTURE = 0,
     RDG_EDGE_TYPE_BUFFER,

     RDG_EDGE_TYPE_MAX_ENUM,    //
 };

class RDGEdge : public DependencyGraph::Edge
{
public:
    RDGEdge(RDGEdgeType edgeType, RHIResourceState state = RESOURCE_STATE_UNDEFINED): state(state), edgeType(edgeType){}

    virtual bool IsOutput() { return false; }

    RDGEdgeType EdgeType() { return edgeType; }

    RHIResourceState state; // 在对应的pass处要求的状态(若作为pass输入,pass不应在内部改变状态)

protected:
    RDGEdgeType edgeType;
};
typedef RDGEdge* RDGEdgeRef;

class RDGTextureEdge : public RDGEdge
{
public:
    RDGTextureEdge()
        : RDGEdge(RDG_EDGE_TYPE_TEXTURE)
    {
    }

    TextureSubresourceRange subresource = {};
    TextureSubresourceLayers subresourceLayer = {};  // 单层mip,多层layer

    // 下面这些每种标记都对应一种用法
    bool asColor = false;   // 颜色缓冲标记
    bool asDepthStencil = false; // 深度缓冲标记
    bool asShaderRead = false;
    bool asShaderReadWrite = false;
    bool asOutputRead = false;
    bool asOutputReadWrite = false;
    bool asPresent = false;
    bool asTransferSrc = false;
    bool asTransferDst = false;

    virtual bool IsOutput() override { return asOutputRead || asOutputReadWrite; }

    uint32_t set = 0; 
    uint32_t binding = 0;
    uint32_t index = 0;
    ResourceType type = RESOURCE_TYPE_TEXTURE;
    TextureViewType viewType = VIEW_TYPE_2D;

    AttachmentLoadOp 	loadOp = ATTACHMENT_LOAD_OP_DONT_CARE; 
    AttachmentStoreOp	storeOp = ATTACHMENT_STORE_OP_DONT_CARE;

    Color4				clearColor = { 0.0f, 0.0f, 0.0f, 0.0f };
    float				clearDepth = 1.0f;
    uint32_t			clearStencil = 0;
};

class RDGBufferEdge : public RDGEdge
{
public:
    RDGBufferEdge()
        : RDGEdge(RDG_EDGE_TYPE_BUFFER)
    {
    }

    uint32_t offset = 0;
    uint32_t size = 0;
    bool asShaderRead = false;   // Shader会读取
    bool asShaderReadWrite = false;
    bool asOutputRead = false;
    bool asOutputReadWrite = false;
    bool asOutputIndirectDraw = false;

    virtual bool IsOutput() override { return asOutputRead || asOutputReadWrite || asOutputIndirectDraw; }

    uint32_t set = 0;       // 描述符使用
    uint32_t binding = 0;
    uint32_t index = 0;
    ResourceType type = RESOURCE_TYPE_UNIFORM_BUFFER;
};
typedef RDGBufferEdge* RDGBufferEdgeRef;

RDGResoruceHandle 给每个Node一个ID

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
using NodeID = DependencyGraph::NodeID;

class RDGResoruceHandle
{
public:
    RDGResoruceHandle(NodeID id) : id(id) {}

    bool operator< (const RDGResoruceHandle& other) const noexcept {
        return id < other.id;
    }

    bool operator== (const RDGResoruceHandle& other) const noexcept {
        return (id == other.id);
    }

    bool operator!= (const RDGResoruceHandle& other) const noexcept {
        return !operator==(other);
    }

    inline NodeID ID() { return id; }

protected:
    NodeID id = UINT32_MAX;
};

class RDGPassHandle : public RDGResoruceHandle
{
public:
    RDGPassHandle(NodeID id) : RDGResoruceHandle(id) {};
};

class RDGRenderPassHandle : public RDGPassHandle
{
public:
    RDGRenderPassHandle(NodeID id) : RDGPassHandle(id) {};
};

class RDGComputePassHandle : public RDGPassHandle
{
public:
    RDGComputePassHandle(NodeID id) : RDGPassHandle(id) {};
};

class RDGRayTracingPassHandle : public RDGPassHandle
{
public:
    RDGRayTracingPassHandle(NodeID id) : RDGPassHandle(id) {};
};

class RDGPresentPassHandle : public RDGPassHandle
{
public:
    RDGPresentPassHandle(NodeID id) : RDGPassHandle(id) {};
};

class RDGCopyPassHandle : public RDGPassHandle
{
public:
    RDGCopyPassHandle(NodeID id) : RDGPassHandle(id) {};
};

class RDGTextureHandle : public RDGResoruceHandle
{
public:
    RDGTextureHandle(NodeID id) : RDGResoruceHandle(id) {};
};

class RDGBufferHandle : public RDGResoruceHandle
{
public:
    RDGBufferHandle(NodeID id) : RDGResoruceHandle(id) {};
};

RDGPool 负责资源的复用

被池化的资源有:Buffer、Texture、ImageView、DescriptorSet

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
    class RDGBufferPool
    {
    public:
        struct PooledBuffer
        {
            RHIBufferRef buffer;
            RHIResourceState state;
        };

        struct Key
        {
            Key(const RHIBufferInfo& info)
                : memoryUsage(info.memoryUsage)
                , type(info.type)
                , creationFlag(info.creationFlag)
            {
            }
            MemoryUsage memoryUsage;
            ResourceType type;
            BufferCreationFlags creationFlag;

            friend bool operator== (const Key& a, const Key& b)
            {
                return  a.memoryUsage == b.memoryUsage &&
                    a.type == b.type &&
                    a.creationFlag == b.creationFlag;
            }

            struct Hash {
                size_t operator()(const Key& a) const {
                    return MurmurHash64A(&a, sizeof(Key), 0);
                }
            };
        };

        PooledBuffer Allocate(const RHIBufferInfo& info);
        void Release(const PooledBuffer& pooledBuffer);

        inline uint32_t PooledSize() { return pooledSize; }
        inline uint32_t AllocatedSize() { return allocatedSize; }
        void Clear() { pooledBuffers.clear(); pooledSize = 0; }

        static std::shared_ptr<RDGBufferPool> Get()
        {
            static std::shared_ptr<RDGBufferPool> pool;
            if (pool == nullptr) pool = std::make_shared<RDGBufferPool>();
            return pool;
        }

    private:
        std::unordered_map<Key, std::list<PooledBuffer>, Key::Hash> pooledBuffers;  // Key一样的Buffer也有一个List(双向链表)
        uint32_t pooledSize = 0;
        uint32_t allocatedSize = 0;
    };


    class RDGTexturePool
    {
    public:
        struct PooledTexture
        {
            RHITextureRef texture;
            RHIResourceState state;
        };

        struct Key
        {
            Key(const RHITextureInfo& info)
                : info(info)
            {
            }

            RHITextureInfo info;

            friend bool operator== (const Key& a, const Key& b)
            {
                return  a.info == b.info;
            }

            struct Hash {
                size_t operator()(const Key& a) const {
                    return MurmurHash64A(&a, sizeof(Key), 0);
                }
            };
        };

        PooledTexture Allocate(const RHITextureInfo& info);
        void Release(const PooledTexture& pooledTexture);

        inline uint32_t PooledSize() { return pooledSize; }
        inline uint32_t AllocatedSize() { return allocatedSize; }
        void Clear() { pooledTextures.clear(); pooledSize = 0; }

        static std::shared_ptr<RDGTexturePool> Get()
        {
            static std::shared_ptr<RDGTexturePool> pool;
            if (pool == nullptr) pool = std::make_shared<RDGTexturePool>();
            return pool;
        }

    private:
        std::unordered_map<Key, std::list<PooledTexture>, Key::Hash> pooledTextures;   // 每个Key对应一个List而不是一个Texture
        uint32_t pooledSize = 0;
        uint32_t allocatedSize = 0;
    };


    class RDGTextureViewPool
    {
    public:
        struct PooledTextureView
        {
            RHITextureViewRef textureView;
        };

        struct Key
        {
            Key(const RHITextureViewInfo& info)
                : info(info)
            {
            }

            RHITextureViewInfo info;

            friend bool operator== (const Key& a, const Key& b)
            {
                return  a.info == b.info;
            }

            struct Hash {
                size_t operator()(const Key& a) const {
                    return MurmurHash64A(&a.info, sizeof(RHITextureViewInfo), 0);
                }
            };
        };

        PooledTextureView Allocate(const RHITextureViewInfo& info);
        void Release(const PooledTextureView& pooledTextureView);

        inline uint32_t PooledSize() { return pooledSize; }
        inline uint32_t AllocatedSize() { return allocatedSize; }
        void Clear() { pooledTextureViews.clear(); pooledSize = 0; }

        static std::shared_ptr<RDGTextureViewPool> Get()
        {
            static std::shared_ptr<RDGTextureViewPool> pool;
            if (pool == nullptr) pool = std::make_shared<RDGTextureViewPool>();
            return pool;
        }

    private:
        std::unordered_map<Key, std::list<PooledTextureView>, Key::Hash> pooledTextureViews;
        uint32_t pooledSize = 0;
        uint32_t allocatedSize = 0;
    };

    class RDGDescriptorSetPool
    {
    public:
        struct PooledDescriptor
        {
            RHIDescriptorSetRef descriptor;
        };

        struct Key  // 根签名和Set一致就可以复用
        {
            Key(const RHIRootSignatureInfo& info, uint32_t set)
                : entries(info.GetEntries())
                , set(set)
            {
            }

            std::vector<ShaderResourceEntry> entries;
            uint32_t set;

            friend bool operator== (const Key& a, const Key& b)
            {
                return  a.entries == b.entries &&
                    a.set == b.set;
            }

            struct HashEntries {
                size_t operator()(std::vector<ShaderResourceEntry> entries) const {
                    return MurmurHash64A(entries.data(), entries.size() * sizeof(ShaderResourceEntry), 0);
                }
            };

            struct Hash {
                size_t operator()(const Key& a) const {
                    return  std::hash<uint32_t>()(a.set) ^
                        (HashEntries()(a.entries) << 1);
                }
            };
        };

        PooledDescriptor Allocate(const RHIRootSignatureRef& rootSignature, uint32_t set);
        void Release(const PooledDescriptor& pooledDescriptor, const RHIRootSignatureRef& rootSignature, uint32_t set);

        inline uint32_t PooledSize() { return pooledSize; }
        inline uint32_t AllocatedSize() { return allocatedSize; }
        void Clear() { pooledDescriptors.clear(); pooledSize = 0; }

        static std::shared_ptr<RDGDescriptorSetPool> Get(uint32_t index)    // 描述符池需要FRAMES_IN_FLIGHT每帧一个,不然下一帧修改可能影响上一帧还未完成的渲染!!!
        {
            static std::shared_ptr<RDGDescriptorSetPool> pool[3];
            if (pool[index] == nullptr) pool[index] = std::make_shared<RDGDescriptorSetPool>();
            return pool[index];
        }

    private:
        std::unordered_map<Key, std::list<PooledDescriptor>, Key::Hash> pooledDescriptors;
        uint32_t pooledSize = 0;
        uint32_t allocatedSize = 0;
    };

RDGBuilder 负责构建图

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
#pragma once
#include "DependencyGraph.h"
#include "RDGHandle.h"
#include "RDGNode.h"
#include "Hazel/Renderer/RHI/RHICommandList.h"

#include <cstdint>
#include <memory>
#include <string>
#include <vector>
// #define RDG_DEBUG
/*
    RDGNode分为PassNode(RenderPass, ComputePass, RayTracingPass, CopyPass, PresentPass)和ResourceNode(TextureNode, BufferNode)
    RDGEdge用于存储 资源->Pass的View,用于在Pass时创建对应的view
*/
namespace GameEngine {

    // name->node
    class RDGBlackBoard
    {
    public:
        RDGPassNodeRef Pass(std::string name);
        RDGBufferNodeRef Buffer(std::string name);
        RDGTextureNodeRef Texture(std::string name);
#ifdef RDG_DEBUG
        std::string PassName(RDGPassNodeRef pass);
        std::string BufferName(RDGBufferNodeRef buffer);
        std::string TextureName(RDGTextureNodeRef texture);
#endif
        void AddPass(RDGPassNodeRef pass);
        void AddBuffer(RDGBufferNodeRef buffer);
        void AddTexture(RDGTextureNodeRef texture);

        void Clear() { passes.clear(); buffers.clear(); textures.clear(); }

    private:
        std::unordered_map<std::string, RDGPassNodeRef> passes;
        std::unordered_map<std::string, RDGBufferNodeRef> buffers;
        std::unordered_map<std::string, RDGTextureNodeRef> textures;
#ifdef RDG_DEBUG
        std::unordered_map<RDGPassNodeRef,std::string> passeNames;
        std::unordered_map<RDGTextureNodeRef,std::string> textureNames;
        std::unordered_map<RDGBufferNodeRef,std::string> bufferNames;
#endif

    };

    class RDGBuilder
    {
    public:
        RDGBuilder() = default;
        RDGBuilder(RHICommandListRef command): command(command){}  // 给一个commandList

        ~RDGBuilder() {};

        RDGTextureBuilder CreateTexture(std::string name);
        RDGBufferBuilder CreateBuffer(std::string name);
        RDGRenderPassBuilder CreateRenderPass(std::string name);    // 假定所有pass的添加顺序就是执行顺序,方便处理排序和依赖关系等
        RDGComputePassBuilder CreateComputePass(std::string name);
        RDGRayTracingPassBuilder CreateRayTracingPass(std::string name);
        RDGPresentPassBuilder CreatePresentPass(std::string name);
        RDGCopyPassBuilder CreateCopyPass(std::string name);

        RDGTextureHandle GetTexture(std::string name);
        RDGBufferHandle GetBuffer(std::string name);
        RDGRenderPassHandle GetRenderPass(std::string name) { return GetPass<RDGRenderPassNodeRef, RDGRenderPassHandle>(name); }
        RDGComputePassHandle GetComputePass(std::string name) { return GetPass<RDGComputePassNodeRef, RDGComputePassHandle>(name); }
        RDGRayTracingPassHandle GetRayTracingPass(std::string name) { return GetPass<RDGRayTracingPassNodeRef, RDGRayTracingPassHandle>(name); }
        RDGPresentPassHandle GetPresentPass(std::string name) { return GetPass<RDGPresentPassNodeRef, RDGPresentPassHandle>(name); }
        RDGCopyPassHandle GetCopyPass(std::string name) { return GetPass<RDGCopyPassNodeRef, RDGCopyPassHandle>(name); }

        RHITextureRef GetRHITexture(std::string name) { return blackBoard.Texture(name)->GetRHITexture(); }
        DependencyGraphRef GetGraph() { return graph; }

        void Execute();

    private:
        void CreateInputBarriers(RDGPassNodeRef pass);
        void CreateOutputBarriers(RDGPassNodeRef pass);
        void PrepareDescriptorSet(RDGPassNodeRef pass);
        void PrepareRenderTarget(RDGRenderPassNodeRef pass, RHIRenderPassInfo& renderPassInfo);
        void ReleaseResource(RDGPassNodeRef pass);

        void ExecutePass(RDGRenderPassNodeRef pass);
        void ExecutePass(RDGComputePassNodeRef pass);
        void ExecutePass(RDGRayTracingPassNodeRef pass);
        void ExecutePass(RDGPresentPassNodeRef pass);
        void ExecutePass(RDGCopyPassNodeRef pass);

        template<typename Type, typename Handle>
        Handle GetPass(std::string name)
        {
            auto node = blackBoard.Pass(name);
            if (node == nullptr)
            {
                LOG_TRACE("Unable to find RDG resource, please check name!");
                return Handle(UINT32_MAX);
            }
            return dynamic_cast<Type>(node)->GetHandle();
        }
        // Resolve 创建真正的资源
        RHITextureRef Resolve(RDGTextureNodeRef textureNode);
        RHIBufferRef Resolve(RDGBufferNodeRef bufferNode);
        void Release(RDGTextureNodeRef textureNode, RHIResourceState state);
        void Release(RDGBufferNodeRef bufferNode, RHIResourceState state);

        RHIResourceState PreviousState(RDGTextureNodeRef textureNode, RDGPassNodeRef passNode, TextureSubresourceRange subresource = {}, bool output = false);    // 获取当前pass(在执行顺序上)的资源的前序状态
        RHIResourceState PreviousState(RDGBufferNodeRef bufferNode, RDGPassNodeRef passNode, uint32_t offset = 0, uint32_t size = 0, bool output = false);      // output用于标记是相对于输入还是输出资源
        bool IsLastUsedPass(RDGTextureNodeRef textureNode, RDGPassNodeRef passNode, bool output = false);
        bool IsLastUsedPass(RDGBufferNodeRef bufferNode, RDGPassNodeRef passNode, bool output = false);

        std::vector<RDGPassNodeRef> passes; // 创建的全部pass,按照创建顺序执行

        DependencyGraphRef graph = std::make_shared<DependencyGraph>();
        RDGBlackBoard blackBoard;

        RHICommandListRef command;
    };

    class RDGTextureBuilder
    {
    public:
        RDGTextureBuilder(RDGBuilder* builder, RDGTextureNodeRef texture)
            : builder(builder)
            , texture(texture) {
        };

        RDGTextureBuilder& Import(RHITextureRef texture, RHIResourceState initState);
        RDGTextureBuilder& Exetent(Extent3D extent);
        RDGTextureBuilder& Format(RHIFormat format);
        RDGTextureBuilder& MemoryUsage(MemoryUsage memoryUsage);
        RDGTextureBuilder& AllowReadWrite();
        RDGTextureBuilder& AllowRenderTarget();
        RDGTextureBuilder& AllowDepthStencil();
        RDGTextureBuilder& MipLevels(uint32_t mipLevels);
        RDGTextureBuilder& ArrayLayers(uint32_t arrayLayers);
        RDGTextureBuilder& CubeMap();

        RDGTextureHandle Finish() { return texture->GetHandle(); }

    private:
        RDGBuilder* builder;
        RDGTextureNodeRef texture;
    };

    class RDGBufferBuilder
    {
    public:
        RDGBufferBuilder(RDGBuilder* builder, RDGBufferNodeRef buffer)
            : builder(builder)
            , buffer(buffer) {
        };

        RDGBufferBuilder& Import(RHIBufferRef buffer, RHIResourceState initState);
        RDGBufferBuilder& Size(uint32_t size);
        RDGBufferBuilder& MemoryUsage(MemoryUsage memoryUsage);
        RDGBufferBuilder& AllowVertexBuffer();
        RDGBufferBuilder& AllowIndexBuffer();
        RDGBufferBuilder& AllowReadWrite();
        RDGBufferBuilder& AllowRead();

        RDGBufferHandle Finish() { return buffer->GetHandle(); }

    private:
        RDGBuilder* builder;
        RDGBufferNodeRef buffer;
    };

    class RDGRenderPassBuilder
    {
    public:
        RDGRenderPassBuilder(RDGBuilder* builder, RDGRenderPassNodeRef pass)
            : builder(builder)
            , pass(pass)
            , graph(builder->GetGraph()) {
        };

        RDGRenderPassBuilder& PassIndex(uint32_t x = 0, uint32_t y = 0, uint32_t z = 0);        // 给一个index设置函数方便给Execute传参
        RDGRenderPassBuilder& RootSignature(RHIRootSignatureRef rootSignature);                 // 若提供根签名未提供描述符,使用池化创建
        RDGRenderPassBuilder& DescriptorSet(uint32_t set, RHIDescriptorSetRef descriptorSet);   // 若提供了描述符,直接用相应的描述符
        
        // 创建边  默认View都是2D,如果需要cubeView,需要手动指定
        RDGRenderPassBuilder& Read(uint32_t set, uint32_t binding, uint32_t index, RDGBufferHandle buffer, uint32_t offset = 0, uint32_t size = 0);
        RDGRenderPassBuilder& Read(uint32_t set, uint32_t binding, uint32_t index, RDGTextureHandle texture, TextureViewType viewType = VIEW_TYPE_2D, TextureSubresourceRange subresource = {});
        RDGRenderPassBuilder& ReadWrite(uint32_t set, uint32_t binding, uint32_t index, RDGBufferHandle buffer, uint32_t offset = 0, uint32_t size = 0);   // 好像和read也没什么区别?
        RDGRenderPassBuilder& ReadWrite(uint32_t set, uint32_t binding, uint32_t index, RDGTextureHandle texture, TextureViewType viewType = VIEW_TYPE_2D, TextureSubresourceRange subresource = {});
        RDGRenderPassBuilder& LabelColor(Color3 color);
        RDGRenderPassBuilder& Color(uint32_t binding, RDGTextureHandle texture,
            AttachmentLoadOp load = ATTACHMENT_LOAD_OP_DONT_CARE,
            AttachmentStoreOp store = ATTACHMENT_STORE_OP_DONT_CARE,
            Color4 clearColor = { 0.0f, 0.0f, 0.0f, 0.0f },
            TextureSubresourceRange subresource = {});
        RDGRenderPassBuilder& DepthStencil(RDGTextureHandle texture,
            AttachmentLoadOp load = ATTACHMENT_LOAD_OP_DONT_CARE,
            AttachmentStoreOp store = ATTACHMENT_STORE_OP_DONT_CARE,
            float clearDepth = 1.0f,
            uint32_t clearStencil = 0,
            TextureSubresourceRange subresource = {});

        // 标识后续还会使用的资源,会用PassNode->ResourceNode
        RDGRenderPassBuilder& OutputRead(RDGBufferHandle buffer, uint32_t offset = 0, uint32_t size = 0);             // 在执行完Pass后作为输出,自动屏障,可能还会在其他地方使用
        RDGRenderPassBuilder& OutputRead(RDGTextureHandle texture, TextureSubresourceRange subresource = {});
        RDGRenderPassBuilder& OutputReadWrite(RDGBufferHandle buffer, uint32_t offset = 0, uint32_t size = 0);
        RDGRenderPassBuilder& OutputReadWrite(RDGTextureHandle texture, TextureSubresourceRange subresource = {});
        RDGRenderPassBuilder& Execute(const RDGPassExecuteFunc& execute);

        RDGRenderPassHandle Finish() { return pass->GetHandle(); }

    private:
        RDGBuilder* builder;
        RDGRenderPassNodeRef pass;

        DependencyGraphRef graph;
    };

    class RDGComputePassBuilder
    {
    public:
        RDGComputePassBuilder(RDGBuilder* builder, RDGComputePassNodeRef pass)
            : builder(builder)
            , pass(pass)
            , graph(builder->GetGraph()) {
        };

        RDGComputePassBuilder& PassIndex(uint32_t x = 0, uint32_t y = 0, uint32_t z = 0);        // 给一个index设置函数方便给Execute传参
        RDGComputePassBuilder& RootSignature(RHIRootSignatureRef rootSignature);                 // 若提供根签名未提供描述符,使用池化创建
        RDGComputePassBuilder& DescriptorSet(uint32_t set, RHIDescriptorSetRef descriptorSet);   // 若提供了描述符,直接用相应的描述符
        RDGComputePassBuilder& Read(uint32_t set, uint32_t binding, uint32_t index, RDGBufferHandle buffer, uint32_t offset = 0, uint32_t size = 0);
        RDGComputePassBuilder& Read(uint32_t set, uint32_t binding, uint32_t index, RDGTextureHandle texture, TextureViewType viewType = VIEW_TYPE_2D, TextureSubresourceRange subresource = {});
        RDGComputePassBuilder& ReadWrite(uint32_t set, uint32_t binding, uint32_t index, RDGBufferHandle buffer, uint32_t offset = 0, uint32_t size = 0);   // 好像和read也没什么区别?
        RDGComputePassBuilder& ReadWrite(uint32_t set, uint32_t binding, uint32_t index, RDGTextureHandle texture, TextureViewType viewType = VIEW_TYPE_2D, TextureSubresourceRange subresource = {});
        RDGComputePassBuilder& OutputRead(RDGBufferHandle buffer, uint32_t offset = 0, uint32_t size = 0);             // 在执行完Pass后作为输出,自动屏障,可能还会在其他地方使用
        RDGComputePassBuilder& OutputRead(RDGTextureHandle texture, TextureSubresourceRange subresource = {});
        RDGComputePassBuilder& OutputReadWrite(RDGBufferHandle buffer, uint32_t offset = 0, uint32_t size = 0);
        RDGComputePassBuilder& OutputReadWrite(RDGTextureHandle texture, TextureSubresourceRange subresource = {});
        RDGComputePassBuilder& OutputIndirectDraw(RDGBufferHandle buffer, uint32_t offset = 0, uint32_t size = 0);


        RDGComputePassBuilder& Execute(const RDGPassExecuteFunc& execute);

        RDGComputePassHandle Finish() { return pass->GetHandle(); }

    private:
        RDGBuilder* builder;
        RDGComputePassNodeRef pass;

        DependencyGraphRef graph;
    };

    class RDGRayTracingPassBuilder
    {
    public:
        RDGRayTracingPassBuilder(RDGBuilder* builder, RDGRayTracingPassNodeRef pass)
            : builder(builder)
            , pass(pass)
            , graph(builder->GetGraph()) {
        };

        RDGRayTracingPassBuilder& PassIndex(uint32_t x = 0, uint32_t y = 0, uint32_t z = 0);        // 给一个index设置函数方便给Execute传参
        RDGRayTracingPassBuilder& RootSignature(RHIRootSignatureRef rootSignature);                 // 若提供根签名未提供描述符,使用池化创建
        RDGRayTracingPassBuilder& DescriptorSet(uint32_t set, RHIDescriptorSetRef descriptorSet);   // 若提供了描述符,直接用相应的描述符
        RDGRayTracingPassBuilder& Read(uint32_t set, uint32_t binding, uint32_t index, RDGBufferHandle buffer, uint32_t offset = 0, uint32_t size = 0);
        RDGRayTracingPassBuilder& Read(uint32_t set, uint32_t binding, uint32_t index, RDGTextureHandle texture, TextureViewType viewType = VIEW_TYPE_2D, TextureSubresourceRange subresource = {});
        RDGRayTracingPassBuilder& ReadWrite(uint32_t set, uint32_t binding, uint32_t index, RDGBufferHandle buffer, uint32_t offset = 0, uint32_t size = 0);   // 好像和read也没什么区别?
        RDGRayTracingPassBuilder& ReadWrite(uint32_t set, uint32_t binding, uint32_t index, RDGTextureHandle texture, TextureViewType viewType = VIEW_TYPE_2D, TextureSubresourceRange subresource = {});
        RDGRayTracingPassBuilder& OutputRead(RDGBufferHandle buffer, uint32_t offset = 0, uint32_t size = 0);             // 在执行完Pass后作为输出,自动屏障,可能还会在其他地方使用
        RDGRayTracingPassBuilder& OutputRead(RDGTextureHandle texture, TextureSubresourceRange subresource = {});
        RDGRayTracingPassBuilder& OutputReadWrite(RDGBufferHandle buffer, uint32_t offset = 0, uint32_t size = 0);
        RDGRayTracingPassBuilder& OutputReadWrite(RDGTextureHandle texture, TextureSubresourceRange subresource = {});


        RDGRayTracingPassBuilder& Execute(const RDGPassExecuteFunc& execute);

        RDGRayTracingPassHandle Finish() { return pass->GetHandle(); }

    private:
        RDGBuilder* builder;
        RDGRayTracingPassNodeRef pass;

        DependencyGraphRef graph;
    };

    class RDGPresentPassBuilder
    {
    public:
        RDGPresentPassBuilder(RDGBuilder* builder, RDGPresentPassNodeRef pass)
            : builder(builder)
            , pass(pass)
            , graph(builder->GetGraph()) {
        };

        RDGPresentPassHandle Finish() { return pass->GetHandle(); }

        RDGPresentPassBuilder& Texture(RDGTextureHandle texture, TextureSubresourceLayers subresource = {});
        RDGPresentPassBuilder& PresentTexture(RDGTextureHandle texture);

    private:
        RDGBuilder* builder;
        RDGPresentPassNodeRef pass;

        DependencyGraphRef graph;
    };

    class RDGCopyPassBuilder
    {
    public:
        RDGCopyPassBuilder(RDGBuilder* builder, RDGCopyPassNodeRef pass)
            : builder(builder)
            , pass(pass)
            , graph(builder->GetGraph()) {
        };

        RDGCopyPassHandle Finish() { return pass->GetHandle(); }

        RDGCopyPassBuilder& From(RDGTextureHandle texture, TextureSubresourceLayers subresource = {});
        RDGCopyPassBuilder& To(RDGTextureHandle texture, TextureSubresourceLayers subresource = {});
        RDGCopyPassBuilder& GenerateMips();
        RDGCopyPassBuilder& OutputRead(RDGTextureHandle texture, TextureSubresourceLayers subresource = {});
        RDGCopyPassBuilder& OutputReadWrite(RDGTextureHandle texture, TextureSubresourceLayers subresource = {});

    private:
        RDGBuilder* builder;
        RDGCopyPassNodeRef pass;

        DependencyGraphRef graph;
    };
}

RDG下定义一个Pass的流程

  1. 创建/获取 资源节点Handler
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 创建资源
RDGTextureBuilder RDGBuilder::CreateTexture(std::string name)
{
	RDGTextureNodeRef textureNode = graph->CreateNode<RDGTextureNode>(name);
	blackBoard.AddTexture(textureNode);
	return RDGTextureBuilder(this, textureNode);
}

RDGBufferBuilder RDGBuilder::CreateBuffer(std::string name)
{
	RDGBufferNodeRef bufferNode = graph->CreateNode<RDGBufferNode>(name);
	blackBoard.AddBuffer(bufferNode);
	return RDGBufferBuilder(this, bufferNode);
}

// 获取资源
RDGTextureHandle RDGBuilder::GetTexture(std::string name)
{
    auto node = blackBoard.Texture(name);
    if (node == nullptr)
    {
        return RDGTextureHandle(UINT32_MAX);
    }
    return node->GetHandle();
}
RDGBufferHandle RDGBuilder::GetBuffer(std::string name)
{
    auto node = blackBoard.Buffer(name);
    if (node == nullptr)
    {
        return RDGBufferHandle(UINT32_MAX);
    }
    return node->GetHandle();
}
  1. 创建Pass结点,并挂载需要的资源

Read资源:资源 -> Pass

ReadWrite资源: Pass-> 资源

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 创建图形PassNode
RDGRenderPassBuilder RDGBuilder::CreateRenderPass(std::string name)
{
    RDGRenderPassNodeRef passNode = graph->CreateNode<RDGRenderPassNode>(name);
    blackBoard.AddPass(passNode);
    passes.push_back(passNode);
    return RDGRenderPassBuilder(this, passNode);
}

// 绑定只读资源(此时需要指定 资源描述符位置、 viewType 和 subresource)
RDGComputePassBuilder& RDGComputePassBuilder::Read(uint32_t set, uint32_t binding, uint32_t index, RDGTextureHandle texture, TextureViewType viewType, TextureSubresourceRange subresource)
{
    RDGTextureEdgeRef edge = graph->CreateEdge<RDGTextureEdge>();
    edge->state = RESOURCE_STATE_SHADER_RESOURCE;   // 因为是Read,所以指定为SRV
    edge->subresource = subresource;
    edge->asShaderRead = true;
    edge->set = set;
    edge->binding = binding;
    edge->index = index;
    edge->type = RESOURCE_TYPE_TEXTURE;
    edge->viewType = viewType;
    graph->Link(graph->GetNode(texture.ID()), pass, edge);
    return *this;
}

// 绑定读写资源
RDGComputePassBuilder& RDGComputePassBuilder::ReadWrite(uint32_t set, uint32_t binding, uint32_t index, RDGBufferHandle buffer, uint32_t offset, uint32_t size)
{
    RDGBufferEdgeRef edge = graph->CreateEdge<RDGBufferEdge>();
    edge->state = RESOURCE_STATE_UNORDERED_ACCESS;
    edge->offset = offset;
    edge->size = size;
    edge->asShaderReadWrite = true;
    edge->set = set;
    edge->binding = binding;
    edge->index = index;
    edge->type = RESOURCE_TYPE_RW_BUFFER;
    graph->Link(pass, graph->GetNode(buffer.ID()), edge);
  	return *this;
}
  1. 定义Pass执行流程 RDGPassContext
1
2
3
4
5
6
7
typedef std::function<void(RDGPassContext)> RDGPassExecuteFunc;

RDGComputePassBuilder& RDGComputePassBuilder::Execute(const RDGPassExecuteFunc& execute)
{
    pass->execute = execute;
    return *this;
}
  1. 也可以指定Output资源,用于在Pass执行完成后,修改资源的状态,这有助于一个Pass有很多个PassNode时(比如Hiz)在Pass内部资源屏障
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
RDGComputePassBuilder& RDGComputePassBuilder::OutputRead(RDGTextureHandle texture, TextureSubresourceRange subresource)
{
	RDGTextureEdgeRef edge = graph->CreateEdge<RDGTextureEdge>();
	edge->state = RESOURCE_STATE_SHADER_RESOURCE;
	edge->subresource = subresource;
	edge->asOutputReadWrite = true;
	edge->type = RESOURCE_TYPE_TEXTURE;

	graph->Link(pass, graph->GetNode(texture.ID()), edge);

	return *this;
}

RDGComputePassBuilder& RDGComputePassBuilder::OutputReadWrite(RDGBufferHandle buffer, uint32_t offset, uint32_t size)
{
	RDGBufferEdgeRef edge = graph->CreateEdge<RDGBufferEdge>();
	edge->state = RESOURCE_STATE_UNORDERED_ACCESS;
	edge->offset = offset;
	edge->size = size;
	edge->asOutputReadWrite = true;
	edge->type = RESOURCE_TYPE_RW_BUFFER;

	graph->Link(pass, graph->GetNode(buffer.ID()), edge);

	return *this;
}
  1. RDG执行
1
2
3
4
5
RDGBuilder rdgBuilder = RDGBuilder(CurCommandList); // 传入CommandList
// 构建图结构
for (auto& pass : passes) { if (pass) pass->Build(rdgBuilder); }
// 执行RDG
rdgBuilder.Execute();

RDG执行内部流程

遍历PassNode,根据类型,执行Pass对应的执行流程

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
void RDGBuilder::Execute()
{
    std::string currentPrefix;

    for (size_t i = 0; i < passes.size(); ++i) {
        auto& pass = passes[i];
        if (!pass || pass->isCulled) continue;
        std::string name = pass->Name();
        size_t pos = name.find('_');
        std::string prefix = (pos != std::string::npos) ? name.substr(0, pos) : name;
        if (!currentPrefix.empty() && currentPrefix != prefix) {
            command->PopLabel();
            currentPrefix.clear();
        }
        if (currentPrefix.empty()) {
            command->PushLabel(prefix, pass->GetLabelColor());
            currentPrefix = prefix;
        }
#ifdef RDG_DEBUG
        LOG_INFO("RDG Pass Begin: [{}]", name);
#endif
        // 执行 pass
        switch (pass->NodeType()) {
        case RDG_PASS_NODE_TYPE_RENDER:         ExecutePass(dynamic_cast<RDGRenderPassNodeRef>(pass)); break;
        case RDG_PASS_NODE_TYPE_COMPUTE:        ExecutePass(dynamic_cast<RDGComputePassNodeRef>(pass)); break;
        case RDG_PASS_NODE_TYPE_RAY_TRACING:    ExecutePass(dynamic_cast<RDGRayTracingPassNodeRef>(pass)); break;
        case RDG_PASS_NODE_TYPE_PRESENT:        ExecutePass(dynamic_cast<RDGPresentPassNodeRef>(pass)); break;
        case RDG_PASS_NODE_TYPE_COPY:           ExecutePass(dynamic_cast<RDGCopyPassNodeRef>(pass)); break;
        default:                                LOG_ERROR("Unsupported RDG pass type!");
        }
#ifdef RDG_DEBUG
        LOG_INFO("RDG Pass End: [{}]", name);
#endif
        // 检查下一个 pass 前缀,如果变化或者是最后一个 pass,pop
        size_t nextIndex = i + 1;
        std::string nextPrefix;
        while (nextIndex < passes.size() && (!passes[nextIndex] || passes[nextIndex]->isCulled)) {
            ++nextIndex;
        }
        if (nextIndex < passes.size()) {
            std::string nextName = passes[nextIndex]->Name();
            size_t nextPos = nextName.find('_');
            nextPrefix = (nextPos != std::string::npos) ? nextName.substr(0, nextPos) : nextName;
        }

        if (nextIndex == passes.size() || nextPrefix != prefix) {
            command->PopLabel();
            currentPrefix.clear();
        }
    }



    for (auto& pass : passes)   // 释放池化资源
    {
        //ReleaseResource(pass);
        for (auto& descriptor : pass->pooledDescriptorSets)
        {
            RDGDescriptorSetPool::Get(APP_FRAMEINDEX)->Release({ descriptor.first }, pass->rootSignature, descriptor.second);
        }
    }
}

图形渲染Pass内部流程

  1. 创建和更新资源描述符集
  2. 创建RenderPass、FrameBuffer(pooled)
  3. 根据DAG自动进行输入屏障
  4. BeginRenderPass
  5. 执行Pass
  6. 根据DAG自动进行输入屏障
  7. 回收Pass使用的资源,用于资源复用
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
void RDGBuilder::ExecutePass(RDGRenderPassNodeRef pass)
{
	PrepareDescriptorSet(pass);
	RHIRenderPassInfo renderPassInfo = {};
	PrepareRenderTarget(pass, renderPassInfo);
	RHIRenderPassRef renderPass = APP_DYNAMICRHI->CreateRenderPass(renderPassInfo); // 根据图创建RenderPass和FranmeBuffer,因为有pool其实无所谓把RenderPass和FrameBuffer放在一起创建
	CreateInputBarriers(pass);
	command->BeginRenderPass(renderPass); // Begin的是RenderPass,把实际的FrameBuffer当作参数传递进去了
	RDGPassContext context;
	context.command = command;
	context.builder = this;
	context.descriptors = pass->descriptorSets;
	context.passIndex[0] = pass->passIndex[0];
	context.passIndex[1] = pass->passIndex[1];
	context.passIndex[2] = pass->passIndex[2];
	pass->execute(context);
	command->EndRenderPass();
	CreateOutputBarriers(pass);
	ReleaseResource(pass);
}

自动创建屏障的过程:

遍历一个Texture的所有Pass,如果这个Pass在当前Pass之前,就根据逻辑找这个Pass与这个纹理如何交互的,就得到了Pass

从这里看目前的流程还是比较简单了,因为Pass都是一条线的顺序

多线程渲染

设计是:Tick一开始会先让渲染线程渲染上一帧的数据,之后主线程同步执行下一帧的收集

RenderCommandQueue用于在主线程收集命令

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#pragma once
#include <cstdint>
#include <vector>
namespace GameEngine {
	struct RenderCommandDebugInfo
	{
		const char* file;
		int line;
		const char* function;
	};
	class RenderCommandQueue
	{
	public:
		typedef void(*RenderCommandFn)(void*);

		RenderCommandQueue();
		~RenderCommandQueue();

		void* Allocate(RenderCommandFn func, uint32_t size); // 收集命令
		std::vector<RenderCommandDebugInfo> m_DebugInfos; // 调试信息

		void Execute();
	private:
		uint8_t* m_CommandBuffer;
		uint8_t* m_CommandBufferPtr;
		uint32_t m_CommandCount = 0;

	};
}

// 分配一个10mb的Buffer
RenderCommandQueue::RenderCommandQueue()
{
    m_CommandBuffer = new uint8_t[10 * 1024 * 1024]; // 10mb buffer
    m_CommandBufferPtr = m_CommandBuffer;
    memset(m_CommandBuffer, 0, 10 * 1024 * 1024); // 填充0
}

Submit的流程

  1. 传入一段Lambda表达式或一个函数指针,Submit内部会统一封装为一个参数为void*的lambda,内部会调用原函数,并进行销毁
  2. 在Queue中分配内存,存储统一封装好的lambda的指针 + 指针大小记录 + 真正的调用函数内容(即需要的void*)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 暴漏的接口
template<typename FuncT>
static void Submit(FuncT&& func, const char* file = nullptr, int line = 0, const char* function = nullptr)
{
    auto& queue = GetRenderCommandQueue();  // 有两个CommandQueue交替工作,一个在主线程收集指令,一个在渲染线程执行指令,每帧都会交替一次
#ifdef RTDEBUG
    queue.m_DebugInfos.push_back({ file, line, function });
#endif
    // 把传入的函数指针或lambda统一封装为一个参数为void*的lambda,这里lambda用于转换void*指针到真正的对象指针,然后执行它
    auto renderCmd = [](void* ptr) {
        auto pFunc = (FuncT*)ptr;
        (*pFunc)();
        pFunc->~FuncT();
        };

    auto storageBuffer = queue.Allocate(renderCmd, sizeof(FuncT));
    new (storageBuffer) FuncT(std::forward<FuncT>(func));  // placement New 最近刚学的 原地创建对象,不分配新内存
}
using RenderCommandFn = void(*)(void*);
// Submit可以传递一个lambda表达式,也可以是一个函数的地址,但必须都是无参的,为了统一
// 	Submit([]() {
//		cout << "hello world" << endl;
// 	});
//	Submit(&test);


void* RenderCommandQueue::Allocate(RenderCommandFn fn, uint32_t size)
 {
     *(RenderCommandFn*)m_CommandBufferPtr = fn;
     m_CommandBufferPtr += sizeof(RenderCommandFn);

     *(uint32_t*)m_CommandBufferPtr = size;
     m_CommandBufferPtr += sizeof(uint32_t);

     void* memory = m_CommandBufferPtr;
     m_CommandBufferPtr += size;

     m_CommandCount++;
     return memory;
 }

Queue执行流程:就是按照上边封装数据的流程读取数据,然后依次执行

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
void RenderCommandQueue::Execute()
{
    uint8_t* buffer = m_CommandBuffer;

    for (uint32_t i = 0; i < m_CommandCount; i++)
    {
        RenderCommandFn function = *(RenderCommandFn*)buffer;
        buffer += sizeof(RenderCommandFn);

        uint32_t size = *(uint32_t*)buffer;
        buffer += sizeof(uint32_t);
        // 执行命令
        function(buffer);
        buffer += size;
    }

    m_CommandBufferPtr = m_CommandBuffer;
    m_CommandCount = 0;
}

以上架构就完成了Queue如何收集指令和执行指令

下面还要看渲染线程和主线程如何同步

渲染线程封装,使用状态机模型 Idle Busy Kick三个状态流转,通过win的线程同步API来进行控制

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
// 简单封装一个线程对象,Dispathc用于开启并执行一个线程,Join表示阻塞等待该线程结束
class Thread
{
public:
    Thread(const std::string& name);

    // 线程执行
    template<typename Fn, typename... Args>
    void Dispatch(Fn&& func, Args&&... args)
    {
        m_Thread = std::thread(func, std::forward<Args>(args)...); // 这行会立即新线程执行传入的函数
        SetName(m_Name);
        LOG_INFO("Thread [{0}] Dispatch and Run!", m_Name);
    }

    void SetName(const std::string& name);

    // 阻塞等待线程结束
    void Join();

    std::thread::id GetID() const;
private:
    std::string m_Name;
    std::thread m_Thread;
};

enum class ThreadingPolicy
{
	// MultiThreaded will create a Render Thread
	None = 0, SingleThreaded, MultiThreaded
};

// 渲染线程的封装
class RenderThread
{
public:
	enum class State
	{
		Idle = 0,
		Busy,
		Kick
	};
public:
	RenderThread(ThreadingPolicy coreThreadingPolicy);
	~RenderThread();

	void Run();
	bool IsRunning() const { return m_IsRunning; }
	void Terminate();

	void Wait(State waitForState);
	void WaitAndSet(State waitForState, State setToState);
	void Set(State setToState);

	void NextFrame();
	void BlockUntilRenderComplete();
	void Kick();
	void Pump();
	static uint32_t RT_GetFrameIndex();
	static bool IsCurrentThreadRT();
private:
	RenderThreadData* m_Data;
	ThreadingPolicy m_ThreadingPolicy;

	Thread m_RenderThread;

	bool m_IsRunning = false;

	std::atomic<uint32_t> m_AppThreadFrame = 0;
};

渲染线程内部

创建流程

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
RenderThread::RenderThread(ThreadingPolicy coreThreadingPolicy)
    : m_RenderThread("Render Thread"), m_ThreadingPolicy(coreThreadingPolicy)
{
    m_Data = new RenderThreadData();

    if (m_ThreadingPolicy == ThreadingPolicy::MultiThreaded)
    {
        InitializeCriticalSection(&m_Data->m_CriticalSection); // 创建临界区
        InitializeConditionVariable(&m_Data->m_ConditionVariable); // 创建条件变量配套
    }
}

渲染线程运行:渲染线程While等待Kick信号,收到信号后,转为Busy状态,进一步就是拿Queue的执行,执行完成后设置为Idle状态

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
void RenderThread::Run()
{
    m_IsRunning = true;
    if (m_ThreadingPolicy == ThreadingPolicy::MultiThreaded) {
        m_RenderThread.Dispatch(RenderManager::RenderThreadFunc, this);
    }
    s_RenderThreadID = m_RenderThread.GetID();
}

// 挂在渲染线程的函数
void RenderManager::RenderThreadFunc(RenderThread* renderThread)
{
    while (renderThread->IsRunning())
    {
        WaitAndRender(renderThread);
    }
}

// 具体渲染线程的函数
void RenderManager::WaitAndRender(RenderThread* renderThread)
{
    // Wait for kick, then set render thread to busy
    {
        // 渲染线程循环等待Kick信号,收到信号后,设置为Busy信号并开始工作
        renderThread->WaitAndSet(RenderThread::State::Kick, RenderThread::State::Busy);
    }
    // 工作就是把缓存命令全部执行
    s_CommandQueue[GetRenderQueueIndex()]->Execute();

    // Rendering has completed, set state to idle
    renderThread->Set(RenderThread::State::Idle);
}

信号流转

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
void RenderThread::WaitAndSet(State waitForState, State setToState)
{
    if (m_ThreadingPolicy == ThreadingPolicy::SingleThreaded)
        return;

    EnterCriticalSection(&m_Data->m_CriticalSection);
    while (m_Data->m_State != waitForState)
    {
        SleepConditionVariableCS(&m_Data->m_ConditionVariable, &m_Data->m_CriticalSection, INFINITE);
    }
    m_Data->m_State = setToState;
    WakeAllConditionVariable(&m_Data->m_ConditionVariable);
    LeaveCriticalSection(&m_Data->m_CriticalSection);
}

来捋一下主线程的工作

  1. 主线程等待渲染线程执行完毕
  2. 主线程交换Queue
  3. 主线程通知渲染线程开始工作(渲染上一帧)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 1. 主线程等待渲染线程执行完毕
void RenderThread::BlockUntilRenderComplete()
{
    if (m_ThreadingPolicy == ThreadingPolicy::SingleThreaded)
        return;

    Wait(State::Idle);
}

// 2. 主线程交换Queue
void RenderManager::SwapRenderCommandQueue()
{
    s_RenderCommandQueueSubmissionIndex = (s_RenderCommandQueueSubmissionIndex + 1) % s_RenderCommandQueueCount;
}

// 3. 主线程通知渲染线程开始工作(渲染上一帧)
void RenderThread::Kick()
{
    if (m_ThreadingPolicy == ThreadingPolicy::MultiThreaded)
    {
        Set(State::Kick);
    }
}

主线程和渲染线程都需要访问临界区资源,主线程为了把状态设置为Kick,等待Idle状态,渲染线程等待Kick信号,转为Busy状态,执行完成后转为Idle状态

两个线程对于状态的修改通过进入和离开临界区代码来互斥,它会自动加锁和解锁

1
2
EnterCriticalSection(&m_Data->m_CriticalSection);
LeaveCriticalSection(&m_Data->m_CriticalSection);

条件变量用于防止CPU轮询,比如一个线程进入临界区后发现当前状态不是想要的状态,于是退出,还是在外部写一个while来轮询

有了条件变量的话,发现状态不对,就会让线程进入休眠,并且释放锁,当满足条件时,重新上锁开始切换状态

1
2
SleepConditionVariableCS(&m_CondVar, &m_CriticalSection, INFINITE);
WakeAllConditionVariable(&m_CondVar); 

所以条件变量的目的是让上锁的线程临时释放锁,等满足条件后,重新再获得锁

渲染Tick执行

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// 在渲染最后执行  CurCommandList中已经收集了当前帧的所有渲染指令,这个提交到渲染线程后,会在下一帧的开始处执行这些函数
RENDER_SUBMIT([this]() {
	//LOG_INFO("渲染第{}帧", APP_FRAMEINDEX_RT);
	auto& CurResource = m_PerFrameBaseResources[APP_FRAMEINDEX_RT];
	CurResource.fence->Wait(); // 先等待这个飞行帧上一帧渲染结束
	m_SwapChain->GetNewFrame(nullptr, CurResource.startSemaphore);
	RHICommandListRef CurCommandList = CurResource.commandList;
	CurCommandList->Execute(CurResource.fence, CurResource.startSemaphore, CurResource.finishSemaphore);
	m_GPUTimeInfos[APP_FRAMEINDEX_RT] = CurCommandList->GetGPUTime();
	m_SwapChain->Present(CurResource.finishSemaphore);
	m_RenderThread.NextFrame();
});

渲染资源管理

全局资源描述符布局

  1. 创建一个Set=0的根签名,填充常用的各种资源类型,其中一部分绑定点是bindless的
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
		// 创建一个全局的资源描述符集来挂载各种全局资源
		RHIRootSignatureInfo info = {};
		// set binding count frequency type
		info.AddEntry({ 0, GLORBAL_RESOURCE_BINDING_BINDLESS_POSITION, MAX_BINDLESS_RESOURCE_SIZE, SHADER_FREQUENCY_ALL, RESOURCE_TYPE_RW_BUFFER });
		info.AddEntry({ 0, GLORBAL_RESOURCE_BINDING_BINDLESS_NORMAL, MAX_BINDLESS_RESOURCE_SIZE, SHADER_FREQUENCY_ALL, RESOURCE_TYPE_RW_BUFFER });
		info.AddEntry({ 0, GLORBAL_RESOURCE_BINDING_BINDLESS_TANGENT, MAX_BINDLESS_RESOURCE_SIZE, SHADER_FREQUENCY_ALL, RESOURCE_TYPE_RW_BUFFER });
		info.AddEntry({ 0, GLORBAL_RESOURCE_BINDING_BINDLESS_TEXCOORD, MAX_BINDLESS_RESOURCE_SIZE, SHADER_FREQUENCY_ALL, RESOURCE_TYPE_RW_BUFFER });
		info.AddEntry({ 0, GLORBAL_RESOURCE_BINDING_BINDLESS_COLOR, MAX_BINDLESS_RESOURCE_SIZE, SHADER_FREQUENCY_ALL, RESOURCE_TYPE_RW_BUFFER });
		info.AddEntry({ 0, GLORBAL_RESOURCE_BINDING_BINDLESS_BONE_INDEX, MAX_BINDLESS_RESOURCE_SIZE, SHADER_FREQUENCY_ALL, RESOURCE_TYPE_RW_BUFFER });
		info.AddEntry({ 0, GLORBAL_RESOURCE_BINDING_BINDLESS_BONE_WEIGHT, MAX_BINDLESS_RESOURCE_SIZE, SHADER_FREQUENCY_ALL, RESOURCE_TYPE_RW_BUFFER });
		info.AddEntry({ 0, GLORBAL_RESOURCE_BINDING_BINDLESS_ANIMATION, MAX_BINDLESS_RESOURCE_SIZE, SHADER_FREQUENCY_ALL, RESOURCE_TYPE_RW_BUFFER });
		info.AddEntry({ 0, GLORBAL_RESOURCE_BINDING_BINDLESS_INDEX, MAX_BINDLESS_RESOURCE_SIZE, SHADER_FREQUENCY_ALL, RESOURCE_TYPE_RW_BUFFER });

		info.AddEntry({ 0, GLORBAL_RESOURCE_BINDING_BINDLESS_SAMPLER, MAX_BINDLESS_RESOURCE_SIZE, SHADER_FREQUENCY_ALL, RESOURCE_TYPE_SAMPLER });
		info.AddEntry({ 0, GLORBAL_RESOURCE_BINDING_BINDLESS_TEXTURE_1D, MAX_BINDLESS_RESOURCE_SIZE, SHADER_FREQUENCY_ALL, RESOURCE_TYPE_TEXTURE });
		info.AddEntry({ 0, GLORBAL_RESOURCE_BINDING_BINDLESS_TEXTURE_1D_ARRAY, MAX_BINDLESS_RESOURCE_SIZE, SHADER_FREQUENCY_ALL, RESOURCE_TYPE_TEXTURE });
		info.AddEntry({ 0, GLORBAL_RESOURCE_BINDING_BINDLESS_TEXTURE_2D, MAX_BINDLESS_RESOURCE_SIZE, SHADER_FREQUENCY_ALL, RESOURCE_TYPE_TEXTURE });
		info.AddEntry({ 0, GLORBAL_RESOURCE_BINDING_BINDLESS_TEXTURE_2D_ARRAY, MAX_BINDLESS_RESOURCE_SIZE, SHADER_FREQUENCY_ALL, RESOURCE_TYPE_TEXTURE });
		info.AddEntry({ 0, GLORBAL_RESOURCE_BINDING_BINDLESS_TEXTURE_CUBE, MAX_BINDLESS_RESOURCE_SIZE, SHADER_FREQUENCY_ALL, RESOURCE_TYPE_TEXTURE }); // 就是得设置Texture,设置Cube不对
		info.AddEntry({ 0, GLORBAL_RESOURCE_BINDING_BINDLESS_TEXTURE_3D, MAX_BINDLESS_RESOURCE_SIZE, SHADER_FREQUENCY_ALL, RESOURCE_TYPE_TEXTURE });

		// 下面这些需要手动去绑定buffer和preFrame资源描述符
		info.AddEntry({ 0, GLORBAL_RESOURCE_BINDING_MESHINSTANCEINFO, 1, SHADER_FREQUENCY_ALL, RESOURCE_TYPE_RW_BUFFER });
		info.AddEntry({ 0, GLORBAL_RESOURCE_BINDING_MATERIALINFO, 1, SHADER_FREQUENCY_ALL, RESOURCE_TYPE_RW_BUFFER });
		info.AddEntry({ 0, GLORBAL_RESOURCE_BINDING_MESHINFO, 1, SHADER_FREQUENCY_ALL, RESOURCE_TYPE_RW_BUFFER });

		info.AddEntry({ 0, GLORBAL_RESOURCE_BINDING_SETTING, 1, SHADER_FREQUENCY_ALL, RESOURCE_TYPE_RW_BUFFER });
		info.AddEntry({ 0, GLORBAL_RESOURCE_BINDING_CAMERA, 2, SHADER_FREQUENCY_ALL, RESOURCE_TYPE_RW_BUFFER });
		info.AddEntry({ 0, GLORBAL_RESOURCE_BINDING_LIGHTINFO, 1, SHADER_FREQUENCY_ALL, RESOURCE_TYPE_RW_BUFFER });
		info.AddEntry({ 0, GLORBAL_RESOURCE_BINDING_GIZMO, 1, SHADER_FREQUENCY_ALL, RESOURCE_TYPE_RW_BUFFER });
		if (RENDER_ENABLE_RAY_TRACING) {
			info.AddEntry({ 0, GLORBAL_RESOURCE_BINDING_TLAS, 1, SHADER_FREQUENCY_ALL, RESOURCE_TYPE_RAY_TRACING });
		}

		m_GlobalResourcePreFrameRootSignature = APP_DYNAMICRHI->CreateRootSignature(info);
		for (auto& resource : m_PerFrameGlobalResources) resource.descriptorSet = m_GlobalResourcePreFrameRootSignature->CreateDescriptorSet(0);
  1. 提前创建好一些资源,绑定到这个资源描述符集中
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
// 挂载默认全局资源
for (auto& resource : m_PerFrameGlobalResources) {
	// camera
	{
		// 0号位是当前摄像机, 1号位存储Scene自带的默认摄像机
		RHIDescriptorUpdateInfo cameraUpdateInfo = {};
		cameraUpdateInfo.resourceType = RESOURCE_TYPE_RW_BUFFER;
		cameraUpdateInfo.buffer = resource.activeCameraDataBuffer.GetRHIBuffer();
		cameraUpdateInfo.index = 0;
		cameraUpdateInfo.binding = GLORBAL_RESOURCE_BINDING_CAMERA;
		resource.descriptorSet->UpdateDescriptor(cameraUpdateInfo);

		cameraUpdateInfo.buffer = resource.defalutCameraDataBuffer.GetRHIBuffer();
		cameraUpdateInfo.index = 1;
		resource.descriptorSet->UpdateDescriptor(cameraUpdateInfo);
	}

	// Setting
	{
		RHIDescriptorUpdateInfo settingUpdateInfo = {};
		settingUpdateInfo.resourceType = RESOURCE_TYPE_RW_BUFFER;
		settingUpdateInfo.buffer = m_MultiFrameGlobalResources.globalSettingInfoBuffer.GetRHIBuffer();
		settingUpdateInfo.index = 0;
		settingUpdateInfo.binding = GLORBAL_RESOURCE_BINDING_SETTING;
		resource.descriptorSet->UpdateDescriptor(settingUpdateInfo);
	}

	// meshInstanceInfo
	{
		RHIDescriptorUpdateInfo meshInfoUpdateInfo = {};
		meshInfoUpdateInfo.resourceType = RESOURCE_TYPE_RW_BUFFER;
		meshInfoUpdateInfo.buffer = m_MultiFrameGlobalResources.meshInfoBuffer.GetRHIBuffer();
		meshInfoUpdateInfo.index = 0;
		meshInfoUpdateInfo.binding = GLORBAL_RESOURCE_BINDING_MESHINSTANCEINFO;
		resource.descriptorSet->UpdateDescriptor(meshInfoUpdateInfo);
	}

	// materialInfo
	{
		RHIDescriptorUpdateInfo materialInfoUpdateInfo = {};
		materialInfoUpdateInfo.resourceType = RESOURCE_TYPE_RW_BUFFER;
		materialInfoUpdateInfo.buffer = m_MultiFrameGlobalResources.materialBuffer.GetRHIBuffer();
		materialInfoUpdateInfo.index = 0;
		materialInfoUpdateInfo.binding = GLORBAL_RESOURCE_BINDING_MATERIALINFO;
		resource.descriptorSet->UpdateDescriptor(materialInfoUpdateInfo);
	}

	// MeshInfo
	{
		RHIDescriptorUpdateInfo vertexInfoUpdateInfo = {};
		vertexInfoUpdateInfo.resourceType = RESOURCE_TYPE_RW_BUFFER;
		vertexInfoUpdateInfo.buffer = m_MultiFrameGlobalResources.vertexBuffer.GetRHIBuffer();
		vertexInfoUpdateInfo.index = 0;
		vertexInfoUpdateInfo.binding = GLORBAL_RESOURCE_BINDING_MESHINFO;
		resource.descriptorSet->UpdateDescriptor(vertexInfoUpdateInfo);
	}

	// sampler
	{
		RHIDescriptorUpdateInfo samplerUpdateInfo = {};
		samplerUpdateInfo.resourceType = RESOURCE_TYPE_SAMPLER;
		samplerUpdateInfo.sampler = m_MultiFrameGlobalResources.samplers[0]->sampler;
		samplerUpdateInfo.index = 0;
		samplerUpdateInfo.binding = GLORBAL_RESOURCE_BINDING_BINDLESS_SAMPLER;
		resource.descriptorSet->UpdateDescriptor(samplerUpdateInfo);
		samplerUpdateInfo.sampler = m_MultiFrameGlobalResources.samplers[1]->sampler;
		samplerUpdateInfo.index = 1;
		samplerUpdateInfo.binding = GLORBAL_RESOURCE_BINDING_BINDLESS_SAMPLER;
		resource.descriptorSet->UpdateDescriptor(samplerUpdateInfo);
		samplerUpdateInfo.sampler = m_MultiFrameGlobalResources.samplers[2]->sampler;
		samplerUpdateInfo.index = 2;
		samplerUpdateInfo.binding = GLORBAL_RESOURCE_BINDING_BINDLESS_SAMPLER;
		resource.descriptorSet->UpdateDescriptor(samplerUpdateInfo);
		samplerUpdateInfo.sampler = m_MultiFrameGlobalResources.samplers[3]->sampler;
		samplerUpdateInfo.index = 3;
		samplerUpdateInfo.binding = GLORBAL_RESOURCE_BINDING_BINDLESS_SAMPLER;
		resource.descriptorSet->UpdateDescriptor(samplerUpdateInfo);
	}

	// lightInfo
	{
		RHIDescriptorUpdateInfo lightUpdateInfo = {};
		lightUpdateInfo.resourceType = RESOURCE_TYPE_RW_BUFFER;
		lightUpdateInfo.buffer = resource.lightInfoBuffer.GetRHIBuffer();
		lightUpdateInfo.index = 0;
		lightUpdateInfo.binding = GLORBAL_RESOURCE_BINDING_LIGHTINFO;
		resource.descriptorSet->UpdateDescriptor(lightUpdateInfo);
	}

	// gizmoDrawData
	{
		RHIDescriptorUpdateInfo gizmoUpdateInfo = {};
		gizmoUpdateInfo.resourceType = RESOURCE_TYPE_RW_BUFFER;
		gizmoUpdateInfo.buffer = resource.gizmoBuffer.GetRHIBuffer();
		gizmoUpdateInfo.index = 0;
		gizmoUpdateInfo.binding = GLORBAL_RESOURCE_BINDING_GIZMO;
		resource.descriptorSet->UpdateDescriptor(gizmoUpdateInfo);
	}
}
  1. 在RDG Build时绑定这个布局到Set=0
1
command->BindDescriptorSet(RENDER_RESOURCEMANAGER->GetGlobalResourcePerFrameDescriptorSet(), 0);

Bindless资源管理

申请一个对应bindless槽位内的ID,并绑定资源描述符集的对应binding的对应index

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
	uint32_t RenderResourceManager::AllocateBindlessID(const BindlessResourceInfo& resoruceInfo, BindlessSlot slot)
	{
		// 给这个资源分配一个ID
		uint32_t index = m_BindlessIDAlloctor[slot].Allocate();

		// 构建更新信息
		RHIDescriptorUpdateInfo updateInfo = {};
		updateInfo.binding = BindlessSlotToPerFrameBinding(slot);
		updateInfo.index = index;  // bindless数组的index
		updateInfo.resourceType = resoruceInfo.resourceType;
		updateInfo.buffer = resoruceInfo.buffer;
		updateInfo.textureView = resoruceInfo.textureView;
		updateInfo.sampler = resoruceInfo.sampler;
		updateInfo.bufferOffset = resoruceInfo.bufferOffset;
		updateInfo.bufferRange = resoruceInfo.bufferRange;

		// 实时更新会因为一些问题报错,暂时不更新本帧,只更新其他帧
		for (size_t i = 0; i < m_PerFrameGlobalResources.size(); ++i) {
			if (i == APP_FRAMEINDEX) {
				auto& resource = m_PerFrameGlobalResources[i];
				resource.isNeedUpdate = true;
				resource.updateInfos.push_back(updateInfo);
			}
			else {
				m_PerFrameGlobalResources[i].descriptorSet->UpdateDescriptor(updateInfo);
			}
		}

		return index;
	}

Mesh渲染架构(Bindless + 间接渲染 )

所有的Mesh相关数据,全部由Bindless管理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
enum BindlessSlot
{
	BINDLESS_SLOT_POSITION = 0,
	BINDLESS_SLOT_NORMAL,
	BINDLESS_SLOT_TANGENT,
	BINDLESS_SLOT_TEXCOORD,
	BINDLESS_SLOT_COLOR,
	BINDLESS_SLOT_BONE_INDEX,
	BINDLESS_SLOT_BONE_WEIGHT,
	BINDLESS_SLOT_ANIMATION,
	BINDLESS_SLOT_INDEX,

	BINDLESS_SLOT_SAMPLER,
	BINDLESS_SLOT_TEXTURE_1D,
	BINDLESS_SLOT_TEXTURE_1D_ARRAY,
	BINDLESS_SLOT_TEXTURE_2D,
	BINDLESS_SLOT_TEXTURE_2D_ARRAY,
	BINDLESS_SLOT_TEXTURE_CUBE,
	BINDLESS_SLOT_TEXTURE_3D,
	BINDLESS_SLOT_MAX_ENUM,     //
};

加载模型时,上传对应数据到对应槽位,并范围ID值,另外有一个Buffer专门存储所有顶点数据的各项(位置、法线、颜色)的ID,组成一个MeshInfo,也作为一个ArrayBuffer上传

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
 void VertexBuffer::SetBufferData(void* data, uint32_t size, RHIBufferRef& buffer, uint32_t& id, uint32_t slot)
 {
     if (size == 0) return;
     if (!buffer || buffer->GetInfo().size < size)  // 创建buffer
     {
         RHIBufferInfo rHIBufferInfo;
         rHIBufferInfo.type = RESOURCE_TYPE_RW_BUFFER | RESOURCE_TYPE_VERTEX_BUFFER;
         rHIBufferInfo.size = size;
         rHIBufferInfo.memoryUsage = MEMORY_USAGE_CPU_TO_GPU;
         rHIBufferInfo.creationFlag = BUFFER_CREATION_PERSISTENT_MAP;
         buffer = APP_DYNAMICRHI->CreateBuffer(rHIBufferInfo);

         if (id != 0) RENDER_RESOURCEMANAGER->ReleaseBindlessID(id, (BindlessSlot)slot);
         BindlessResourceInfo bindlessResourceInfo;
         bindlessResourceInfo.buffer = buffer;
         bindlessResourceInfo.resourceType = RESOURCE_TYPE_RW_BUFFER;
         bindlessResourceInfo.bufferOffset = 0;
         bindlessResourceInfo.bufferRange = size;

         id = RENDER_RESOURCEMANAGER->AllocateBindlessID(bindlessResourceInfo, (BindlessSlot)slot);
     }

     memcpy(buffer->Map(), data, size);

     RENDER_RESOURCEMANAGER->SetMeshInfo(vertexInfo, vertexID);
 }

另外每个Mesh实例也会有一个ID,用于存储这个实例对应的MeshInfo的ID以及材质的ID,也作为ArrayBuffer上传

所以每个实例在GPU层只是一些底层资源的ID集合(再加一个Model矩阵)

1
2
3
4
5
6
7
8
9
// Mesh的实例信息
typedef struct MeshInstanceInfo {
    glm::mat4 modelMatrix;
    glm::mat4 prevModelMatrix;
    uint32_t animationID;           //TODO:动画索引
    uint32_t materialID;
    uint32_t vertexID;
    uint32_t indexID;
}MeshInstanceInfo;

渲染时,DrawCall指定起始实例为对应的实例ID,在Shader内就能拿到这个ID,然后去全局资源中拿到关于这个实例的所有信息

1
2
3
4
5
6
7
8
9
for (auto& batch : meshBatch) { // 收集这个PSO的绘制命令到m_IndirectCommands(最终一并上传)
    RHIIndirectCommand meshDrawCommand;
    meshDrawCommand.firstInstance = batch.instanceID;   // 在Shader中通过这个拿到实例ID
    meshDrawCommand.vertexCount = batch.indexCount;
    meshDrawCommand.instanceCount = 1;
    meshDrawCommand.firstVertex = 0;

    m_IndirectCommands.push_back(meshDrawCommand);
}

这样设计下配合间接渲染,可以一口气上传所有实例的DrawCall,因为中间并不涉及顶点缓冲的切换

可以做到Pipeline一致的所有Mesh只需要一个DrawCall

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
void MeshPassProcessor::Draw(RHICommandListRef command) {
    for (auto& drawCommand : m_MeshDrawCommands)
    {
        auto [w, h] = APP_WINDOWSIZE;

        command->SetGraphicsPipeline(drawCommand.pipeline);

        if (drawCommand.meshCommandRange.size > 0)
        {
            command->DrawIndirect(
                m_MeshIndirectDrawDataBuffer[APP_FRAMEINDEX]->GetRHIBuffer(),
                4 * sizeof(uint32_t) + drawCommand.meshCommandRange.begin * sizeof(RHIIndirectCommand),
                drawCommand.meshCommandRange.size);
        }
    }
}

序列化

定义各种序列化规则 以及一些好用的宏

cereal库的使用就是定义好一个类或结构体的序列化和反序列化如何进行即可

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
#pragma once
#include <cereal/access.hpp>
#include <cereal/types/utility.hpp>
#include <cereal/types/string.hpp>
#include <cereal/types/polymorphic.hpp>
#include <cereal/types/vector.hpp>
#include <cereal/types/map.hpp>
#include <cereal/types/list.hpp>
#include <cereal/types/array.hpp>
#include <cereal/types/queue.hpp>
#include <cereal/archives/json.hpp>
#include <cereal/archives/binary.hpp>
#include <Hazel/Renderer/RHI/RHIBase.h>
namespace cereal {
	// 指定各种类型的序列化规则
	template<class Archive> void serialize(Archive& ar, GameEngine::Extent2D& e) { ar(cereal::make_nvp("width", e.width), cereal::make_nvp("height", e.height)); }
	template<class Archive> void serialize(Archive& ar, GameEngine::Extent3D& e) { ar(cereal::make_nvp("width", e.width), cereal::make_nvp("height", e.height), cereal::make_nvp("depth", e.depth)); }
    template<class Archive> void serialize(Archive& ar, glm::vec3& e) { ar(cereal::make_nvp("x", e.x), cereal::make_nvp("y", e.y), cereal::make_nvp("z", e.z)); }
    template<class Archive> void serialize(Archive& ar, glm::vec2& e) { ar(cereal::make_nvp("x", e.x), cereal::make_nvp("y", e.y)); }
    template<class Archive> void serialize(Archive& ar, glm::vec4& e) { ar(cereal::make_nvp("x", e.x), cereal::make_nvp("y", e.y), cereal::make_nvp("z", e.z),cereal::make_nvp("w", e.w)); }
    template<class Archive> void serialize(Archive& ar, glm::ivec4& e) { ar(cereal::make_nvp("x", e.x), cereal::make_nvp("y", e.y), cereal::make_nvp("z", e.z),cereal::make_nvp("w", e.w)); }
	template<class Archive> void serialize(Archive& ar, glm::uvec3& e) { ar(cereal::make_nvp("x", e.x), cereal::make_nvp("y", e.y), cereal::make_nvp("z", e.z)); }
	template<class Archive> void serialize(Archive& ar, glm::ivec3& e) { ar(cereal::make_nvp("x", e.x), cereal::make_nvp("y", e.y), cereal::make_nvp("z", e.z)); }
	template<class Archive> void serialize(Archive& ar, glm::quat& e) { ar(cereal::make_nvp("x", e.x), cereal::make_nvp("y", e.y), cereal::make_nvp("z", e.z), cereal::make_nvp("w", e.w)); }
	template<class Archive> void serialize(Archive& ar, std::filesystem::path& e) { ar(cereal::make_nvp("path", e.string())); }
    template<class Archive>
    void serialize(Archive& ar, glm::mat4& m)
    {
        ar(
            cereal::make_nvp("c0", m[0]),
            cereal::make_nvp("c1", m[1]),
            cereal::make_nvp("c2", m[2]),
            cereal::make_nvp("c3", m[3])
        );
    }

#define BeginSerailize               	\
friend class cereal::access;            	\
template<class Archive>                 	\
void serialize(Archive& ar)             	\
{
#define SerializeBaseClass(className) \
try { \
    ar(cereal::make_nvp(#className, cereal::base_class<className>(this))); \
} catch (const std::exception& e) { \
    LOG_WARN("{}: failed to serialize base class '{}', reason: {}", __FUNCTION__, #className, e.what()); \
}


#define SerailizeEntry(entry) \
try { \
    ar(cereal::make_nvp(#entry, entry)); \
} catch (const std::exception& e) { \
    LOG_WARN("{}: failed to serialize entry '{}', reason: {}", __FUNCTION__, #entry, e.what()); \
}


#define SerailizeComponent(COMPONENT_TYPE) \
if (HasComponent<COMPONENT_TYPE>()) { \
    ar(cereal::make_nvp("Has" #COMPONENT_TYPE, true)); \
    ar(cereal::make_nvp(#COMPONENT_TYPE, GetComponentConst<COMPONENT_TYPE>())); \
} else { \
    ar(cereal::make_nvp("Has" #COMPONENT_TYPE, false)); \
}

#define DeserializeComponent(COMPONENT_TYPE) \
do { \
    bool hasComponent_##COMPONENT_TYPE = false; \
    try { \
        ar(cereal::make_nvp("Has" #COMPONENT_TYPE, hasComponent_##COMPONENT_TYPE)); \
    } catch (const cereal::Exception&) { \
        hasComponent_##COMPONENT_TYPE = false; \
    } \
    \
    if (hasComponent_##COMPONENT_TYPE) { \
        AddComponent<COMPONENT_TYPE>(); \
        try { \
            ar(cereal::make_nvp(#COMPONENT_TYPE, GetComponent<COMPONENT_TYPE>())); \
        } catch (const cereal::Exception&) { \
            LOG_INFO("Old file missing data for {} component, using default values", #COMPONENT_TYPE); \
        } \
    } \
} while(0)

#define BeginIfSave    if constexpr (Archive::is_saving::value) {
#define EndIfSave }

#define BeginIfLoad    if constexpr (Archive::is_loading::value) {
#define EndIfLoad }

#define SerailizeAssetEntry(entry)          \
ar(cereal::make_nvp(#entry, entry));		\
if(entry) entry->OnLoadAsset();


#define EndSerailize }
	}


#define SerailizeAssetParent         ar(cereal::base_class<Asset>(this));

比如Model,每个被序列化的对象都必须有序列化规则,否则就会报错,而且还不好找是哪个不存在导致的报错。。。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
	class Model : public Asset {
    public:
        Model() = default;
		Model(std::string path, ModelSpec m_ModelSpec);
        void LoadFromFile(std::string path);
        virtual std::string GetAssetTypeName() override { return "Asset_Model"; }
        virtual AssetType GetAssetType() override { return ASSET_TYPE_MODEL; }
        virtual void OnLoadAsset() override;
        virtual void OnSaveAsset() override;
        bool hasBone() {return findBone;}
        std::vector<SubmeshData>& GetSubmeshes() { return submeshes; }
        SubmeshData& GetSubmeshData(uint32_t index) { return submeshes[index]; }
        MeshRef GetSubMesh(uint32_t index) { return submeshes[index].mesh; }
        std::vector<MaterialRef>& GetMaterials() { return materials; }
        MaterialRef GetMaterial(uint32_t index) { return materials[index]; }
        std::string GetPath() { return path; }
        VertexBufferRef GetVertexBuffer(int index){return submeshes[index].vertexBuffer;}
        IndexBufferRef GetIndexBuffer(int index){return submeshes[index].indexBuffer;}
    private:
        void ProcessNode(aiNode* node, const aiScene* scene, std::vector<aiMesh*>& processMeshes);
        void ProcessMesh(aiMesh* mesh, const aiScene* scene, int index);
        void ExtractBoneWeights(Mesh* submesh, aiMesh* mesh, const aiScene* scene);
        std::shared_ptr<Texture> Model::LoadMaterialTexture(std::string texturePath);
    private:
        std::string path;
        ModelSpec m_ModelSpec;
        uint64_t totalIndex = 0; 
        uint64_t totalVertex = 0;
        uint32_t totalClusterCnt = 0;
        uint32_t totalClusterMaxMip = 0;
        std::vector<SubmeshData> submeshes;
        std::vector<MaterialRef> materials;
        std::unordered_map<std::string, TextureRef> textureMap;

        bool findBone = false;
    private:
        BeginSerailize
            SerailizeAssetParent
            SerailizeEntry(path)
            SerailizeEntry(m_ModelSpec)
            SerailizeEntry(totalIndex)
            SerailizeEntry(totalVertex)
            SerailizeEntry(totalClusterCnt)
            SerailizeEntry(totalClusterMaxMip)
            SerailizeEntry(submeshes)
            SerailizeEntry(materials)
        EndSerailize
    };

如果序列化和反序列化操作不一致,比如要上传到GPU 用写好的BeginIfLoad BeginIfSave 来区分这是在加载还是在保存

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
 struct SubmeshData
 {
     std::shared_ptr<Mesh> mesh;                                
     VertexBufferRef vertexBuffer;
     IndexBufferRef indexBuffer;
     RHIBottomLevelAccelerationStructureRef blas;

     BeginSerailize
         SerailizeEntry(mesh)
      
         BeginIfLoad //加载时需要把顶点数据上传到GPU
         LOG_TRACE("  - Vertex Count: {}", mesh->position.size());
         LOG_TRACE("  - Index Count: {}", mesh->index.size());
         // 上传到GPU
         vertexBuffer = std::make_shared<VertexBuffer>();
         vertexBuffer->SetPosition(mesh->position);
         vertexBuffer->SetNormal(mesh->normal);
         vertexBuffer->SetTangent(mesh->tangent);
         vertexBuffer->SetTexCoord(mesh->texCoord);
         vertexBuffer->SetColor(mesh->color);
         vertexBuffer->SetBoneIndex(mesh->boneIndex);
         vertexBuffer->SetBoneWeight(mesh->boneWeight);
         vertexBuffer->SetBoundingBox(mesh->box);

         const MeshInfo& vi = vertexBuffer->vertexInfo;
         LOG_TRACE("  - Vertex Buffer Info:");
         LOG_TRACE("    positionID:    {}", vi.positionID);
         LOG_TRACE("    normalID:      {}", vi.normalID);
         LOG_TRACE("    tangentID:     {}", vi.tangentID);
         LOG_TRACE("    texCoordID:    {}", vi.texCoordID);
         LOG_TRACE("    colorID:       {}", vi.colorID);
         LOG_TRACE("    boneIndexID:   {}", vi.boneIndexID);
         LOG_TRACE("    boneWeightID:  {}", vi.boneWeightID);
         indexBuffer = std::make_shared<IndexBuffer>();
         indexBuffer->SetIndex(mesh->index);
         LOG_TRACE("    IndexBufferID: {}", indexBuffer->indexID);

         // RayTracing
         if (RENDER_ENABLE_RAY_TRACING) {
             RHIBottomLevelAccelerationStructureInfo blasInfo = {};
             blasInfo.vertexBuffer = vertexBuffer->positionBuffer;
             blasInfo.indexBuffer = indexBuffer->buffer;
             blasInfo.triangleCount = mesh->TriangleNum();

             blasInfo.vertexStride = sizeof(glm::vec3);
             blasInfo.indexOffset = 0;
             blasInfo.vertexOffset = 0;
             blas = APP_DYNAMICRHI->CreateBottomLevelAccelerationStructure(blasInfo);
         }
         EndIfLoad

     EndSerailize
 };

使用时比较麻烦的一点是如果一个类成员是第三方库,那就需要很多的trick来解决这个问题,比如Entity底层用的entt,那就很麻烦

序列化场景时就需要手动处理逻辑,而不能直接序列化Entity

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    template<class Archive>
    void serialize(Archive& ar)
    {
        if constexpr (Archive::is_saving::value)
        {
            ar(cereal::make_nvp("EntityCount", m_EntityIDMap.size())); 
            for (auto& [uuid, entity] : m_EntityIDMap)
            {
                ar(cereal::make_nvp("Entity", entity));
            }
        }
        else {
            m_EntityIDMap.clear();
            size_t entityCount = 0;
            ar(cereal::make_nvp("EntityCount", entityCount));
            for (size_t i = 0; i < entityCount; ++i) {
                Entity entity;
                ar(cereal::make_nvp("Entity", entity));
                m_EntityIDMap[entity.GetUUID()] = entity;
            }
        }

    }

基础功能

摄像机系统

分为两种模式 FPS和轨迹球

1
2
3
4
enum class CameraMode
{
    NONE, FLYCAM, ARCBALL
};

Tick函数,主要就是设计两种模式下如何响应鼠标移动和键盘移动来更新 Position 和 YawPitch(间接更新朝向)

另外m_IsCapturing设计是为了防止两次操作方向时鼠标位置不一致导致的屏幕突然旋转(感觉可以设计的更简洁。。。TODO)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
void EditorCamera::OnUpdate(const Timestep ts)
{
	bool isMouseInViewport = m_IsMouseInViewport;
	bool isButtonPressed = Input::IsMouseButtonDown(MouseButton::Right) || Input::IsMouseButtonDown(MouseButton::Middle) || (Input::IsMouseButtonDown(MouseButton::Left) && Input::IsKeyDown(KeyCode::LeftAlt));

	// 鼠标开始控制的第一帧,初始化Delta资源
	if (isButtonPressed && isMouseInViewport && !m_IsCapturing) {
		m_IsCapturing = true;
		m_InitialMousePosition = { Input::GetMouseX(), Input::GetMouseY() };
		m_YawDelta = 0.0f;
		m_PitchDelta = 0.0f;
		m_PositionDelta = glm::vec3(0.0f);
	}

	// 结束鼠标控制
	if (!isButtonPressed)
		m_IsCapturing = false;


	if (!m_IsCapturing)
	{
		EnableMouse();
	}else {
		// 鼠标控制中的一帧
		const glm::vec2 mouse{ Input::GetMouseX(), Input::GetMouseY() };
		const glm::vec2 delta = (mouse - m_InitialMousePosition) * 0.002f;
		if (Input::IsMouseButtonDown(MouseButton::Right) && !Input::IsKeyDown(KeyCode::LeftAlt))
		{
			m_CameraMode = CameraMode::FLYCAM;
			DisableMouse();

			const float yawSign = GetUpDirection().y < 0 ? -1.0f : 1.0f;
			const float speed = GetCameraSpeed();
			// 位置调整 Position
			if (Input::IsKeyDown(KeyCode::Q))
				m_PositionDelta -= ts.GetMilliseconds() * speed * glm::vec3{ 0.f, yawSign, 0.f };
			if (Input::IsKeyDown(KeyCode::E))
				m_PositionDelta += ts.GetMilliseconds() * speed * glm::vec3{ 0.f, yawSign, 0.f };
			if (Input::IsKeyDown(KeyCode::S))
				m_PositionDelta -= ts.GetMilliseconds() * speed * m_Direction;
			if (Input::IsKeyDown(KeyCode::W))
				m_PositionDelta += ts.GetMilliseconds() * speed * m_Direction;
			if (Input::IsKeyDown(KeyCode::A))
				m_PositionDelta -= ts.GetMilliseconds() * speed * m_RightDirection;
			if (Input::IsKeyDown(KeyCode::D))
				m_PositionDelta += ts.GetMilliseconds() * speed * m_RightDirection;

			// 旋转调整Direction
			constexpr float maxRate{ 0.12f };
			// Yaw * yawSign是为了相机倒置的时候,偏航角和鼠标的移动还是一致的
			m_YawDelta += glm::clamp(yawSign * delta.x * RotationSpeed(), -maxRate, maxRate);
			m_PitchDelta += glm::clamp(delta.y * RotationSpeed(), -maxRate, maxRate);
			m_RightDirection = glm::cross(m_Direction, glm::vec3{ 0.f, yawSign, 0.f });

			// 重新调整焦点
			const float distance = glm::distance(m_FocalPoint, m_Position);
			m_FocalPoint = m_Position + GetForwardDirection() * distance;
			m_Distance = distance;
		}
		else if (Input::IsKeyDown(KeyCode::LeftAlt))
		{
			m_CameraMode = CameraMode::ARCBALL;

			if (Input::IsMouseButtonDown(MouseButton::Middle))
			{
				DisableMouse();
				MousePan(delta);
			}
			else if (Input::IsMouseButtonDown(MouseButton::Left))
			{
				DisableMouse();
				MouseRotate(delta);
			}
			else if (Input::IsMouseButtonDown(MouseButton::Right))
			{
				DisableMouse();
				MouseZoom((delta.x + delta.y) * 0.1f);
			}
			else
				EnableMouse();
		}
		else
		{
			EnableMouse();
		}

		m_InitialMousePosition = mouse;
		m_Position += m_PositionDelta;
		m_Yaw += m_YawDelta;
		m_Pitch += m_PitchDelta;

		if (m_CameraMode == CameraMode::ARCBALL)
			m_Position = CalculatePosition();
		UpdateCameraView();
	}
}

好像别的基础功能也没啥需要专门记录的了。。。。

Licensed under CC BY-NC-SA 4.0
📚 文章数: 72 ✍️ 总字数: 245.55K