/hg/icedtea6: java2d performance backports.

dlila at icedtea.classpath.org dlila at icedtea.classpath.org
Wed Feb 23 07:53:21 PST 2011


changeset 514ec565d646 in /hg/icedtea6
details: http://icedtea.classpath.org/hg/icedtea6?cmd=changeset;node=514ec565d646
author: Denis Lila <dlila at redhat.com>
date: Wed Feb 23 10:47:03 2011 -0500

	java2d performance backports.


diffstat:

5 files changed, 5528 insertions(+), 1 deletion(-)
ChangeLog                                                     |    7 
Makefile.am                                                   |    4 
NEWS                                                          |    2 
patches/openjdk/6766342-AA-simple-shape-performance.patch     | 3819 ++++++++++
patches/openjdk/6775317-non-AA-simple-shape-performance.patch | 1697 ++++

diffs (truncated from 5564 to 500 lines):

diff -r e7cf01c42190 -r 514ec565d646 ChangeLog
--- a/ChangeLog	Wed Feb 23 15:19:53 2011 +0000
+++ b/ChangeLog	Wed Feb 23 10:47:03 2011 -0500
@@ -1,3 +1,10 @@ 2011-02-23  Andrew John Hughes  <ahughes
+2011-02-23  Denis Lila  <dlila at redhat.com>
+
+	* Makefile.am: Added 2 patches.
+	* NEWS: Updated.
+	* patches/openjdk/6775317-non-AA-simple-shape-performance.patch
+	* patches/openjdk/6766342-AA-simple-shape-performance.patch
+
 2011-02-23  Andrew John Hughes  <ahughes at redhat.com>
 
 	* INSTALL:
diff -r e7cf01c42190 -r 514ec565d646 Makefile.am
--- a/Makefile.am	Wed Feb 23 15:19:53 2011 +0000
+++ b/Makefile.am	Wed Feb 23 10:47:03 2011 -0500
@@ -327,7 +327,9 @@ ICEDTEA_PATCHES = \
 	patches/pr633-no_javaws_man_page.patch \
 	patches/pr586-include_all_srcs.patch \
 	patches/jtreg-LastErrorString.patch \
-	patches/com-sun-awt.patch
+	patches/com-sun-awt.patch \
+	patches/openjdk/6775317-non-AA-simple-shape-performance.patch \
+	patches/openjdk/6766342-AA-simple-shape-performance.patch
 
 if !WITH_ALT_HSBUILD
 ICEDTEA_PATCHES += \
diff -r e7cf01c42190 -r 514ec565d646 NEWS
--- a/NEWS	Wed Feb 23 15:19:53 2011 +0000
+++ b/NEWS	Wed Feb 23 10:47:03 2011 -0500
@@ -427,6 +427,8 @@ New in release 1.10 (2011-XX-XX):
   - S6779717: A Window does not show applet security warning icon on X platforms
   - S6785058: Parent don't get the focus after dialog is closed if security warning is applied
   - S6444769: java/awt/Insets/WindowWithWarningTest/WindowWithWarningTest.html fails
+  - S6775317: Improve performance of non-AA transformed rectangles and single wide lines in software pipelines
+  - S6766342: Improve performance of Ductus rasterizer
 * Bug fixes
   - RH661505: JPEGs with sRGB IEC61966-2.1 color profiles have wrong colors
   - PR600: HS19 upgrade broke CACAO build on ARM
