当前位置: 首页 > news >正文

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();
}

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

bool EngineLoop::Initialize()
{m_pWindowSystem = MakeShared<WindowSystem>();if (!m_pWindowSystem->Initialize(windowSystemCfg)){return false;}m_pGraphicsSystem = MakeShared<GraphicsSystem>();if (!m_pGraphicsSystem->Initialize(graphicsSystemCfg)){return false;}return true;
}

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

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

bool WindowSystem::Initialize(const WindowSystemCfg& cfg)
{m_pWindow = MakeShared<WindowsAPIWindow>();if (!m_pWindow->Initialize(windowCfg)){return false;}return true;
}

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

bool GraphicsSystem::Initialize(const GraphicsSystemCfg& cfg)
{m_pRHI = MakeShared<D3D12RHI>();if (!m_pRHI->Initialize(rhiCfg)){return false;}return true;
}

对于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<ID3D12Device> m_pDevice;ComPtr<IDXGIFactory4> m_pDxGiFactory;ComPtr<ID3D12Fence> m_pFence;ComPtr<ID3D12CommandQueue> m_pCommandQueue;ComPtr<ID3D12CommandAllocator> m_pDirectCmdListAlloc;ComPtr<ID3D12GraphicsCommandList> m_pCommandList;ComPtr<IDXGISwapChain> m_pSwapChain;
};

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<ID3D12DescriptorHeap> m_pDescriptorHeap;
};

初始化过程中我们需要创建若干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<ID3D12Resource> 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;
};

初始化流程完毕之后,我们就要准备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的窗口,并没有什么稀奇,然而背后的代码量却有数百行了。

在这里插入图片描述

相关文章:

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

Step 1 搭建一个简单的渲染框架 万事开头难。从萌生到自己到处看源码手抄一个mini engine出来的想法&#xff0c;到真正敲键盘去抄&#xff0c;转眼过去了很久的时间。这次大概的确是抱着认真的想法&#xff0c;打开VS从零开始抄代码。不知道能坚持多久呢。。。 本次的主题是搭…...

Excel 插入和提取超链接

构造超链接 HYPERLINK(D1,C1)提取超链接 Sheet页→右键→查看代码Sub link()Dim hl As HyperlinkFor Each hl In ActiveSheet.Hyperlinkshl.Range.Offset(0, 1).Value hl.AddressNext End Sub工具栏→运行→运行子过程→提取所有超链接地址参考&#xff1a; https://blog.cs…...

基础架构开发-操作系统、编译器、云原生、嵌入式、ic

基础架构开发-操作系统、编译器、云原生、嵌入式、ic 操作系统编译器词法分析AST语法树生成语法优化生成机器码 云原生容器开发一般遇到的岗位描述RDMA、DPDK是什么东西NFV和VNF是什么RisingWave云原生存储引擎开发实践 单片机、嵌入式雷达路线规划 ic开发 操作系统 以C和Rust…...

C++-Mongoose(3)-http-server-https-restful

1.url 结构 2.http和 http-restful区别在于对于mg_tls_opts的赋值 2.1 http和https 区分 a) port地址 static const char *s_http_addr "http://0.0.0.0:8000"; // HTTP port static const char *s_https_addr "https://0.0.0.0:8443"; // HTTP…...

git多分支、git远程仓库、ssh方式连接远程仓库、协同开发(避免冲突)、解决协同冲突(多人在同一分支开发、 合并分支)

1 git多分支 2 git远程仓库 2.1 普通开发者&#xff0c;使用流程 3 ssh方式连接远程仓库 4 协同开发 4.1 避免冲突 4.2 协同开发 5 解决协同冲突 5.1 多人在同一分支开发 5.2 合并分支 1 git多分支 ## 命令操作分支-1 创建分支git branch dev-2 查看分支git branch-3 分支合…...

ChatGPT或将引发现代知识体系转变

作为当下大语言模型的典型代表&#xff0c;ChatGPT对人类学习方式和教育发展所产生的变革效应已然引起了广泛关注。技术的快速发展在某种程度上正在“倒逼”教育领域开启更深层次的变革。在此背景下&#xff0c;教育从业者势必要学会准确识变、科学应变、主动求变、以变应变&am…...

【爬虫实战】用pyhon爬百度故事会专栏

