Ogre 1.6.2 下 DepthStencilbuffer 的问题

本文分析了使用Ogre引擎时遇到的DepthStencilBuffer资源频繁创建问题,尤其是在D3D9渲染系统中。通过深入研究源代码,提出了一种改进方案,以减少资源创建次数并提升渲染效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近一直在用 Ogre 做项目,为了提高描画效率问题大大头痛了一番。

然后用 GPA 和 PerfHUD 反复检查,意外的发现每帧有很多的资源创建调用。

很莫名啊~

所有资源都在初始化的时候完成了啊,怎么会在运行期每帧都有呢~

检查了一百遍啊一百遍,最后发现原来 Ogre RenderSystem 的 D3D9 实现又一次忽悠了我~哭!!

 

Ogre在 DepthStencil 的处理上很保守,基本不开放任何相关接口。

这意味着,你只能任由它来管理你的 DepthStencil,而不能自己指定某RenderTarget的 DepthStencilBuffer。对于某些懒人来说也可以算是个福音,不过我更倾向于在提供自动管理的基础上,也能提供自定义的途径。

如果这个自动管理很完美也就算了,可惜很不幸的,在 D3D9RenderSystem 的实现里存在一个很严重的隐患。

为了管理不同格式的DepthStencilBuffer,D3D9RenderSystem 使用了一个名为 mZBufferHash 的 map 来存放所有的DepthStencilBuffer,采用的策略是,每次从map中查找符合格式和大小的 DepthStencilBuffer,如果没找到符合要求的结果,就进行创建过程。

实现部分的代码如下

 
ZBufferFormat zbfmt(dsfmt, multisample);
ZBufferHash::iterator i = mZBufferHash.find(zbfmt);
if(i != mZBufferHash.end())
{
/// Check if size is larger or equal
if(i->second.width >= width && i->second.height >= height)
{
surface = i->second.surface;
}
else
{
/// If not, destroy current buffer
i->second.surface->Release();
mZBufferHash.erase(i);
}
}
if(!surface)
{
/// If not, create the depthstencil surface
HRESULT hr = mpD3DDevice->CreateDepthStencilSurface(
width,
height,
dsfmt,
multisample,
NULL,
TRUE, // discard true or false?
&surface,
NULL);
if(FAILED(hr))
{
String msg = DXGetErrorDescription9(hr);
OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "Error CreateDepthStencilSurface : " + msg, "D3D9RenderSystem::_getDepthStencilFor" );
}
/// And cache it
ZBufferRef zb;
zb.surface = surface;
zb.width = width;
zb.height = height;
mZBufferHash[zbfmt] = zb;
}
return surface;
在这里,它认为如果原有的Buffer在宽度和高度上都能满足要求,则直接使用即有的Buffer,否则,则销毁旧的Buffer,并采用新大小创建新的Buffer。
基本思路没有问题,问题在于判断条件和创建大小之间的关系:
if(i->second.width >= width && i->second.height >= height) 
 
/// If not, create the depthstencil surface
HRESULT hr = mpD3DDevice->CreateDepthStencilSurface(
width,
height,
 

假设存在如下情况:

RenderTargetA 请求 DepthBuffer 大小为 1360 X 768 ,作为 主画面

RenderTargetB 请求 DepthBuffer 大小为 1024 X 1024,作为 ShadowMap,

在这种情况下, 每次切换到描画 RenderTargetA的时候,因为 width 为 1360 > 1024 ,所以会重新创建DepthBuffer,大小为 1360 X 768,当再切回 RenderTargetB描画时, 因为 Height 为 1024 > 768,又要重新创建DepthBuffer,大小为 1024 X 1024。结果每帧都要创建两次资源。

这还不是最坏情况。

我们都知道,游戏如果需要好的效果,那么会大量使用后期。对于 Ogre 来说,就是 Compositor 系统,这里面大量采用中间过渡 RenderTarget 的方式来实现效果。这些 RenderTarget 的切换会很频繁(比如 HDR)。如此下去,我们就必须面对大量重复创建 DepthBuffer 的风险。

这都是拜此 DepthStencilBuffer管理方式所赐。

 

其实修改也很方便,每次创建 DepthStencilBuffer 的时候,重新计算大小,采用最大的维度来创建就可以了。

基本实现如下,在删除旧有的Buffer之时,同时调整新Buffer的大小,以兼容新旧两个Buffer:

if( i->second.width > width )
{
width = i->second.width;
}
if( i->second.height > height )
{
height = i->second.height;
}

//
i->second.surface->Release();
mZBufferHash.erase(i);

 

至于如果你很有XX情节,一定要 2^n的话,自我调整一下也不是很难的事情。(玩笑,这基本都看显卡能不能适应啦,哈哈)

转载于:https://www.cnblogs.com/limiy/articles/1753151.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值