blob: b41de7b34e9d57b060f731a7c05f8801b7735c88 [file] [log] [blame]
/*
* Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.java2d.d3d;
import java.awt.AlphaComposite;
import java.awt.Composite;
import java.awt.GraphicsEnvironment;
import java.awt.geom.AffineTransform;
import sun.awt.Win32GraphicsDevice;
import sun.java2d.InvalidPipeException;
import sun.java2d.SurfaceData;
import sun.java2d.pipe.Region;
import sun.java2d.windows.WindowsFlags;
public class D3DContext {
public static final int NO_CONTEXT_FLAGS = 0;
/**
* Used in D3DBlitLoops: if the source surface is opaque
* alpha blending can be turned off on the native level
* (if there's no ea), thus improving performance.
*/
public static final int SRC_IS_OPAQUE = 1;
/**
* This is a list of capabilities supported by the device this
* context is associated with.
* @see getDeviceCaps
*/
public static final int J2D_D3D_FAILURE = (0 << 0);
/**
* Device supports depth buffer for d3d render targets
*/
public static final int J2D_D3D_DEPTH_SURFACE_OK = (1 << 0);
/**
* Device supports creation of plain d3d surfaces
*/
public static final int J2D_D3D_PLAIN_SURFACE_OK = (1 << 1);
/**
* Device supports creation of opaque textures
*/
public static final int J2D_D3D_OP_TEXTURE_SURFACE_OK = (1 << 2);
/**
* Device supports creation of bitmask textures
*/
public static final int J2D_D3D_BM_TEXTURE_SURFACE_OK = (1 << 3);
/**
* Device supports creation of translucent textures
*/
public static final int J2D_D3D_TR_TEXTURE_SURFACE_OK = (1 << 4);
/**
* Device supports creation of opaque render-to-textures
*/
public static final int J2D_D3D_OP_RTT_SURFACE_OK = (1 << 5);
/**
* Device can render lines correctly (no pixelization issues)
*/
public static final int J2D_D3D_LINES_OK = (1 << 6);
/**
* Device supports texture mapping (no pixelization issues)
*/
public static final int J2D_D3D_TEXTURE_BLIT_OK = (1 << 7);
/**
* Device supports texture mapping with transforms (no pixelization issues)
*/
public static final int J2D_D3D_TEXTURE_TRANSFORM_OK = (1 << 8);
/**
* Device can render clipped lines correctly.
*/
public static final int J2D_D3D_LINE_CLIPPING_OK = (1 << 9);
/**
* Device has all hw capabilities the d3d pipeline requires
*/
public static final int J2D_D3D_DEVICE_OK = (1 <<10);
/**
* Device supports all necessary texture formats required by d3d pipeline
*/
public static final int J2D_D3D_PIXEL_FORMATS_OK = (1 <<11);
/**
* Device supports geometry transformations
*/
public static final int J2D_D3D_SET_TRANSFORM_OK = (1 <<12);
/**
* The device is not from a list of known bad devices
* (see D3DRuntimeTest.cpp)
*/
public static final int J2D_D3D_HW_OK = (1 <<13);
/**
* Direct3D pipeline is enabled on this device
*/
public static final int J2D_D3D_ENABLED_OK = (1 <<14);
/**
* The lock object used to synchronize access to the native windowing
* system layer. Note that rendering methods should always synchronize on
* D3DContext.LOCK before calling the D3DContext.getContext() method,
* or any other method that invokes native D3d commands.
* REMIND: in D3D case we should really be synchronizing on per-device
* basis.
*/
static Object LOCK;
private Win32GraphicsDevice gd;
private boolean valid;
protected long nativeContext;
private SurfaceData validatedDstData;
private Region validatedClip;
private Composite validatedComp;
private int validatedPixel;
private int validatedFlags;
private boolean xformInUse;
// validated transform's data
private double vScaleX, vScaleY, vShearX, vShearY, vTransX, vTransY;
private int deviceCaps;
private native void setRenderTarget(long pCtx, long pDst);
private native void setClip(long pCtx, long pDst, Region clip, boolean isRect,
int x1, int y1, int x2, int y2);
private native void resetClip(long pCtx, long pDst);
private native void resetComposite(long pCtx);
private native void setAlphaComposite(long pCtx, int rule,
float extraAlpha, int flags);
private native void setTransform(long pCtx, long pDst,
AffineTransform xform,
double m00, double m10, double m01,
double m11, double m02, double m12);
private native void resetTransform(long pCtx, long pDst);
private native void setColor(long pCtx, int pixel, int flags);
private native long initNativeContext(int screen);
private native int getNativeDeviceCaps(long pCtx);
static {
if (!GraphicsEnvironment.isHeadless()) {
LOCK = D3DContext.class;
}
}
public D3DContext(Win32GraphicsDevice gd) {
this.gd = gd;
reinitNativeContext();
}
/**
* Reinitializes the context by retrieving a pointer to the native
* D3DContext object, and resetting the device caps.
*/
void reinitNativeContext() {
nativeContext = initNativeContext(gd.getScreen());
deviceCaps = nativeContext != 0L ?
getNativeDeviceCaps(nativeContext) : J2D_D3D_FAILURE;
valid = ((deviceCaps & J2D_D3D_ENABLED_OK) != 0);
if (WindowsFlags.isD3DVerbose()) {
if (valid) {
System.out.println("Direct3D pipeline enabled on screen " +
gd.getScreen());
} else {
System.out.println("Could not enable Direct3D pipeline on " +
"screen " + gd.getScreen() +
". Device Caps: " +
Integer.toHexString(deviceCaps));
}
}
}
/**
* Invalidates this context by resetting its status: the validated
* destination surface, and a pointer to the native context.
* This method is called in the following cases:
* - if a surface loss situation is detected at the native level
* during any of the validation methods (setClip, setRenderTarget etc)
* and an InvalidPipeException is thrown.
* This situation happens when there was a surface loss, but
* there were no display change event (like in case of command prompt
* going fullscreen).
* - as part of surface restoration when a surface is the current
* target surface for this context. Since surface restoration
* resets the depth buffer contents, we need to make sure the clip
* is reset, and since the target surface is reset, we'll set a new
* clip the next time we attempt to render to the target surface.
* - when a display change occurs, the native D3DContext object is
* released and recreated as part of primary surface recreation.
* At the time of the release, the java D3DContext object need to be
* invalidated because a new D3D device is created and the target
* surface will need to be reset.
*
* Invalidation of the context causes its revalidation the next time
* someone tries to get the D3DContext for rendering or creating a new
* surface.
*
* @see #reinitNativeContext
*/
private void invalidateContext() {
valid = false;
nativeContext = 0L;
validatedDstData = null;
// We don't set deviceCaps to J2D_D3D_FAILURE here because
// it will prevent from creating d3d surfaces, which means that
// we'll never get a chance to continue using d3d after a single
// invalidation event (for example, a display change).
}
/**
* Fetches the D3DContext associated with the current
* thread/GraphicsConfig pair, validates the context using the given
* parameters, then returns the handle to the native context object.
* Most rendering operations will call this method first in order to
* prepare the native D3d layer before issuing rendering commands.
*/
static long getContext(SurfaceData srcData,
SurfaceData dstData,
Region clip, Composite comp,
AffineTransform xform,
int pixel, int flags)
{
if (dstData instanceof D3DSurfaceData == false) {
throw new InvalidPipeException("Incorrect destination surface");
}
D3DContext d3dc = ((D3DSurfaceData)dstData).getContext();
try {
d3dc.validate(srcData, dstData, clip, comp, xform, pixel, flags);
} catch (InvalidPipeException e) {
d3dc.invalidateContext();
// note that we do not propagate the exception. Once the context
// is invalidated, any d3d rendering operations are noops, and
// we are waiting for the primary surface restoration, which
// happens when VolatileImage is validated. At this point
// the native D3DContext will be reinitialized, and the next
// time around validation of the context will succeed.
// Throwing the exception here will do no good, since the
// destination surface (which is associated with a VolatileImage
// or a BufferStrategy) will not be restored until VI.validate()
// is called by the rendering thread.
}
return d3dc.getNativeContext();
}
public int getDeviceCaps() {
return deviceCaps;
}
boolean isRTTSupported() {
return ((deviceCaps & J2D_D3D_OP_RTT_SURFACE_OK) != 0);
}
/**
* Returns a handle to the native D3DContext structure associated with
* this object.
*/
long getNativeContext() {
return nativeContext;
}
/**
* Validates the given parameters against the current state for this
* context. If this context is not current, it will be made current
* for the given source and destination surfaces, and the viewport will
* be updated. Then each part of the context state (clip, composite,
* etc.) is checked against the previous value. If the value has changed
* since the last call to validate(), it will be updated accordingly.
*/
private void validate(SurfaceData srcData, SurfaceData dstData,
Region clip, Composite comp, AffineTransform xform,
int pixel, int flags)
{
boolean updateClip = false;
if ((srcData != null && !srcData.isValid()) || !dstData.isValid() ||
dstData.getNativeOps() == 0L || dstData.isSurfaceLost())
{
throw new InvalidPipeException("Invalid surface");
}
if (!valid) {
// attempt to reinitialize the context. If the device has been
// reset, the following calls to setRenderTarget/setClip will
// succeed and not throw InvalidPipeException.
reinitNativeContext();
}
if (dstData != validatedDstData) {
// invalidate pixel and clip (so they will be updated below)
validatedPixel = ~pixel;
updateClip = true;
// update the viewport
long pDst = dstData.getNativeOps();
setRenderTarget(nativeContext, pDst);
// keep the reference to the old data until we set the
// new one on the native level, preventing it from being disposed
SurfaceData tmpData = dstData;
validatedDstData = dstData;
tmpData = null;
}
// it's better to use dstData instead of validatedDstData because
// the latter may be set to null via invalidateContext at any moment.
long pDest = dstData.getNativeOps();
// validate clip
if ((clip != validatedClip) || updateClip) {
if (clip != null) {
/**
* It's cheaper to make this check than set clip every time.
*
* Set the new clip only if:
* - we were asked to do it (updateClip == true)
* - no clip was set before
* - if both the old and the new clip are shapes
* - if they're both rectangular but don't represent
* the same rectangle
*/
if (updateClip ||
validatedClip == null ||
!(validatedClip.isRectangular() && clip.isRectangular()) ||
((clip.getLoX() != validatedClip.getLoX() ||
clip.getLoY() != validatedClip.getLoY() ||
clip.getHiX() != validatedClip.getHiX() ||
clip.getHiY() != validatedClip.getHiY())))
{
setClip(nativeContext, pDest,
clip, clip.isRectangular(),
clip.getLoX(), clip.getLoY(),
clip.getHiX(), clip.getHiY());
}
} else {
resetClip(nativeContext, pDest);
}
validatedClip = clip;
}
if ((comp != validatedComp) || (flags != validatedFlags)) {
// invalidate pixel
validatedPixel = ~pixel;
validatedComp = comp;
if (comp != null) {
AlphaComposite ac = (AlphaComposite)comp;
setAlphaComposite(nativeContext, ac.getRule(),
ac.getAlpha(), flags);
} else {
resetComposite(nativeContext);
}
}
// validate transform
if (xform == null) {
if (xformInUse) {
resetTransform(nativeContext, pDest);
xformInUse = false;
vScaleX = vScaleY = 1.0;
vShearX = vShearY = vTransX = vTransY = 0.0;
}
} else {
double nScaleX = xform.getScaleX();
double nScaleY = xform.getScaleY();
double nShearX = xform.getShearX();
double nShearY = xform.getShearY();
double nTransX = xform.getTranslateX();
double nTransY = xform.getTranslateY();
if (nTransX != vTransX || nTransY != vTransY ||
nScaleX != vScaleX || nScaleY != vScaleY ||
nShearX != vShearX || nShearY != vShearY)
{
setTransform(nativeContext, pDest,
xform,
nScaleX, nShearY, nShearX, nScaleY,
nTransX, nTransY);
vScaleX = nScaleX;
vScaleY = nScaleY;
vShearX = nShearX;
vShearY = nShearY;
vTransX = nTransY;
vTransY = nTransY;
xformInUse = true;
}
}
// validate pixel
if (pixel != validatedPixel) {
validatedPixel = pixel;
setColor(nativeContext, pixel, flags);
}
// save flags for later comparison
validatedFlags = flags;
// mark dstData dirty
dstData.markDirty();
}
}