GDI 的计时器问题+
我目前遇到了 GDI 和计时器的一个非常奇怪的问题。
首先是代码:
class Graph : UserControl {
private System.Threading.Timer timer;
private int refreshRate = 25; //Hz (redrawings per second)
private float offsetX = 0; //X offset for moving graph
public Graph() {
timer = new System.Threading.Timer(timerTick);
}
private void timerTick(object data) {
offsetX -= 1 / refreshRate;
this.Invalidate();
}
public void timerStart() {
timer.Change(0, 1000 / refreshRate);
}
private void onPaint(object sender, PaintEventArgs e) {
//350 lines of code for drawing the graph
//Here the offsetX will be used to move the graph
}
}
我在这里尝试将特定时间内绘制的图形向左移动 1 个“图形单位”。所以我使用一个计时器,它将以小步长的方式改变偏移量,因此它将是一个平滑的移动(这就是刷新率)。
第一次看到这段代码是有效的,但后来我发现了以下问题: 如果我使用 1 (1Hz) 的刷新率,它将很好地将我的图形在 1 步骤 1(图形单位)中移动到左侧。如果我增加刷新率,我的移动速度就会变慢。在 20 FPS 时,它有点慢,在 200 FPS 时,它真的很慢。
所以这是我尝试过的:
我使用刷新或更新而不是无效
我使用普通线程(带有睡眠)而不是计时器
两个代码更改都没有改变结果。
除了使用计时器进行移动之外,我还可以使用以下方法移动图形我的鼠标,如果计时器正在运行,我仍然可以用鼠标平滑地移动图表。所以这不是性能问题..
我想到了绘画队列中的问题,因为我刷新的速度比绘画完成的速度快? (但是为什么我仍然可以用鼠标平滑地移动图形?!)
所以我在这里需要一点帮助。 谢谢
I am currently stuck at a really strange problem with GDI and timers.
First the Code:
class Graph : UserControl {
private System.Threading.Timer timer;
private int refreshRate = 25; //Hz (redrawings per second)
private float offsetX = 0; //X offset for moving graph
public Graph() {
timer = new System.Threading.Timer(timerTick);
}
private void timerTick(object data) {
offsetX -= 1 / refreshRate;
this.Invalidate();
}
public void timerStart() {
timer.Change(0, 1000 / refreshRate);
}
private void onPaint(object sender, PaintEventArgs e) {
//350 lines of code for drawing the graph
//Here the offsetX will be used to move the graph
}
}
I am trying here to move a painted graph in a specific time to 1 "graph unit" to the left. So i use a timer which will change the offset in little steps, so it will be a smooth moving (thats the refreshRate for).
At the first view this code worked, but later i found following problem:
If i am using a refreshRate of 1 (1Hz) it will just fine move my graph in 1 step 1 (graph unit) to the left. If i am increasing the refreshRate my movement will slow done. At 20 FPS its slighly slow, at 200 FPS its really slow..
So here is what i tried:
I used Refresh or Update instead of Invalidate
I used a normal Thread (with Sleep) instead of the timer
Both code changes didnt changed the result..
Beside the movement with the timer I also can move the graph with my mouse and if the timer is running i can still smoothly move the graph with my mouse. So its not a peformance problem..
I thought of a problem in the painting queue, because I am refreshing faster than the painting is done? (But why can I sill move the graph smoothly with my mouse?!)
So i need a little help here.
Thanks
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
这里有一个根本问题;要获得 200fps 的刷新率,您需要每 5 毫秒重新绘制一次。这永远不会发生。无论您将计时器的间隔设置为多少,其分辨率都限制在大约 10-15 毫秒。因此,您的最佳帧速率约为 66-100fps,并且假设您的绘图代码花费的时间为零,当然事实并非如此。
最重要的是,您使用的 System.Threading.Timer 不会在 UI 线程上执行回调,因此从那里调用 Invalidate() 可能甚至不会执行安全的。您通常不会将 System.Threading.Timer 用于 UI 代码。
您可能想尝试所谓的此处显示的多媒体计时器,它使用CPU的高性能定时器,分辨率约为1ms。
There is a fundamental problem here; to get a refresh rate of 200fps you would need to repaint every 5ms. This will never happen. No matter what you set your timer's interval to it's resolution is limited to about 10-15ms. So your best possible frame rate is about 66-100fps, and that's assuming that your drawing code takes zero time, which of course it does not.
On top of that, you are using a
System.Threading.Timer
which does not perform it's callbacks on the UI thread, so calling Invalidate() from there is probably not even safe. You don't typically use aSystem.Threading.Timer
for UI code.You may want to try something like the so called Multi-Media Timer shown here, which uses the CPU's high performance timer and gives a resolution of about 1ms.
你可以尝试稍微改变一下问题。
由于您不知道您的绘画需要多长时间,因此您无法对此做出假设。
您所知道的是您希望转换发生的时间量,假设为 30 秒,如下所示:
此时,您还可以通过记录时间来跟踪自操作开始以来已经过去的时间量你的操作已经开始,比方说
现在你能做的就是确保你手头有正确的例程,并根据 startMoment 和 now 之间的差异计算你的位置
从现在起你能做的就是根据你的情况进行绘制已过去时间的百分比。
因此,在 OnPaint 完成后,您应该能够刷新它。如果您使用一个 UI 计时器,则可以执行以下操作:
其中 Timer1 必须是 Timer 控件。
在间隔事件中,您只需写
希望这有帮助
You may try to change the problem slightly.
Since you do not know how long your painting will take, you cannot make an assumption about that.
What you do know, is the amount of time you want the transition to happen, let's say 30 seconds, something like this:
At this point you can also track the amount of time it has elapsed since the start of your operation, by recording when your operation has started, let's say
Now what you can do is to make sure you have the right routine at hand and you calculate your position based on the difference between the startMoment and now
From now on what you can do is to paint accordingly to your percent of time elapsed.
So after your OnPaint is done you should be able to refresh it. If you use one UI timer, you can do this:
Where Timer1 has to be the Timer control.
In the Interval event you just write
Hope this helps