JDK-4185726 : JDK 1.2 drawImage very slow compared to JDK 1.1.7.
  • Type: Bug
  • Component: client-libs
  • Sub-Component: 2d
  • Affected Version: 1.2.0,1.2.2
  • Priority: P3
  • Status: Closed
  • Resolution: Duplicate
  • OS: generic,windows_95
  • CPU: generic,x86
  • Submitted: 1998-10-29
  • Updated: 2013-11-01
  • Resolved: 1999-09-25
Related Reports
Duplicate :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Description

Name: tb29552			Date: 10/29/98


/*

drawImage is very slow in JDK 1.2rc1. My "Plasma" benchmark at
http://rsb.info.nih.gov/plasma/ runs at 1/2 speed compared to JDK 1.1.7.

Details:
   JDK 1.1.7 speed: 89 frames per second 
   JDK 1.2rc1 speed: 46 frames per second
    OS: Windows 95
    Machine: P2/400, 64MB
    Graphics: ATI 3D Rage Pro
    Display depth: 16-bits

Source code is at:
    http://rsb.info.nih.gov/plasma/source.html

This applet creates an animated display by summing four
sine waves into an array. Example FPS rates are at
http://rsb.info.nih.gov/plasma.
It is based on "Sam's Java Plasma Applet"
(http://www.dur.ac.uk/~d405ua/Plasma.html) by Sam Marshall
(###@###.###). It was modified to use 8-bit images
by Menno van Gangelen (###@###.###).

Here is what the applet tag used to run
the Plasma benchmark looks like:

    <applet code="Plasma.class" width=320 height=240>
       <param name=scale value="2">
       <param name=showfps value="true">
    </applet>

*/

import java.awt.*;
import java.awt.image.*;

public class Plasma extends java.applet.Applet implements Runnable {
  Image img;
  Thread runThread;
  long firstFrame, frames, fps;
  int width, height;
  int w,h,size;
  int scale=3;
  boolean showFPS = true;
  IndexColorModel icm;
  int[] waveTable;
  byte[][] paletteTable;
  byte[] pixels;

  public void init() {
     width = size().width;
     height = size().height;
     String p = getParameter("scale");
     if (p != null)
        scale = Integer.parseInt(p);
     p = getParameter("showfps");
     if (p != null)
         showFPS = p.equals("true");
     w = width/scale;
     h = w;
     size = (int) ((w+h)/2)*4;
     pixels = new byte[w*h];
     waveTable = new int[size];
     paletteTable = new byte[3][256];
     calculatePaletteTable();
     img=createImage(new MemoryImageSource(w,h,icm,pixels,0,w));
  }

  public void start() {
     if (runThread == null) {
       runThread=new Thread(this);
       runThread.start();
       firstFrame=System.currentTimeMillis();
       frames = 0;
     };
  }

  public void stop() {
    if (runThread != null) {
       runThread.stop();
       runThread=null;
    }
  }

  public void update(Graphics g) {
     img.flush();
     g.drawImage(img, 0, 0, width, height, null);
     if (showFPS) {
        frames++;
        fps = (frames*10000) / (System.currentTimeMillis()-firstFrame);
        g.drawString(fps/10 + "." + fps%10 + " fps", 2, height - 2);
     }
  }

  void calculateWaveTable() {
    for(int i=0;i<size;i++)
       waveTable[i]=(int)(32*(1+Math.sin(((double)i*2*Math.PI)/size)));
  }

  int FadeBetween(int start,int end,int proportion) {
    return ((end-start)*proportion)/128+start;
  }

  void calculatePaletteTable() {
     for(int i=0;i<128;i++) {
        paletteTable[0][i]=(byte)FadeBetween(0,255,i);
        paletteTable[1][i]=(byte)0;
        paletteTable[2][i]=(byte)FadeBetween(255,0,i);
     }
     for(int i=0;i<128;i++) {
        paletteTable[0][i+128]=(byte)FadeBetween(255,0,i);
        paletteTable[1][i+128]=(byte)0;
        paletteTable[2][i+128]=(byte)FadeBetween(0,255,i);
     }
     icm = new IndexColorModel(8, 256, paletteTable[0], paletteTable[1], paletteTable[2]);
  }

  public void run() {
    int x,y;
    int index;
    int tempval,result;
    int spd1=2,spd2=5,spd3=1,spd4=4;
    int pos1=0,pos2=0,pos3=0,pos4=0;
    int tpos1,tpos2,tpos3,tpos4;
    int inc1=6,inc2=3,inc3=3,inc4=9;

    runThread.setPriority(Thread.MIN_PRIORITY);
    calculateWaveTable();
    while(true) {
      index=0;
      tpos1=pos1; tpos2=pos2;
      for(y=0;y<h;y++) {
        tpos3=pos3; tpos4=pos4;
        tpos1%=size; tpos2%=size;
        tempval=waveTable[tpos1] + waveTable[tpos2];
        for(x=0;x<w;x++) {
          tpos3%=size; tpos4%=size;
          result=tempval + waveTable[tpos3] + waveTable[tpos4];
          pixels[index++]=(byte)result;
          tpos3+=inc3; tpos4+=inc4;
        }
        tpos1+=inc1; tpos2+=inc2;
      }
      pos1+=spd1; pos2+=spd2; pos3+=spd3; pos4+=spd4;
      repaint();
      Thread.yield();
      //try {Thread.sleep(10);}
      //catch (InterruptedException e) { }
    }
  }

}

(Review ID: 41544)
======================================================================

Comments
WORK AROUND Name: tb29552 Date: 10/29/98 I know of no workaround. ======================================================================
11-06-2004

