4

I am trying to scale up a backbuffer using Java2D with some sort of decent anti-aliased interpolation (such as bilinear) at runtime. The idea is that I render the scene to this image, and then scale up the image in fullscreen mode to match whatever resolution the user has.

Note that fullscreen mode is important. This does not occur in windowed mode.

Is there a fast way of doing this using hardware scaling? Javadocs suggest that it exists (-Dsun.java2d.ddscale=true) but it is having no effect for me.

Here is the code:

// 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);

And the results:

  • Nearest Neighbour (about 2ms)
  • Bilinear (about 40ms)
  • Bicubic (about 140ms)

The image is a TYPE_INT_RGB opaque BufferedImage and I am drawing onto an AWT Canvas.

Other things I have tried:

  • VolatileImage (no performance change)
  • Dsun.java2d.ddscale=true (no change)
  • Dsun.java2d.opengl=true (about 3x slower on all commands)
  • AffineTransform (no change)
  • createBufferStrategy and "pre-scaling" by calling Graphics2D.scale (much slower, scales every draw call instead of 1 large buffer). Note that the BufferStrategy without scaling offers no speed improvement anyways.
  • JPanel instead of Canvas (about 1.5x slower)
  • imgScalr "library" (this just calls g.drawImage exactly as I have it above)

Some other useful notes:

  • buffer_.getCapabilities(gc).isAccelerated() returns false (in windowed mode it is true)
  • This is legacy code, I have newer code that is all GL which does the scaling very quick, but don't want to have to rewrite the legacy program. Suggests that my hardware does in fact support HW scaling of images.
  • AMD HD5770 graphics card
  • Windows 7 latest Java 8 running as JVM.

Thanks for any help. At this point, I am sadly considering converting everything over to GL just for this one thing... there must be an answer though!

Jesse
  • 51
  • 7
  • As a side note: converting to OpenGL (either JOGL, LWJGL or any framework based on them) usually *is* the solution for high-speed graphics with flexible rendering pipeline (scaling, rotations, texture filtering, all sort of 3D effects, shaders, SIMD parallel computing etc), so if you want to get HQ/high-speed graphics, just use OpenGL. Once you have the grip on it, rewriting will take you less time than trying to fix low-level/native Java2D implementations; AFAIR they were numerous attempts to streamline acceleration into Java, all ended with forks because it was such a royal PITA for everyone –  Oct 30 '14 at 18:19
  • Certainly! Though with an application this size I don't agree that rewriting would take less time. I have written a 2D layer in GL in the past and am very comfortable with it, but integrating it into this code would take several weeks. Not to mention, it has its own issues. The semi-solution I pasted below is (so far) what I'm going with, it only took a couple days of headache and runs quite fast. Honestly, the issue I'm hitting seems like a bug. Maybe even driver-related. Runs fine in window, loses HW acceleration in FSEM? so weird. – Jesse Oct 31 '14 at 20:06

2 Answers2

0

While I regard you approach technically feasible, I wonder why you are so set on rendering in a fixed resolution.

From a graphics quality standpoint, things should be rendered to the target resolution right away, eliminating the scaling completely. I realize that bitmap graphics (as in tiles etc.) will need to be scaled at some point, only I don't see why this needs to be done on the critical path (in realtime). My preferred aproach to this would be to pre-scale bitmaps to their required resolutions once (then either keep them cached scaled in memory or even on disk, depending which is more feasible).

There are a few things you may want to check, ensure that the image type used internally is compatible with the rendering surface (instead of using hardcoded TYPE_INT_RGB, create back buffers using a variant of createCompatibleImage, to ensure no format conversions are necessary). Also there is a whole buch of options that can alter what Graphics2D uses under the hood: http://docs.oracle.com/javase/7/docs/technotes/guides/2d/flags.html.

Finally, you may want to check that you aren't doing any of the "no go" things with your buffered images, like directly accessing the underlying buffer arrays (Can't accelerate pixel-modified BufferedImages), which interferes with javas ability to use hardware acceleration.

Community
  • 1
  • 1
Durandal
  • 19,919
  • 4
  • 36
  • 70
  • Keeping a large number of pre-scaled images in memory (or disk) isn't feasible for this application. We require fast load times, have strict memory limitations. We already stream images from disk to save memory, etc. doing this would make that process impossibly slow. Using a smaller backbuffer has fillrate performance benefits because we can render fewer pixels to a downscaled buffer and upscale. HW scaling is a (typically) extremely fast (<1ms) and industry standard solution to this sort of problem that also allows real-time resizing of the window. RE: "no-go" things, no we don't do that. – Jesse Oct 23 '14 at 20:19
0

I have yet to get full-screen exclusive mode to work in the above case, but I do have a workaround.

Stay in windowed mode, set the window size to match the size of the screen, set its position to graphicsDevice.getDefaultConfiguration().getBounds() and then setUndecorated(true). Finally, use java.awt.Robot to confine the mouse pointer to the window.

Not ideal, but visually it's indistinguishable from fullscreen and at least now the bilinear scale is fast. Unfortunately it means users cannot set their fullscreen resolution lower than their native resolution.

As an aside, I am seeing some pretty significant draw performance improvements using BufferedImage (drops from 2ms to 1ms) and even more with VolatileImage (drops to 0.3ms) provided that I had -Dsun.java2d.accthreshold=0 in the VM arguments. But in fullscreen mode these performance improvements vanished (likely resorting to software scaling solution)

Jesse
  • 51
  • 7