Skip to content

feat: vectorize image processing with Java Vector API - #191

Draft
crramirez with Copilot wants to merge 9 commits into
mainfrom
copilot/investigate-java-vector-api
Draft

feat: vectorize image processing with Java Vector API#191
crramirez with Copilot wants to merge 9 commits into
mainfrom
copilot/investigate-java-vector-api

Conversation

Copilot AI commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Summary

  • vectorize image processing with the Java Vector API
  • upgrade the GraalVM native build plugin and improve OS detection for native build arguments

Testing

  • Not run in this session

…vector)

- Enable jdk.incubator.vector in module-info.java, build.gradle compile
  options, test JVM args, and native-image buildArgs
- ArrayImageRGB.alphaBlendOver: vectorized inner blend loop using IntVector
  (8 lanes on AVX2, 4 on NEON) with scalar tail fallback
- ScaleImageUtils: vectorized Mitchell-Netravali [R,G,B,W] accumulation
  using DoubleVector in both resampleHorizontal and resampleVertical
- Rgb.distanceSquaredSum: new SIMD batch method summing squared RGB
  distances across a pixel array using IntVector
- UnicodeGlyphImage: use Rgb.distanceSquaredSum in computeRegionStdDev
  and computeFullBlockStdDev; update CC0 header to Apache 2.0

Co-authored-by: crramirez <8397379+crramirez@users.noreply.github.com>
Copilot AI and others added 2 commits August 5, 2026 10:10
Co-authored-by: crramirez <8397379+crramirez@users.noreply.github.com>
Co-authored-by: crramirez <8397379+crramirez@users.noreply.github.com>
@crramirez

Copy link
Copy Markdown
Owner

@copilot you added a new bundle, remember to add it also to the native jsons for its native image inclusion

Co-authored-by: crramirez <8397379+crramirez@users.noreply.github.com>

Copilot AI commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

@copilot you added a new bundle, remember to add it also to the native jsons for its native image inclusion

Fixed in 33b0771.

@crramirez
crramirez marked this pull request as ready for review August 5, 2026 15:56
Copilot AI lite review requested due to automatic review settings August 5, 2026 15:56

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces Java Vector API–accelerated kernels for image processing in Casciian’s bits package, and wires in a demo UI to compare vectorized vs scalar baselines. It also updates build/native-image settings so the incubator Vector module is available during compilation, tests, and GraalVM native builds.

Changes:

  • Vectorized core image kernels (alpha blending and RGB distance-sum) and updated call sites to use the new SIMD kernel.
  • Added a demo “Vector performance” window + localized resource bundles and native-image resource configuration.
  • Updated module/build/native-image configuration to include jdk.incubator.vector.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
code/src/main/resources/META-INF/native-image/io.crramirez/casciian-demo/resource-config.json Registers the new demo resource bundle for native-image builds.
code/src/main/resources/demo/DemoVectorPerformanceWindowBundle.properties Adds English strings for the new vector performance demo window.
code/src/main/resources/demo/DemoVectorPerformanceWindowBundle_es.properties Adds Spanish strings for the new vector performance demo window.
code/src/main/resources/demo/DemoMainWindowBundle.properties Adds labels/buttons to open the new performance comparison window.
code/src/main/resources/demo/DemoMainWindowBundle_es.properties Spanish localization for the new main-window entries.
code/src/main/resources/demo/DemoApplicationBundle.properties Adds a menu item to launch the vector performance window.
code/src/main/resources/demo/DemoApplicationBundle_es.properties Spanish localization for the new menu item.
code/src/main/java/module-info.java Declares a module dependency on jdk.incubator.vector.
code/src/main/java/demo/DemoVectorPerformanceWindow.java New demo UI window that benchmarks vector vs scalar kernels.
code/src/main/java/demo/DemoMainWindow.java Hooks the new demo window into the main demo UI.
code/src/main/java/demo/DemoApplication.java Adds a demo menu item and handler to open the new window.
code/src/main/java/casciian/bits/UnicodeGlyphImage.java Switches stddev computation to use the new distance-sum kernel (with new flattening/allocation behavior).
code/src/main/java/casciian/bits/ScaleImageUtils.java Introduces a Vector API path for convolution accumulation in resampling.
code/src/main/java/casciian/bits/Rgb.java Adds distanceSquaredSum(...) SIMD kernel.
code/src/main/java/casciian/bits/ArrayImageRGB.java Vectorizes alphaBlendOver(...) via a SIMD row kernel.
code/build.gradle Adds --add-modules jdk.incubator.vector for compile/test/native-image.
Suppressed comments (5)

