Commits

Michael Ludwig committed 887ebe0

Implement memory estimates.

Comments (0)

Files changed (15)

             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-release-plugin</artifactId>
-                <version>2.2.1</version>
                 <configuration>
                     <autoVersionSubmodules>true</autoVersionSubmodules>
                     <mavenExecutorId>forked-path</mavenExecutorId>

src/main/java/com/lhkbob/entreri/ComponentRepository.java

             entityIndexToComponentRepository = Arrays.copyOf(entityIndexToComponentRepository, (int) (numEntities * 1.5f) + 1);
         }
     }
+    
+    /**
+     * @return Estimated memory usage of this component repository
+     */
+    public long estimateMemory() {
+        long total = 0L;
+        for (int i = 0; i < declaredProperties.size(); i++) {
+            total += declaredProperties.get(i).property.getDataStore().memory();
+        }
+        for (int i = 0; i < decoratedProperties.size(); i++) {
+            total += decoratedProperties.get(i).property.getDataStore().memory();
+        }
+        
+        // also add in an estimate for other structures used by
+        // this repository
+        total += 4 * entityIndexToComponentRepository.length;
+        total += 4 * componentIndexToEntityIndex.length;
+        
+        // estimate each Component object as 4 bytes for a pointer, 
+        // 4 bytes for its index, 4 bytes for its repository reference
+        total += 12 * components.length;
+        
+        return total;
+    }
 
     /**
      * @param componentIndex The component to look up

src/main/java/com/lhkbob/entreri/Entity.java

      * @throws NullPointerException if id is null
      */
     public <T extends ComponentData<T>> Component<T> get(TypeId<T> componentId, boolean ignoreEnable) {
-        ComponentRepository<T> ci = system.getIndex(componentId);
+        ComponentRepository<T> ci = system.getRepository(componentId);
         Component<T> c = ci.getComponent(ci.getComponentIndex(index));
         
         if (c == null || ignoreEnable || c.isEnabled())
      * @throws NullPointerException if componentId is null
      */
     public <T extends ComponentData<T>> Component<T> add(TypeId<T> componentId) {
-        ComponentRepository<T> ci = system.getIndex(componentId);
+        ComponentRepository<T> ci = system.getRepository(componentId);
         return ci.addComponent(index);
     }
 
     public <T extends ComponentData<T>> Component<T> add(Component<T> toClone) {
         if (toClone == null)
             throw new NullPointerException("ComponentData template, toClone, cannot be null");
-        ComponentRepository ci = system.getIndex(toClone.getTypeId());
+        ComponentRepository ci = system.getRepository(toClone.getTypeId());
         return ci.addComponent(index, toClone);
     }
 
      * @throws NullPointerException if componentId is null
      */
     public <T extends ComponentData<T>> boolean remove(TypeId<T> componentId) {
-        ComponentRepository<T> ci = system.getIndex(componentId);
+        ComponentRepository<T> ci = system.getRepository(componentId);
         return ci.removeComponent(index);
     }
 

src/main/java/com/lhkbob/entreri/EntitySystem.java

  * @author Michael Ludwig
  */
 public final class EntitySystem implements Iterable<Entity> {
-    private ComponentRepository<?>[] componentIndices;
+    private ComponentRepository<?>[] componentRepositories;
     
     private Entity[] entities;
     
     public EntitySystem() {
         manager = new ControllerManager(this);
         entities = new Entity[1];
-        componentIndices = new ComponentRepository[0];
+        componentRepositories = new ComponentRepository[0];
         
         entityIdSeq = 1; // start at 1, id 0 is reserved for index = 0 
         entityInsert = 1;
             throw new NullPointerException("ComponentDataFactory cannot be null");
         
         int index = id.getId();
-        if (index >= componentIndices.length) {
+        if (index >= componentRepositories.length) {
             // make sure it's the correct size
-            componentIndices = Arrays.copyOf(componentIndices, index + 1);
+            componentRepositories = Arrays.copyOf(componentRepositories, index + 1);
         }
         
-        ComponentRepository<T> i = (ComponentRepository<T>) componentIndices[index];
+        ComponentRepository<T> i = (ComponentRepository<T>) componentRepositories[index];
         if (i != null) {
             // a factory is already defined
             throw new IllegalStateException("A ComponentDataFactory is already assigned to the type: " + id);
         
         i = new ComponentRepository<T>(this, id, factory);
         i.expandEntityIndex(entities.length);
-        componentIndices[index] = i;
+        componentRepositories[index] = i;
     }
 
     /**
      * @return A new instance of T linked to this EntitySystem
      */
     public <T extends ComponentData<T>> T createDataInstance(TypeId<T> id) {
-        return getIndex(id).createDataInstance();
+        return getRepository(id).createDataInstance();
+    }
+    
+    /**
+     * Estimate the memory usage of the components with the given TypeId in this
+     * EntitySystem. The returned long is measured in bytes. For ComponentData
+     * types that store primitive data, the estimate will be quite accurate. For
+     * types that store references to objects, it is likely be an underestimate.
+     * 
+     * @param id The TypeId to estimate
+     * @return The memory estimate for the given type
+     * @throws NullPointerException if id is null
+     */
+    public long estimateMemory(TypeId<?> id) {
+        int index = id.getId();
+        if (index < componentRepositories.length) {
+            ComponentRepository<?> repo = componentRepositories[index];
+            return repo.estimateMemory();
+        } else {
+            return 0L;
+        }
     }
     
     /**
         }
         
         // Now index and update all ComponentIndices
-        for (int i = 0; i < componentIndices.length; i++) {
-            if (componentIndices[i] != null)
-                componentIndices[i].compact(oldToNew, entityInsert);
+        for (int i = 0; i < componentRepositories.length; i++) {
+            if (componentRepositories[i] != null)
+                componentRepositories[i].compact(oldToNew, entityInsert);
         }
     }
 
             entities = Arrays.copyOf(entities, (int) (entityIndex * 1.5f) + 1);
         }
         
-        for (int i = 0; i < componentIndices.length; i++) {
-            if (componentIndices[i] != null)
-                componentIndices[i].expandEntityIndex(entityIndex + 1);
+        for (int i = 0; i < componentRepositories.length; i++) {
+            if (componentRepositories[i] != null)
+                componentRepositories[i].expandEntityIndex(entityIndex + 1);
         }
         
         Entity newEntity = new Entity(this, entityIndex, entityIdSeq++);
             throw new IllegalArgumentException("Entity has already been removed");
         
         // Remove all components from the entity
-        for (int i = 0; i < componentIndices.length; i++) {
-            if (componentIndices[i] != null)
-                componentIndices[i].removeComponent(e.index);
+        for (int i = 0; i < componentRepositories.length; i++) {
+            if (componentRepositories[i] != null)
+                componentRepositories[i].removeComponent(e.index);
         }
         
         // clear out the entity
      * @throws NullPointerException if type or factory are null
      */
     public <T extends ComponentData<T>, P extends Property> P decorate(TypeId<T> type, PropertyFactory<P> factory) {
-        ComponentRepository<?> index = getIndex(type);
+        ComponentRepository<?> index = getRepository(type);
         return index.decorate(factory);
     }
 
      * @throws NullPointerException if type is null
      */
     public <T extends ComponentData<T>> void undecorate(TypeId<T> type, Property p) {
-        ComponentRepository<?> index = getIndex(type);
+        ComponentRepository<?> index = getRepository(type);
         index.undecorate(p);
     }
 
      * @return The ComponentRepository for the type
      */
     @SuppressWarnings("unchecked")
-    <T extends ComponentData<T>> ComponentRepository<T> getIndex(TypeId<T> id) {
+    <T extends ComponentData<T>> ComponentRepository<T> getRepository(TypeId<T> id) {
         int index = id.getId();
-        if (index >= componentIndices.length) {
+        if (index >= componentRepositories.length) {
             // make sure it's the correct size
-            componentIndices = Arrays.copyOf(componentIndices, index + 1);
+            componentRepositories = Arrays.copyOf(componentRepositories, index + 1);
         }
         
-        ComponentRepository<T> i = (ComponentRepository<T>) componentIndices[index];
+        ComponentRepository<T> i = (ComponentRepository<T>) componentRepositories[index];
         if (i == null) {
             // if the index does not exist, then we need to use the default component data factory
             i = new ComponentRepository<T>(this, id, createDefaultFactory(id));
             i.expandEntityIndex(entities.length);
-            componentIndices[index] = i;
+            componentRepositories[index] = i;
         }
         
         return i;
     
     @SuppressWarnings({ "rawtypes", "unchecked" })
     private <T extends ComponentData<T>> void addFromTemplate(int entityIndex, TypeId typeId, Component<T> c) {
-        ComponentRepository index = getIndex(typeId);
+        ComponentRepository index = getRepository(typeId);
         index.addComponent(entityIndex, c);
     }
 
         public boolean hasNext() {
             if (!advanced)
                 advance();
-            return index < componentIndices.length;
+            return index < componentRepositories.length;
         }
 
         @Override
             if (!hasNext())
                 throw new NoSuchElementException();
             advanced = false;
-            return componentIndices[index];
+            return componentRepositories[index];
         }
 
         @Override
         private void advance() {
             do {
                 index++;
-            } while(index < componentIndices.length && componentIndices[index] == null);
+            } while(index < componentRepositories.length && componentRepositories[index] == null);
             advanced = true;
         }
     }

src/main/java/com/lhkbob/entreri/property/BooleanProperty.java

         }
         
         @Override
+        public long memory() {
+            return array.length;
+        }
+        
+        @Override
         public BooleanDataStore create(int size) {
             return new BooleanDataStore(elementSize, new boolean[elementSize * size]);
         }

src/main/java/com/lhkbob/entreri/property/ByteProperty.java

         }
         
         @Override
+        public long memory() {
+            return array.length;
+        }
+        
+        @Override
         public ByteDataStore create(int size) {
             return new ByteDataStore(elementSize, new byte[elementSize * size]);
         }

src/main/java/com/lhkbob/entreri/property/CharProperty.java

         }
         
         @Override
+        public long memory() {
+            return 2 * array.length;
+        }
+        
+        @Override
         public CharDataStore create(int size) {
             return new CharDataStore(elementSize, new char[elementSize * size]);
         }

src/main/java/com/lhkbob/entreri/property/DoubleProperty.java

         }
         
         @Override
