| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- /*
- * Copyright (c) 2009-2012 jMonkeyEngine
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- package com.jme3.util;
- import com.jme3.renderer.Renderer;
- import java.lang.ref.PhantomReference;
- import java.lang.ref.ReferenceQueue;
- import java.lang.ref.WeakReference;
- import java.util.ArrayDeque;
- import java.util.ArrayList;
- import java.util.HashMap;
- import java.util.logging.Level;
- import java.util.logging.Logger;
- /**
- * GLObjectManager tracks all GLObjects used by the Renderer. Using a
- * <code>ReferenceQueue</code> the <code>GLObjectManager</code> can delete
- * unused objects from GPU when their counterparts on the CPU are no longer used.
- *
- * On restart, the renderer may request the objects to be reset, thus allowing
- * the GLObjects to re-initialize with the new display context.
- */
- public class NativeObjectManager {
- private static final Logger logger = Logger.getLogger(NativeObjectManager.class.getName());
-
- /**
- * Set to <code>true</code> to enable deletion of native buffers together with GL objects
- * when requested. Note that usage of object after deletion could cause undefined results
- * or native crashes, therefore by default this is set to <code>false</code>.
- */
- public static boolean UNSAFE = false;
-
- /**
- * The maximum number of objects that should be removed per frame.
- * If the limit is reached, no more objects will be removed for that frame.
- */
- private static final int MAX_REMOVES_PER_FRAME = 100;
-
- /**
- * Reference queue for {@link NativeObjectRef native object references}.
- */
- private ReferenceQueue<Object> refQueue = new ReferenceQueue<Object>();
- /**
- * List of currently active GLObjects.
- */
- private HashMap<Long, NativeObjectRef> refMap = new HashMap<Long, NativeObjectRef>();
-
- /**
- * List of real objects requested by user for deletion.
- */
- private ArrayDeque<NativeObject> userDeletionQueue = new ArrayDeque<NativeObject>();
- private static class NativeObjectRef extends PhantomReference<Object> {
-
- private NativeObject objClone;
- private WeakReference<NativeObject> realObj;
- public NativeObjectRef(ReferenceQueue<Object> refQueue, NativeObject obj){
- super(obj.handleRef, refQueue);
- assert obj.handleRef != null;
- this.realObj = new WeakReference<NativeObject>(obj);
- this.objClone = obj.createDestructableClone();
- assert objClone.getId() == obj.getId();
- }
- }
- /**
- * (Internal use only) Register a <code>NativeObject</code> with the manager.
- */
- public void registerObject(NativeObject obj) {
- if (obj.getId() <= 0) {
- throw new IllegalArgumentException("object id must be greater than zero");
- }
- NativeObjectRef ref = new NativeObjectRef(refQueue, obj);
- refMap.put(obj.getUniqueId(), ref);
-
- obj.setNativeObjectManager(this);
- if (logger.isLoggable(Level.FINEST)) {
- logger.log(Level.FINEST, "Registered: {0}", new String[]{obj.toString()});
- }
- }
-
- private void deleteNativeObject(Object rendererObject, NativeObject obj, NativeObjectRef ref,
- boolean deleteGL, boolean deleteBufs) {
- assert rendererObject != null;
-
- // "obj" is considered the real object (with buffers and everything else)
- // if "ref" is null.
- NativeObject realObj = ref != null ?
- ref.realObj.get() :
- obj;
-
- assert realObj == null || obj.getId() == realObj.getId();
-
- if (deleteGL) {
- if (obj.getId() <= 0) {
- logger.log(Level.WARNING, "Object already deleted: {0}", obj.getClass().getSimpleName() + "/" + obj.getId());
- } else {
- // Unregister it from cleanup list.
- NativeObjectRef ref2 = refMap.remove(obj.getUniqueId());
- if (ref2 == null) {
- throw new IllegalArgumentException("This NativeObject is not " +
- "registered in this NativeObjectManager");
- }
- assert ref == null || ref == ref2;
- int id = obj.getId();
- // Delete object from the GL driver
- obj.deleteObject(rendererObject);
- assert obj.getId() == NativeObject.INVALID_ID;
- if (logger.isLoggable(Level.FINEST)) {
- logger.log(Level.FINEST, "Deleted: {0}", obj.getClass().getSimpleName() + "/" + id);
- }
- if (realObj != null){
- // Note: make sure to reset them as well
- // They may get used in a new renderer in the future
- realObj.resetObject();
- }
- }
- }
- if (deleteBufs && UNSAFE && realObj != null) {
- // Only the real object has native buffers.
- // The destructable clone has nothing and cannot be used in this case.
- realObj.deleteNativeBuffersInternal();
- }
- }
-
- /**
- * (Internal use only) Deletes unused NativeObjects.
- * Will delete at most {@link #MAX_REMOVES_PER_FRAME} objects.
- *
- * @param rendererObject The renderer object.
- * For graphics objects, {@link Renderer} is used, for audio, {#link AudioRenderer} is used.
- */
- public void deleteUnused(Object rendererObject){
- int removed = 0;
- while (removed < MAX_REMOVES_PER_FRAME && !userDeletionQueue.isEmpty()) {
- // Remove user requested objects.
- NativeObject obj = userDeletionQueue.pop();
- deleteNativeObject(rendererObject, obj, null, true, true);
- removed++;
- }
- while (removed < MAX_REMOVES_PER_FRAME) {
- // Remove objects reclaimed by GC.
- NativeObjectRef ref = (NativeObjectRef) refQueue.poll();
- if (ref == null) {
- break;
- }
- deleteNativeObject(rendererObject, ref.objClone, ref, true, false);
- removed++;
- }
- if (removed >= 1) {
- logger.log(Level.FINE, "NativeObjectManager: {0} native objects were removed from native", removed);
- }
- }
- /**
- * (Internal use only) Deletes all objects.
- * Must only be called when display is destroyed.
- */
- public void deleteAllObjects(Object rendererObject){
- deleteUnused(rendererObject);
- ArrayList<NativeObjectRef> refMapCopy = new ArrayList<NativeObjectRef>(refMap.values());
- for (NativeObjectRef ref : refMapCopy) {
- deleteNativeObject(rendererObject, ref.objClone, ref, true, false);
- }
- assert refMap.size() == 0;
- }
- /**
- * Marks the given <code>NativeObject</code> as unused,
- * to be deleted on the next frame.
- * Usage of this object after deletion will cause an exception.
- * Note that native buffers are only reclaimed if
- * {@link #UNSAFE} is set to <code>true</code>.
- *
- * @param obj The object to mark as unused.
- */
- void enqueueUnusedObject(NativeObject obj) {
- userDeletionQueue.push(obj);
- }
-
- /**
- * (Internal use only) Resets all {@link NativeObject}s.
- * This is typically called when the context is restarted.
- */
- public void resetObjects(){
- for (NativeObjectRef ref : refMap.values()) {
- // Must use the real object here, for this to be effective.
- NativeObject realObj = ref.realObj.get();
- if (realObj == null) {
- continue;
- }
-
- realObj.resetObject();
- if (logger.isLoggable(Level.FINEST)) {
- logger.log(Level.FINEST, "Reset: {0}", realObj);
- }
- }
- refMap.clear();
- }
- // public void printObjects(){
- // System.out.println(" ------------------- ");
- // System.out.println(" GL Object count: "+ objectList.size());
- // for (GLObject obj : objectList){
- // System.out.println(obj);
- // }
- // }
- }
|