Управління графічним станом та компонентами?


11

Я часто схильний робити передчасну оптимізацію при роботі з графікою. Я завжди намагаюся дотримуватися кількох принципів:

  • Зведіть до мінімуму кількість компонентів D3D. (Стани візуалізації, буфери, шейдери тощо)
  • Зв'язуйте компоненти лише в разі крайньої необхідності. (Не пов'язано вже тощо)
  • Максимально спеціалізуйте компоненти. (Встановити лише необхідні BindFlags тощо)

Це привело мене до створення дуже складних обгортків для управління створеними компонентами та поточним станом трубопроводу. Це не тільки забирає багато мого цінного часу на розробку, але й додає ще один великий рівень складності.

І найгірше: я навіть не знаю, чи все це варте клопоту.

Деякі мої міркування щодо оптимізації вже можуть бути впроваджені на нижчому рівні, і я просто тиражую їх, додатково витрачаючи час на процесор. Інші міркування можуть бути абсолютно непотрібними, оскільки вплив на продуктивність незначний.

Отже, мої запитання:

  1. Які з перерахованих вище вказівок є дійсними і в якій мірі слід їх виконувати?
  2. Як GPU обробляє зміни стану?
  3. Що станеться, якщо я зміню стан, який ніколи не використовується? (Під час активного дзвінка не робиться дзвінок.)
  4. Які фактичні штрафи за ефективність прив’язки різних різних компонентів?
  5. Які ще міркування щодо ефективності слід враховувати?

Будь ласка, не кажіть мені, що я просто не повинен дбати про продуктивність, поки я не досяг фактичних меж. Хоча це, очевидно, вірно з практичної точки зору, мене в основному цікавить теорія. Мені якось потрібно боротися з прагненням створити оптимальну графічну структуру, і я не думаю, що я можу це зробити за допомогою звичайної "лекції передчасної оптимізації".

Управління компонентами

Зараз я пишу програми DirectX 11 в C #, використовуючи SlimDX як керовану обгортку. Це дуже низький рівень обгортки, і моя поточна абстракція побудована поверх неї.

Є деякі очевидні переваги при використанні абстракції 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 Мені майже соромно визнати, що це якась досить гідна порада. Я, безумовно, повинен робити більше профілювання.
Луцій

1
Профілюючі номери - це гамдевська версія "фотографій, або цього не сталося" напевно =)
Патрік Х'юз

Відповіді:


3

Мені подобається абстракційний підхід, викладений Ходжманом у цих темах на gamedev.net:

Він описує трирівневу систему візуалізації:

  1. API низького рівня візуалізації, який приймає "команди", абстрагуючи не більше, ніж відмінності між різними графічними API, такими як Direct3D 9, Direct3D 11 та OpenGL. Кожна "команда" відображає інший стан або малює виклик, наприклад, прив'язування вершинного потоку або текстури або малювання примітивів.
  2. API, який приймає "елементи візуалізації", які об'єднують усі стани і єдиний виклик виклику, необхідний для візуалізації певного об'єкта, і сортує та переводить їх у команди, що надсилаються на перший рівень. Стан візуалізації містить дзвінок дзвінка та стек "груп груп", які логічно групують зміни станів. Наприклад, у вас є група стану для пропуску візуалізації, група стану для матеріалу, група стану для геометрії, група стану для екземпляра тощо. Цей рівень відповідає за сортування цих елементів візуалізації для зменшення надмірних змін стану та усунення будь-яких змін стану, які насправді є зайвими.
  3. Системи високого рівня, такі як графік сцени або GUI-рендер, які відправляють елементи візуалізації на другий рівень. Важливо зауважити, що ці системи не знають ні алгоритмів сортування стану, ні конкретного API візуалізації, що робить їх повністю платформенними. Вони також прості у використанні після впровадження API нижчого рівня.

На закінчення ця модель вирішує обидві ваші проблеми одразу.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.