Я часто склонен делать преждевременную оптимизацию при работе с графикой. Есть несколько принципов, которым я всегда стараюсь следовать:
- Сократите количество компонентов D3D до минимума. (Рендеринг состояний, буферов, шейдеров и т. Д.)
- Связывайте компоненты только в случае крайней необходимости. (Уже не связаны и т. Д.)
- Специализируйте компоненты как можно больше. (Только установите необходимые BindFlags и т. Д.)
Это привело меня к созданию очень сложных оболочек для управления созданными компонентами и текущим состоянием конвейера. Это не только отнимает у меня много драгоценного времени на разработку, но также добавляет еще один большой уровень сложности.
И что хуже всего: я даже не знаю, стоит ли это того.
Некоторые из моих соображений по оптимизации уже могут быть реализованы на более низком уровне, и я просто копирую их, дополнительно тратя время на процессор. Другие соображения могут быть совершенно ненужными, так как влияние на производительность незначительно.
Итак, мои вопросы:
- Какие из вышеперечисленных правил действительны и в какой степени я должен им следовать?
- Как GPU обрабатывает изменения состояния?
- Что произойдет, если я изменю состояние, которое никогда не используется? (Не активен вызов при отрисовке, пока он активен.)
- Каковы фактические потери производительности для связывания различных компонентов?
- Какие еще соображения производительности следует учитывать?
Пожалуйста, не говорите мне, что я не должен заботиться о производительности, пока не достигну реальных пределов. Хотя это очевидно с практической точки зрения, я в основном интересуюсь теорией. Мне как-то нужно бороться с желанием создать оптимальную графическую среду, и я не думаю, что смогу сделать это с помощью обычной «лекции по преждевременной оптимизации».
Управление компонентами
В настоящее время я пишу приложения DirectX 11 на C #, используя SlimDX в качестве управляемой оболочки. Это очень низкоуровневая оболочка, и моя текущая абстракция построена на ней.
Есть некоторые очевидные преимущества при использовании абстракции Direct3D. Настройка среды, загрузка шейдеров, установка констант и отрисовка сетки намного проще и использует намного меньше кода. Кроме того, поскольку он управляет созданием и удалением большинства компонентов, их можно автоматически повторно использовать везде, и я почти полностью избегаю утечек памяти.
- Как вы обычно управляете всеми графическими компонентами и ресурсами?
- Можете ли вы порекомендовать какие-либо управляемые оболочки, делающие что-то похожее на мой пример ниже?
Вот пример моей текущей реализации. Я вполне доволен интерфейсом. Он обладает достаточной гибкостью для моих нужд и очень прост в использовании и понимании:
// 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();
}