【问题标题】:java paint(g), repaint(), update(g) backend and local repaintsjava paint(g)、repaint()、update(g) 后端和本地重绘
【发布时间】:2013-01-12 07:44:48
【问题描述】:

我今天和一群同学讨论了很多这个话题,他们每个人都有不同的想法来“滥用”Java2D API 来制作一个简单的 2D 游戏。

我正在使用扩展JComponent 的类并覆盖paintComponent() 方法来获取Graphics2D 对象并从那里定义我的渲染逻辑。修改后的JComponent 是我覆盖的JFramecontentPane

我的问题来了:在JFrame 的后端调用paint(Graphics)repaint()update(Graphics) 对我的屏幕有何不同?我提到,当从我的“帧计时器”调用除repaint() 之外的任何东西时(每秒调用有问题的方法 50 次)屏幕的某些部分有时会闪烁,我清楚地告诉它渲染的某些东西不可见(或闪烁迅速),一切都感觉不对。这里有什么区别?我试图深入了解 AWT 后端的源代码,直到 EventQueue 以某种方式管理 PaintEvent,但我停在那里以从极其丑陋的代码中拯救我的大脑。

在讨论整个repaint() 的事情时,我们谈到了仅重新绘制与模型中的明确变化相对应的屏幕位置的“策略”,从而节省了我们的 CPU/GPU 功率。虽然完成这些事情(以及动画)仍然需要逻辑,但下一个问题是我如何“访问”已绘制屏幕的FrameBuffer,以便我可以参考我的渲染已经完成的工作。

是的,我们已经经常听到 Java 可能不是以最佳方式支持我们正在寻找的所有操作的编程语言...

【问题讨论】:

  • 操作系统或底层库处理为您节省一些 CPU 和 GPU。有一个优化可以在屏幕上重新绘制最小的正方形,并且只重新绘制该部分。所以我不会为这样的优化而烦恼(在某些情况下在 3D 渲染中除外)。
  • 你知道这实际上是在哪个版本的 java 中实现的 - 我感觉在 1.6 中它没有这样做(升级到 1.7 不知何故将性能提高了 10 到 20 倍)
  • 这通常由操作系统缓冲区处理来处理。 Java 1.7 总体上增加了代码改进,因此它可能不是绘图本身,而是总体上的性能改进。
  • 忘了提到该技术称为位块传输。这意味着它将操作系统中的位图缓冲区合并在一起,并且可以快速确定包含所有更改的最小矩形,以仅重绘该部分。由于硬件限制,我认为您无法获得比这更精细的颗粒。
  • 基本上我们可以忽略这里的任何优化,让操作系统做“正确”的事情? - 我不知道即使它看起来合乎逻辑,Graphics2D 对象的工作方式和从程序员那里获取指令的方式似乎我们告诉它做的所有工作都已经完成了,即使屏幕上“没有”发生,我们仍然执行代码...

标签: java swing awt backend graphics2d


【解决方案1】:

我的问题来了:调用有什么不同的东西 JFrame 中的paint(Graphics)、repaint() 和 update(Graphics) 后端对我的屏幕做什么?我提到,当调用任何东西时 我的“帧计时器”中的 repaint() (调用问题 50 中的方法 每秒次)屏幕的某些部分有时会闪烁, 我明确告诉它渲染的某些东西不可见(或闪烁 迅速),一切都感觉不对。这里有什么区别? 我试图深入到 EventQueue 的 AWT 后端的来源 PaintEvent 以某种方式管理的地方,但我停在那里保存我的 来自极其丑陋的代码的大脑。

在 Swing 中安排绘画是 RepaintManager 的责任。它(除其他外)负责确定应用程序的哪些区域需要重新绘制,并安排这些更新在事件调度线程的上下文中发生。

何时可能发生重绘完全取决于重绘管理器。重绘哪些区域也部分取决于重绘管理器,并且重绘管理器可以选择将多个重绘请求合并到单个重绘事件中,从而节省时间和 CPU。

一般来说,你不应该调用paint(Graphics)update(Graphics),除了你不能真正创建图形上下文之外,重绘管理器会为你做这件事。即使你想打印屏幕,你也应该使用组件的print(Graphics) 方法,除了它不是双缓冲之外,还有与尝试将缓冲区复制回本机对等点有关的问题;)

闪烁通常是因为您从非双缓冲上下文中绘制,例如覆盖 paint 而不是 paintComponent。一般来说,在极少数情况下,您实际上需要覆盖顶级容器的 paint 方法,例如 JFrame

在讨论整个 repaint() 事情时,我们来到了 仅重绘屏幕对应位置的“策略” 模型中的明确变化,为我们节省了 CPU/GPU 功率。虽然它 仍然需要逻辑来完成这些事情(以及动画), 下一个问题是如何“访问”绘制的 FrameBuffer 屏幕,以便我可以参考我已经完成的工作 渲染。

一般来说,除非您确实需要这样做,否则不要担心。如果你对你的重绘代码很小心(你实际上可以安排一个区域被重绘,而不是重绘整个组件),你不应该真的需要关心。确保您使用的是JComponent 的祖先并使用paintComponent 方法,您将获得自动双缓冲...

另一个问题是,您真的不知道组件何时会请求重绘它自己,例如响应鼠标移动或组件属性的更改...

访问“帧缓冲区”可能真的不是一个好主意,更好的是生成自己的缓冲区(使用类似 BufferedImage 的东西),渲染到它,然后渲染到屏幕上(关闭屏幕缓冲)。这样您就可以生成自己的“FrameBuffer”

现在,如果你真的很绝望,你可以看看BufferStrategy

但我建议您在深入研究之前先看看Painting in AWT and Swing,这将使您对绘画的工作原理有更深入的了解。

另外,永远不要认为你可以控制,你不是。如果您尝试“控制”,请准备好让事情迅速发生在您的脸上。了解流程并使用它。

看看这些(简单的)例子......

【讨论】:

  • +1 你提到了这一切,对@salbeira 来说只有一件事,我们得出的“策略”是只重新绘制与模型中的明确变化相对应的屏幕位置,从而节省了我们CPU/GPU 功率 Swing 为我们做了这件事,看看Refining the Design,它展示了如何使用repaint(int x,int y,int w,int h),这将有助于节省 CPU 和 GPU 功率,而不是重新绘制整个屏幕。除非当然这是你实现的......
猜你喜欢
  • 2012-06-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-21
  • 1970-01-01
  • 1970-01-01
  • 2013-10-17
  • 2014-07-05
相关资源
最近更新 更多