com.ardor3d.scenegraph.AbstractBufferData.java Source code

Java tutorial

Introduction

Here is the source code for com.ardor3d.scenegraph.AbstractBufferData.java

Source

/**
 * Copyright (c) 2008-2012 Ardor Labs, Inc.
 *
 * This file is part of Ardor3D.
 *
 * Ardor3D is free software: you can redistribute it and/or modify it 
 * under the terms of its license which may be found in the accompanying
 * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
 */

package com.ardor3d.scenegraph;

import java.io.IOException;
import java.lang.ref.ReferenceQueue;
import java.nio.Buffer;
import java.util.Map;
import java.util.Set;

import com.ardor3d.renderer.ContextCleanListener;
import com.ardor3d.renderer.ContextManager;
import com.ardor3d.renderer.RenderContext;
import com.ardor3d.renderer.Renderer;
import com.ardor3d.renderer.RendererCallable;
import com.ardor3d.util.Constants;
import com.ardor3d.util.ContextIdReference;
import com.ardor3d.util.GameTaskQueueManager;
import com.ardor3d.util.export.InputCapsule;
import com.ardor3d.util.export.OutputCapsule;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.MapMaker;
import com.google.common.collect.Multimap;

public abstract class AbstractBufferData<T extends Buffer> {

    private static Map<AbstractBufferData<?>, Object> _identityCache = new MapMaker().weakKeys().makeMap();
    private static final Object STATIC_REF = new Object();

    private static ReferenceQueue<AbstractBufferData<?>> _vboRefQueue = new ReferenceQueue<AbstractBufferData<?>>();

    static {
        ContextManager.addContextCleanListener(new ContextCleanListener() {
            public void cleanForContext(final RenderContext renderContext) {
                AbstractBufferData.cleanAllVBOs(null, renderContext);
            }
        });
    }

    protected transient ContextIdReference<AbstractBufferData<T>> _vboIdCache;

    /** Buffer holding the data. */
    protected T _buffer;

    /** Access mode of the buffer when using Vertex Buffer Objects. */
    public enum VBOAccessMode {
        StaticDraw, StaticCopy, StaticRead, StreamDraw, StreamCopy, StreamRead, DynamicDraw, DynamicCopy, DynamicRead
    }

    /** VBO Access mode for this buffer. */
    protected VBOAccessMode _vboAccessMode = VBOAccessMode.StaticDraw;

    /** Flag for notifying the renderer that the VBO buffer needs to be updated. */
    protected boolean _needsRefresh = false;

    AbstractBufferData() {
        _identityCache.put(this, STATIC_REF);
    }

    /**
     * @return the number of bytes per entry in the buffer. For example, an IntBuffer would return 4.
     */
    public abstract int getByteCount();

    /**
     * Gets the count.
     * 
     * @return the count
     */
    public int getBufferLimit() {
        if (_buffer != null) {
            return _buffer.limit();
        }

        return 0;
    }

    /**
     * Gets the count.
     * 
     * @return the count
     */
    public int getBufferCapacity() {
        if (_buffer != null) {
            return _buffer.capacity();
        }

        return 0;
    }

    /**
     * Get the buffer holding the data.
     * 
     * @return the buffer
     */
    public T getBuffer() {
        return _buffer;
    }

    /**
     * Set the buffer holding the data.
     * 
     * @param buffer
     *            the buffer to set
     */
    public void setBuffer(final T buffer) {
        _buffer = buffer;
    }

    /**
     * @param glContext
     *            the object representing the OpenGL context a vbo belongs to. See
     *            {@link RenderContext#getGlContextRep()}
     * @return the vbo id of a vbo in the given context. If the vbo is not found in the given context, 0 is returned.
     */
    public int getVBOID(final Object glContext) {
        if (_vboIdCache != null) {
            final Integer id = _vboIdCache.getValue(glContext);
            if (id != null) {
                return id.intValue();
            }
        }
        return 0;
    }

    /**
     * Removes any vbo id from this buffer's data for the given OpenGL context.
     * 
     * @param glContext
     *            the object representing the OpenGL context a vbo would belong to. See
     *            {@link RenderContext#getGlContextRep()}
     * @return the id removed or 0 if not found.
     */
    public int removeVBOID(final Object glContext) {
        if (_vboIdCache != null) {
            return _vboIdCache.removeValue(glContext);
        } else {
            return 0;
        }
    }

    /**
     * Sets the id for a vbo based on this buffer's data in regards to the given OpenGL context.
     * 
     * @param glContextRep
     *            the object representing the OpenGL context a vbo belongs to. See
     *            {@link RenderContext#getGlContextRep()}
     * @param vboId
     *            the vbo id of a vbo. To be valid, this must be not equals to 0.
     * @throws IllegalArgumentException
     *             if vboId is less than or equal to 0.
     */
    public void setVBOID(final Object glContextRep, final int vboId) {
        if (vboId == 0) {
            throw new IllegalArgumentException("vboId must != 0");
        }

        if (_vboIdCache == null) {
            _vboIdCache = new ContextIdReference<AbstractBufferData<T>>(this, _vboRefQueue);
        }
        _vboIdCache.put(glContextRep, vboId);
    }

