【问题标题】:Fast Java2D hardware scaling of images图像的快速 Java2D 硬件缩放
【发布时间】:2014-10-23 17:59:54
【问题描述】:

我正在尝试使用 Java2D 在运行时使用某种体面的抗锯齿插值(例如双线性)来扩展后缓冲区。我的想法是我将场景渲染到这个图像,然后在全屏模式下放大图像以匹配用户拥有的任何分辨率。

请注意,全屏模式很重要。这在窗口模式下不会发生。

有没有使用硬件缩放的快速方法? Javadocs 建议它存在(-Dsun.java2d.ddscale=true),但它对我没有影响。

代码如下:

// Initialization in AWT Canvas
buffer_ = createImage(1280, 800);

// Several hundred large draw calls into the buffer that renders the entire scene - executes fast (~10ms)
drawScene(buffer_.getGraphics());

// Upscaling buffer to screen. If I don't upscale it executes very fast (<1ms)
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.drawImage(buffer_, 0, 0, 1920, 1200, null);

结果:

  • 最近邻(大约 2 毫秒)
  • 双线性(约 40 毫秒)
  • 双三次(约 140 毫秒)

图像是 TYPE_INT_RGB 不透明的 BufferedImage,我正在 AWT 画布上绘图。

我尝试过的其他方法:

  • VolatileImage(性能无变化)
  • Dsun.java2d.ddscale=true(无变化)
  • Dsun.java2d.opengl=true(所有命令的速度大约慢 3 倍)
  • 仿射变换(无变化)
  • createBufferStrategy 并通过调用 Graphics2D.scale 进行“预缩放”(慢得多,缩放每个绘制调用而不是 1 个大缓冲区)。请注意,没有缩放的 BufferStrategy 无论如何都无法提高速度。
  • JPanel 代替 Canvas(大约慢 1.5 倍)
  • imgScalr “library”(这只是调用g.drawImage,就像我上面所说的那样)

其他一些有用的说明:

  • buffer_.getCapabilities(gc).isAccelerated() 返回false(在窗口模式下为true
  • 这是遗留代码,我有更新的代码,全部是 GL,可以非常快速地进行缩放,但不想重写遗留程序。表明我的硬件确实支持图像的硬件缩放。
  • AMD HD5770 显卡
  • Windows 7 最新 Java 8 作为 JVM 运行。

感谢您的帮助。在这一点上,我很遗憾地考虑将所有东西都转换为 GL 只是为了这一件事......不过肯定有答案!

【问题讨论】:

  • 附带说明:转换为 OpenGL(JOGL、LWJGL 或任何基于它们的框架)通常具有灵活渲染管道(缩放、旋转、纹理过滤、各种 3D 效果、着色器、SIMD 并行计算等),所以如果你想获得 HQ/高速图形,只需使用 OpenGL。一旦掌握了它,重写将比尝试修复低级/原生 Java2D 实现花费更少的时间; AFAIR 他们多次尝试简化 Java 的加速,都以分叉结束,因为它对每个人来说都是如此皇家 PITA
  • 当然!尽管对于这种大小的应用程序,我不同意重写会花费更少的时间。我过去在 GL 中编写了一个 2D 图层,并且对它非常满意,但是将其集成到此代码中需要数周时间。更不用说,它有自己的问题。我在下面粘贴的半解决方案(到目前为止)是我要使用的,只花了几天的时间就头痛并且运行速度非常快。老实说,我遇到的问题似乎是一个错误。甚至可能与驱动程序有关。在窗口中运行良好,在 FSEM 中失去硬件加速?太奇怪了。

标签: java


【解决方案1】:

虽然我认为您的方法在技术上是可行的,但我想知道您为什么如此专注于以固定分辨率进行渲染。

从图形质量的角度来看,应该立即将事物渲染到目标分辨率,完全消除缩放。我意识到位图图形(如瓷砖等)在某些时候需要缩放,只是我不明白为什么需要在关键路径上(实时)完成。我首选的方法是将位图预缩放到所需的分辨率一次(然后将它们按比例缓存在内存甚至磁盘上,这取决于哪个更可行)。

您可能需要检查几件事,确保内部使用的图像类型与渲染表面兼容(而不是使用硬编码的 TYPE_INT_RGB,而是使用 createCompatibleImage 的变体创建后台缓冲区,以确保不需要进行格式转换)。还有很多选项可以改变 Graphics2D 在后台使用的内容:http://docs.oracle.com/javase/7/docs/technotes/guides/2d/flags.html

最后,您可能想要检查您是否没有对缓冲图像执行任何“禁止”操作,例如直接访问底层缓冲区数组 (Can't accelerate pixel-modified BufferedImages),这会干扰 java 使用硬件的能力加速。

【讨论】:

  • 在内存(或磁盘)中保存大量预缩放图像对于此应用程序是不可行的。我们需要快速的加载时间,有严格的内存限制。我们已经从磁盘流式传输图像以节省内存等。这样做会使该过程非常缓慢。使用较小的后缓冲区具有填充率性能优势,因为我们可以将较少的像素渲染到缩小的缓冲区和放大的缓冲区。硬件缩放是(通常)极快(
【解决方案2】:

我还没有让全屏独占模式在上述情况下工作,但我有一个解决方法。

保持窗口模式,设置窗口大小以匹配屏幕大小,将其位置设置为graphicsDevice.getDefaultConfiguration().getBounds(),然后设置为setUndecorated(true)。最后,使用java.awt.Robot 将鼠标指针限制在窗口上。

不理想,但在视觉上它与全屏没有区别,至少现在双线性比例很快。不幸的是,这意味着用户无法将其全屏分辨率设置为低于其原始分辨率。

顺便说一句,如果我在 VM 中有 -Dsun.java2d.accthreshold=0,我看到使用 BufferedImage(从 2 毫秒下降到 1 毫秒)和使用 VolatileImage(下降到 0.3 毫秒)获得了一些相当显着的绘图性能改进论据。但在全屏模式下,这些性能改进消失了(可能求助于软件缩放解决方案)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-16
    • 1970-01-01
    • 2023-03-25
    相关资源
    最近更新 更多