EVALUATION This has been identified as a key area by the JDK performance team. steve.wilson@eng 1999-01-12 I've been looking into this bug via the built-in 1.2 hprof profiler, and OptimizeIt. Looking at the Plasma demo which is shown in the bug description (with OptimizeIt) it quickly shows up that most of the time is not spent blitting the actual bits to the screen. It turns out that the current implementation of drawImage creates an intermediate representation of the image when scaling is required. This causes memory to be allocated, and extra memory copying to be done. It seemed that 60-70% of the time was being taken up by this memory allocation. I tried tweaking the html page to create a non-scaled version of the image and found that I could get frame rates of over 70-fps, where before I could only get 10-15%. I also looked at the submitter's ImageJ <http://rsb.info.nih.gov/ij/> program's Waves option (at the submitters request). For this trial I used the built-in 1.2 hprof profiler. This showed that almost 37% of the time (the largest component) is taken up by the setDataElements() function. This turns out to be part of the producer/consumer chain. For 1.2 this code was moved from C to Java, and may be suffering effects from array bounds checking, or other problem. I haven't yet dug into this yet. There also is some memory allocation going on here of a similar type to mentioned above, but it only accounts for about 10%. More to come later. steve.wilson@eng 1999-02-02 ----------------------------------------------- I've also tested a case supplied by ###@###.###. His test case renders grayscale images. I've attached the source code to his test case to this bug. I ran his test case against OptimizeIt (pressing the applet's button several times). Like the plama benchmark, this one had a large %-age of it's time taken up in DataBufferInt.<init>. Below is a the stack trace which lead to the construction of these objects: java.awt.image.DataBufferInt.<init>() java.awt.image.Raster.createPackedRaster() java.awt.image.DirectColorModel.createCompatibleWritableRaster() sun.java2d.loops.LockableRaster.<init>() sun.java2d.loops.RasterOutputManager.convertFrom() sun.java2d.loops.RasterOutputManager.performOpaqueBlit() sun.java2d.loops.RasterOutputManager.compositeSrcDst() sun.java2d.loops.RasterOutputManager.renderImage() sun.java2d.SunGraphics2D.renderingPipeImage() sun.java2d.SunGraphics2D.drawImage() sun.awt.image.BufferedImageGraphics2D.drawImage() sun.awt.image.ImageRepresentation.drawToBufImage() sun.awt.image.BufferedImageGraphics2D.drawImage() com.mitra.jimageqa.TestDraw$ImagePanel.paint() What's interesting about this case is that it doesn't involve image scaling (as the Plasma demo does). This seems to be the result of translating between color models. It seems that we don't have loops to handle some of these color models, and this requires a conversion to a color model we have a loop for. This conversion is done each time we paint. steve.wilson@eng 1999-02-09 There are a number of reasons that drawImage in JDK1.2+ is slower than JDK1.1. We are trying to address some of the problems but it will not be solved for Kestrel. One reason is that we currently do not store the image into hardware buffers. That is an optimization that we still hope to implement but have not as yet. (It might be easier to do so when Jim finishes the new pipeline architecture -- however that is slated for the next major release after Kestrel.) Another big reason is that we are slow when we scale images. JDK1.1 scaled images directly into the pixmap. We currently scale images into a BufferedImage and then blit it so we have a lot more overhead. Getting rid of the extra BufferedImage will be easier with Jim's new architecture. Another reason is that there is a lot of overhead in our rendering pipeline (and Jim's work will have cleaned that up). Jim has made some of our blitting loops more efficient (and these changes will be in Kestrel FCS) so there will be some noticeable changes there. jeannette.hung@Eng 1999-09-07 The Plasma demo suffers primarily from a lack of direct-to-screen image scaling code (bug #4268962). If you remove the scaling then it suffers a lesser amount from the inefficiency of the 8-bit rendering loops which convert the source palette entry for each destination pixel rather than converting the palette once up front and then using a lookup for each pixel (bug #4268438). The com.mitra demo suffers a bit from the inefficiency of the 8-bit loops when running with 8-bit grayscale (bug #4268438) and from a bug in the way that DirectColorModels construct their Rasters in the 24-bit grayscale mode (bug #4275538). I have tried to download the latest ImageJ program, but unfortunately it no longer seems to have the "Test Display Speed" option so I am at a loss as to how to reproduce or investigate the problems with that application. In any case, the primary focus of this bug is the Plasma demo as it was originally submitted. The problems in the Plasma demo are really a duplicate of 4268438, which was fixed for 1.3FCS and 4268962 which will probably not be fixed for JDK1.3. Thus, I am closing this bug as a duplicate of the 4268962 bug - the main remaining performance bottleneck which affects the Plasma demo. Other more specific bugs dealing with drawImage performance will be opened as new problems are identified. Since drawImage is just a front end for a very complex matrix of operations, no single bug could ever describe problems with such a system as generally as the synopsis for this bug would suggest. jim.graham@Eng 1999-09-24 Developers looking for a list of related bugs identifying specific performance issues with drawImage should examine the following list of bugids to see if these bugs represent their concerns. Note that some of these bugs are already fixed in an upcoming release. 4268962 Scaled drawImage slow in 1.2 due to use of intermediate buffers 4204845 Remote use of double buffering on JDK 1.2 is very slow 4276423 drawImage of offscreen image to screen much slower in JDK 1.2 4276434 Specifying alpha component for opaque images will slow rendering 4275538 DirectColorModel.isCompatibleRaster is overly strict 4272634 compiler flags hurt the performance of some of the rendering loops 4268438 drawImage from 8-bit image to a 15/16/24/32 bit destination is slow jim.graham@Eng 1999-09-29
29-09-1999