1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 * in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the License
10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 * or implied. See the License for the specific language governing permissions and limitations under
12 * the License.
13 */
14
15package androidx.media.filterfw;
16
17import android.annotation.TargetApi;
18import android.graphics.Bitmap;
19import android.os.Build;
20import android.renderscript.Allocation;
21import android.renderscript.Element;
22import android.renderscript.RenderScript;
23import android.renderscript.Type;
24import android.util.Log;
25
26import java.nio.ByteBuffer;
27import java.nio.ByteOrder;
28import java.util.Arrays;
29import java.util.Vector;
30
31final class BackingStore {
32
33    /** Access mode None: Frame data will not be accessed at all. */
34    static final int ACCESS_NONE = 0x00;
35    /** Access mode Bytes: Frame data will be accessed as a ByteBuffer. */
36    static final int ACCESS_BYTES = 0x01;
37    /** Access mode Texture: Frame data will be accessed as a TextureSource. */
38    static final int ACCESS_TEXTURE = 0x02;
39    /** Access mode RenderTarget: Frame data will be accessed as a RenderTarget. */
40    static final int ACCESS_RENDERTARGET = 0x04;
41    /** Access mode Object: Frame data will be accessed as a generic Object. */
42    static final int ACCESS_OBJECT = 0x08;
43    /** Access mode Bitmap: Frame data will be accessed as a Bitmap. */
44    static final int ACCESS_BITMAP = 0x10;
45    /** Access mode Allocation: Frame data will be accessed as a RenderScript Allocation. */
46    static final int ACCESS_ALLOCATION = 0x20;
47
48    private static final int BACKING_BYTEBUFFER = 1;
49    private static final int BACKING_TEXTURE = 2;
50    private static final int BACKING_OBJECT = 3;
51    private static final int BACKING_BITMAP = 4;
52    private static final int BACKING_ALLOCATION = 5;
53
54    private final FrameType mType;
55    private int[] mDimensions;
56    private long mTimestamp = Frame.TIMESTAMP_NOT_SET;
57
58    private final FrameManager mFrameManager;
59
60    private Vector<Backing> mBackings = new Vector<Backing>();
61
62    private boolean mWriteLocked = false;
63    private int mReadLocks = 0;
64
65    private int mRefCount = 1;
66
67    /** The most up-to-date data backing */
68    private Backing mCurrentBacking = null;
69
70    /** The currently locked backing */
71    private Backing mLockedBacking = null;
72
73    // Public Methods //////////////////////////////////////////////////////////////////////////////
74    public BackingStore(FrameType type, int[] dimensions, FrameManager frameManager) {
75        mType = type;
76        mDimensions = dimensions != null ? Arrays.copyOf(dimensions, dimensions.length) : null;
77        mFrameManager = frameManager;
78    }
79
80    public FrameType getFrameType() {
81        return mType;
82    }
83
84    public Object lockData(int mode, int accessFormat) {
85        return lockBacking(mode, accessFormat).lock(accessFormat);
86    }
87
88    public Backing lockBacking(int mode, int access) {
89        Backing backing = fetchBacking(mode, access);
90        if (backing == null) {
91            throw new RuntimeException("Could not fetch frame data!");
92        }
93        lock(backing, mode);
94        return backing;
95    }
96
97    public boolean unlock() {
98        if (mWriteLocked) {
99            mWriteLocked = false;
100        } else if (mReadLocks > 0) {
101            --mReadLocks;
102        } else {
103            return false;
104        }
105        mLockedBacking.unlock();
106        mLockedBacking = null;
107        return true;
108    }
109
110    public BackingStore retain() {
111        if (mRefCount >= 10) {
112            Log.w("BackingStore", "High ref-count of " + mRefCount + " on " + this + "!");
113        }
114        if (mRefCount <= 0) {
115            throw new RuntimeException("RETAINING RELEASED");
116        }
117        ++mRefCount;
118        return this;
119    }
120
121    public BackingStore release() {
122        if (mRefCount <= 0) {
123            throw new RuntimeException("DOUBLE-RELEASE");
124        }
125        --mRefCount;
126        if (mRefCount == 0) {
127            releaseBackings();
128            return null;
129        }
130        return this;
131    }
132
133    /**
134     * Resizes the backing store. This invalidates all data in the store.
135     */
136    public void resize(int[] newDimensions) {
137        Vector<Backing> resized = new Vector<Backing>();
138        for (Backing backing : mBackings) {
139            if (backing.resize(newDimensions)) {
140                resized.add(backing);
141            } else {
142                releaseBacking(backing);
143            }
144        }
145        mBackings = resized;
146        mDimensions = newDimensions;
147    }
148
149    public int[] getDimensions() {
150        return mDimensions;
151    }
152
153    public int getElementCount() {
154        int result = 1;
155        if (mDimensions != null) {
156            for (int dim : mDimensions) {
157                result *= dim;
158            }
159        }
160        return result;
161    }
162
163    public void importStore(BackingStore store) {
164        // TODO: Better backing selection?
165        if (store.mBackings.size() > 0) {
166            importBacking(store.mBackings.firstElement());
167        }
168        mTimestamp = store.mTimestamp;
169    }
170
171    /**
172     * @return the timestamp
173     */
174    public long getTimestamp() {
175        return mTimestamp;
176    }
177
178    /**
179     * @param timestamp the timestamp to set
180     */
181    public void setTimestamp(long timestamp) {
182        mTimestamp = timestamp;
183    }
184
185    // Internal Methods ////////////////////////////////////////////////////////////////////////////
186    private Backing fetchBacking(int mode, int access) {
187        Backing backing = getBacking(mode, access);
188        if (backing == null) {
189            backing = attachNewBacking(mode, access);
190        }
191        syncBacking(backing);
192        return backing;
193    }
194
195    private void syncBacking(Backing backing) {
196        if (backing != null && backing.isDirty() && mCurrentBacking != null) {
197            backing.syncTo(mCurrentBacking);
198        }
199    }
200
201    private Backing getBacking(int mode, int access) {
202        // [Non-iterator looping]
203        for (int i = 0; i < mBackings.size(); ++i) {
204            final Backing backing = mBackings.get(i);
205
206            int backingAccess =
207                    (mode == Frame.MODE_WRITE) ? backing.writeAccess() : backing.readAccess();
208            if ((backingAccess & access) == access) {
209                return backing;
210            }
211        }
212        return null;
213    }
214
215    private Backing attachNewBacking(int mode, int access) {
216        Backing backing = createBacking(mode, access);
217        if (mBackings.size() > 0) {
218            backing.markDirty();
219        }
220        mBackings.add(backing);
221        return backing;
222    }
223
224    private Backing createBacking(int mode, int access) {
225        // TODO: If the read/write access flags indicate, make/fetch a GraphicBuffer backing.
226        Backing backing = null;
227        int elemSize = mType.getElementSize();
228        if (shouldFetchCached(access)) {
229            backing = mFrameManager.fetchBacking(mode, access, mDimensions, elemSize);
230        }
231        if (backing == null) {
232            switch (access) {
233                case ACCESS_BYTES:
234                    backing = new ByteBufferBacking();
235                    break;
236                case ACCESS_TEXTURE:
237                case ACCESS_RENDERTARGET:
238                    backing = new TextureBacking();
239                    break;
240                case ACCESS_OBJECT:
241                    backing = new ObjectBacking();
242                    break;
243                case ACCESS_BITMAP:
244                    backing = new BitmapBacking();
245                    break;
246                case ACCESS_ALLOCATION:
247                    if (!AllocationBacking.isSupported()) {
248                        throw new RuntimeException(
249                                "Attempted to create an AllocationBacking in context that does " +
250                                "not support RenderScript!");
251                    }
252                    backing = new AllocationBacking(mFrameManager.getContext().getRenderScript());
253                    break;
254            }
255            if (backing == null) {
256                throw new RuntimeException(
257                        "Could not create backing for access type " + access + "!");
258            }
259            if (backing.requiresGpu() && !mFrameManager.getRunner().isOpenGLSupported()) {
260                throw new RuntimeException(
261                        "Cannot create backing that requires GPU in a runner that does not " +
262                        "support OpenGL!");
263            }
264            backing.setDimensions(mDimensions);
265            backing.setElementSize(elemSize);
266            backing.setElementId(mType.getElementId());
267            backing.allocate(mType);
268            mFrameManager.onBackingCreated(backing);
269        }
270        return backing;
271    }
272
273    private void importBacking(Backing backing) {
274        // TODO: This actually needs synchronization between the two BackingStore threads for the
275        // general case
276        int access = backing.requiresGpu() ? ACCESS_BYTES : backing.readAccess();
277        Backing newBacking = createBacking(Frame.MODE_READ, access);
278        newBacking.syncTo(backing);
279        mBackings.add(newBacking);
280        mCurrentBacking = newBacking;
281    }
282
283    private void releaseBackings() {
284        // [Non-iterator looping]
285        for (int i = 0; i < mBackings.size(); ++i) {
286            releaseBacking(mBackings.get(i));
287        }
288        mBackings.clear();
289        mCurrentBacking = null;
290    }
291
292    private void releaseBacking(Backing backing) {
293        mFrameManager.onBackingAvailable(backing);
294    }
295
296    private void lock(Backing backingToLock, int mode) {
297        if (mode == Frame.MODE_WRITE) {
298            // Make sure frame is not read-locked
299            if (mReadLocks > 0) {
300                throw new RuntimeException(
301                        "Attempting to write-lock the read-locked frame " + this + "!");
302            } else if (mWriteLocked) {
303                throw new RuntimeException(
304                        "Attempting to write-lock the write-locked frame " + this + "!");
305            }
306            // Mark all other backings dirty
307            // [Non-iterator looping]
308            for (int i = 0; i < mBackings.size(); ++i) {
309                final Backing backing = mBackings.get(i);
310                if (backing != backingToLock) {
311                    backing.markDirty();
312                }
313            }
314            mWriteLocked = true;
315            mCurrentBacking = backingToLock;
316        } else {
317            if (mWriteLocked) {
318                throw new RuntimeException("Attempting to read-lock locked frame " + this + "!");
319            }
320            ++mReadLocks;
321        }
322        mLockedBacking = backingToLock;
323    }
324
325    private static boolean shouldFetchCached(int access) {
326        return access != ACCESS_OBJECT;
327    }
328
329
330    // Backings ////////////////////////////////////////////////////////////////////////////////////
331    static abstract class Backing {
332        protected int[] mDimensions = null;
333        private int mElementSize;
334        private int mElementID;
335        protected boolean mIsDirty = false;
336
337        int cachePriority = 0;
338
339        public abstract void allocate(FrameType frameType);
340
341        public abstract int readAccess();
342
343        public abstract int writeAccess();
344
345        public abstract void syncTo(Backing backing);
346
347        public abstract Object lock(int accessType);
348
349        public abstract int getType();
350
351        public abstract boolean shouldCache();
352
353        public abstract boolean requiresGpu();
354
355        public abstract void destroy();
356
357        public abstract int getSize();
358
359        public void unlock() {
360            // Default implementation does nothing.
361        }
362
363        public void setData(Object data) {
364            throw new RuntimeException("Internal error: Setting data on frame backing " + this
365                    + ", which does not support setting data directly!");
366        }
367
368        public void setDimensions(int[] dimensions) {
369            mDimensions = dimensions;
370        }
371
372        public void setElementSize(int elemSize) {
373            mElementSize = elemSize;
374        }
375
376        public void setElementId(int elemId) {
377            mElementID = elemId;
378        }
379
380        public int[] getDimensions() {
381            return mDimensions;
382        }
383
384        public int getElementSize() {
385            return mElementSize;
386        }
387
388        public int getElementId() {
389            return mElementID;
390        }
391
392        public boolean resize(int[] newDimensions) {
393            return false;
394        }
395
396        public void markDirty() {
397            mIsDirty = true;
398        }
399
400        public boolean isDirty() {
401            return mIsDirty;
402        }
403
404        protected void assertImageCompatible(FrameType type) {
405            if (type.getElementId() != FrameType.ELEMENT_RGBA8888) {
406                throw new RuntimeException("Cannot allocate texture with non-RGBA data type!");
407            } else if (mDimensions == null || mDimensions.length != 2) {
408                throw new RuntimeException("Cannot allocate non 2-dimensional texture!");
409            }
410        }
411
412    }
413
414    static class ObjectBacking extends Backing {
415
416        private Object mObject = null;
417
418        @Override
419        public void allocate(FrameType frameType) {
420            mObject = null;
421        }
422
423        @Override
424        public int readAccess() {
425            return ACCESS_OBJECT;
426        }
427
428        @Override
429        public int writeAccess() {
430            return ACCESS_OBJECT;
431        }
432
433        @Override
434        public void syncTo(Backing backing) {
435            switch (backing.getType()) {
436                case BACKING_OBJECT:
437                    mObject = backing.lock(ACCESS_OBJECT);
438                    backing.unlock();
439                    break;
440                case BACKING_BITMAP:
441                    mObject = backing.lock(ACCESS_BITMAP);
442                    backing.unlock();
443                    break;
444                default:
445                    mObject = null;
446            }
447            mIsDirty = false;
448        }
449
450        @Override
451        public Object lock(int accessType) {
452            return mObject;
453        }
454
455        @Override
456        public int getType() {
457            return BACKING_OBJECT;
458        }
459
460        @Override
461        public boolean shouldCache() {
462            return false;
463        }
464
465        @Override
466        public boolean requiresGpu() {
467            return false;
468        }
469
470        @Override
471        public void destroy() {
472            mObject = null;
473        }
474
475        @Override
476        public int getSize() {
477            return 0;
478        }
479
480        @Override
481        public void setData(Object data) {
482            mObject = data;
483        }
484
485    }
486
487    static class BitmapBacking extends Backing {
488
489        private Bitmap mBitmap = null;
490
491        @Override
492        public void allocate(FrameType frameType) {
493            assertImageCompatible(frameType);
494        }
495
496        @Override
497        public int readAccess() {
498            return ACCESS_BITMAP;
499        }
500
501        @Override
502        public int writeAccess() {
503            return ACCESS_BITMAP;
504        }
505
506        @Override
507        public void syncTo(Backing backing) {
508            int access = backing.readAccess();
509            if ((access & ACCESS_BITMAP) != 0) {
510                mBitmap = (Bitmap) backing.lock(ACCESS_BITMAP);
511            } else if ((access & ACCESS_BYTES) != 0) {
512                createBitmap();
513                ByteBuffer buffer = (ByteBuffer) backing.lock(ACCESS_BYTES);
514                mBitmap.copyPixelsFromBuffer(buffer);
515                buffer.rewind();
516            } else if ((access & ACCESS_TEXTURE) != 0) {
517                createBitmap();
518                RenderTarget renderTarget = (RenderTarget) backing.lock(ACCESS_RENDERTARGET);
519                mBitmap.copyPixelsFromBuffer(
520                        renderTarget.getPixelData(mDimensions[0], mDimensions[1]));
521            } else if ((access & ACCESS_ALLOCATION) != 0 && AllocationBacking.isSupported()) {
522                createBitmap();
523                syncToAllocationBacking(backing);
524            } else {
525                throw new RuntimeException("Cannot sync bytebuffer backing!");
526            }
527            backing.unlock();
528            mIsDirty = false;
529        }
530
531        @TargetApi(11)
532        private void syncToAllocationBacking(Backing backing) {
533            Allocation allocation = (Allocation) backing.lock(ACCESS_ALLOCATION);
534            allocation.copyTo(mBitmap);
535        }
536
537        @Override
538        public Object lock(int accessType) {
539            return mBitmap;
540        }
541
542        @Override
543        public int getType() {
544            return BACKING_BITMAP;
545        }
546
547        @Override
548        public boolean shouldCache() {
549            return false;
550        }
551
552        @Override
553        public boolean requiresGpu() {
554            return false;
555        }
556
557        @Override
558        public void destroy() {
559            // As we share the bitmap with other backings (such as object backings), we must not
560            // recycle it here.
561            mBitmap = null;
562        }
563
564        @Override
565        public int getSize() {
566            return 4 * mDimensions[0] * mDimensions[1];
567        }
568
569        @Override
570        public void setData(Object data) {
571            // We can assume that data will always be a Bitmap instance.
572            mBitmap = (Bitmap) data;
573        }
574
575        private void createBitmap() {
576            mBitmap = Bitmap.createBitmap(mDimensions[0], mDimensions[1], Bitmap.Config.ARGB_8888);
577        }
578    }
579
580    static class TextureBacking extends Backing {
581
582        private RenderTarget mRenderTarget = null;
583        private TextureSource mTexture = null;
584
585        @Override
586        public void allocate(FrameType frameType) {
587            assertImageCompatible(frameType);
588            mTexture = TextureSource.newTexture();
589        }
590
591        @Override
592        public int readAccess() {
593            return ACCESS_TEXTURE;
594        }
595
596        @Override
597        public int writeAccess() {
598            return ACCESS_RENDERTARGET;
599        }
600
601        @Override
602        public void syncTo(Backing backing) {
603            int access = backing.readAccess();
604            if ((access & ACCESS_BYTES) != 0) {
605                ByteBuffer pixels = (ByteBuffer) backing.lock(ACCESS_BYTES);
606                mTexture.allocateWithPixels(pixels, mDimensions[0], mDimensions[1]);
607            } else if ((access & ACCESS_BITMAP) != 0) {
608                Bitmap bitmap = (Bitmap) backing.lock(ACCESS_BITMAP);
609                mTexture.allocateWithBitmapPixels(bitmap);
610            } else if ((access & ACCESS_TEXTURE) != 0) {
611                TextureSource texture = (TextureSource) backing.lock(ACCESS_TEXTURE);
612                int w = mDimensions[0];
613                int h = mDimensions[1];
614                ImageShader.renderTextureToTarget(texture, getRenderTarget(), w, h);
615            } else if ((access & ACCESS_ALLOCATION) != 0 && AllocationBacking.isSupported()) {
616                syncToAllocationBacking(backing);
617            } else {
618                throw new RuntimeException("Cannot sync bytebuffer backing!");
619            }
620            backing.unlock();
621            mIsDirty = false;
622        }
623
624        @TargetApi(11)
625        private void syncToAllocationBacking(Backing backing) {
626            Allocation allocation = (Allocation) backing.lock(ACCESS_ALLOCATION);
627            ByteBuffer pixels = ByteBuffer.allocateDirect(getSize());
628            allocation.copyTo(pixels.array());
629            mTexture.allocateWithPixels(pixels, mDimensions[0], mDimensions[1]);
630        }
631
632        @Override
633        public Object lock(int accessType) {
634            switch (accessType) {
635                case ACCESS_TEXTURE:
636                    return getTexture();
637
638                case ACCESS_RENDERTARGET:
639                    return getRenderTarget();
640
641                default:
642                    throw new RuntimeException("Illegal access to texture!");
643            }
644        }
645
646        @Override
647        public int getType() {
648            return BACKING_TEXTURE;
649        }
650
651        @Override
652        public boolean shouldCache() {
653            return true;
654        }
655
656        @Override
657        public boolean requiresGpu() {
658            return true;
659        }
660
661        @Override
662        public void destroy() {
663            if (mRenderTarget != null) {
664                mRenderTarget.release();
665            }
666            if (mTexture.isAllocated()) {
667                mTexture.release();
668            }
669        }
670
671        @Override
672        public int getSize() {
673            return 4 * mDimensions[0] * mDimensions[1];
674        }
675
676        private TextureSource getTexture() {
677            if (!mTexture.isAllocated()) {
678                mTexture.allocate(mDimensions[0], mDimensions[1]);
679            }
680            return mTexture;
681        }
682
683        private RenderTarget getRenderTarget() {
684            if (mRenderTarget == null) {
685                int w = mDimensions[0];
686                int h = mDimensions[1];
687                mRenderTarget = RenderTarget.currentTarget().forTexture(getTexture(), w, h);
688            }
689            return mRenderTarget;
690        }
691
692    }
693
694    static class ByteBufferBacking extends Backing {
695
696        ByteBuffer mBuffer = null;
697
698        @Override
699        public void allocate(FrameType frameType) {
700            int size = frameType.getElementSize();
701            for (int dim : mDimensions) {
702                size *= dim;
703            }
704            mBuffer = ByteBuffer.allocateDirect(size);
705        }
706
707        @Override
708        public int readAccess() {
709            return ACCESS_BYTES;
710        }
711
712        @Override
713        public int writeAccess() {
714            return ACCESS_BYTES;
715        }
716
717        @Override
718        public boolean requiresGpu() {
719            return false;
720        }
721
722        @Override
723        public void syncTo(Backing backing) {
724            int access = backing.readAccess();
725            if ((access & ACCESS_TEXTURE) != 0) {
726                RenderTarget target = (RenderTarget) backing.lock(ACCESS_RENDERTARGET);
727                GLToolbox.readTarget(target, mBuffer, mDimensions[0], mDimensions[1]);
728            } else if ((access & ACCESS_BITMAP) != 0) {
729                Bitmap bitmap = (Bitmap) backing.lock(ACCESS_BITMAP);
730                bitmap.copyPixelsToBuffer(mBuffer);
731                mBuffer.rewind();
732            } else if ((access & ACCESS_BYTES) != 0) {
733                ByteBuffer otherBuffer = (ByteBuffer) backing.lock(ACCESS_BYTES);
734                mBuffer.put(otherBuffer);
735                otherBuffer.rewind();
736            } else if ((access & ACCESS_ALLOCATION) != 0 && AllocationBacking.isSupported()) {
737                syncToAllocationBacking(backing);
738            } else {
739                throw new RuntimeException("Cannot sync bytebuffer backing!");
740            }
741            backing.unlock();
742            mBuffer.rewind();
743            mIsDirty = false;
744        }
745
746        @TargetApi(11)
747        private void syncToAllocationBacking(Backing backing) {
748            Allocation allocation = (Allocation) backing.lock(ACCESS_ALLOCATION);
749            if (getElementId() == FrameType.ELEMENT_RGBA8888) {
750                byte[] bytes = mBuffer.array();
751                allocation.copyTo(bytes);
752            } else if (getElementId() == FrameType.ELEMENT_FLOAT32) {
753                float[] floats = new float[getSize() / 4];
754                allocation.copyTo(floats);
755                mBuffer.asFloatBuffer().put(floats);
756            } else {
757                throw new RuntimeException(
758                        "Trying to sync to an allocation with an unsupported element id: "
759                        + getElementId());
760            }
761        }
762
763        @Override
764        public Object lock(int accessType) {
765            return mBuffer.rewind();
766        }
767
768        @Override
769        public void unlock() {
770            mBuffer.rewind();
771        }
772
773        @Override
774        public int getType() {
775            return BACKING_BYTEBUFFER;
776        }
777
778        @Override
779        public boolean shouldCache() {
780            return true;
781        }
782
783        @Override
784        public void destroy() {
785            mBuffer = null;
786        }
787
788        @Override
789        public int getSize() {
790            return mBuffer.remaining();
791        }
792
793    }
794
795    @TargetApi(11)
796    static class AllocationBacking extends Backing {
797
798        private final RenderScript mRenderScript;
799        private Allocation mAllocation = null;
800
801        public AllocationBacking(RenderScript renderScript) {
802            mRenderScript = renderScript;
803        }
804
805        @Override
806        public void allocate(FrameType frameType) {
807            assertCompatible(frameType);
808
809            Element element = null;
810            switch (frameType.getElementId()) {
811                case FrameType.ELEMENT_RGBA8888:
812                    element = Element.RGBA_8888(mRenderScript);
813                    break;
814                case FrameType.ELEMENT_FLOAT32:
815                    element = Element.F32(mRenderScript);
816                    break;
817            }
818            Type.Builder imageTypeBuilder = new Type.Builder(mRenderScript, element);
819            imageTypeBuilder.setX(mDimensions.length >= 1 ? mDimensions[0] : 1);
820            imageTypeBuilder.setY(mDimensions.length == 2 ? mDimensions[1] : 1);
821            Type imageType = imageTypeBuilder.create();
822
823            mAllocation = Allocation.createTyped(mRenderScript, imageType);
824        }
825
826        @Override
827        public int readAccess() {
828            return ACCESS_ALLOCATION;
829        }
830
831        @Override
832        public int writeAccess() {
833            return ACCESS_ALLOCATION;
834        }
835
836        @Override
837        public boolean requiresGpu() {
838            return false;
839        }
840
841        @Override
842        public void syncTo(Backing backing) {
843            int access = backing.readAccess();
844            if ((access & ACCESS_TEXTURE) != 0) {
845                RenderTarget target = (RenderTarget) backing.lock(ACCESS_RENDERTARGET);
846                ByteBuffer pixels = ByteBuffer.allocateDirect(getSize());
847                GLToolbox.readTarget(target, pixels, mDimensions[0], mDimensions[1]);
848                mAllocation.copyFrom(pixels.array());
849            } else if ((access & ACCESS_BITMAP) != 0) {
850                Bitmap bitmap = (Bitmap) backing.lock(ACCESS_BITMAP);
851                mAllocation.copyFrom(bitmap);
852            } else if ((access & ACCESS_BYTES) != 0) {
853                ByteBuffer buffer = (ByteBuffer) backing.lock(ACCESS_BYTES);
854                if (buffer.order() != ByteOrder.nativeOrder()) {
855                    throw new RuntimeException(
856                            "Trying to sync to the ByteBufferBacking with non-native byte order!");
857                }
858                byte[] bytes;
859                if (buffer.hasArray()) {
860                    bytes = buffer.array();
861                } else {
862                    bytes = new byte[getSize()];
863                    buffer.get(bytes);
864                    buffer.rewind();
865                }
866                mAllocation.copyFromUnchecked(bytes);
867            } else {
868                throw new RuntimeException("Cannot sync allocation backing!");
869            }
870            backing.unlock();
871            mIsDirty = false;
872        }
873
874        @Override
875        public Object lock(int accessType) {
876            return mAllocation;
877        }
878
879        @Override
880        public void unlock() {
881        }
882
883        @Override
884        public int getType() {
885            return BACKING_ALLOCATION;
886        }
887
888        @Override
889        public boolean shouldCache() {
890            return true;
891        }
892
893        @Override
894        public void destroy() {
895            if (mAllocation != null) {
896                mAllocation.destroy();
897                mAllocation = null;
898            }
899        }
900
901        @Override
902        public int getSize() {
903            int elementCount = 1;
904            for (int dim : mDimensions) {
905                elementCount *= dim;
906            }
907            return getElementSize() * elementCount;
908        }
909
910        public static boolean isSupported() {
911            return Build.VERSION.SDK_INT >= 11;
912        }
913
914        private void assertCompatible(FrameType type) {
915            // TODO: consider adding support for other data types.
916            if (type.getElementId() != FrameType.ELEMENT_RGBA8888
917                    && type.getElementId() != FrameType.ELEMENT_FLOAT32) {
918                throw new RuntimeException(
919                        "Cannot allocate allocation with a non-RGBA or non-float data type!");
920            }
921            if (mDimensions == null || mDimensions.length > 2) {
922                throw new RuntimeException(
923                        "Cannot create an allocation with more than 2 dimensions!");
924            }
925        }
926
927    }
928
929}
930