aboutsummaryrefslogtreecommitdiff
path: root/src/windows/classes/sun/java2d/d3d/D3DContext.java
blob: b41de7b34e9d57b060f731a7c05f8801b7735c88 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
/*
 * 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();
    }
}