• Step 1 搭建一个简单的渲染框架


    Step 1 搭建一个简单的渲染框架

    万事开头难。从萌生到自己到处看源码手抄一个mini engine出来的想法,到真正敲键盘去抄,转眼过去了很久的时间。这次大概的确是抱着认真的想法,打开VS从零开始抄代码。不知道能坚持多久呢。。。

    本次的主题是搭一个简单的渲染框架。这里我们先假定要使用的底层图形API为DX12,当然代码设计上渲染和API层面肯定是要解耦的,如果有一天去抄OpenGL或是Vulcan的代码的时候,总不可能要重写已有的逻辑。另外,由于DX12的初始化逻辑中需要HWND类型的窗口句柄,所以这里也就需要引入原生的Windows API来绘制窗口了。当然同样的道理,窗口系统与绘制的API也没有什么耦合关系。

    那么先从Main函数写起,它是最简单的,只做初始化和运行两件事情:

    int main()
    {
    	EngineLoop loop(hInstance);
    	if (!loop.Initialize())
    		return 0;
    
    	return loop.Run();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    EngineLoop类里目前包含我们这里需要的窗口系统和图形系统。初始化的逻辑是有顺序的,这里我们先初始化窗口系统,再初始化图形系统:

    bool EngineLoop::Initialize()
    {
        m_pWindowSystem = MakeShared();
        if (!m_pWindowSystem->Initialize(windowSystemCfg))
        {
            return false;
        }
    
        m_pGraphicsSystem = MakeShared();
        if (!m_pGraphicsSystem->Initialize(graphicsSystemCfg))
        {
            return false;
        }
        return true;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    我们用智能指针shared_ptr管理这些System。这样它们的生命周期就和EngineLoop保持一致,不用担心析构的时候忘记释放它们。

    为了把创建绘制窗口的API与窗口本身的逻辑分开,窗口系统持有一个LowLevelWindow抽象类的指针。通过这个指针去真正地创建窗口,绘制窗口,以及响应窗口的事件等等:

    bool WindowSystem::Initialize(const WindowSystemCfg& cfg)
    {
        m_pWindow = MakeShared();
    
        if (!m_pWindow->Initialize(windowCfg))
        {
            return false;
        }
        return true;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    同样图形系统也是,我们使用RHI抽象类的指针来真正跟图形硬件打交道:

    bool GraphicsSystem::Initialize(const GraphicsSystemCfg& cfg)
    {
        m_pRHI = MakeShared();
    
        if (!m_pRHI->Initialize(rhiCfg))
        {
            return false;
        }
        return true;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    对于DX12来说,那么就有一个D3D12RHI的子类啦。目前我们先不考虑渲染任何东西,只是把初始化的工作做掉,那需要哪些东西呢?

    首先IDXGIFactory和ID3D12Device这两货肯定是需要的,如果没有它们,整个初始化逻辑就没法跑;IDXGISwapChain也是必要的,不然连back buffer都没有;然后我们需要使用绘制指令来进行各种底层操作,那就需要ID3D12CommandQueue,ID3D12CommandAllocator和ID3D12GraphicsCommandList这三剑客了。ID3D12CommandQueue是指令的执行者,它可以包含多个command list;command allocator是存储指令的数据结构,command list里记录的指令实际上是保存到这里。另外,由于GPU指令的执行对CPU来说是异步的,因此还需要一个ID3D12Fence用于同步。我们使用ComPtr来管理这些类,ComPtr对象当引用计数为0时,会自动调用Release接口,从而避免内存泄漏。

    class D3D12RHI : public RHI
    {
    private:
    
        ComPtr m_pDevice;
        ComPtr m_pDxGiFactory;
        ComPtr m_pFence;
        ComPtr m_pCommandQueue;
        ComPtr m_pDirectCmdListAlloc;
        ComPtr m_pCommandList;
        ComPtr m_pSwapChain;
    };
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    DX12的资源和view是分开的,一个资源可以对应多种view,这里的view以D3D12_CPU_DESCRIPTOR_HANDLE来区分。一个资源每使用一个view,就需要往ID3D12DescriptorHeap申请一个空闲的handle。那么我们可以把这个过程抽象一下,封装一个D3D12DescriptorHeap类,它负责分配空闲的handle给申请者:

    class D3D12DescriptorHeap
    {
    public:
        D3D12DescriptorHeap(ID3D12Device* pDevice, D3D12_DESCRIPTOR_HEAP_TYPE type, UInt32 numDescriptors);
        void Initialize();
        D3D12_CPU_DESCRIPTOR_HANDLE Allocate();
    
    private:
        ID3D12Device* m_pDevice = nullptr;
        D3D12_DESCRIPTOR_HEAP_TYPE m_type;
        UInt32 m_numDescriptors = 0;
        UInt32 m_descriptorSize = 0;
        UInt32 m_remainingFreeHandles;
        CD3DX12_CPU_DESCRIPTOR_HANDLE m_cpuHandle;
        ComPtr m_pDescriptorHeap;
    };
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    初始化过程中我们需要创建若干back buffer和一个depth buffer,这两个buffer的创建方式有区别,但它们本质上都属于资源,因此给它们各自一个类,然后共同继承D3D12Resource这个类,这个类包含一些对资源的通用操作。

    class D3D12Resource : public D3D12RHIChild
    {
    public:
        D3D12Resource(D3D12RHI* pRHI) : D3D12RHIChild(pRHI), m_state(D3D12_RESOURCE_STATE_COMMON) {}
        void SetState(D3D12_RESOURCE_STATES state);
    
    protected:
        D3D12_RESOURCE_STATES m_state;
        ComPtr m_pResource;
    };
    
    class D3D12BackBuffer : public D3D12Resource
    {
    public:
        D3D12BackBuffer(D3D12RHI* pRHI) : D3D12Resource(pRHI), m_rtvHandle() {}
        void Initialize(Int32 index);
        void Clear(const Color& color);
        D3D12_CPU_DESCRIPTOR_HANDLE GetRtvHandle() const { return m_rtvHandle; }
    private:
        D3D12_CPU_DESCRIPTOR_HANDLE m_rtvHandle;
    };
    
    class D3D12DepthBuffer : public D3D12Resource
    {
    public:
        D3D12DepthBuffer(D3D12RHI* pRHI) : D3D12Resource(pRHI), m_dsvHandle() {}
        void Initialize(Int32 clientWidth, Int32 clientHeight, DXGI_FORMAT format);
        void Clear(D3D12_CLEAR_FLAGS clearFlags, Float depth, UInt8 stencil);
        D3D12_CPU_DESCRIPTOR_HANDLE GetDsvHandle() const { return m_dsvHandle; }
    
    private:
        D3D12_CPU_DESCRIPTOR_HANDLE m_dsvHandle;
    };
    
    
    • 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

    初始化流程完毕之后,我们就要准备update了。现阶段我们啥也不做,就准备一下渲染环境吧。我们在RHI类中添加了PrepareRender和FinishRender两个抽象接口,分别表示准备渲染以及完成渲染提交显示的逻辑。对于DX12来说,在渲染前/后要准备哪些事情呢?

    渲染前,首先是清空command,让command相关的数据结构保证可用;然后获取当前要渲染的back buffer,设置其状态为D3D12_RESOURCE_STATE_RENDER_TARGET;然后对back buffer和depth buffer执行clear操作,最后提交给硬件。在渲染结束之后,同样我们要把当前back buffer的状态切回渲染前的,如果是多缓冲要切到下一个可用的back buffer,执行掉中间产生的所有渲染指令,显示到屏幕上。

    自此,一个最简单的渲染框架就搭好了,运行起来也就是一个填充满clear color的窗口,并没有什么稀奇,然而背后的代码量却有数百行了。

    在这里插入图片描述

  • 相关阅读:
    初识TOML-配置文件格式
    视频怎么消除人声?一款视频去人声软件,轻松去除视频人声
    PHP指的是什么 PhP程序语言基本注意事项
    nginx php-fpm swoole
    Javaweb之request对象
    【Web】在前端中CSS的语法
    使用ssh连接被拒
    lvs(Linux virual server)
    EtherNet/IP转profienrt协议网关连接EtherNet/IP协议的川崎机器人配置方法
    CleanMyMac X4.11版本MAC电脑系统清理软件大更新
  • 原文地址:https://blog.csdn.net/weixin_45776473/article/details/133826983