code/src/main/java/casciian/bits/ScaleImageUtils.java:112

  • This block declares sumR/sumG/sumB/sumW and then immediately resets them to 0 again; the second assignment is redundant and makes the inner loop harder to read.
                double sumR = 0, sumG = 0, sumB = 0, sumW = 0;
                sumR = 0; sumG = 0; sumB = 0; sumW = 0;

code/src/main/java/casciian/bits/ScaleImageUtils.java:126

  • The vectorized accumulation currently allocates new double[] arrays inside the tap loop (one per source sample). This turns the hot inner loop into an allocation-heavy path and can easily dominate runtime/GC, masking any SIMD benefit.
                        // [R, G, B, W] * weight  (pad with 0 for lanes > 4)
                        double[] contrib = new double[laneCount];
                        contrib[0] = ((pixel >>> 16) & 0xFF) * weight;

code/src/main/java/casciian/bits/ScaleImageUtils.java:194

  • Same issue in the vertical pass: allocating a new contrib/result array inside the tap loop makes the SIMD path allocation-bound. Reusing a single scratch array per output pixel avoids O(kernelWidth) allocations per destination pixel.
                        double[] contrib = new double[laneCount];
                        contrib[0] = ((pixel >>> 16) & 0xFF) * weight;
                        contrib[1] = ((pixel >>>  8) & 0xFF) * weight;
                        contrib[2] = ( pixel         & 0xFF) * weight;
                        contrib[3] = weight;
                        acc = acc.add(DoubleVector.fromArray(DOUBLE_SPECIES, contrib, 0));

code/src/main/java/casciian/bits/ScaleImageUtils.java:49

  • The comment claims AVX2 does a fused-multiply-add (FMA) per pixel, but the implementation uses scalar multiplies into a temporary array plus a vector add (no FMA). This is misleading for future maintainers trying to reason about the vectorization strategy.
     * Preferred {@code double} vector species for SIMD convolution.
     * On x86 with AVX2 this is 4 lanes; on ARM NEON it is 2 lanes.
     * The channel layout used in the kernel is [R, G, B, W] — exactly
     * 4 doubles — so AVX2 processes one pixel's full kernel contribution
     * in a single fused-multiply-add step.

code/src/main/java/demo/DemoVectorPerformanceWindow.java:335

  • This file defines a private LongSupplier even though the JDK already provides java.util.function.LongSupplier. Using the JDK type would reduce custom surface area and avoid confusing readers who expect the standard functional interface.
    @FunctionalInterface
    private interface LongSupplier {
        long getAsLong();
    }

Comment thread code/src/main/java/casciian/bits/ScaleImageUtils.java Outdated
Comment thread code/src/main/java/demo/DemoVectorPerformanceWindow.java
Comment on lines +333 to +338
public static long distanceSquaredSum(final int[] pixels,
final int count,
final int color) {
final VectorSpecies<Integer> species = IntVector.SPECIES_PREFERRED;
final int laneCount = species.length();

Comment on lines +146 to +151
// Separate the pixels into two regions so we can call the vectorized
// distanceSquaredSum on each contiguous array.
int splitFore = leftRight ? (width / 2) * height : width * (height / 2);
int[] forePixels = new int[splitFore];
int[] backPixels = new int[pixelCount - splitFore];
int fi = 0, bi = 0;
@crramirez

Copy link
Copy Markdown
Owner

@copilot Fix the code for all comments in this review thread.

When a review comment includes a suggested change, apply the suggestion exactly.

Do not make changes beyond what is described in the linked review thread.

Co-authored-by: crramirez <8397379+crramirez@users.noreply.github.com>

Copilot AI commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

@copilot Fix the code for all comments in this review thread.

When a review comment includes a suggested change, apply t...

Addressed in cb4f313: removed the unused imports, added direct tests for Rgb.distanceSquaredSum across varied lengths/tails, and reworked UnicodeGlyphImage stddev paths to reuse buffers instead of allocating per call.

@crramirez

Copy link
Copy Markdown
Owner

@copilot this screenshot was taken from a native build

**
image
**

and this running the jar in java the first time

image

and this after ten times

image

Co-authored-by: crramirez <8397379+crramirez@users.noreply.github.com>

Copilot AI commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

@copilot this screenshot was taken from a native build

**
...

Addressed in d05d930. I added native-image-aware scalar fallbacks for ArrayImageRGB.alphaBlendOver and Rgb.distanceSquaredSum while keeping the SIMD path on regular JVM runs, so native builds no longer pay Vector API overhead where it regresses.

Copilot AI and others added 2 commits August 6, 2026 20:40
… harness

Co-authored-by: crramirez <8397379+crramirez@users.noreply.github.com>
Co-authored-by: crramirez <8397379+crramirez@users.noreply.github.com>
…or` for specific modules and adjust native build arguments to enable fallback compilation.
@crramirez
crramirez marked this pull request as draft August 7, 2026 23:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants