United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: 4185726 JDK 1.2 drawImage very slow compared to JDK 1.1.7.
4185726 : JDK 1.2 drawImage very slow compared to JDK 1.1.7.

Details
Type:
Bug
Submit Date:
1998-10-29
Status:
Closed
Updated Date:
1999-09-25
Project Name:
JDK
Resolved Date:
1999-09-25
Component:
client-libs
OS:
generic,windows_95
Sub-Component:
2d
CPU:
x86,generic
Priority:
P3
Resolution:
Duplicate
Affected Versions:
1.2.0,1.2.2
Fixed Versions:

Related Reports
Duplicate:
Relates:
Relates:
Relates:
Relates:
Relates:
Relates:

Sub Tasks

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.
======================================================================
                                     
2004-06-11
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
                                     
1999-09-29



Hardware and Software, Engineered to Work Together