管理图形状态和组件?


11

在处理图形时,我经常倾向于做很多过早的优化。我一直尝试遵循一些原则:

  • 使D3D组件的数量最少。(渲染状态,缓冲区,着色器等)
  • 仅在绝对必要时绑定组件。(尚未绑定,等等。)
  • 尽可能专门化组件。(仅设置必要的BindFlags等)

这导致我构建非常复杂的包装器来管理创建的组件和当前管道状态。这不仅消耗了我宝贵的开发时间,还增加了另一层复杂性。

最糟糕的是:我什至不知道这一切是否值得麻烦。

我的一些优化考虑因素可能已经在较低级别上实现,而我只是在复制它们,从而浪费了CPU时间。由于对性能的影响可以忽略不计,因此其他考虑可能完全没有必要。

所以我的问题是:

  1. 以上哪些准则有效,我应在多大程度上遵循这些准则?
  2. GPU如何处理状态变化?
  3. 如果我更改了一个从未使用过的状态会怎样?(处于活动状态时,不会进行抽奖。)
  4. 绑定各种不同组件的实际性能损失是什么?
  5. 还应考虑哪些其他性能方面的考虑?

请不要只是告诉我,在达到实际极限之前,我不应该关心性能。从实践的角度来看,这显然是正确的,但我主要对该理论感兴趣。我不知何故需要抵制建立最佳图形框架的冲动,而且我认为我通常无法通过“过早的优化讲座”来做到这一点。

管理组件

我目前正在使用SlimDX作为托管包装用C#编写DirectX 11应用程序。这是一个非常低级的包装器,而我当前的抽象是基于它的。

使用Direct3D抽象时,有一些明显的优势。设置环境,加载着色器,设置常量和绘制网格更加简单,并且使用代码更少。而且,由于它管理着大多数组件的创建和处理,因此它们可以在任何地方自动重用而且我几乎完全避免了内存泄漏。

  1. 您通常如何管理所有图形组件和资源?
  2. 您能否推荐任何与下面的示例类似的托管包装器?

这是我当前实现的一个示例。我对界面非常满意。它具有足够的灵活性来满足我的需求,并且易于使用和理解:

// Init D3D environment
var window = new RenderForm();
var d3d = new Direct3D(window, GraphicsSettings.Default);
var graphics = new GraphicsManager(d3d.Device);

// Load assets
var mesh = GeometryPackage.FromFile(d3d, "teapot.gp");
var texture = Texture.FromFile(d3d, "bricks.dds");

// Render states
graphics.SetViewports(new Viewport(0, 0, 800, 600);
graphics.SetRasterizer(wireFrame: false, culling: CullMode.Back);
graphics.SetDepthState(depthEnabled: true, depthWriteEnabled: true);
graphics.SetBlendState(BlendMethod.Transparency);

// Input layout
graphics.SetLayout("effect.fx", "VS", "vs_4_0",
    new InputElement("POSITION", 0, Format.R32G32B32_Float, 0),
    new InputElement("TEXCOORD", 0, Format.R32G32_Float, 0)
);

// Vertex shader
graphics.SetShader(Shader.Vertex, "effect.fx", "VS", "vs_4_0");
graphics.SetConstants(Shader.Vertex, 0, 4, stream => stream.Write(wvpMatrix));

// Pixel shader
graphics.SetShader(Shader.Pixel, "effect.fx", "PS", "ps_4_0");
graphics.SetTexture(Shader.Pixel, 0, texture);
graphics.SetSampler(Shader.Pixel, 0, Sampler.AnisotropicWrap);
graphics.SetConstants(Shader.Pixel, 0, 1, stream => stream.Write(new Color4(1, 0, 1, 0);

d3d.Run(() =>
{
    // Draw and present
    d3d.BackBuffer.Clear(new Color4(1, 0, 0.5f, 1));
    graphics.SetOutput(d3d.BackBuffer);
    graphics.Draw(mesh);
    d3d.Present();
}

8
对于此类问题,我不会讲“过早的优化”,而只会讲“概要文件更改,以便您自己查看”。
Tetrad

@Tetrad我几乎as愧地承认这是一些相当不错的建议。我绝对应该做更多分析。
Lucius

1
剖析数字是肯定是“图片或未发生”的gamedev版本=)
Patrick Hughes 2013年

Answers:


3

我喜欢Hodgman在gamedev.net上的这些线程中概述的抽象方法:

他描述了一个三层渲染系统:

  1. 接受“命令”的低级渲染API,仅抽象出不同图形API之间的差异,例如Direct3D 9,Direct3D 11和OpenGL。每个“命令”都映射到不同的状态或绘制调用,例如绑定顶点流或纹理或绘制图元。
  2. 接受“渲染项”的API,该组件将所有状态和渲染特定对象所需的单个绘制调用组合在一起,并对它们进行排序并将其转换为发送到第一级的命令。渲染状态包含绘图调用和“状态组”堆栈,这些逻辑上将状态更改分组。例如,您将具有一个用于渲染通道的状态组,一个用于材质的状态组,一个用于几何图形的状态组,一个用于实例的状态组,等等。该级别负责对这些渲染项目进行排序,以减少冗余状态更改,并剔除实际上是冗余的任何状态更改。
  3. 诸如场景图或GUI渲染器之类的高级系统将渲染项发送到第二级。需要注意的重要一点是,这些系统不了解状态排序算法或特定的渲染API,从而使其完全与平台无关。一旦实现了较低级别的API,它们也很容易使用。

总之,此模型可以同时解决您的两个问题。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.