DX学习笔记之Surfaces

声明:本文完全翻译自DX SDK Documentation

一个surface代表了显存中的一块线性区域,通常在显卡的显存中,尽管surface也可以存在系统内存中。surface是被IDirect3DSurface9接口管理的。

 1. front buffer:一块矩形内存,会被graphics adapter翻译并在显示屏上显示。在D3D中,程序从来不会直接往front buffer上写的。
 2. back buffer:一块矩形内存,程序可以直接写;它永远不会直接被显示到显示器上。
 3. flipping surface: 将back buffer移到front buffer的过程。
 4. swap chain: 一组,一个或多个,back buffer,可以被顺序地present到front buffer上。

Getting a Surface

可以通过调用如下方法,创建一个surface:
IDirect3DDevice9::CreateDepthStencilSurface
IDirect3DDevice9::CreateOffscreenPlainSurface
IDirect3DDevice9::CreateRenderTarget

surface format决定了surface内存中的pixel数据是如何解析的。D3D使用结构体D3DSURFACE_DESC中的D3DFORMAT成员来描述surface format。我们可以查看某一个存在surface的格式通过调用IDirect3DSurface9::GetDesc方法。

一旦一个surface已经创建,可以调用如下方法来获得它的指针:
IDirect3DCubeTexture9::GetCubeMapSurface
IDirect3DDevice9::GetBackBuffer
IDirect3DDevice9::GetDepthStencilSurface
IDirect3DDevice9::GetFrontBufferData
IDirect3DDevice9::GetRenderTarget
IDirect3DDevice9::GetBackBuffer
IDirect3DDevice9::GetSurfaceLevel

IDirect3DDevice9接口允许我们直接访问内存,通过IDirect3DDevice9::UpdateSurface方法。该方法允许我们将一个IDirect3DDevice9 surface的矩形区域拷贝到另一个IDirect3DDevice9 surface。surface接口同样提供直接访问显存的方法。例如,你可以使用IDirect3DDevice9::LockRect方法锁住显存上的一块矩形区域。千万别忘记调用IDirect3DDevice9::UnlockRect解锁surface上的那块区域,当你完成工作后。

Surface Format

pixel的类型用枚举类型D3DFORMAT来表示。例如D3DFMT_R5G6B5,5位给red,6为给green,5位给blue。

What is a Swap Chain

显卡hold一个指针,指向一个正在被显示到显示器上的surface,叫做front buffer。当显示器刷新的时候,显卡就会将front buffer的内容送到显示器上去显示。然后,这可能会导致一个问题,跟计算机的其它部件相比,显示器的刷新频率非常低,60~100hz。如果你的程序正在更新front buffer,而这时显示器正在刷新,或许用户会看到一个撕扯的画面,一半新的,一半旧的。

D3D提供两种方法来解决这个问题:

 1. 我们在显示器做vertical retrace操作时,做front buffer的更新。显示器典型的刷新就从左上角到右下角逐行扫描的,在扫描完后,会有一个vertical retrace操作,即从右下角回到左上角去,在这个操作期间,我们更新front buffer是不会出现问题的。vertical retrace的操作是比较慢的,但是,不够慢到可以等待我们渲染一个非常大的场景,因此我们还需要一个新的机制,back buffering。

 2. back buffering就是将场景画到off-screen的surface上,及back buffer。通过使用back buffer,我们就无需担心显示器的刷新频率了,程序可以决定什么时候将back buffer 移动到front buffer去。

移动back buffer到front buffer的过程叫做surface flipping。因为,surface都是用指针指着的,所以这个过程只需做一个指针改变而已。当程序命令D3D将back buffer移动到front buffer去时,D3D其实就做了一个flip pointer的动作。D3D一般会将flip的请求放到一个queue里,等待vertical retrace的发生。surface flipping非常重要,在多媒体,动画,游戏中;它等价于迅速翻一本漫画书,看到动画的意味。

Width vs. Pitch

D3D用结构体D3DSURFACE_DESC来描述surface,其中包括了surface的大小(宽跟高,单位是pixel),以及在内存中的表示。但我们使用IDirect3DSurface9::LockRect方法时,描述结构体D3DLOCKED_RECT里是用pitch来形容surface的。pitch值描述了surface的pitch,俗称stride,它表示图片中的一行(值就是两个内存地址的差,图片某行的开始,跟图片下行的开始),单位是byte。pitch的变动很大,它往往不仅仅是宽度×pixel的bytes,还有cache(pitch的值可能是照片被cache那部分的bytes总和),对其等因素。所以我们一定要使用IDirect3DSurface9::LockRect方法返回的ptich值,不要自己擅自算一个自以为是正确的pitch值。

Flipping Surfaces

每一个设备至少有一个swap chain。当初始化D3D设备时,设置D3DPRESENT_PARAMETERS中的BackBufferCount值来告诉D3D我们swap chain中back buffer的数量。然后调用IDirect3D9::CreateDevcie来创建设备及swap chain。

我们调用IDirect3DDevcie9::Prensent时,会发出surface flip请求。front buffer跟其他back buffer间的flipping是一个循环的模式。

当程序是多视图时,我们可以用IDirect3DDevice9::CreateAddtitionalSwapChain来创建额外的swap chain,然后每一个swap chain结合一个视图窗口。在调用该函数的时候,需要两个参数,一个是D3DPRESENT_PARAMETERS,另一个是IDirect3DSwapChain9接口的指针。然后,我们就可以直接调用IDirect3DSwapChain9::Present了。

