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 15 package androidx.media.filterfw; 16 17 import android.annotation.TargetApi; 18 import android.graphics.Bitmap; 19 import android.os.Build; 20 import android.renderscript.Allocation; 21 import android.renderscript.Element; 22 import android.renderscript.RenderScript; 23 import android.renderscript.Type; 24 import android.util.Log; 25 26 import java.nio.ByteBuffer; 27 import java.nio.ByteOrder; 28 import java.util.Arrays; 29 import java.util.Vector; 30 31 final 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