mirror of
https://github.com/JetBrains/JetBrainsRuntime.git
synced 2025-12-06 09:29:38 +01:00
JBR-8448 Vulkan: Cleanup & fix Sw->Surface blit
This commit is contained in:
committed by
Alexey Ushakov
parent
ac68b628c7
commit
8120022a66
@@ -48,7 +48,6 @@ import java.awt.image.BufferedImageOp;
|
||||
import java.lang.annotation.Native;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static sun.java2d.pipe.BufferedOpCodes.BLIT;
|
||||
@@ -59,90 +58,19 @@ import static java.awt.Transparency.TRANSLUCENT;
|
||||
final class VKBlitLoops {
|
||||
|
||||
static void register() {
|
||||
Blit blitIntArgbPreToSurface =
|
||||
new VKSwToSurfaceBlit(SurfaceType.IntArgbPre,
|
||||
VKSurfaceData.PF_INT_ARGB_PRE);
|
||||
Blit blitIntArgbPreToTexture =
|
||||
new VKSwToTextureBlit(SurfaceType.IntArgbPre,
|
||||
VKSurfaceData.PF_INT_ARGB_PRE);
|
||||
TransformBlit transformBlitIntArgbPreToSurface =
|
||||
new VKSwToSurfaceTransform(SurfaceType.IntArgbPre,
|
||||
VKSurfaceData.PF_INT_ARGB_PRE);
|
||||
VKSurfaceToSwBlit blitSurfaceToIntArgbPre =
|
||||
new VKSurfaceToSwBlit(VKFormat.B8G8R8A8_UNORM, TRANSLUCENT); // TODO this is a placeholder.
|
||||
|
||||
GraphicsPrimitive[] primitiveArray = {
|
||||
// sw->surface ops
|
||||
blitIntArgbPreToSurface,
|
||||
new VKSwToSurfaceBlit(SurfaceType.IntRgb,
|
||||
VKSurfaceData.PF_INT_RGB),
|
||||
new VKSwToSurfaceBlit(SurfaceType.IntRgbx,
|
||||
VKSurfaceData.PF_INT_RGBX),
|
||||
new VKSwToSurfaceBlit(SurfaceType.IntBgr,
|
||||
VKSurfaceData.PF_INT_BGR),
|
||||
new VKSwToSurfaceBlit(SurfaceType.IntBgrx,
|
||||
VKSurfaceData.PF_INT_BGRX),
|
||||
new VKGeneralBlit(VKSurfaceData.VKSurface,
|
||||
CompositeType.AnyAlpha,
|
||||
blitIntArgbPreToSurface),
|
||||
|
||||
new VKAnyCompositeBlit(VKSurfaceData.VKSurface,
|
||||
blitSurfaceToIntArgbPre,
|
||||
blitSurfaceToIntArgbPre,
|
||||
blitIntArgbPreToSurface),
|
||||
new VKAnyCompositeBlit(SurfaceType.Any,
|
||||
null,
|
||||
blitSurfaceToIntArgbPre,
|
||||
blitIntArgbPreToSurface),
|
||||
|
||||
new VKSwToSurfaceScale(SurfaceType.IntRgb,
|
||||
VKSurfaceData.PF_INT_RGB),
|
||||
new VKSwToSurfaceScale(SurfaceType.IntRgbx,
|
||||
VKSurfaceData.PF_INT_RGBX),
|
||||
new VKSwToSurfaceScale(SurfaceType.IntBgr,
|
||||
VKSurfaceData.PF_INT_BGR),
|
||||
new VKSwToSurfaceScale(SurfaceType.IntBgrx,
|
||||
VKSurfaceData.PF_INT_BGRX),
|
||||
new VKSwToSurfaceScale(SurfaceType.IntArgbPre,
|
||||
VKSurfaceData.PF_INT_ARGB_PRE),
|
||||
|
||||
new VKSwToSurfaceTransform(SurfaceType.IntRgb,
|
||||
VKSurfaceData.PF_INT_RGB),
|
||||
new VKSwToSurfaceTransform(SurfaceType.IntRgbx,
|
||||
VKSurfaceData.PF_INT_RGBX),
|
||||
new VKSwToSurfaceTransform(SurfaceType.IntBgr,
|
||||
VKSurfaceData.PF_INT_BGR),
|
||||
new VKSwToSurfaceTransform(SurfaceType.IntBgrx,
|
||||
VKSurfaceData.PF_INT_BGRX),
|
||||
transformBlitIntArgbPreToSurface,
|
||||
|
||||
new VKGeneralTransformedBlit(transformBlitIntArgbPreToSurface),
|
||||
|
||||
// sw->texture ops
|
||||
blitIntArgbPreToTexture,
|
||||
new VKSwToTextureBlit(SurfaceType.IntRgb,
|
||||
VKSurfaceData.PF_INT_RGB),
|
||||
new VKSwToTextureBlit(SurfaceType.IntRgbx,
|
||||
VKSurfaceData.PF_INT_RGBX),
|
||||
new VKSwToTextureBlit(SurfaceType.IntBgr,
|
||||
VKSurfaceData.PF_INT_BGR),
|
||||
new VKSwToTextureBlit(SurfaceType.IntBgrx,
|
||||
VKSurfaceData.PF_INT_BGRX),
|
||||
new VKGeneralBlit(VKSurfaceData.VKTexture,
|
||||
CompositeType.SrcNoEa,
|
||||
blitIntArgbPreToTexture),
|
||||
};
|
||||
|
||||
List<GraphicsPrimitive> primitives = new ArrayList<>();
|
||||
Collections.addAll(primitives, primitiveArray);
|
||||
// Surface->Surface
|
||||
primitives.add(new VKSurfaceToSurfaceBlit(CompositeType.AnyAlpha));
|
||||
primitives.add(new VKSurfaceToSurfaceBlit(CompositeType.Xor));
|
||||
primitives.add(new VKSurfaceToSurfaceScale(CompositeType.AnyAlpha));
|
||||
primitives.add(new VKSurfaceToSurfaceScale(CompositeType.Xor));
|
||||
primitives.add(new VKSurfaceToSurfaceTransform(CompositeType.AnyAlpha));
|
||||
primitives.add(new VKSurfaceToSurfaceTransform(CompositeType.Xor));
|
||||
// Surface->Sw
|
||||
VKSwToSurfaceBlitContext blitContext = new VKSwToSurfaceBlitContext();
|
||||
for (CompositeType compositeType : new CompositeType[] { CompositeType.AnyAlpha, CompositeType.Xor }) {
|
||||
// Sw->Surface
|
||||
primitives.add(new VKSwToSurfaceBlit(compositeType, blitContext));
|
||||
primitives.add(new VKSwToSurfaceScale(compositeType, blitContext));
|
||||
primitives.add(new VKSwToSurfaceTransform(compositeType, blitContext));
|
||||
// Surface->Surface
|
||||
primitives.add(new VKSurfaceToSurfaceBlit(compositeType));
|
||||
primitives.add(new VKSurfaceToSurfaceScale(compositeType));
|
||||
primitives.add(new VKSurfaceToSurfaceTransform(compositeType));
|
||||
}
|
||||
// Surface->Sw & Any composite per format
|
||||
for (VKFormat format : VKFormat.values()) {
|
||||
primitives.add(new VKSurfaceToSwBlit(format, OPAQUE));
|
||||
if (format.isTranslucencyCapable()) {
|
||||
@@ -322,6 +250,44 @@ final class VKBlitLoops {
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface VKStageSurfaceFactory {
|
||||
BufferedImage create(int width, int height);
|
||||
}
|
||||
|
||||
final class VKStageSurface implements AutoCloseable {
|
||||
|
||||
private final VKStageSurfaceFactory factory;
|
||||
private WeakReference<SurfaceData> cachedSD;
|
||||
private SurfaceData currentSD;
|
||||
|
||||
VKStageSurface(VKStageSurfaceFactory factory) {
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
SurfaceData acquire(int width, int height) {
|
||||
if(currentSD != null) throw new IllegalStateException("Already acquired, recursive blit?");
|
||||
if (cachedSD != null) {
|
||||
currentSD = cachedSD.get();
|
||||
if (currentSD != null) {
|
||||
Rectangle b = currentSD.getBounds();
|
||||
if (b.width < width || b.height < height) currentSD = null;
|
||||
} else cachedSD = null;
|
||||
}
|
||||
if (currentSD == null) {
|
||||
BufferedImage bi = factory.create(width, height);
|
||||
currentSD = SurfaceData.getPrimarySurfaceData(bi);
|
||||
cachedSD = new WeakReference<>(currentSD);
|
||||
}
|
||||
return currentSD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
currentSD = null;
|
||||
}
|
||||
}
|
||||
|
||||
final class VKSurfaceToSurfaceBlit extends Blit {
|
||||
|
||||
VKSurfaceToSurfaceBlit(CompositeType compositeType) {
|
||||
@@ -385,53 +351,17 @@ final class VKSurfaceToSurfaceTransform extends TransformBlit {
|
||||
|
||||
final class VKSurfaceToSwBlit extends Blit {
|
||||
|
||||
private final VKFormat format;
|
||||
private final int transparency;
|
||||
private final VKStageSurface stageSurface;
|
||||
private final SurfaceType bufferedSurfaceType;
|
||||
private WeakReference<SurfaceData> srcTmp;
|
||||
|
||||
VKSurfaceToSwBlit(VKFormat format, int transparency) {
|
||||
// TODO Support for any composite via staged blit?
|
||||
// This way we could do one intermediate blit instead of two.
|
||||
super(format.getSurfaceType(transparency), CompositeType.SrcNoEa, SurfaceType.Any);
|
||||
this.format = format;
|
||||
this.transparency = transparency;
|
||||
stageSurface = new VKStageSurface((w, h) -> format.createCompatibleImage(w, h, transparency));
|
||||
bufferedSurfaceType = format.getFormatModel(transparency).getSurfaceType();
|
||||
}
|
||||
|
||||
private void stagedBlit(SurfaceData src, SurfaceData dst,
|
||||
Composite comp, Region clip,
|
||||
int sx, int sy, int dx, int dy,
|
||||
int w, int h) {
|
||||
SurfaceData tmp = null;
|
||||
if (srcTmp != null) {
|
||||
// Use cached intermediate surface, if available.
|
||||
tmp = srcTmp.get();
|
||||
if (tmp != null) {
|
||||
Rectangle b = tmp.getBounds();
|
||||
if (b.width < w || b.height < h) tmp = null;
|
||||
}
|
||||
}
|
||||
if (tmp == null) {
|
||||
BufferedImage bi = format.createCompatibleImage(w, h, transparency);
|
||||
tmp = SurfaceData.getPrimarySurfaceData(bi);
|
||||
if (tmp.getSurfaceType() != bufferedSurfaceType) throw new RuntimeException("Incompatible surface type");
|
||||
srcTmp = null;
|
||||
}
|
||||
|
||||
// Blit from Vulkan to intermediate SW image.
|
||||
Blit(src, tmp, null, null, sx, sy, 0, 0, w, h);
|
||||
|
||||
// Copy intermediate SW to destination SW using complex clip.
|
||||
Blit performop = Blit.getFromCache(tmp.getSurfaceType(), CompositeType.SrcNoEa, dst.getSurfaceType());
|
||||
performop.Blit(tmp, dst, comp, clip, 0, 0, dx, dy, w, h);
|
||||
|
||||
if (srcTmp == null) {
|
||||
// Cache the intermediate surface.
|
||||
srcTmp = new WeakReference<>(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void Blit(SurfaceData src, SurfaceData dst,
|
||||
Composite comp, Region clip,
|
||||
@@ -453,7 +383,14 @@ final class VKSurfaceToSwBlit extends Blit {
|
||||
}
|
||||
|
||||
if ((clip != null && !clip.isRectangular()) || dst.getSurfaceType() != bufferedSurfaceType) {
|
||||
stagedBlit(src, dst, comp, clip, sx, sy, dx, dy, w, h);
|
||||
try (stageSurface) {
|
||||
SurfaceData stage = stageSurface.acquire(w, h);
|
||||
// Blit from Vulkan to intermediate SW image.
|
||||
Blit(src, stage, null, null, sx, sy, 0, 0, w, h);
|
||||
// Copy intermediate SW to destination SW using complex clip.
|
||||
Blit op = Blit.getFromCache(stage.getSurfaceType(), CompositeType.SrcNoEa, dst.getSurfaceType());
|
||||
op.Blit(stage, dst, comp, clip, 0, 0, dx, dy, w, h);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -473,7 +410,7 @@ final class VKSurfaceToSwBlit extends Blit {
|
||||
buf.putInt(sx).putInt(sy);
|
||||
buf.putInt(dx).putInt(dy);
|
||||
buf.putInt(w).putInt(h);
|
||||
buf.putInt(format.getValue(dst.getTransparency()));
|
||||
buf.putInt(0 /*unused*/);
|
||||
buf.putLong(src.getNativeOps());
|
||||
buf.putLong(dst.getNativeOps());
|
||||
|
||||
@@ -485,39 +422,70 @@ final class VKSurfaceToSwBlit extends Blit {
|
||||
}
|
||||
}
|
||||
|
||||
final class VKSwToSurfaceBlitContext {
|
||||
|
||||
// TODO switch to TYPE_INT_ARGB when blit shader is fixed to handle straight alpha?
|
||||
// Don't use pre-multiplied alpha to preserve color values whenever possible.
|
||||
private final VKStageSurface stage4Byte = new VKStageSurface((w, h) -> new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB_PRE));
|
||||
|
||||
VKStageSurface getStage(SurfaceData src, VKSurfaceData dst) {
|
||||
// We assume that 4 byte sampled format is always supported.
|
||||
return stage4Byte;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode src surface type for native blit.
|
||||
* Return -1 if the surface type is not supported.
|
||||
* All stage surfaces from getStage() must always be supported.
|
||||
*/
|
||||
int encodeSrcType(SurfaceData src) {
|
||||
// TODO Needs to be implemented.
|
||||
// Currently, all blits are performed via a staged surface.
|
||||
if (src.getSurfaceType() == SurfaceType.IntArgbPre) return 0;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
class VKSwToSurfaceBlit extends Blit {
|
||||
|
||||
private int typeval;
|
||||
private final VKSwToSurfaceBlitContext blitContext;
|
||||
|
||||
VKSwToSurfaceBlit(SurfaceType srcType, int typeval) {
|
||||
super(srcType,
|
||||
CompositeType.AnyAlpha,
|
||||
VKSurfaceData.VKSurface);
|
||||
this.typeval = typeval;
|
||||
VKSwToSurfaceBlit(CompositeType compositeType, VKSwToSurfaceBlitContext blitContext) {
|
||||
super(SurfaceType.Any, compositeType, VKSurfaceData.VKSurface);
|
||||
this.blitContext = blitContext;
|
||||
}
|
||||
|
||||
public void Blit(SurfaceData src, SurfaceData dst,
|
||||
Composite comp, Region clip,
|
||||
int sx, int sy, int dx, int dy, int w, int h)
|
||||
{
|
||||
int sx, int sy, int dx, int dy, int w, int h) {
|
||||
int srcType = blitContext.encodeSrcType(src);
|
||||
if (srcType == -1) {
|
||||
try (VKStageSurface stageSurface = blitContext.getStage(src, (VKSurfaceData) dst)) {
|
||||
SurfaceData stage = stageSurface.acquire(w, h);
|
||||
// Blit from src to intermediate SW image.
|
||||
Blit op = Blit.getFromCache(src.getSurfaceType(), CompositeType.SrcNoEa, stage.getSurfaceType());
|
||||
op.Blit(src, stage, AlphaComposite.Src, null, sx, sy, 0, 0, w, h);
|
||||
// Copy intermediate SW to Vulkan surface.
|
||||
Blit(stage, dst, comp, clip, 0, 0, dx, dy, w, h);
|
||||
}
|
||||
return;
|
||||
}
|
||||
VKBlitLoops.Blit(src, dst,
|
||||
comp, clip, null,
|
||||
AffineTransformOp.TYPE_NEAREST_NEIGHBOR,
|
||||
sx, sy, sx+w, sy+h,
|
||||
dx, dy, dx+w, dy+h,
|
||||
typeval, false);
|
||||
srcType, false);
|
||||
}
|
||||
}
|
||||
|
||||
class VKSwToSurfaceScale extends ScaledBlit {
|
||||
|
||||
private int typeval;
|
||||
private final VKSwToSurfaceBlitContext blitContext;
|
||||
|
||||
VKSwToSurfaceScale(SurfaceType srcType, int typeval) {
|
||||
super(srcType,
|
||||
CompositeType.AnyAlpha,
|
||||
VKSurfaceData.VKSurface);
|
||||
this.typeval = typeval;
|
||||
VKSwToSurfaceScale(CompositeType compositeType, VKSwToSurfaceBlitContext blitContext) {
|
||||
super(SurfaceType.Any, compositeType, VKSurfaceData.VKSurface);
|
||||
this.blitContext = blitContext;
|
||||
}
|
||||
|
||||
public void Scale(SurfaceData src, SurfaceData dst,
|
||||
@@ -525,222 +493,58 @@ class VKSwToSurfaceScale extends ScaledBlit {
|
||||
int sx1, int sy1,
|
||||
int sx2, int sy2,
|
||||
double dx1, double dy1,
|
||||
double dx2, double dy2)
|
||||
{
|
||||
double dx2, double dy2) {
|
||||
int srcType = blitContext.encodeSrcType(src);
|
||||
if (srcType == -1) {
|
||||
try (VKStageSurface stageSurface = blitContext.getStage(src, (VKSurfaceData) dst)) {
|
||||
int w = sx2-sx1, h = sy2-sy1;
|
||||
SurfaceData stage = stageSurface.acquire(w, h);
|
||||
// Blit from src to intermediate SW image.
|
||||
Blit op = Blit.getFromCache(src.getSurfaceType(), CompositeType.SrcNoEa, stage.getSurfaceType());
|
||||
op.Blit(src, stage, AlphaComposite.Src, null, sx1, sy1, 0, 0, w, h);
|
||||
// Copy intermediate SW to Vulkan surface.
|
||||
Scale(stage, dst, comp, clip, 0, 0, w, h, dx1, dy1, dx2, dy2);
|
||||
}
|
||||
return;
|
||||
}
|
||||
VKBlitLoops.Blit(src, dst,
|
||||
comp, clip, null,
|
||||
AffineTransformOp.TYPE_NEAREST_NEIGHBOR,
|
||||
sx1, sy1, sx2, sy2,
|
||||
dx1, dy1, dx2, dy2,
|
||||
typeval, false);
|
||||
srcType, false);
|
||||
}
|
||||
}
|
||||
|
||||
class VKSwToSurfaceTransform extends TransformBlit {
|
||||
|
||||
private int typeval;
|
||||
private final VKSwToSurfaceBlitContext blitContext;
|
||||
|
||||
VKSwToSurfaceTransform(SurfaceType srcType, int typeval) {
|
||||
super(srcType,
|
||||
CompositeType.AnyAlpha,
|
||||
VKSurfaceData.VKSurface);
|
||||
this.typeval = typeval;
|
||||
VKSwToSurfaceTransform(CompositeType compositeType, VKSwToSurfaceBlitContext blitContext) {
|
||||
super(SurfaceType.Any, compositeType, VKSurfaceData.VKSurface);
|
||||
this.blitContext = blitContext;
|
||||
}
|
||||
|
||||
public void Transform(SurfaceData src, SurfaceData dst,
|
||||
Composite comp, Region clip,
|
||||
AffineTransform at, int hint,
|
||||
int sx, int sy, int dx, int dy, int w, int h)
|
||||
{
|
||||
int sx, int sy, int dx, int dy, int w, int h) {
|
||||
int srcType = blitContext.encodeSrcType(src);
|
||||
if (srcType == -1) {
|
||||
try (VKStageSurface stageSurface = blitContext.getStage(src, (VKSurfaceData) dst)) {
|
||||
SurfaceData stage = stageSurface.acquire(w, h);
|
||||
// Blit from src to intermediate SW image.
|
||||
Blit op = Blit.getFromCache(src.getSurfaceType(), CompositeType.SrcNoEa, stage.getSurfaceType());
|
||||
op.Blit(src, stage, AlphaComposite.Src, null, sx, sy, 0, 0, w, h);
|
||||
// Copy intermediate SW to Vulkan surface.
|
||||
Transform(stage, dst, comp, clip, at, hint, 0, 0, dx, dy, w, h);
|
||||
}
|
||||
return;
|
||||
}
|
||||
VKBlitLoops.Blit(src, dst,
|
||||
comp, clip, at, hint,
|
||||
sx, sy, sx+w, sy+h,
|
||||
dx, dy, dx+w, dy+h,
|
||||
typeval, false);
|
||||
}
|
||||
}
|
||||
|
||||
class VKSwToTextureBlit extends Blit {
|
||||
|
||||
private int typeval;
|
||||
|
||||
VKSwToTextureBlit(SurfaceType srcType, int typeval) {
|
||||
super(srcType,
|
||||
CompositeType.SrcNoEa,
|
||||
VKSurfaceData.VKTexture);
|
||||
this.typeval = typeval;
|
||||
}
|
||||
|
||||
public void Blit(SurfaceData src, SurfaceData dst,
|
||||
Composite comp, Region clip,
|
||||
int sx, int sy, int dx, int dy, int w, int h)
|
||||
{
|
||||
VKBlitLoops.Blit(src, dst,
|
||||
comp, clip, null,
|
||||
AffineTransformOp.TYPE_NEAREST_NEIGHBOR,
|
||||
sx, sy, sx+w, sy+h,
|
||||
dx, dy, dx+w, dy+h,
|
||||
typeval, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This general Blit implementation converts any source surface to an
|
||||
* intermediate IntArgbPre surface, and then uses the more specific
|
||||
* IntArgbPre->VKSurface/Texture loop to get the intermediate
|
||||
* (premultiplied) surface down to Metal using simple blit.
|
||||
*/
|
||||
class VKGeneralBlit extends Blit {
|
||||
|
||||
private final Blit performop;
|
||||
private WeakReference<SurfaceData> srcTmp;
|
||||
|
||||
VKGeneralBlit(SurfaceType dstType,
|
||||
CompositeType compType,
|
||||
Blit performop)
|
||||
{
|
||||
super(SurfaceType.Any, compType, dstType);
|
||||
this.performop = performop;
|
||||
}
|
||||
|
||||
public synchronized void Blit(SurfaceData src, SurfaceData dst,
|
||||
Composite comp, Region clip,
|
||||
int sx, int sy, int dx, int dy,
|
||||
int w, int h)
|
||||
{
|
||||
Blit convertsrc = Blit.getFromCache(src.getSurfaceType(),
|
||||
CompositeType.SrcNoEa,
|
||||
SurfaceType.IntArgbPre);
|
||||
|
||||
SurfaceData cachedSrc = null;
|
||||
if (srcTmp != null) {
|
||||
// use cached intermediate surface, if available
|
||||
cachedSrc = srcTmp.get();
|
||||
}
|
||||
|
||||
// convert source to IntArgbPre
|
||||
src = convertFrom(convertsrc, src, sx, sy, w, h,
|
||||
cachedSrc, BufferedImage.TYPE_INT_ARGB_PRE);
|
||||
|
||||
// copy IntArgbPre intermediate surface to Metal surface
|
||||
performop.Blit(src, dst, comp, clip,
|
||||
0, 0, dx, dy, w, h);
|
||||
|
||||
if (src != cachedSrc) {
|
||||
// cache the intermediate surface
|
||||
srcTmp = new WeakReference<>(src);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This general TransformedBlit implementation converts any source surface to an
|
||||
* intermediate IntArgbPre surface, and then uses the more specific
|
||||
* IntArgbPre->VKSurface/Texture loop to get the intermediate
|
||||
* (premultiplied) surface down to Metal using simple transformBlit.
|
||||
*/
|
||||
final class VKGeneralTransformedBlit extends TransformBlit {
|
||||
|
||||
private final TransformBlit performop;
|
||||
private WeakReference<SurfaceData> srcTmp;
|
||||
|
||||
VKGeneralTransformedBlit(final TransformBlit performop) {
|
||||
super(SurfaceType.Any, CompositeType.AnyAlpha,
|
||||
VKSurfaceData.VKSurface);
|
||||
this.performop = performop;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void Transform(SurfaceData src, SurfaceData dst,
|
||||
Composite comp, Region clip,
|
||||
AffineTransform at, int hint, int srcx,
|
||||
int srcy, int dstx, int dsty, int width,
|
||||
int height){
|
||||
Blit convertsrc = Blit.getFromCache(src.getSurfaceType(),
|
||||
CompositeType.SrcNoEa,
|
||||
SurfaceType.IntArgbPre);
|
||||
// use cached intermediate surface, if available
|
||||
final SurfaceData cachedSrc = srcTmp != null ? srcTmp.get() : null;
|
||||
// convert source to IntArgbPre
|
||||
src = convertFrom(convertsrc, src, srcx, srcy, width, height, cachedSrc,
|
||||
BufferedImage.TYPE_INT_ARGB_PRE);
|
||||
|
||||
// transform IntArgbPre intermediate surface to Metal surface
|
||||
performop.Transform(src, dst, comp, clip, at, hint, 0, 0, dstx, dsty,
|
||||
width, height);
|
||||
|
||||
if (src != cachedSrc) {
|
||||
// cache the intermediate surface
|
||||
srcTmp = new WeakReference<>(src);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This general VKAnyCompositeBlit implementation can convert any source/target
|
||||
* surface to an intermediate surface using convertsrc/convertdst loops, applies
|
||||
* necessary composite operation, and then uses convertresult loop to get the
|
||||
* intermediate surface down to Metal.
|
||||
*/
|
||||
final class VKAnyCompositeBlit extends Blit {
|
||||
|
||||
private WeakReference<SurfaceData> dstTmp;
|
||||
private WeakReference<SurfaceData> srcTmp;
|
||||
private final Blit convertsrc;
|
||||
private final Blit convertdst;
|
||||
private final Blit convertresult;
|
||||
|
||||
VKAnyCompositeBlit(SurfaceType srctype, Blit convertsrc, Blit convertdst,
|
||||
Blit convertresult) {
|
||||
super(srctype, CompositeType.Any, VKSurfaceData.VKSurface);
|
||||
this.convertsrc = convertsrc;
|
||||
this.convertdst = convertdst;
|
||||
this.convertresult = convertresult;
|
||||
}
|
||||
|
||||
public synchronized void Blit(SurfaceData src, SurfaceData dst,
|
||||
Composite comp, Region clip,
|
||||
int sx, int sy, int dx, int dy,
|
||||
int w, int h)
|
||||
{
|
||||
if (convertsrc != null) {
|
||||
SurfaceData cachedSrc = null;
|
||||
if (srcTmp != null) {
|
||||
// use cached intermediate surface, if available
|
||||
cachedSrc = srcTmp.get();
|
||||
}
|
||||
// convert source to IntArgbPre
|
||||
src = convertFrom(convertsrc, src, sx, sy, w, h, cachedSrc,
|
||||
BufferedImage.TYPE_INT_ARGB_PRE);
|
||||
if (src != cachedSrc) {
|
||||
// cache the intermediate surface
|
||||
srcTmp = new WeakReference<>(src);
|
||||
}
|
||||
}
|
||||
|
||||
SurfaceData cachedDst = null;
|
||||
|
||||
if (dstTmp != null) {
|
||||
// use cached intermediate surface, if available
|
||||
cachedDst = dstTmp.get();
|
||||
}
|
||||
|
||||
// convert destination to IntArgbPre
|
||||
SurfaceData dstBuffer = convertFrom(convertdst, dst, dx, dy, w, h,
|
||||
cachedDst, BufferedImage.TYPE_INT_ARGB_PRE);
|
||||
Region bufferClip =
|
||||
clip == null ? null : clip.getTranslatedRegion(-dx, -dy);
|
||||
|
||||
Blit performop = Blit.getFromCache(src.getSurfaceType(),
|
||||
CompositeType.Any, dstBuffer.getSurfaceType());
|
||||
performop.Blit(src, dstBuffer, comp, bufferClip, sx, sy, 0, 0, w, h);
|
||||
|
||||
if (dstBuffer != cachedDst) {
|
||||
// cache the intermediate surface
|
||||
dstTmp = new WeakReference<>(dstBuffer);
|
||||
}
|
||||
// now blit the buffer back to the destination
|
||||
convertresult.Blit(dstBuffer, dst, AlphaComposite.Src, clip, 0, 0, dx,
|
||||
dy, w, h);
|
||||
srcType, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,4 @@ layout(location = 0) out vec4 out_Color;
|
||||
void main() {
|
||||
// TODO consider in/out alpha type.
|
||||
out_Color = texture(u_TexSampler, in_TexCoord);
|
||||
// TODO: Temporary fix of unexpected transparency with blit operations
|
||||
out_Color.a = 1.0;
|
||||
}
|
||||
|
||||
@@ -53,11 +53,6 @@ static void VKBlitSwToTextureViaPooledTexture(VKRenderingContext* context,
|
||||
const int dw = dx2 - dx1;
|
||||
const int dh = dy2 - dy1;
|
||||
|
||||
if (dw < sw || dh < sh) {
|
||||
J2dTraceLn4(J2D_TRACE_ERROR, "VKBlitSwToTextureViaPooledTexture: dest size: (%d, %d) less than source size: (%d, %d)", dw, dh, sw, sh);
|
||||
return;
|
||||
}
|
||||
|
||||
ARRAY(VKTxVertex) vertices = ARRAY_ALLOC(VKTxVertex, 4);
|
||||
/*
|
||||
* (p1)---------(p2)
|
||||
|
||||
@@ -32,7 +32,6 @@ import java.awt.image.BufferedImage;
|
||||
import java.awt.image.VolatileImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
/*
|
||||
* @test
|
||||
@@ -47,10 +46,8 @@ import java.util.Arrays;
|
||||
|
||||
|
||||
public class VulkanBlitTest {
|
||||
static boolean SUPPRESS_ALPHA_VALIDATION = false; // TODO this is temporary.
|
||||
final static int W = 256*3;
|
||||
final static int H = 600;
|
||||
final static int[] FILL_SCANLINE = new int[W];
|
||||
final static Color FILL_COLOR = new Color(0xffa1babe);
|
||||
final static Rectangle[] CLIP_RECTS = new Rectangle[] {
|
||||
new Rectangle(W*1/12, H*0/10, W/6, H/10+1),
|
||||
@@ -65,7 +62,6 @@ public class VulkanBlitTest {
|
||||
};
|
||||
final static Area COMPLEX_CLIP = new Area();
|
||||
static {
|
||||
Arrays.fill(FILL_SCANLINE, 0xffa1babe);
|
||||
for (Rectangle clip : CLIP_RECTS) COMPLEX_CLIP.add(new Area(clip));
|
||||
}
|
||||
|
||||
@@ -144,63 +140,71 @@ public class VulkanBlitTest {
|
||||
validate(image, W*j/256+1, H*7/10, new Color(j, j, j), "opaque gradient stripe " + j, 1);
|
||||
}
|
||||
for (int j = 0; j < 256; j++) {
|
||||
if (SUPPRESS_ALPHA_VALIDATION) break;
|
||||
Color expected = new Color(j, j, j, hasAlpha ? j : 255);
|
||||
validate(image, W*j/256+1, H*9/10, expected, "alpha gradient stripe " + j, 12);
|
||||
}
|
||||
}
|
||||
|
||||
static void testSurfaceToSwBlit(BufferedImage bi, VolatileImage image, String prefix, boolean hasAlpha) throws IOException {
|
||||
// Fill the buffered image with plain color.
|
||||
bi.setRGB(0, 0, W, H, FILL_SCANLINE, 0, 0);
|
||||
{ // Blit into software image.
|
||||
Graphics2D g = bi.createGraphics();
|
||||
g.setComposite(AlphaComposite.Src);
|
||||
g.drawImage(image, 0, 0, null);
|
||||
g.dispose();
|
||||
}
|
||||
ImageIO.write(bi, "PNG", new File(prefix + "no-clip.png"));
|
||||
try {
|
||||
validate(bi, hasAlpha);
|
||||
} catch (Throwable t) {
|
||||
throw new Error(prefix + "no-clip", t);
|
||||
}
|
||||
static void testBlit(Image src, Image dst, String prefix, boolean hasAlpha) throws IOException {
|
||||
BufferedImage validationImage;
|
||||
|
||||
// Fill the buffered image with plain color.
|
||||
bi.setRGB(0, 0, W, H, FILL_SCANLINE, 0, 0);
|
||||
{ // Blit again with rect clips.
|
||||
Graphics2D g = bi.createGraphics();
|
||||
{
|
||||
Graphics2D g = (Graphics2D) dst.getGraphics();
|
||||
g.setComposite(AlphaComposite.Src);
|
||||
g.setColor(FILL_COLOR);
|
||||
g.fillRect(0, 0, W, H);
|
||||
for (Rectangle clip : CLIP_RECTS) {
|
||||
g.setClip(clip);
|
||||
g.drawImage(image, 0, 0, null);
|
||||
g.drawImage(src, 0, 0, null);
|
||||
}
|
||||
g.dispose();
|
||||
}
|
||||
ImageIO.write(bi, "PNG", new File(prefix + "rect-clip.png"));
|
||||
validationImage = dst instanceof BufferedImage ? (BufferedImage) dst : ((VolatileImage) dst).getSnapshot();
|
||||
ImageIO.write(validationImage, "PNG", new File(prefix + "rect-clip.png"));
|
||||
try {
|
||||
validate(bi, hasAlpha);
|
||||
validateClip(bi);
|
||||
validate(validationImage, hasAlpha);
|
||||
validateClip(validationImage);
|
||||
} catch (Throwable t) {
|
||||
throw new Error(prefix + "rect-clip", t);
|
||||
}
|
||||
|
||||
// Fill the buffered image with plain color.
|
||||
bi.setRGB(0, 0, W, H, FILL_SCANLINE, 0, 0);
|
||||
{ // Blit again with a complex clip.
|
||||
Graphics2D g = bi.createGraphics();
|
||||
{
|
||||
Graphics2D g = (Graphics2D) dst.getGraphics();
|
||||
g.setComposite(AlphaComposite.Src);
|
||||
g.setColor(FILL_COLOR);
|
||||
g.fillRect(0, 0, W, H);
|
||||
g.setClip(COMPLEX_CLIP);
|
||||
g.drawImage(image, 0, 0, null);
|
||||
g.drawImage(src, 0, 0, null);
|
||||
g.dispose();
|
||||
}
|
||||
ImageIO.write(bi, "PNG", new File(prefix + "complex-clip.png"));
|
||||
validationImage = dst instanceof BufferedImage ? (BufferedImage) dst : ((VolatileImage) dst).getSnapshot();
|
||||
ImageIO.write(validationImage, "PNG", new File(prefix + "complex-clip.png"));
|
||||
try {
|
||||
validate(bi, hasAlpha);
|
||||
validateClip(bi);
|
||||
validate(validationImage, hasAlpha);
|
||||
validateClip(validationImage);
|
||||
} catch (Throwable t) {
|
||||
throw new Error(prefix + "complex-clip", t);
|
||||
}
|
||||
|
||||
{
|
||||
Graphics2D g = (Graphics2D) dst.getGraphics();
|
||||
g.setComposite(AlphaComposite.Src);
|
||||
g.setColor(FILL_COLOR);
|
||||
g.fillRect(0, 0, W, H);
|
||||
g.drawImage(src, 0, 0, null);
|
||||
g.dispose();
|
||||
}
|
||||
validationImage = dst instanceof BufferedImage ? (BufferedImage) dst : ((VolatileImage) dst).getSnapshot();
|
||||
ImageIO.write(validationImage, "PNG", new File(prefix + "no-clip.png"));
|
||||
try {
|
||||
validate(validationImage, hasAlpha);
|
||||
try {
|
||||
validateClip(validationImage);
|
||||
throw new Error("Clip validation succeeded");
|
||||
} catch (Throwable ignore) {}
|
||||
} catch (Throwable t) {
|
||||
throw new Error(prefix + "no-clip", t);
|
||||
}
|
||||
}
|
||||
|
||||
static void test(VKGraphicsConfig vkgc, boolean hasAlpha) throws IOException {
|
||||
@@ -222,46 +226,44 @@ public class VulkanBlitTest {
|
||||
throw new Error(prefix + "snapshot", t);
|
||||
}
|
||||
|
||||
// Repeat blit into the same snapshot image.
|
||||
testSurfaceToSwBlit(bi, image, prefix + "snapshot-", hasAlpha);
|
||||
// Create another Vulkan image.
|
||||
VolatileImage check = config.createCompatibleVolatileImage(W, H, transparency);
|
||||
if (check.validate(config) == VolatileImage.IMAGE_INCOMPATIBLE) {
|
||||
throw new Error("Image validation failed");
|
||||
}
|
||||
|
||||
// Repeat blit to/from the same snapshot image.
|
||||
testBlit(image, bi, prefix + "snapshot, surface-sw, ", hasAlpha);
|
||||
testBlit(bi, check, prefix + "snapshot, sw-surface, ", hasAlpha);
|
||||
|
||||
// Repeat with generic buffered image formats.
|
||||
bi = new BufferedImage(W, H, BufferedImage.TYPE_INT_RGB);
|
||||
testSurfaceToSwBlit(bi, image, prefix + "INT_RGB, ", false);
|
||||
testBlit(image, bi, prefix + "INT_RGB, surface-sw, ", false);
|
||||
testBlit(bi, check, prefix + "INT_RGB, sw-surface, ", false);
|
||||
bi = new BufferedImage(W, H, BufferedImage.TYPE_INT_ARGB);
|
||||
testSurfaceToSwBlit(bi, image, prefix + "INT_ARGB, ", hasAlpha);
|
||||
testBlit(image, bi, prefix + "INT_ARGB, surface-sw, ", hasAlpha);
|
||||
testBlit(bi, check, prefix + "INT_ARGB, sw-surface, ", hasAlpha);
|
||||
bi = new BufferedImage(W, H, BufferedImage.TYPE_INT_ARGB_PRE);
|
||||
testSurfaceToSwBlit(bi, image, prefix + "INT_ARGB_PRE, ", hasAlpha);
|
||||
testBlit(image, bi, prefix + "INT_ARGB_PRE, surface-sw, ", hasAlpha);
|
||||
testBlit(bi, check, prefix + "INT_ARGB_PRE, sw-surface, ", hasAlpha);
|
||||
bi = new BufferedImage(W, H, BufferedImage.TYPE_INT_BGR);
|
||||
testSurfaceToSwBlit(bi, image, prefix + "INT_BGR, ", false);
|
||||
testBlit(image, bi, prefix + "INT_BGR, surface-sw, ", false);
|
||||
testBlit(bi, check, prefix + "INT_BGR, sw-surface, ", false);
|
||||
bi = new BufferedImage(W, H, BufferedImage.TYPE_3BYTE_BGR);
|
||||
testSurfaceToSwBlit(bi, image, prefix + "3BYTE_BGR, ", false);
|
||||
testBlit(image, bi, prefix + "3BYTE_BGR, surface-sw, ", false);
|
||||
testBlit(bi, check, prefix + "3BYTE_BGR, sw-surface, ", false);
|
||||
bi = new BufferedImage(W, H, BufferedImage.TYPE_4BYTE_ABGR);
|
||||
testSurfaceToSwBlit(bi, image, prefix + "4BYTE_ABGR, ", hasAlpha);
|
||||
testBlit(image, bi, prefix + "4BYTE_ABGR, surface-sw, ", hasAlpha);
|
||||
testBlit(bi, check, prefix + "4BYTE_ABGR, sw-surface, ", hasAlpha);
|
||||
bi = new BufferedImage(W, H, BufferedImage.TYPE_4BYTE_ABGR_PRE);
|
||||
testSurfaceToSwBlit(bi, image, prefix + "4BYTE_ABGR_PRE, ", hasAlpha);
|
||||
testBlit(image, bi, prefix + "4BYTE_ABGR_PRE, surface-sw, ", hasAlpha);
|
||||
testBlit(bi, check, prefix + "4BYTE_ABGR_PRE, sw-surface, ", hasAlpha);
|
||||
|
||||
// Blit into another Vulkan image.
|
||||
hasAlpha = false; // TODO our blit currently ignores alpha, remove when fixed.
|
||||
SUPPRESS_ALPHA_VALIDATION = true; // TODO same.
|
||||
VolatileImage anotherImage = config.createCompatibleVolatileImage(W, H, transparency);
|
||||
if (anotherImage.validate(config) == VolatileImage.IMAGE_INCOMPATIBLE) {
|
||||
throw new Error("Image validation failed");
|
||||
}
|
||||
{
|
||||
Graphics2D g = anotherImage.createGraphics();
|
||||
g.drawImage(image, 0, 0, null);
|
||||
g.dispose();
|
||||
}
|
||||
if (anotherImage.contentsLost()) throw new Error("Image contents lost");
|
||||
// Take a snapshot (blit into Sw) and validate.
|
||||
bi = anotherImage.getSnapshot();
|
||||
ImageIO.write(bi, "PNG", new File(prefix + "another-snapshot.png"));
|
||||
try {
|
||||
validate(bi, hasAlpha);
|
||||
} catch (Throwable t) {
|
||||
throw new Error(prefix + "another-snapshot", t);
|
||||
}
|
||||
testBlit(image, check, prefix + "surface-surface, ", hasAlpha);
|
||||
|
||||
if (image.contentsLost()) throw new Error("Image contents lost");
|
||||
if (check.contentsLost()) throw new Error("Check contents lost");
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
@@ -281,9 +283,6 @@ public class VulkanBlitTest {
|
||||
else if (args.length > 0 && args[0].equals("OPAQUE")) hasAlpha = false;
|
||||
else throw new Error("Usage: VulkanBlitTest <TRANSLUCENT|OPAQUE>");
|
||||
|
||||
// TODO We don't have proper support for alpha rendering into OPAQUE surface atm.
|
||||
if (!hasAlpha) SUPPRESS_ALPHA_VALIDATION = true;
|
||||
|
||||
VKEnv.getDevices().flatMap(VKGPU::getOffscreenGraphicsConfigs).forEach(gc -> {
|
||||
System.out.println("Testing " + gc);
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user