diff -r e7cf01c42190 -r 514ec565d646 patches/openjdk/6766342-AA-simple-shape-performance.patch
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/patches/openjdk/6766342-AA-simple-shape-performance.patch	Wed Feb 23 10:47:03 2011 -0500
@@ -0,0 +1,3819 @@
+# HG changeset patch
+# User flar
+# Date 1292361929 28800
+# Node ID 4d6f9aaa2600eec36725ab7cd47cb4b652755723
+# Parent  0eeac8ca33e329aab7a9ad0fcfb3a321cd1abd06
+6766342: Improve performance of Ductus rasterizer
+Reviewed-by: jgodinez, prr
+
+diff -r 0eeac8ca33e3 -r 4d6f9aaa2600 make/sun/awt/mapfile-vers
+--- openjdk.orig/jdk/make/sun/awt/mapfile-vers	Fri Dec 10 16:14:04 2010 -0800
++++ openjdk/jdk/make/sun/awt/mapfile-vers	Tue Dec 14 13:25:29 2010 -0800
+@@ -118,6 +118,8 @@
+ 		Java_sun_java2d_loops_GraphicsPrimitiveMgr_registerNativeLoops;
+ 		Java_sun_java2d_loops_MaskBlit_MaskBlit;
+ 		Java_sun_java2d_loops_MaskFill_MaskFill;
++		Java_sun_java2d_loops_MaskFill_FillAAPgram;
++		Java_sun_java2d_loops_MaskFill_DrawAAPgram;
+ 		Java_sun_java2d_loops_TransformHelper_Transform;
+ 		Java_sun_java2d_pipe_Region_initIDs;
+ 		Java_sun_java2d_pipe_SpanClipRenderer_initIDs;
+diff -r 0eeac8ca33e3 -r 4d6f9aaa2600 make/sun/awt/mapfile-vers-linux
+--- openjdk.orig/jdk/make/sun/awt/mapfile-vers-linux	Fri Dec 10 16:14:04 2010 -0800
++++ openjdk/jdk/make/sun/awt/mapfile-vers-linux	Tue Dec 14 13:25:29 2010 -0800
+@@ -115,6 +115,8 @@
+ 		Java_sun_java2d_loops_GraphicsPrimitiveMgr_registerNativeLoops;
+ 		Java_sun_java2d_loops_MaskBlit_MaskBlit;
+ 		Java_sun_java2d_loops_MaskFill_MaskFill;
++		Java_sun_java2d_loops_MaskFill_FillAAPgram;
++		Java_sun_java2d_loops_MaskFill_DrawAAPgram;
+                 Java_sun_java2d_pipe_BufferedRenderPipe_fillSpans;
+ 		Java_sun_java2d_pipe_SpanClipRenderer_initIDs;
+ 		sun_awt_image_GifImageDecoder_initIDs;
+diff -r 0eeac8ca33e3 -r 4d6f9aaa2600 src/share/classes/sun/dc/DuctusRenderingEngine.java
+--- openjdk.orig/jdk/src/share/classes/sun/dc/DuctusRenderingEngine.java	Fri Dec 10 16:14:04 2010 -0800
++++ openjdk/jdk/src/share/classes/sun/dc/DuctusRenderingEngine.java	Tue Dec 14 13:25:29 2010 -0800
+@@ -635,6 +635,88 @@
+         return r;
+     }
+ 
++    /**
++     * {@inheritDoc}
++     */
++    @Override
++    public AATileGenerator getAATileGenerator(double x, double y,
++                                              double dx1, double dy1,
++                                              double dx2, double dy2,
++                                              double lw1, double lw2,
++                                              Region clip,
++                                              int bbox[])
++    {
++        // REMIND: Deal with large coordinates!
++        double ldx1, ldy1, ldx2, ldy2;
++        boolean innerpgram = (lw1 > 0 && lw2 > 0);
++
++        if (innerpgram) {
++            ldx1 = dx1 * lw1;
++            ldy1 = dy1 * lw1;
++            ldx2 = dx2 * lw2;
++            ldy2 = dy2 * lw2;
++            x -= (ldx1 + ldx2) / 2.0;
++            y -= (ldy1 + ldy2) / 2.0;
++            dx1 += ldx1;
++            dy1 += ldy1;
++            dx2 += ldx2;
++            dy2 += ldy2;
++            if (lw1 > 1 && lw2 > 1) {
++                // Inner parallelogram was entirely consumed by stroke...
++                innerpgram = false;
++            }
++        } else {
++            ldx1 = ldy1 = ldx2 = ldy2 = 0;
++        }
++
++        Rasterizer r = getRasterizer();
++
++        r.setUsage(Rasterizer.EOFILL);
++
++        r.beginPath();
++        r.beginSubpath((float) x, (float) y);
++        r.appendLine((float) (x+dx1), (float) (y+dy1));
++        r.appendLine((float) (x+dx1+dx2), (float) (y+dy1+dy2));
++        r.appendLine((float) (x+dx2), (float) (y+dy2));
++        r.closedSubpath();
++        if (innerpgram) {
++            x += ldx1 + ldx2;
++            y += ldy1 + ldy2;
++            dx1 -= 2.0 * ldx1;
++            dy1 -= 2.0 * ldy1;
++            dx2 -= 2.0 * ldx2;
++            dy2 -= 2.0 * ldy2;
++            r.beginSubpath((float) x, (float) y);
++            r.appendLine((float) (x+dx1), (float) (y+dy1));
++            r.appendLine((float) (x+dx1+dx2), (float) (y+dy1+dy2));
++            r.appendLine((float) (x+dx2), (float) (y+dy2));
++            r.closedSubpath();
++        }
++
++        try {
++            r.endPath();
++            r.getAlphaBox(bbox);
++            clip.clipBoxToBounds(bbox);
++            if (bbox[0] >= bbox[2] || bbox[1] >= bbox[3]) {
++                dropRasterizer(r);
++                return null;
++            }
++            r.setOutputArea(bbox[0], bbox[1],
++                            bbox[2] - bbox[0],
++                            bbox[3] - bbox[1]);
++        } catch (PRException e) {
++            /*
++             * This exeption is thrown from the native part of the Ductus
++             * (only in case of a debug build) to indicate that some
++             * segments of the path have very large coordinates.
++             * See 4485298 for more info.
++             */
++            System.err.println("DuctusRenderingEngine.getAATileGenerator: "+e);
++        }
++
++        return r;
++    }
++
+     private void feedConsumer(PathConsumer consumer, PathIterator pi) {
+         try {
+             consumer.beginPath();
+diff -r 0eeac8ca33e3 -r 4d6f9aaa2600 src/share/classes/sun/java2d/SurfaceData.java
+--- openjdk.orig/jdk/src/share/classes/sun/java2d/SurfaceData.java	Fri Dec 10 16:14:04 2010 -0800
++++ openjdk/jdk/src/share/classes/sun/java2d/SurfaceData.java	Tue Dec 14 13:25:29 2010 -0800
+@@ -367,16 +367,17 @@
+     public static final TextPipe aaTextRenderer;
+     public static final TextPipe lcdTextRenderer;
+ 
+-    protected static final CompositePipe colorPipe;
++    protected static final AlphaColorPipe colorPipe;
+     protected static final PixelToShapeConverter colorViaShape;
+     protected static final PixelToParallelogramConverter colorViaPgram;
+     protected static final TextPipe colorText;
+     protected static final CompositePipe clipColorPipe;
+     protected static final TextPipe clipColorText;
+     protected static final AAShapePipe AAColorShape;
+-    protected static final PixelToShapeConverter AAColorViaShape;
++    protected static final PixelToParallelogramConverter AAColorViaShape;
++    protected static final PixelToParallelogramConverter AAColorViaPgram;
+     protected static final AAShapePipe AAClipColorShape;
+-    protected static final PixelToShapeConverter AAClipColorViaShape;
++    protected static final PixelToParallelogramConverter AAClipColorViaShape;
+ 
+     protected static final CompositePipe paintPipe;
+     protected static final SpanShapeRenderer paintShape;
+@@ -385,9 +386,9 @@
+     protected static final CompositePipe clipPaintPipe;
+     protected static final TextPipe clipPaintText;
+     protected static final AAShapePipe AAPaintShape;
+-    protected static final PixelToShapeConverter AAPaintViaShape;
++    protected static final PixelToParallelogramConverter AAPaintViaShape;
+     protected static final AAShapePipe AAClipPaintShape;
+-    protected static final PixelToShapeConverter AAClipPaintViaShape;
++    protected static final PixelToParallelogramConverter AAClipPaintViaShape;
+ 
+     protected static final CompositePipe compPipe;
+     protected static final SpanShapeRenderer compShape;
+@@ -396,9 +397,9 @@
+     protected static final CompositePipe clipCompPipe;
+     protected static final TextPipe clipCompText;
+     protected static final AAShapePipe AACompShape;
+-    protected static final PixelToShapeConverter AACompViaShape;
++    protected static final PixelToParallelogramConverter AACompViaShape;
+     protected static final AAShapePipe AAClipCompShape;
+-    protected static final PixelToShapeConverter AAClipCompViaShape;
++    protected static final PixelToParallelogramConverter AAClipCompViaShape;
+ 
+     protected static final DrawImagePipe imagepipe;
+ 
+@@ -427,6 +428,22 @@
+         }
+     }
+ 
++    private static PixelToParallelogramConverter
++        makeConverter(AAShapePipe renderer,
++                      ParallelogramPipe pgrampipe)
++    {
++        return new PixelToParallelogramConverter(renderer,
++                                                 pgrampipe,
++                                                 1.0/8.0, 0.499,
++                                                 false);
++    }
++
++    private static PixelToParallelogramConverter
++        makeConverter(AAShapePipe renderer)
++    {
++        return makeConverter(renderer, renderer);
++    }
++
+     static {
+         colorPrimitives = new LoopPipe();
+ 
+@@ -445,9 +462,10 @@
+         clipColorPipe = new SpanClipRenderer(colorPipe);
+         clipColorText = new TextRenderer(clipColorPipe);
+         AAColorShape = new AAShapePipe(colorPipe);
+-        AAColorViaShape = new PixelToShapeConverter(AAColorShape);
++        AAColorViaShape = makeConverter(AAColorShape);
++        AAColorViaPgram = makeConverter(AAColorShape, colorPipe);
+         AAClipColorShape = new AAShapePipe(clipColorPipe);
+-        AAClipColorViaShape = new PixelToShapeConverter(AAClipColorShape);
++        AAClipColorViaShape = makeConverter(AAClipColorShape);
+ 
+         paintPipe = new AlphaPaintPipe();
+         paintShape = new SpanShapeRenderer.Composite(paintPipe);
+@@ -456,9 +474,9 @@
+         clipPaintPipe = new SpanClipRenderer(paintPipe);
+         clipPaintText = new TextRenderer(clipPaintPipe);
+         AAPaintShape = new AAShapePipe(paintPipe);
+-        AAPaintViaShape = new PixelToShapeConverter(AAPaintShape);
++        AAPaintViaShape = makeConverter(AAPaintShape);
+         AAClipPaintShape = new AAShapePipe(clipPaintPipe);
+-        AAClipPaintViaShape = new PixelToShapeConverter(AAClipPaintShape);
++        AAClipPaintViaShape = makeConverter(AAClipPaintShape);
+ 
+         compPipe = new GeneralCompositePipe();
+         compShape = new SpanShapeRenderer.Composite(compPipe);
+@@ -467,9 +485,9 @@
+         clipCompPipe = new SpanClipRenderer(compPipe);
+         clipCompText = new TextRenderer(clipCompPipe);
+         AACompShape = new AAShapePipe(compPipe);
+-        AACompViaShape = new PixelToShapeConverter(AACompShape);
++        AACompViaShape = makeConverter(AACompShape);
+         AAClipCompShape = new AAShapePipe(clipCompPipe);
+-        AAClipCompViaShape = new PixelToShapeConverter(AAClipCompShape);
++        AAClipCompViaShape = makeConverter(AAClipCompShape);
+ 
+         imagepipe = new DrawImage();
+     }
+@@ -591,12 +609,12 @@
+                 if (sg2d.clipState == sg2d.CLIP_SHAPE) {
+                     sg2d.drawpipe = AAClipCompViaShape;
+                     sg2d.fillpipe = AAClipCompViaShape;
+-                    sg2d.shapepipe = AAClipCompShape;
++                    sg2d.shapepipe = AAClipCompViaShape;
+                     sg2d.textpipe = clipCompText;
+                 } else {
+                     sg2d.drawpipe = AACompViaShape;
+                     sg2d.fillpipe = AACompViaShape;
+-                    sg2d.shapepipe = AACompShape;
++                    sg2d.shapepipe = AACompViaShape;
+                     sg2d.textpipe = compText;
+                 }
+             } else {
+@@ -616,12 +634,16 @@
+                 if (sg2d.clipState == sg2d.CLIP_SHAPE) {
+                     sg2d.drawpipe = AAClipColorViaShape;
+                     sg2d.fillpipe = AAClipColorViaShape;
+-                    sg2d.shapepipe = AAClipColorShape;
++                    sg2d.shapepipe = AAClipColorViaShape;
+                     sg2d.textpipe = clipColorText;
+                 } else {
+-                    sg2d.drawpipe = AAColorViaShape;
+-                    sg2d.fillpipe = AAColorViaShape;
+-                    sg2d.shapepipe = AAColorShape;
++                    PixelToParallelogramConverter converter =
++                        (sg2d.alphafill.canDoParallelograms()
++                         ? AAColorViaPgram
++                         : AAColorViaShape);
++                    sg2d.drawpipe = converter;
++                    sg2d.fillpipe = converter;
++                    sg2d.shapepipe = converter;
+                     if (sg2d.paintState > sg2d.PAINT_OPAQUECOLOR ||
+                         sg2d.compositeState > sg2d.COMP_ISCOPY)
+                     {
+@@ -634,12 +656,12 @@
+                 if (sg2d.clipState == sg2d.CLIP_SHAPE) {
+                     sg2d.drawpipe = AAClipPaintViaShape;
+                     sg2d.fillpipe = AAClipPaintViaShape;
+-                    sg2d.shapepipe = AAClipPaintShape;
++                    sg2d.shapepipe = AAClipPaintViaShape;
+                     sg2d.textpipe = clipPaintText;
+                 } else {
+                     sg2d.drawpipe = AAPaintViaShape;
+                     sg2d.fillpipe = AAPaintViaShape;
+-                    sg2d.shapepipe = AAPaintShape;
++                    sg2d.shapepipe = AAPaintViaShape;
+                     sg2d.textpipe = paintText;
+                 }
+             }
+@@ -793,6 +815,18 @@
+         }
+     }
+ 
++    private static CompositeType getFillCompositeType(SunGraphics2D sg2d) {
++        CompositeType compType = sg2d.imageComp;
++        if (sg2d.compositeState == sg2d.COMP_ISCOPY) {
++            if (compType == CompositeType.SrcOverNoEa) {
++                compType = CompositeType.OpaqueSrcOverNoEa;
++            } else {
++                compType = CompositeType.SrcNoEa;
++            }
++        }
++        return compType;
++    }
++
+     /**
+      * Returns a MaskFill object that can be used on this destination
+      * with the source (paint) and composite types determined by the given
+@@ -802,9 +836,10 @@
+      * surface) before returning a specific MaskFill object.
+      */
+     protected MaskFill getMaskFill(SunGraphics2D sg2d) {
+-        return MaskFill.getFromCache(getPaintSurfaceType(sg2d),
+-                                     sg2d.imageComp,
+-                                     getSurfaceType());
++        SurfaceType src = getPaintSurfaceType(sg2d);
++        CompositeType comp = getFillCompositeType(sg2d);
++        SurfaceType dst = getSurfaceType();
++        return MaskFill.getFromCache(src, comp, dst);
+     }
+ 
+     private static RenderCache loopcache = new RenderCache(30);
+@@ -816,9 +851,7 @@
+      */
+     public RenderLoops getRenderLoops(SunGraphics2D sg2d) {
+         SurfaceType src = getPaintSurfaceType(sg2d);
+-        CompositeType comp = (sg2d.compositeState == sg2d.COMP_ISCOPY
+-                              ? CompositeType.SrcNoEa
+-                              : sg2d.imageComp);
++        CompositeType comp = getFillCompositeType(sg2d);
+         SurfaceType dst = sg2d.getSurfaceData().getSurfaceType();
+ 
+         Object o = loopcache.get(src, comp, dst);
+diff -r 0eeac8ca33e3 -r 4d6f9aaa2600 src/share/classes/sun/java2d/loops/CompositeType.java
+--- openjdk.orig/jdk/src/share/classes/sun/java2d/loops/CompositeType.java	Fri Dec 10 16:14:04 2010 -0800
++++ openjdk/jdk/src/share/classes/sun/java2d/loops/CompositeType.java	Tue Dec 14 13:25:29 2010 -0800
+@@ -27,6 +27,7 @@
+ 
+ import java.awt.image.BufferedImage;
+ import java.awt.AlphaComposite;
++import java.util.HashMap;
+ 
+ /**
+  * A CompositeType object provides a chained description of a type of
+@@ -51,6 +52,11 @@
+  * the indicated algorithm if all of the more specific searches fail.
+  */
+ public final class CompositeType {
++
++    private static int unusedUID = 1;
++    private static final HashMap<String,Integer> compositeUIDMap =
++        new HashMap<String,Integer>(100);
++
+     /*
+      * CONSTANTS USED BY ALL PRIMITIVES TO DESCRIBE THE COMPOSITING
+      * ALGORITHMS THEY CAN PERFORM
+@@ -153,6 +159,22 @@
+         SrcOverNoEa   = SrcOver.deriveSubType(DESC_SRC_OVER_NO_EA);
+ 
+     /*
++     * A special CompositeType for the case where we are filling in
++     * SrcOverNoEa mode with an opaque color.  In that case then the
++     * best loop for us to use would be a SrcNoEa loop, but what if
++     * there is no such loop?  In that case then we would end up
++     * backing off to a Src loop (which should still be fine) or an
++     * AnyAlpha loop which would be slower than a SrcOver loop in
++     * most cases.
++     * The fix is to use the following chain which looks for loops
++     * in the following order:
++     *    SrcNoEa, Src, SrcOverNoEa, SrcOver, AnyAlpha
++     */
++    public static final CompositeType
++        OpaqueSrcOverNoEa = SrcOverNoEa.deriveSubType(DESC_SRC)
++                                       .deriveSubType(DESC_SRC_NO_EA);
++
++    /*
+      * END OF CompositeType OBJECTS FOR THE VARIOUS CONSTANTS
+      */
+ 
+@@ -210,7 +232,6 @@
+         }
+     }
+ 
+-    private static int unusedUID = 1;
+     private int uniqueID;
+     private String desc;
+     private CompositeType next;
+@@ -218,14 +239,20 @@
+     private CompositeType(CompositeType parent, String desc) {
+         next = parent;
+         this.desc = desc;
+-        this.uniqueID = makeUniqueID();
++        this.uniqueID = makeUniqueID(desc);
+     }
+ 
+-    private synchronized static final int makeUniqueID() {
+-        if (unusedUID > 255) {
+-            throw new InternalError("composite type id overflow");
++    public synchronized static final int makeUniqueID(String desc) {
++        Integer i = compositeUIDMap.get(desc);
++
++        if (i == null) {
++            if (unusedUID > 255) {
++                throw new InternalError("composite type id overflow");
++            }
++            i = unusedUID++;
++            compositeUIDMap.put(desc, i);
+         }
+-        return unusedUID++;
++        return i;
+     }
+ 
+     public int getUniqueID() {
+diff -r 0eeac8ca33e3 -r 4d6f9aaa2600 src/share/classes/sun/java2d/loops/MaskFill.java
+--- openjdk.orig/jdk/src/share/classes/sun/java2d/loops/MaskFill.java	Fri Dec 10 16:14:04 2010 -0800
++++ openjdk/jdk/src/share/classes/sun/java2d/loops/MaskFill.java	Tue Dec 14 13:25:29 2010 -0800
+@@ -50,6 +50,10 @@
+ public class MaskFill extends GraphicsPrimitive
+ {
+     public static final String methodSignature = "MaskFill(...)".toString();
++    public static final String fillPgramSignature =
++        "FillAAPgram(...)".toString();
++    public static final String drawPgramSignature =
++        "DrawAAPgram(...)".toString();
+ 
+     public static final int primTypeID = makePrimTypeID();
+ 
+@@ -92,6 +96,14 @@
+         return fill;
+     }
+ 
++    protected MaskFill(String alternateSignature,
++                       SurfaceType srctype,
++                       CompositeType comptype,
++                       SurfaceType dsttype)
++    {
++        super(alternateSignature, primTypeID, srctype, comptype, dsttype);
++    }
++
+     protected MaskFill(SurfaceType srctype,
+                        CompositeType comptype,
+                        SurfaceType dsttype)
+@@ -115,6 +127,23 @@
+                                 int x, int y, int w, int h,
+                                 byte[] mask, int maskoff, int maskscan);
+ 
++    public native void FillAAPgram(SunGraphics2D sg2d, SurfaceData sData,
++                                   Composite comp,
++                                   double x, double y,
++                                   double dx1, double dy1,
++                                   double dx2, double dy2);
++
++    public native void DrawAAPgram(SunGraphics2D sg2d, SurfaceData sData,
++                                   Composite comp,
++                                   double x, double y,
++                                   double dx1, double dy1,
++                                   double dx2, double dy2,
++                                   double lw1, double lw2);
++
++    public boolean canDoParallelograms() {
++        return (getNativePrim() != 0);
++    }



More information about the distro-pkg-dev mailing list