Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/windows/classes/sun/java2d/d3d/D3DScreenUpdateManager.java
32288 views
/*1* Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.java2d.d3d;2627import java.awt.Color;28import java.awt.Component;29import java.awt.Container;30import java.awt.Font;31import java.awt.Graphics2D;32import java.awt.Rectangle;33import java.awt.Window;34import java.security.AccessController;35import java.security.PrivilegedAction;36import java.util.ArrayList;37import java.util.HashMap;3839import sun.awt.AWTAccessor;40import sun.misc.ThreadGroupUtils;41import sun.awt.Win32GraphicsConfig;42import sun.awt.windows.WComponentPeer;43import sun.java2d.InvalidPipeException;44import sun.java2d.ScreenUpdateManager;45import sun.java2d.SunGraphics2D;46import sun.java2d.SurfaceData;47import sun.java2d.windows.GDIWindowSurfaceData;48import sun.java2d.d3d.D3DSurfaceData.D3DWindowSurfaceData;49import sun.java2d.windows.WindowsFlags;5051/**52* This class handles rendering to the screen with the D3D pipeline.53*54* Since it is not possible to render directly to the front buffer55* with D3D9, we create a swap chain surface (with COPY effect) in place of the56* GDIWindowSurfaceData. A background thread handles the swap chain flips.57*58* There are some restrictions to which windows we would use this for.59* @see #createScreenSurface()60*/61public class D3DScreenUpdateManager extends ScreenUpdateManager62implements Runnable63{64/**65* A window must be at least MIN_WIN_SIZE in one or both dimensions66* to be considered for the update manager.67*/68private static final int MIN_WIN_SIZE = 150;6970private volatile boolean done;71private volatile Thread screenUpdater;72private boolean needsUpdateNow;7374/**75* Object used by the screen updater thread for waiting76*/77private Object runLock = new Object();78/**79* List of D3DWindowSurfaceData surfaces. Surfaces are added to the80* list when a graphics object is created, and removed when the surface81* is invalidated.82*/83private ArrayList<D3DWindowSurfaceData> d3dwSurfaces;84/**85* Cache of GDIWindowSurfaceData surfaces corresponding to the86* D3DWindowSurfaceData surfaces. Surfaces are added to the list when87* a d3dw surface is lost and could not be restored (due to lack of vram,88* for example), and removed then the d3dw surface is invalidated.89*/90private HashMap<D3DWindowSurfaceData, GDIWindowSurfaceData> gdiSurfaces;9192public D3DScreenUpdateManager() {93done = false;94AccessController.doPrivileged(95(PrivilegedAction<Void>) () -> {96ThreadGroup rootTG = ThreadGroupUtils.getRootThreadGroup();97Thread shutdown = new Thread(rootTG, () -> {98done = true;99wakeUpUpdateThread();100});101shutdown.setContextClassLoader(null);102try {103Runtime.getRuntime().addShutdownHook(shutdown);104} catch (Exception e) {105done = true;106}107return null;108}109);110}111112/**113* If possible, creates a D3DWindowSurfaceData (which is actually114* a back-buffer surface). If the creation fails, returns GDI115* onscreen surface instead.116*117* Note that the created D3D surface does not initialize the native118* resources (and is marked lost) to avoid wasting video memory. It is119* restored when a graphics object is requested from the peer.120*121* Note that this method is called from a synchronized block in122* WComponentPeer, so we don't need to synchronize123*124* Note that we only create a substibute d3dw surface if certain conditions125* are met126* <ul>127* <li>the fake d3d rendering on screen is not disabled via flag128* <li>d3d on the device is enabled129* <li>surface is larger than MIN_WIN_SIZE (don't bother for smaller ones)130* <li>it doesn't have a backBuffer for a BufferStrategy already131* <li>the peer is either Canvas, Panel, Window, Frame,132* Dialog or EmbeddedFrame133* </ul>134*135* @param gc GraphicsConfiguration on associated with the surface136* @param peer peer for which the surface is to be created137* @param bbNum number of back-buffers requested. if this number is >0,138* method returns GDI surface (we don't want to have two swap chains)139* @param isResize whether this surface is being created in response to140* a component resize event. This determines whether a repaint event will141* be issued after a surface is created: it will be if <code>isResize</code>142* is <code>true</code>.143* @return surface data to be use for onscreen rendering144*/145@Override146public SurfaceData createScreenSurface(Win32GraphicsConfig gc,147WComponentPeer peer,148int bbNum, boolean isResize)149{150if (done || !(gc instanceof D3DGraphicsConfig)) {151return super.createScreenSurface(gc, peer, bbNum, isResize);152}153154SurfaceData sd = null;155156if (canUseD3DOnScreen(peer, gc, bbNum)) {157try {158// note that the created surface will be in the "lost"159// state, it will be restored prior to rendering to it160// for the first time. This is done so that vram is not161// wasted for surfaces never rendered to162sd = D3DSurfaceData.createData(peer);163} catch (InvalidPipeException ipe) {164sd = null;165}166}167if (sd == null) {168sd = GDIWindowSurfaceData.createData(peer);169// note that we do not add this surface to the list of cached gdi170// surfaces as there's no d3dw surface to associate it with;171// this peer will have a gdi surface until next time a surface172// will need to be replaced173}174175if (isResize) {176// since we'd potentially replaced the back-buffer surface177// (either with another bb, or a gdi one), the178// component will need to be completely repainted;179// this only need to be done when the surface is created in180// response to a resize event since when a component is created it181// will be repainted anyway182repaintPeerTarget(peer);183}184185return sd;186}187188/**189* Determines if we can use a d3d surface for onscreen rendering for this190* peer.191* We only create onscreen d3d surfaces if the following conditions are met:192* - d3d is enabled on this device and onscreen emulation is enabled193* - window is big enough to bother (either dimension > MIN_WIN_SIZE)194* - this heavyweight doesn't have a BufferStrategy195* - if we are in full-screen mode then it must be the peer of the196* full-screen window (since there could be only one SwapChain in fs)197* and it must not have any heavyweight children198* (as Present() doesn't respect component clipping in fullscreen mode)199* - it's one of the classes likely to have custom rendering worth200* accelerating201*202* @returns true if we can use a d3d surface for this peer's onscreen203* rendering204*/205public static boolean canUseD3DOnScreen(final WComponentPeer peer,206final Win32GraphicsConfig gc,207final int bbNum)208{209if (!(gc instanceof D3DGraphicsConfig)) {210return false;211}212D3DGraphicsConfig d3dgc = (D3DGraphicsConfig)gc;213D3DGraphicsDevice d3dgd = d3dgc.getD3DDevice();214String peerName = peer.getClass().getName();215Rectangle r = peer.getBounds();216Component target = (Component)peer.getTarget();217Window fsw = d3dgd.getFullScreenWindow();218219return220WindowsFlags.isD3DOnScreenEnabled() &&221d3dgd.isD3DEnabledOnDevice() &&222peer.isAccelCapable() &&223(r.width > MIN_WIN_SIZE || r.height > MIN_WIN_SIZE) &&224bbNum == 0 &&225(fsw == null || (fsw == target && !hasHWChildren(target))) &&226(peerName.equals("sun.awt.windows.WCanvasPeer") ||227peerName.equals("sun.awt.windows.WDialogPeer") ||228peerName.equals("sun.awt.windows.WPanelPeer") ||229peerName.equals("sun.awt.windows.WWindowPeer") ||230peerName.equals("sun.awt.windows.WFramePeer") ||231peerName.equals("sun.awt.windows.WEmbeddedFramePeer"));232}233234/**235* Creates a graphics object for the passed in surface data. If236* the surface is lost, it is restored.237* If the surface wasn't lost or the restoration was successful238* the surface is added to the list of maintained surfaces239* (if it hasn't been already).240*241* If the updater thread hasn't been created yet , it will be created and242* started.243*244* @param sd surface data for which to create SunGraphics2D245* @param peer peer associated with the surface data246* @param fgColor fg color to be used in graphics247* @param bgColor bg color to be used in graphics248* @param font font to be used in graphics249* @return a SunGraphics2D object for the surface (or for temp GDI250* surface data)251*/252@Override253public Graphics2D createGraphics(SurfaceData sd,254WComponentPeer peer, Color fgColor, Color bgColor, Font font)255{256if (!done && sd instanceof D3DWindowSurfaceData) {257D3DWindowSurfaceData d3dw = (D3DWindowSurfaceData)sd;258if (!d3dw.isSurfaceLost() || validate(d3dw)) {259trackScreenSurface(d3dw);260return new SunGraphics2D(sd, fgColor, bgColor, font);261}262// could not restore the d3dw surface, use the cached gdi surface263// instead for this graphics object; note that we do not track264// this new gdi surface, it is only used for this graphics265// object266sd = getGdiSurface(d3dw);267}268return super.createGraphics(sd, peer, fgColor, bgColor, font);269}270271/**272* Posts a repaint event for the peer's target to the EDT273* @param peer for which target's the repaint should be issued274*/275private void repaintPeerTarget(WComponentPeer peer) {276Component target = (Component)peer.getTarget();277Rectangle bounds = AWTAccessor.getComponentAccessor().getBounds(target);278// the system-level painting operations should call the handlePaint()279// method of the WComponentPeer class to repaint the component;280// calling repaint() forces AWT to make call to update()281peer.handlePaint(0, 0, bounds.width, bounds.height);282}283284/**285* Adds a surface to the list of tracked surfaces.286*287* @param d3dw the surface to be added288*/289private void trackScreenSurface(SurfaceData sd) {290if (!done && sd instanceof D3DWindowSurfaceData) {291synchronized (this) {292if (d3dwSurfaces == null) {293d3dwSurfaces = new ArrayList<D3DWindowSurfaceData>();294}295D3DWindowSurfaceData d3dw = (D3DWindowSurfaceData)sd;296if (!d3dwSurfaces.contains(d3dw)) {297d3dwSurfaces.add(d3dw);298}299}300startUpdateThread();301}302}303304@Override305public synchronized void dropScreenSurface(SurfaceData sd) {306if (d3dwSurfaces != null && sd instanceof D3DWindowSurfaceData) {307D3DWindowSurfaceData d3dw = (D3DWindowSurfaceData)sd;308removeGdiSurface(d3dw);309d3dwSurfaces.remove(d3dw);310}311}312313@Override314public SurfaceData getReplacementScreenSurface(WComponentPeer peer,315SurfaceData sd)316{317SurfaceData newSurface = super.getReplacementScreenSurface(peer, sd);318// if some outstanding graphics context wants to get a replacement we319// need to make sure that the new surface (if it is accelerated) is320// being tracked321trackScreenSurface(newSurface);322return newSurface;323}324325/**326* Remove the gdi surface corresponding to the passed d3dw surface327* from list of the cached gdi surfaces.328*329* @param d3dw surface for which associated gdi surface is to be removed330*/331private void removeGdiSurface(final D3DWindowSurfaceData d3dw) {332if (gdiSurfaces != null) {333GDIWindowSurfaceData gdisd = gdiSurfaces.get(d3dw);334if (gdisd != null) {335gdisd.invalidate();336gdiSurfaces.remove(d3dw);337}338}339}340341/**342* If the update thread hasn't yet been created, it will be;343* otherwise it is awaken344*/345private synchronized void startUpdateThread() {346if (screenUpdater == null) {347screenUpdater = AccessController.doPrivileged(348(PrivilegedAction<Thread>) () -> {349ThreadGroup rootTG = ThreadGroupUtils.getRootThreadGroup();350Thread t = new Thread(rootTG,351D3DScreenUpdateManager.this,352"D3D Screen Updater");353// REMIND: should it be higher?354t.setPriority(Thread.NORM_PRIORITY + 2);355t.setDaemon(true);356return t;357});358screenUpdater.start();359} else {360wakeUpUpdateThread();361}362}363364/**365* Wakes up the screen updater thread.366*367* This method is not synchronous, it doesn't wait368* for the updater thread to complete the updates.369*370* It should be used when it is not necessary to wait for the371* completion, for example, when a new surface had been added372* to the list of tracked surfaces (which means that it's about373* to be rendered to).374*/375public void wakeUpUpdateThread() {376synchronized (runLock) {377runLock.notifyAll();378}379}380381/**382* Wakes up the screen updater thread and waits for the completion383* of the update.384*385* This method is called from Toolkit.sync() or386* when there was a copy from a VI to the screen387* so that swing applications would not appear to be388* sluggish.389*/390public void runUpdateNow() {391synchronized (this) {392// nothing to do if the updater thread hadn't been started or if393// there are no tracked surfaces394if (done || screenUpdater == null ||395d3dwSurfaces == null || d3dwSurfaces.size() == 0)396{397return;398}399}400synchronized (runLock) {401needsUpdateNow = true;402runLock.notifyAll();403while (needsUpdateNow) {404try {405runLock.wait();406} catch (InterruptedException e) {}407}408}409}410411public void run() {412while (!done) {413synchronized (runLock) {414// If the list is empty, suspend the thread until a415// new surface is added. Note that we have to check before416// wait() (and inside the runLock), otherwise we could miss a417// notify() when a new surface is added and sleep forever.418long timeout = d3dwSurfaces.size() > 0 ? 100 : 0;419420// don't go to sleep if there's a thread waiting for an update421if (!needsUpdateNow) {422try { runLock.wait(timeout); }423catch (InterruptedException e) {}424}425// if we were woken up, there are probably surfaces in the list,426// no need to check if the list is empty427}428429// make a copy to avoid synchronization during the loop430D3DWindowSurfaceData surfaces[] = new D3DWindowSurfaceData[] {};431synchronized (this) {432surfaces = d3dwSurfaces.toArray(surfaces);433}434for (D3DWindowSurfaceData sd : surfaces) {435// skip invalid surfaces (they could have become invalid436// after we made a copy of the list) - just a precaution437if (sd.isValid() && (sd.isDirty() || sd.isSurfaceLost())) {438if (!sd.isSurfaceLost()) {439// the flip and the clearing of the dirty state440// must be done under the lock, otherwise it's441// possible to miss an update to the surface442D3DRenderQueue rq = D3DRenderQueue.getInstance();443rq.lock();444try {445Rectangle r = sd.getBounds();446D3DSurfaceData.swapBuffers(sd, 0, 0,447r.width, r.height);448sd.markClean();449} finally {450rq.unlock();451}452} else if (!validate(sd)) {453// it is possible that the validation may never454// succeed, we need to detect this and replace455// the d3dw surface with gdi; the replacement of456// the surface will also trigger a repaint457sd.getPeer().replaceSurfaceDataLater();458}459}460}461synchronized (runLock) {462needsUpdateNow = false;463runLock.notifyAll();464}465}466}467468/**469* Restores the passed surface if it was lost, resets the lost status.470* @param sd surface to be validated471* @return true if surface wasn't lost or if restoration was successful,472* false otherwise473*/474private boolean validate(D3DWindowSurfaceData sd) {475if (sd.isSurfaceLost()) {476try {477sd.restoreSurface();478// if succeeded, first fill the surface with bg color479// note: use the non-synch method to avoid incorrect lock order480Color bg = sd.getPeer().getBackgroundNoSync();481SunGraphics2D sg2d = new SunGraphics2D(sd, bg, bg, null);482sg2d.fillRect(0, 0, sd.getBounds().width, sd.getBounds().height);483sg2d.dispose();484// now clean the dirty status so that we don't flip it485// next time before it gets repainted; it is safe486// to do without the lock because we will issue a487// repaint anyway so we will not lose any rendering488sd.markClean();489// since the surface was successfully restored we need to490// repaint whole window to repopulate the back-buffer491repaintPeerTarget(sd.getPeer());492} catch (InvalidPipeException ipe) {493return false;494}495}496return true;497}498499/**500* Creates (or returns a cached one) gdi surface for the same peer as501* the passed d3dw surface has.502*503* @param d3dw surface used as key into the cache504* @return gdi window surface associated with the d3d window surfaces' peer505*/506private synchronized SurfaceData getGdiSurface(D3DWindowSurfaceData d3dw) {507if (gdiSurfaces == null) {508gdiSurfaces =509new HashMap<D3DWindowSurfaceData, GDIWindowSurfaceData>();510}511GDIWindowSurfaceData gdisd = gdiSurfaces.get(d3dw);512if (gdisd == null) {513gdisd = GDIWindowSurfaceData.createData(d3dw.getPeer());514gdiSurfaces.put(d3dw, gdisd);515}516return gdisd;517}518519/**520* Returns true if the component has heavyweight children.521*522* @param comp component to check for hw children523* @return true if Component has heavyweight children524*/525private static boolean hasHWChildren(Component comp) {526if (comp instanceof Container) {527for (Component c : ((Container)comp).getComponents()) {528if (c.getPeer() instanceof WComponentPeer || hasHWChildren(c)) {529return true;530}531}532}533return false;534}535}536537538