Skip to content

Archive

Category: Tech article
好久没写这样的系列了,上一次总结渲染的改进,还是4.5的时候。最近,又对KlayGE的渲染做了一些系统性的修改,所以在这里总结一下。 材质系统 原先的材质系统里,diffuse颜色和specular颜色是分开的。要放到GBuffer里的时候,specular颜色就剩下亮度,之后跟光的亮度操作后,从diffuse的颜色恢复出specular颜色。这只是一个情非得已的hack,因为G-Buffer里只有4个通道可供使用,放不下diffuse和specular颜色。 2015年的Physics and Math of Shading里,详细分析了导体、绝缘体、半导体的diffuse和specular分布,得到的结论是,只需要有个albedo的颜色(3通道)以及一个metalness(1通道),就已经足够。因为导体的diffuse是0,绝缘体的s ...
Win10 TH2 (10586)正式版已于11月12日发布,其SDK也在11月30日随着VS2015 Update 1正式发布。本来不觉得D3D/DXGI部分会有什么变化,毕竟没有任何正式宣布。但SDK里俨然出现了d3d11_4.h和dxgi1_5.h。换句话说,D3D 11.4和DXGI 1.5就这么悄悄发布了。 但是,到目前为止,不管是msdn还是网上搜索,都找不到没有任何资料。从头文件的内容来看,其实与其说是一个新版本,不如说是对以前的略微增强。下面我们就来看看到底变了什么。 D3D 11.4 d3d11_4.h里面,唯一增加的是定义了ID3D11Device4,包含两个新函数。 HRESULT RegisterDeviceRemovedEvent(HANDLE hEvent,  DWORD *pdwCookie); void UnregisterDeviceRemoved(DWORD dwCookie); 从 ...
自从Win8引入WinRT以来,主流开发者的说法一直是,C++/CX可以方便地把现有C++代码移植到WinRT。一般来说确实如此,绝大部分原有C++代码可以的保留,只需要写UI的部分就可以了。但因为C++/CX的引入,还是会带来一些副作用,不完全都是好处。本篇将以KlayGE为例,讲解如何在不用C++/CX的情况下完成WinRT应用(包括UWP)。 C++/CX的功与过 C++/CX使用起来挺方便,语法和原先的C++/CLR极其相似,但是native的,没有虚拟机执行开销。任何懂C++的人都可以很快上手,把程序移植到WinRT。同时,WinRT背后一大套COM机制都被隐藏掉了,可以直接用普通的类一样使用COM对象。 但是,仔细观察就能发现,在C++项目中加入C++/CX有几个缺点。 C++/CX ...
本系列前面的五篇已经让引擎可以在D3D12下跑通所有的例子。但这并不代表功能已经齐全。其中很多例子能跑通的原因,是因为例子本身有fall back的代码,允许在没有各种高级功能的情况下运行。Compute shader就是其中的一个。本篇将会讲解如何加入compute shader,以及在此过程中遇到的一个大坑。 计算引擎 和以前的API不同的是,D3D12是个多引擎的API,可以让硬件的不同的独立部分异步执行,以提高效率。D3D12里的引擎有,渲染引擎、计算引擎和拷贝引擎。这三个引擎有不同的指令队列,可以在程序的控制下并行执行和互相同步。所以,在D3D12里,推荐的做法是,渲染和计算分开,放到不同引擎执行。所以虽然compute shader也可以在渲染引擎执行 ...
经过前面的练习,实现query已经没什么难的了。 Query heap 在D3D12里,query也是放在heap中。但和其他heap不同的是,query heap与其说是heap,不如说是array。它并不需要设置给设备,而是相当于一次可以提交多个query。Query heap的数量不限,所以为了简单实现,可以给每个query建立一个query heap。 D3D12_QUERY_HEAP_DESC query_heap_desc; query_heap_desc.Type = D3D12_QUERY_HEAP_TYPE_OCCLUSION; query_heap_desc.Count = 1; query_heap_desc.NodeMask = 0; ID3D12QueryHeap* query_heap; TIF(device->CreateQueryHeap(&query_heap_desc, IID_ID3D12QueryHeap, reinterpret_cast<void**>(&query_heap))); ...
上一篇把完成了一个最基本的D3D12程序,画一个三角形。同时我也说了,没有回头路。本篇将开始从11on12转向纯D3D12。 上一篇我们的假设假设是最基本的系统,关掉所有post process、UI、文字,就渲染一个三角形。这样的系统至少需要一个vertex buffer、一个rtv、一个vs、一个ps、一次clear、一次draw call。进一步的发展需要一个稍微复杂的系统,有文字和UI。也就是还需要一个index buffer、一个cbv、一个srv、一个sampler。Index buffer和vertex buffer的构建没区别,所以就是cbv、srv和sampler的事情。 在此之前,需要先介绍两个D3D12的概念,heap和root signature。因为CBV/SRV/UAV/Sampler都需要依赖于这两个。 Heap Heap是D3D12新 ...
上一篇我们讲了如何建立D3D12的设备,并在其之上建立出11on12的设备。接下去就要开始一步一步转移到纯D3D12下了。 第一个应该转的是相对独立的资源,包括buffer和texture。建立D3D12的资源,之后用前文说的CreateWrappedResource转成D3D11的资源,继续交给D3D11on12渲染就可以了。这样仍然可以往前走一小步,保证引擎还能工作。 Buffer Buffer包括vertex buffer、index buffer和constant buffer。 D3D12_HEAP_PROPERTIES heap_prop; heap_prop.Type = D3D12_HEAP_TYPE_UPLOAD; heap_prop.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; heap_prop.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; heap_prop.CreationNode ...
多年前我写过编译期字符串Hash和再探编译期字符串Hash两篇博文,分别证明了C++98下无法实现编译期的字符串hash,以及如何在C++11下用constexpr实现。过了这么多年,原有的实现在Clang上出现了严重的编译性能下降,需要一些修改才能顺利编译。而vc14也开始支持constexpr了,经过实验,发现问题仍很严重。所以这里不得不再次试着改进编译期字符串hash的方法。 旧方法回顾 上次的实现用了constexpr配合模板嵌套,实现了一个初步的编译期计算字符串hash的方法。 constexpr size_t _Hash(const char (&str)[1]) { return *str + 0x9e3779b9; } template <size_t N> constexpr size_t _Hash(const char (&str)[N]) { ...
这几年Intel和AMD都推出了集成GPU的消费级CPU,并强调它们的内存是共享的架构,也就是UMA(Unified Memory Architeture)。最近AMD和NVIDIA的独立显卡也加入战团,开始逐步支持UMA。最新的D3D12直接内置了UMA的支持,开发者可以让自己的程序充分利用上UMA所带来的优势。那么UMA能带来什么好处?它的限制在哪里? 各种平台的状况 自从PC上第一块GPU,NVIDIA Geforce 256问世以来,GPU一直都是自带一块显存的。当年的AGP总线是非对称的设计,数据传到GPU要远快于从GPU读回。CPU和GPU的访存是完全分开的。后来的PCIE总线让两端传输的速度相等了,并提供了一些相互访问的能力。在驱动里,system memory的区域可以映射成可以被GPU访问的,反过来 ...
在游戏引擎里,每一帧都可能有UI和文字的渲染。这些东西的特点是,琐碎,随机,但每一部分的数据量很小。比如UI由很多矩形块组成,每个只有4个顶点。这样的数据对GPU来说是很头疼的。所以引擎往往需要在Buffer上做一些工作来改善渲染的性能。 由于在目前常见的架构上,CPU和GPU不能同时读写一块内存,CPU在写入数据的时候GPU只能读取另一个地方来渲染。所以一定需要某个机制,来避免这样的冲突。 常见方法1:Discard 最古老的一个做法就是,自己维护一块内存,每一次需要画东西的时候先放在那块内存中。每一帧用一次discard的方式对GPU buffer做一次map,把数据拷贝进去。这么做很简单,所有复杂的同步都交给驱动去完成。 在内部,di ...