一.爬虫需求 获取对应所有专栏数据&#xff1b;自动实现分页&#xff1b;多线程爬取&#xff1b;批量多账号爬取&#xff1b;保存到mysql、csv&#xff08;本案例以mysql为例&#xff09;&#xff1b;保存数据时已存在就更新&#xff0c;无数据就添加&#xff1b; 二.最终效果…...

焦炭反应性及反应后强度试验方法

声明 本文是学习GB-T 4000-2017 焦炭反应性及反应后强度试验方法. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 7— 进气口&#xff1b; 8— 测温热电偶。 图 A.1 单点测温加热炉体结构示意图 A.3 温度控制装置 控制精度&#xff1a;(11003)℃。…...

链表(3):双链表

引入 我们之前学的单向链表有什么缺点呢&#xff1f; 缺点&#xff1a;后一个节点无法看到前一个节点的内容 那我们就多设置一个格子prev用来存放前面一个节点的地址&#xff0c;第一个节点的prev存最后一个节点的地址&#xff08;一般是null&#xff09; 这样一个无头双向…...

【TES720D】基于复旦微的FMQL20S400全国产化ARM核心模块

TES720D是一款基于上海复旦微电子FMQL20S400的全国产化核心模块。该核心模块将复旦微的FMQL20S400&#xff08;兼容FMQL10S400&#xff09;的最小系统集成在了一个50*70mm的核心板上&#xff0c;可以作为一个核心模块&#xff0c;进行功能性扩展&#xff0c;特别是用在控制领域…...

Python 列表切片陷阱:引用、复制与深复制

大家早好、午好、晚好吖 ❤ ~欢迎光临本文章 如果有什么疑惑/资料需要的可以点击文章末尾名片领取源码 Python 列表的切片和赋值操作很基础&#xff0c;之前也遇到过一些坑&#xff0c; 但今天刷 Codewars 时发现了一个更大的坑&#xff0c;故在此记录。 Python 列表赋值&am…...

macbook电脑删除app怎么才能彻底清理?

macBook是苹果公司推出的一款笔记本电脑&#xff0c;它的操作系统是macOS。在macBook上安装的app可能会占用大量的存储空间&#xff0c;因此&#xff0c;当我们不再需要某个app时&#xff0c;需要将其彻底删除。macbook删除app&#xff0c;怎么才能彻底呢&#xff1f;本文将给大…...

【数据结构】二叉树--链式结构的实现 (遍历)

目录 一 二叉树的遍历 1 构建一个二叉树 2 前序遍历 3 中序遍历 4 后续遍历 5 层序 6 二叉树销毁 二 应用(递归思想) 1 二叉树节点个数 2 叶子节点个数 3 第K层的节点个数 4 二叉树查找值为x的节点 5 判断是否是二叉树 一 二叉树的遍历 学习二叉树结构&#xff0…...

reids基础数据结构

文章目录 一.整体1.RedisDb2.对象头 二.string三.list1.ziplist2.quicklist 四.hash五.set六.zset1.查找2.插入3.删除4.更新5.元素排名 一.整体 1.RedisDb redis内部的所有键值对是两个hash结构&#xff0c;维护了键值对和过期时间 dict *dictdict *expire 2.对象头 int t…...

gitlab 维护

一 环境信息 二 日常维护 2.1 gitlab mirror 2.1.1 常见方法 社区版本gitab mirror 只能push&#xff0c;默认限制了局域网内mirror 需要修改admin/setting/network(网络)/outbound(出站请求) 勾选允许局域网即可。 2.1.2 疑难问题 内网有三个gitlab A: GITLAB 12 B\C GI…...

ABB机器人RWS连接方法

目录 方法一&#xff1a;curl 方法二&#xff1a;网页地址 方法三&#xff1a;Postman 与ABB机器人通讯&#xff0c;较新机器人&#xff0c;可以使用Robot Web Services&#xff0c;直接方便地使用网页进行查看当前数据&#xff0c;但是网页需要用户名密码验证&#xff0c;测…...

Spring Boot的循环依赖问题

目录 1.循环依赖的概念 2.解决循环依赖的方法 1.构造器方法注入&#xff1a; 2.Lazy注解 3.DependsOn注解 1.循环依赖的概念 两个或多个bean之间互相依赖&#xff0c;形成循环&#xff0c;此时&#xff0c;Spring容器无法确定先实例化哪个bean&#xff0c;导致循环依赖的…...

postgresql|数据库|恢复备份的时候报错:pg_restore: implied data-only restore的处理方案

一&#xff0c; 前情回顾 某次在使用pg_dump命令逻辑备份出来的备份文件对指定的几个表恢复的时候&#xff0c;报错pg_restore: implied data-only restore 当然&#xff0c;遇到问题首先就是百度了&#xff0c;但好像没有什么明确的解决方案&#xff0c;具体的报错命令和…...

Elasticsearch:使用 Langchain 和 OpenAI 进行问答

这款交互式 jupyter notebook 使用 Langchain 将虚构的工作场所文档拆分为段落 (chunks)&#xff0c;并使用 OpenAI 将这些段落转换为嵌入并将其存储到 Elasticsearch 中。然后&#xff0c;当我们提出问题时&#xff0c;我们从向量存储中检索相关段落&#xff0c;并使用 langch…...

安全巡检管理系统—隐患排查治理

安全管理越来越重要&#xff0c;每个生产企业都需要一个安全隐患排查治理小程序&#xff01;利用凡尔码平台搭建安全巡检管理系统主要有以下四个功能 1、制定巡检计划&#xff1a;安全巡检管理系统可以帮助用户制定巡检计划&#xff0c;用户可以根据需要创建不同的计划&#xf…...

Spark 之 入门讲解详细版(1)

1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室&#xff08;Algorithms, Machines, and People Lab&#xff09;开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目&#xff0c;8个月后成为Apache顶级项目&#xff0c;速度之快足见过人之处&…...

VB.net复制Ntag213卡写入UID

本示例使用的发卡器&#xff1a;https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》

引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...

Ascend NPU上适配Step-Audio模型

1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统&#xff0c;支持多语言对话&#xff08;如 中文&#xff0c;英文&#xff0c;日语&#xff09;&#xff0c;语音情感&#xff08;如 开心&#xff0c;悲伤&#xff09;&#x…...

GO协程(Goroutine)问题总结

在使用Go语言来编写代码时&#xff0c;遇到的一些问题总结一下 [参考文档]&#xff1a;https://www.topgoer.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/goroutine.html 1. main()函数默认的Goroutine 场景再现&#xff1a; 今天在看到这个教程的时候&#xff0c;在自己的电…...

【51单片机】4. 模块化编程与LCD1602Debug

1. 什么是模块化编程 传统编程会将所有函数放在main.c中&#xff0c;如果使用的模块多&#xff0c;一个文件内会有很多代码&#xff0c;不利于组织和管理 模块化编程则是将各个模块的代码放在不同的.c文件里&#xff0c;在.h文件里提供外部可调用函数声明&#xff0c;其他.c文…...

Redis上篇--知识点总结

Redis上篇–解析 本文大部分知识整理自网上&#xff0c;在正文结束后都会附上参考地址。如果想要深入或者详细学习可以通过文末链接跳转学习。 1. 基本介绍 Redis 是一个开源的、高性能的 内存键值数据库&#xff0c;Redis 的键值对中的 key 就是字符串对象&#xff0c;而 val…...

【QT控件】显示类控件

目录 一、Label 二、LCD Number 三、ProgressBar 四、Calendar Widget QT专栏&#xff1a;QT_uyeonashi的博客-CSDN博客 一、Label QLabel 可以用来显示文本和图片. 核心属性如下 代码示例: 显示不同格式的文本 1) 在界面上创建三个 QLabel 尺寸放大一些. objectName 分别…...

轻量安全的密码管理工具Vaultwarden

一、Vaultwarden概述 Vaultwarden主要作用是提供一个自托管的密码管理器服务。它是Bitwarden密码管理器的第三方轻量版&#xff0c;由国外开发者在Bitwarden的基础上&#xff0c;采用Rust语言重写而成。 &#xff08;一&#xff09;Vaultwarden镜像的作用及特点 轻量级与高性…...

【笔记】结合 Conda任意创建和配置不同 Python 版本的双轨隔离的 Poetry 虚拟环境

如何结合 Conda 任意创建和配置不同 Python 版本的双轨隔离的Poetry 虚拟环境&#xff1f; 在 Python 开发中&#xff0c;为不同项目配置独立且适配的虚拟环境至关重要。结合 Conda 和 Poetry 工具&#xff0c;能高效创建不同 Python 版本的 Poetry 虚拟环境&#xff0c;接下来…...