Tôi thường có xu hướng làm tối ưu hóa sớm khi làm việc với đồ họa. Có một vài nguyên tắc tôi luôn cố gắng tuân theo:
- Giữ số lượng thành phần D3D ở mức tối thiểu. (Kết xuất trạng thái, bộ đệm, đổ bóng, v.v.)
- Chỉ ràng buộc các thành phần nếu thực sự cần thiết. (Chưa bị ràng buộc, v.v.)
- Chuyên các thành phần càng nhiều càng tốt. (Chỉ đặt BindFlags cần thiết, v.v.)
Điều này dẫn tôi đến việc xây dựng các trình bao bọc rất công phu để quản lý các thành phần được tạo và trạng thái đường ống hiện tại. Điều này không chỉ tiêu tốn rất nhiều thời gian phát triển quý giá của tôi, mà còn thêm một lớp phức tạp lớn khác.
Và điều tồi tệ nhất: tôi thậm chí không biết liệu tất cả có đáng để gặp rắc rối không.
Một số cân nhắc tối ưu hóa của tôi có thể đã được triển khai ở cấp độ thấp hơn và tôi chỉ sao chép chúng, ngoài ra còn lãng phí thời gian cho CPU. Những cân nhắc khác có thể hoàn toàn không cần thiết, vì ảnh hưởng đến hiệu suất là không đáng kể.
Vì vậy, câu hỏi của tôi là:
- Những hướng dẫn trên là hợp lệ và tôi nên tuân theo chúng ở mức độ nào?
- Làm thế nào để GPU xử lý các thay đổi trạng thái?
- Điều gì xảy ra nếu tôi thay đổi trạng thái không bao giờ được sử dụng? (Không có cuộc gọi rút thăm nào được thực hiện trong khi nó đang hoạt động.)
- Các hình phạt hiệu suất thực tế để ràng buộc các thành phần khác nhau là gì?
- Những cân nhắc hiệu suất khác nên được thực hiện?
Xin đừng nói với tôi rằng tôi không nên quan tâm đến hiệu suất cho đến khi tôi đạt đến giới hạn thực tế. Trong khi điều đó rõ ràng là đúng theo quan điểm thực tế, tôi chủ yếu quan tâm đến lý thuyết. Bằng cách nào đó tôi cần phải chống lại sự thôi thúc xây dựng khung đồ họa tối ưu và tôi không nghĩ mình có thể làm điều đó với "bài giảng tối ưu hóa sớm" thông thường.
Quản lý linh kiện
Tôi hiện đang viết các ứng dụng DirectX 11 trong C # bằng cách sử dụng SlimDX làm trình bao bọc được quản lý. Nó là một trình bao bọc cấp độ rất thấp và sự trừu tượng hiện tại của tôi được xây dựng trên nó.
Có một số lợi thế rõ ràng khi sử dụng trừu tượng Direct3D. Thiết lập môi trường, tải shaders, thiết lập các hằng số và vẽ một lưới là đơn giản hơn nhiều và sử dụng rất nhiều mã ít hơn. Ngoài ra, vì nó quản lý việc tạo và xử lý hầu hết các thành phần, chúng có thể được tự động sử dụng lại ở mọi nơi và tôi gần như hoàn toàn tránh rò rỉ bộ nhớ.
- Làm thế nào để bạn thường quản lý tất cả các thành phần đồ họa và tài nguyên?
- Bạn có thể đề nghị bất kỳ trình bao bọc được quản lý nào làm một cái gì đó tương tự như ví dụ của tôi dưới đây không?
Dưới đây là một ví dụ về việc thực hiện hiện tại của tôi. Tôi khá hài lòng với giao diện. Nó có đủ linh hoạt cho nhu cầu của tôi và rất đơn giản để sử dụng và hiểu:
// 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();
}