+        public long memory() {
+            return 8 * array.length;
+        }
+        
+        @Override
         public DoubleDataStore create(int size) {
             return new DoubleDataStore(elementSize, new double[elementSize * size]);
         }

src/main/java/com/lhkbob/entreri/property/FloatProperty.java

         }
         
         @Override
+        public long memory() {
+            return 4 * array.length;
+        }
+        
+        @Override
         public FloatDataStore create(int size) {
             return new FloatDataStore(elementSize, new float[elementSize * size]);
         }

src/main/java/com/lhkbob/entreri/property/IndexedDataStore.java

      * @return The number of properties that can fit into this IndexedDataStore
      */
     public int size();
+    
+    /**
+     * @return Memory usage estimate of this data store
+     */
+    public long memory();
 
     /**
      * <p>

src/main/java/com/lhkbob/entreri/property/IntProperty.java

         }
         
         @Override
+        public long memory() {
+            return 4 * array.length;
+        }
+        
+        @Override
         public IntDataStore create(int size) {
             return new IntDataStore(elementSize, new int[elementSize * size]);
         }

src/main/java/com/lhkbob/entreri/property/LongProperty.java

         }
         
         @Override
+        public long memory() {
+            return 8 * array.length;
+        }
+        
+        @Override
         public LongDataStore create(int size) {
             return new LongDataStore(elementSize, new long[elementSize * size]);
         }

src/main/java/com/lhkbob/entreri/property/ObjectProperty.java

         }
         
         @Override
+        public long memory() {
+            // since this is just an approximate, we use 12 bytes as a minimum
+            // size for each object
+            return 12 * array.length;
+        }
+        
+        @Override
         public ObjectDataStore create(int size) {
             return new ObjectDataStore(elementSize, new Object[elementSize * size]);
         }

src/main/java/com/lhkbob/entreri/property/ShortProperty.java

         }
         
         @Override
+        public long memory() {
+            return 2 * array.length;
+        }
+        
+        @Override
         public ShortDataStore create(int size) {
             return new ShortDataStore(elementSize, new short[elementSize * size]);
         }

src/test/java/com/lhkbob/entreri/component/OnSetComponent.java

+/*
+ * Entreri, an entity-component framework in Java
+ *
+ * Copyright (c) 2012, Michael Ludwig
+ * 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.
+ *
+ * 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 HOLDER 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.lhkbob.entreri.component;
 
 import com.lhkbob.entreri.ComponentData;