2010-01-14 24 views
6

Tôi có một số hoạt ảnh đang chạy trong cửa sổ ngữ cảnh OpenGL, vì vậy tôi cần phải thường xuyên vẽ lại hình ảnh đó. Vì vậy, tôi đã đưa ra mã sau:.NET Vẽ lại ở 60 FPS?

void InitializeRedrawTimer() 
    { 
     var timer = new Timer(); 
     timer.Interval = 1000/60; 
     timer.Tick += new EventHandler(timer_Tick); 
     timer.Start(); 
    } 

    void timer_Tick(object sender, EventArgs e) 
    { 
     glWin.Draw(); 
    } 

Điều đó chỉ mang lại cho tôi 40 FPS mặc dù. Tuy nhiên, nếu tôi đặt khoảng thời gian là 1 ms, tôi có thể đạt được 60. Vậy thì 20 ms còn lại đi đâu? Đó có phải là do độ chính xác kém của bộ hẹn giờ hay không? Điều gì sẽ xảy ra nếu tôi muốn chương trình của tôi chạy nhanh nhất có thể, có cách nào liên tục gọi func vẽ không?

Trả lời

7

Bạn có thể thử triển khai vòng lặp trò chơi.

http://blogs.msdn.com/tmiller/archive/2005/05/05/415008.aspx

Vòng lặp cơ bản (chút thay đổi từ phiên bản của ông gốc và phiên bản trong SDK mới cho dễ đọc):

public void MainLoop() 
{ 
     // Hook the application’s idle event 
     System.Windows.Forms.Application.Idle += new EventHandler(OnApplicationIdle); 
     System.Windows.Forms.Application.Run(myForm); 
}  

private void OnApplicationIdle(object sender, EventArgs e) 
{ 
    while (AppStillIdle) 
    { 
     // Render a frame during idle time (no messages are waiting) 
     UpdateEnvironment(); 
     Render3DEnvironment(); 
    } 
} 

private bool AppStillIdle 
{ 
    get 
    { 
     NativeMethods.Message msg; 
     return !NativeMethods.PeekMessage(out msg, IntPtr.Zero, 0, 0, 0); 
    } 
} 

//And the declarations for those two native methods members:   
[StructLayout(LayoutKind.Sequential)] 
public struct Message 
{ 
    public IntPtr hWnd; 
    public WindowMessage msg; 
    public IntPtr wParam; 
    public IntPtr lParam; 
    public uint time; 
    public System.Drawing.Point p; 
} 

[System.Security.SuppressUnmanagedCodeSecurity] // We won’t use this maliciously 
[DllImport(“User32.dll”, CharSet=CharSet.Auto)] 
public static extern bool PeekMessage(out Message msg, IntPtr hWnd, uint messageFilterMin, uint messageFilterMax, uint flags); 

đơn giản, thanh lịch, hiệu quả . Không có phân bổ bổ sung, không có bộ sưu tập thêm, nó chỉ hoạt động .. Sự kiện nhàn rỗi cháy khi không có thư trong hàng đợi, và sau đó trình xử lý liên tục lặp cho đến khi thông báo xuất hiện, trong trường hợp nó dừng lại. được xử lý, sự kiện nhàn rỗi được kích hoạt lại và quá trình bắt đầu lại.

Liên kết này mô tả liên kết sử dụng sự kiện không hoạt động của ứng dụng. Nó có thể được sử dụng. Bạn có thể chỉ cần thực hiện một bài kiểm tra thời gian ngắn hoặc ngủ để làm chậm nó xuống fps yêu cầu của bạn. Hãy thử sử dụng lớp System.Diagnostics.StopWatch để có bộ hẹn giờ chính xác nhất.

Tôi hy vọng điều này sẽ giúp ích một chút.

+0

Tuyệt vời! Sử dụng 'Application.Idle' có vẻ thanh lịch hơn một bộ hẹn giờ 1 ms. Tôi cũng sẽ phải thử 'StopWatch' này vào ngày mai. Cảm ơn! ** Chỉnh sửa: ** Thực ra, tôi đã nói dối. Khi bạn thay đổi kích thước hoặc di chuyển cửa sổ, nó dường như ngừng chạy không tải, và do đó dừng vẽ lại. Điều này về cơ bản làm giảm ưu tiên của bản vẽ. Có lẽ tôi sẽ dính với một bộ đếm thời gian để thực thi một tỷ lệ vẽ lại tối thiểu ... không có lý do tôi không thể kết hợp cả hai phương pháp. – mpen

+3

Đồng thời, Đồng hồ bấm giờ chỉ tốt cho việc định thời gian, không phải cho các sự kiện gọi lại. – mpen

+3

Bạn có thể thử khởi động bộ hẹn giờ khi không hoạt động thậm chí kết thúc, sau đó dừng/kiểm tra lại khi sự kiện nhàn rỗi kích hoạt. Bạn có thể nhận được khá gần theo cách này. – Nanook

2

Đồng hồ Windows Forms Timer cuối cùng bị hạn chế về độ phân giải cho độ phân giải đồng hồ hệ thống chuẩn của máy tính của bạn. Điều này thay đổi từ máy tính đến máy tính, nhưng thường từ 1 ms đến 20 ms.

Trong WinForms, bất cứ khi nào bạn yêu cầu một khoảng thời gian trong mã, Windows sẽ chỉ đảm bảo rằng bộ hẹn giờ của bạn sẽ được gọi là không thường xuyên hơn rồi xác định số mili giây. Nó không đảm bảo rằng Bộ hẹn giờ của bạn sẽ được gọi chính xác đến mili giây mà bạn chỉ định.

Điều này là do Windows là hệ điều hành chia sẻ thời gian chứ không phải theo thời gian thực. Các quy trình và chủ đề khác sẽ được lên lịch để chạy đồng thời và do đó, chuỗi của bạn sẽ bị buộc phải chờ thường xuyên để sử dụng bộ xử lý. Do đó, bạn không nên viết mã hẹn giờ WinForms dựa trên chính xác các khoảng thời gian hoặc sự chậm trễ của mili giây.

Trong trường hợp của bạn, đặt khoảng thời gian thành 1 ms thực sự chỉ yêu cầu Windows kích hoạt bộ hẹn giờ này thường xuyên nhất có thể. Như bạn đã thấy điều này chắc chắn ít hơn nhiều so với 1 ms (60fps = khoảng 17 ms).

Nếu bạn cần bộ hẹn giờ có độ phân giải chính xác và cao hơn, API Windows sẽ cung cấp bộ tính giờ hiệu suất/độ phân giải cao, nếu phần cứng của bạn hỗ trợ, xem How to use the high resolution timer. Một nhược điểm của việc này là việc sử dụng CPU ứng dụng của bạn sẽ được tăng lên để hỗ trợ hiệu năng cao hơn.

Nói chung, tôi nghĩ bạn nên thực hiện vòng lặp trò chơi độc lập về phần cứng/hệ thống. Bằng cách đó, hoạt ảnh cảm nhận của cảnh của bạn xuất hiện không đổi bất kể tỷ lệ khung hình thực tế đạt được.Để biết thêm thông tin these articles cho một nền tảng tốt.

3

Tùy thuộc vào những gì bạn đang thực hiện, hãy xem WPF và hỗ trợ hoạt ảnh, có thể là giải pháp tốt hơn để viết riêng các hình ảnh của bạn trong WinForms.

Cũng xem xét việc sử dụng DirectX vì nó hiện có trình bao bọc .NET và rất nhanh, tuy nhiên, khó sử dụng WPF hoặc WinForms hơn.

Tiếp tục gọi chức năng vẽ của bạn:

  • Do bản vẽ trong phương thức repaint
  • Thêm vào cuối của phương pháp repaint làm một BeginInvoke
  • Trong các đại biểu bạn truyền cho BeginInvoke, làm mất hiệu lực kiểm soát bạn đang vẽ

Tuy nhiên, người dùng của bạn có thể không thích bạn giết máy tính của họ!

Application.Idle cũng là một lựa chọn tốt khác, tuy nhiên tôi thích cửa sổ thực tế sẽ làm chậm tốc độ gửi tin nhắn WmPaint khi cần.

+0

Tôi cũng đã tìm ra rằng việc vô hiệu hóa ngay sau khi vẽ tạo ra tỷ lệ khung hình cao hơn nhiều so với phương pháp hẹn giờ (65 khung hình/giây là giá trị cao nhất với bộ đếm thời gian, vì một lý do nào đó). –

-1

tôi đã nhận thấy trong các mã mẫu được đưa ra trong dòng:

timer.Interval = 1000/60; 

Nó sử dụng các hoạt động của bộ phận nguyên, vì vậy kết quả sẽ được làm tròn đến số nguyên và sẽ không được chính xác 16.6666ms, nhưng 17ms . Do đó bộ đếm thời gian làm cho bọ ve lớn hơn làm mới màn hình.

+0

Cắt phân chia số nguyên, không làm tròn. 1000/60 là 16 ([bằng chứng] (https://repl.it/GGNW/0)). Tôi nên nhận được ~ 62,5 FPS nếu bộ đếm thời gian chính xác. – mpen

+0

Ồ. Tôi đã quên nó. Nhưng dù sao, nếu là 16ms, các dấu tích không được đồng bộ với màn hình. – Dennis

+1

Điều đó tùy thuộc vào màn hình. Khá chắc chắn đây không phải là vấn đề với gsync/freesync, nhưng bất kể, tôi khá chắc chắn 'timer.Interval' không phải là câu trả lời ở đây. – mpen