1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.replica.replicaisland;
18
19
20/**
21 * Manages a double-buffered queue of renderable objects.  The game thread submits drawable objects
22 * to the the active render queue while the render thread consumes drawables from the alternate
23 * queue.  When both threads complete a frame the queues are swapped.  Note that this class can
24 * manage any number (>=2) of render queues, but increasing the number over two means that the game
25 * logic will be running significantly ahead of the rendering thread, which may make the user feel
26 * that the controls are "loose."
27 */
28public class RenderSystem extends BaseObject {
29    private static final int TEXTURE_SORT_BUCKET_SIZE = 1000;
30    private RenderElementPool mElementPool;
31    private ObjectManager[] mRenderQueues;
32    private int mQueueIndex;
33
34    private final static int DRAW_QUEUE_COUNT = 2;
35    private final static int MAX_RENDER_OBJECTS_PER_FRAME = 384;
36    private final static int MAX_RENDER_OBJECTS = MAX_RENDER_OBJECTS_PER_FRAME * DRAW_QUEUE_COUNT;
37
38    public RenderSystem() {
39        super();
40        mElementPool = new RenderElementPool(MAX_RENDER_OBJECTS);
41        mRenderQueues = new ObjectManager[DRAW_QUEUE_COUNT];
42        for (int x = 0; x < DRAW_QUEUE_COUNT; x++) {
43            mRenderQueues[x] = new PhasedObjectManager(MAX_RENDER_OBJECTS_PER_FRAME);
44        }
45        mQueueIndex = 0;
46    }
47
48    @Override
49    public void reset() {
50
51    }
52
53    public void scheduleForDraw(DrawableObject object, Vector2 position, int priority, boolean cameraRelative) {
54        RenderElement element = mElementPool.allocate();
55        if (element != null) {
56            element.set(object, position, priority, cameraRelative);
57            mRenderQueues[mQueueIndex].add(element);
58        }
59    }
60
61    private void clearQueue(FixedSizeArray<BaseObject> objects) {
62        final int count = objects.getCount();
63        final Object[] objectArray = objects.getArray();
64        final RenderElementPool elementPool = mElementPool;
65        for (int i = count - 1; i >= 0; i--) {
66            RenderElement element = (RenderElement)objectArray[i];
67            elementPool.release(element);
68            objects.removeLast();
69        }
70
71    }
72
73    public void swap(GameRenderer renderer, float cameraX, float cameraY) {
74        mRenderQueues[mQueueIndex].commitUpdates();
75
76        // This code will block if the previous queue is still being executed.
77        renderer.setDrawQueue(mRenderQueues[mQueueIndex], cameraX, cameraY);
78
79        final int lastQueue = (mQueueIndex == 0) ? DRAW_QUEUE_COUNT - 1 : mQueueIndex - 1;
80
81        // Clear the old queue.
82        FixedSizeArray<BaseObject> objects = mRenderQueues[lastQueue].getObjects();
83        clearQueue(objects);
84
85        mQueueIndex = (mQueueIndex + 1) % DRAW_QUEUE_COUNT;
86    }
87
88    /* Empties all draw queues and disconnects the game thread from the renderer. */
89    public void emptyQueues(GameRenderer renderer) {
90        renderer.setDrawQueue(null, 0.0f, 0.0f);
91        for (int x = 0; x < DRAW_QUEUE_COUNT; x++) {
92            mRenderQueues[x].commitUpdates();
93            FixedSizeArray<BaseObject> objects = mRenderQueues[x].getObjects();
94            clearQueue(objects);
95
96        }
97    }
98
99    public class RenderElement extends PhasedObject {
100        public RenderElement() {
101            super();
102        }
103
104        public void set(DrawableObject drawable, Vector2 position, int priority, boolean isCameraRelative) {
105            mDrawable = drawable;
106            x = position.x;
107            y = position.y;
108            cameraRelative = isCameraRelative;
109            final int sortBucket = priority * TEXTURE_SORT_BUCKET_SIZE;
110            int sortOffset = 0;
111            if (drawable != null) {
112                Texture tex = drawable.getTexture();
113                if (tex != null) {
114                    sortOffset = (tex.resource % TEXTURE_SORT_BUCKET_SIZE) * Utils.sign(priority);
115                }
116            }
117            setPhase(sortBucket + sortOffset);
118        }
119
120        public void reset() {
121            mDrawable = null;
122            x = 0.0f;
123            y = 0.0f;
124            cameraRelative = false;
125        }
126
127        public DrawableObject mDrawable;
128        public float x;
129        public float y;
130        public boolean cameraRelative;
131    }
132
133    protected class RenderElementPool extends TObjectPool<RenderElement> {
134
135        RenderElementPool(int max) {
136            super(max);
137        }
138
139        @Override
140        public void release(Object element) {
141            RenderElement renderable = (RenderElement)element;
142            // if this drawable came out of a pool, make sure it is returned to that pool.
143            final ObjectPool pool = renderable.mDrawable.getParentPool();
144            if (pool != null) {
145            	pool.release(renderable.mDrawable);
146            }
147            // reset on release
148            renderable.reset();
149            super.release(element);
150        }
151
152        @Override
153        protected void fill() {
154            for (int x = 0; x < getSize(); x++) {
155                getAvailable().add(new RenderElement());
156            }
157        }
158    }
159}
160