    public VBOAccessMode getVboAccessMode() {
        return _vboAccessMode;
    }

    public void setVboAccessMode(final VBOAccessMode vboAccessMode) {
        this._vboAccessMode = vboAccessMode;
    }

    public boolean isNeedsRefresh() {
        return _needsRefresh;
    }

    public void setNeedsRefresh(final boolean needsRefresh) {
        this._needsRefresh = needsRefresh;
    }

    public static void cleanAllVBOs(final Renderer deleter) {
        final Multimap<Object, Integer> idMap = ArrayListMultimap.create();

        // gather up expired vbos... these don't exist in our cache
        gatherGCdIds(idMap);

        // Walk through the cached items and delete those too.
        for (final AbstractBufferData<?> buf : _identityCache.keySet()) {
            if (buf._vboIdCache != null) {
                if (Constants.useMultipleContexts) {
                    final Set<Object> contextObjects = buf._vboIdCache.getContextObjects();
                    for (final Object o : contextObjects) {
                        // Add id to map
                        idMap.put(o, buf.getVBOID(o));
                    }
                } else {
                    idMap.put(ContextManager.getCurrentContext().getGlContextRep(), buf.getVBOID(null));
                }
                buf._vboIdCache.clear();
            }
        }

        handleVBODelete(deleter, idMap);
    }

    public static void cleanAllVBOs(final Renderer deleter, final RenderContext context) {
        final Multimap<Object, Integer> idMap = ArrayListMultimap.create();

        // gather up expired vbos... these don't exist in our cache
        gatherGCdIds(idMap);

        final Object glRep = context.getGlContextRep();
        // Walk through the cached items and delete those too.
        for (final AbstractBufferData<?> buf : _identityCache.keySet()) {
            // only worry about buffers that have received ids.
            if (buf._vboIdCache != null) {
                final Integer id = buf._vboIdCache.removeValue(glRep);
                if (id != null && id.intValue() != 0) {
                    idMap.put(context.getGlContextRep(), id);
                }
            }
        }

        handleVBODelete(deleter, idMap);
    }

    /**
     * Clean any VBO ids from the hardware, using the given Renderer object to do the work immediately, if given. If
     * not, we will delete in the next execution of the appropriate context's game task render queue.
     * 
     * @param deleter
     *            the Renderer to use. If null, execution will not occur immediately.
     */
    public static void cleanExpiredVBOs(final Renderer deleter) {
        // gather up expired vbos...
        final Multimap<Object, Integer> idMap = gatherGCdIds(null);

        if (idMap != null) {
            // send to be deleted (perhaps on next render.)
            handleVBODelete(deleter, idMap);
        }
    }

    /**
     * @return a deep copy of this buffer data object
     */
    public abstract AbstractBufferData<T> makeCopy();

    @SuppressWarnings("unchecked")
    private static final Multimap<Object, Integer> gatherGCdIds(Multimap<Object, Integer> store) {
        // Pull all expired vbos from ref queue and add to an id multimap.
        ContextIdReference<AbstractBufferData<?>> ref;
        while ((ref = (ContextIdReference<AbstractBufferData<?>>) _vboRefQueue.poll()) != null) {
            if (Constants.useMultipleContexts) {
                final Set<Object> contextObjects = ref.getContextObjects();
                for (final Object o : contextObjects) {
                    // Add id to map
                    final Integer id = ref.getValue(o);
                    if (id != null) {
                        if (store == null) { // lazy init
                            store = ArrayListMultimap.create();
                        }
                        store.put(o, id);
                    }
                }
            } else {
                final Integer id = ref.getValue(null);
                if (id != null) {
                    if (store == null) { // lazy init
                        store = ArrayListMultimap.create();
                    }
                    store.put(ContextManager.getCurrentContext().getGlContextRep(), id);
                }
            }
            ref.clear();
        }

        return store;
    }

    private static void handleVBODelete(final Renderer deleter, final Multimap<Object, Integer> idMap) {
        Object currentGLRef = null;
        // Grab the current context, if any.
        if (deleter != null && ContextManager.getCurrentContext() != null) {
            currentGLRef = ContextManager.getCurrentContext().getGlContextRep();
        }
        // For each affected context...
        for (final Object glref : idMap.keySet()) {
            // If we have a deleter and the context is current, immediately delete
            if (deleter != null && glref.equals(currentGLRef)) {
                deleter.deleteVBOs(idMap.get(glref));
            }
            // Otherwise, add a delete request to that context's render task queue.
            else {
                GameTaskQueueManager.getManager(ContextManager.getContextForRef(glref))
                        .render(new RendererCallable<Void>() {
                            public Void call() throws Exception {
                                getRenderer().deleteVBOs(idMap.get(glref));
                                return null;
                            }
                        });
            }
        }
    }

    public void read(final InputCapsule capsule) throws IOException {
        _vboAccessMode = capsule.readEnum("vboAccessMode", VBOAccessMode.class, VBOAccessMode.StaticDraw);
    }

    public void write(final OutputCapsule capsule) throws IOException {
        capsule.write(_vboAccessMode, "vboAccessMode", VBOAccessMode.StaticDraw);
    }
}