我们可以访问某一个特定的back buffer,通过调用IDirect3DSwapChain9::GetBackBuffer或者IDirect3DDevcie9::GetBackBuffer,它会返回IDirect3DSurface接口指针,代表back buffer surface。该调用会自动增加IDirect3DDevcie9接口的引用计数,所以一定要记住调用IUnknown当我们使用完该surface,否则会内存泄漏。

要记住:swap chain对应的是present动作,display adapter driver做的是flipping操作。

While such "Present" operations are almost invariably implemented by flip operations when the swap chain is a full-screen one, they are necessarily implemented by copy operations when the swap chain is windowed. Furthermore, a display adapter driver may use flipping to implement Present operations against full-screen swap chains based on the D3DSWAPEFFECT_DISCARD and D3DSWAPEFFECT_COPY.

Page Flipping and Back Buffering

page flipping在多媒体,动画,游戏中非常关键。它类似于用一个本子做的动画。 D3D通过swap chain实现page flipping。

Copying to Surfaces

当使用IDirect3DDevice9::UpdateSurface,传入一个矩形,或者NULL代表整个surface。同时传入一个目标surface上的点,告诉源surface要拷贝到的位置。该方法不支持flipping。只有在源举行跟相关目标矩形完全在源surface跟目标surface内的时候,该操作才可能成功。该方法也不支持alpha blending,color keys,格式转换。同时,源跟目标surface必须不同!

如下方法同样可以做从图片到surface的拷贝。
D3DXLoadSurfaceFromFile
D3DXLoadSurfaceFromFileInMemory
D3DXLoadSurfaceFromMemory
D3DXLoadSurfaceFromResource
D3DXLoadSurfaceFromSurface

Copying Surfaces

术语blit是bit block transfer的缩写,它是传输blocks of data从内存的一个地方到另一个地方的过程。blitting device driver infterface(DDI)是D3D9中主要的移动大块矩形pixes,per-frame basis,的机制。IDirect3DDevice9::Present就是用的这个机制。blit操作中的“运输”是通过IDirect3DDevice9::UpdateTexture方法进行的。

Accessing Surface Memory Directly

可以通过IDirect3DSurface9::LockRect方法直接访问surface memory。当调用该方法时,pRect参数是一个指向RECT结构体的指针,描述了surface上的矩形区域。如果想锁住整个surface,设置pRect为NULL。如果两个矩形不重叠,两个线程或这进程可以同时锁住一个surface上的多个矩形区域。multisample back buffer是不能锁的。

IDirect3DSurface9::LockRect方法会填充D3DLOCKED_RECT结构体,信息包括pitch跟锁住区域bits的指针。当使用完后,一定要记住IDirect3DSurface9::UnlockRect。

当操纵锁住的矩形区域时,以下是要注意的问题:
永远要检查pitch信息,不要想当然的自己算出一个常数来。

不要忘记unlock。

限制程序的活动,当surface被锁时。

在拷贝时,一定不要导致display memory溢出。

只有在swap chain以D3DPRESENTFLAG_LOCKABLE_BACKBUFFER标志创建时,back buffer surfaces才有可能被lock。

Private Surface Data

我们可以存储任何类型的程序数据在surface中。比如,代表地图的一个surface可能含有terrain,地形,信息。

一个surface可能还有不止一个私有data buffer。每个buffer由GUID识别,当你attach数据到surface上时。

要存储私有surface data,调用setPrivateData,参数为source buffer的指针,数据的大小,数据的GUID。SetPrivateData会在内部分配内存,然后做拷贝。这时,你就可以安全的删除源buffer了。内存会被释放,当FreePrivateData被调用。这是自动发生的,当surface被free的时候。

要为某个surface取得private data,必须分配好大小正确的一块buffer,然后调用GetPrivateData方法,传入GUID参数。这里,需要我们负责我们创建的buffer的内存管理。如果你不知道分配多到size的buffer,先以零大小的size调用一次GetPrivateData,他会返回buffer的大小。

Gamma Controls

Gamma Controls允许我们改变系统如何显示surface的内容,并不会影响surface内容本身。可以把这些controls当作非常简单的过滤器,在surface被渲染到屏幕之前的过程中所加的对surface上数据的操作。

gamma control是swap chain的一个属性。gamma control可以使我们动态控制surface的RGB level如何map到系统显示器的真实level。

IDirect3DDevice9::SetGammaRamp和IDirect3DDevice9::GetGammaRamp方法允许我们控制ramp level,当swap chain是windowed,gamma ramp可以被应用。swap chain一旦被创建,gamma chain就会立即生效。

Gamma Ramp Level

DAC: digital to Analog Converter. GUID:globally unique identifier

术语gamma ramp,灰度系数斜率,描述了pixel中RGB的map关系。D3D从frame buffer中拿出一个pixel,查看它的R,G,B component,每个component都被一个0~65535之间的值代表。D3D拿着这个值在一个有256个元素的数组中索引出一个值,然后用该值替代原来的值。这样就改变了surface最终显示在屏幕上的样子。

Setting and Retriving Gamma Ramp Levels

我们设置的ramp level只有在程序是全屏的时候,才生效。SetGammaRamp接受两个参数,一个是D3DSGR_CALIBRATE/D3DSGR_NO_CALIBRATION,另一个是D3DGAMMARAMP结构体,包含了三个含有256个WORD元素,分别代表RGB.GetGammaRamp的参数是D3DGAMMARAMP结构体。calibrator会设置新的gamma level,它会保证gamma值的一致性,绝对性,无论用户是什么样的display adapter跟显示屏。但是该动作是有overhead的,也并不是所有的显卡都支持,需要看一下D3DCAPS9来确定一下。

你可能感兴趣的:(DX学习笔记之Surfaces)