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.graphics.Bitmap; 18 import android.util.Log; 19 20 import java.nio.ByteBuffer; 21 import java.util.Arrays; 22 import java.util.Vector; 23 24 final class BackingStore { 25 26 /** Access mode None: Frame data will not be accessed at all. */ 27 static final int ACCESS_NONE = 0x00; 28 /** Access mode Bytes: Frame data will be accessed as a ByteBuffer. */ 29 static final int ACCESS_BYTES = 0x01; 30 /** Access mode Texture: Frame data will be accessed as a TextureSource. */ 31 static final int ACCESS_TEXTURE = 0x02; 32 /** Access mode RenderTarget: Frame data will be accessed as a RenderTarget. */ 33 static final int ACCESS_RENDERTARGET = 0x04; 34 /** Access mode Object: Frame data will be accessed as a generic Object. */ 35 static final int ACCESS_OBJECT = 0x08; 36 /** Access mode Bitmap: Frame data will be accessed as a Bitmap. */ 37 static final int ACCESS_BITMAP = 0x10; 38 39 private static final int BACKING_BYTEBUFFER = 1; 40 private static final int BACKING_TEXTURE = 2; 41 private static final int BACKING_OBJECT = 3; 42 private static final int BACKING_BITMAP = 4; 43 44 private final FrameType mType; 45 private int[] mDimensions; 46 private long mTimestamp = Frame.TIMESTAMP_NOT_SET; 47 48 private final FrameManager mFrameManager; 49 50 private Vector<Backing> mBackings = new Vector<Backing>(); 51 52 private boolean mWriteLocked = false; 53 private int mReadLocks = 0; 54 55 private int mRefCount = 1; 56 57 /** The most up-to-date data backing */ 58 private Backing mCurrentBacking = null; 59 60 /** The currently locked backing */ 61 private Backing mLockedBacking = null; 62 63 // Public Methods ////////////////////////////////////////////////////////////////////////////// BackingStore(FrameType type, int[] dimensions, FrameManager frameManager)64 public BackingStore(FrameType type, int[] dimensions, FrameManager frameManager) { 65 mType = type; 66 mDimensions = dimensions != null ? Arrays.copyOf(dimensions, dimensions.length) : null; 67 mFrameManager = frameManager; 68 } 69 getFrameType()70 public FrameType getFrameType() { 71 return mType; 72 } 73 lockData(int mode, int accessFormat)74 public Object lockData(int mode, int accessFormat) { 75 return lockBacking(mode, accessFormat).lock(accessFormat); 76 } 77 lockBacking(int mode, int access)78 public Backing lockBacking(int mode, int access) { 79 Backing backing = fetchBacking(mode, access); 80 if (backing == null) { 81 throw new RuntimeException("Could not fetch frame data!"); 82 } 83 lock(backing, mode); 84 return backing; 85 } 86 unlock()87 public boolean unlock() { 88 if (mWriteLocked) { 89 mWriteLocked = false; 90 } else if (mReadLocks > 0) { 91 --mReadLocks; 92 } else { 93 return false; 94 } 95 mLockedBacking.unlock(); 96 mLockedBacking = null; 97 return true; 98 } 99 retain()100 public BackingStore retain() { 101 if (mRefCount >= 10) { 102 Log.w("BackingStore", "High ref-count of " + mRefCount + " on " + this + "!"); 103 } 104 if (mRefCount <= 0) { 105 throw new RuntimeException("RETAINING RELEASED"); 106 } 107 ++mRefCount; 108 return this; 109 } 110 release()111 public BackingStore release() { 112 if (mRefCount <= 0) { 113 throw new RuntimeException("DOUBLE-RELEASE"); 114 } 115 --mRefCount; 116 if (mRefCount == 0) { 117 releaseBackings(); 118 return null; 119 } 120 return this; 121 } 122 123 /** 124 * Resizes the backing store. This invalidates all data in the store. 125 */ resize(int[] newDimensions)126 public void resize(int[] newDimensions) { 127 Vector<Backing> resized = new Vector<Backing>(); 128 for (Backing backing : mBackings) { 129 if (backing.resize(newDimensions)) { 130 resized.add(backing); 131 } else { 132 releaseBacking(backing); 133 } 134 } 135 mBackings = resized; 136 mDimensions = newDimensions; 137 } 138 getDimensions()139 public int[] getDimensions() { 140 return mDimensions; 141 } 142 getElementCount()143 public int getElementCount() { 144 int result = 1; 145 if (mDimensions != null) { 146 for (int dim : mDimensions) { 147 result *= dim; 148 } 149 } 150 return result; 151 } 152 importStore(BackingStore store)153 public void importStore(BackingStore store) { 154 // TODO: Better backing selection? 155 if (store.mBackings.size() > 0) { 156 importBacking(store.mBackings.firstElement()); 157 } 158 mTimestamp = store.mTimestamp; 159 } 160 161 /** 162 * @return the timestamp 163 */ getTimestamp()164 public long getTimestamp() { 165 return mTimestamp; 166 } 167 168 /** 169 * @param timestamp the timestamp to set 170 */ setTimestamp(long timestamp)171 public void setTimestamp(long timestamp) { 172 mTimestamp = timestamp; 173 } 174 175 // Internal Methods //////////////////////////////////////////////////////////////////////////// fetchBacking(int mode, int access)176 private Backing fetchBacking(int mode, int access) { 177 Backing backing = getBacking(mode, access); 178 if (backing == null) { 179 backing = attachNewBacking(mode, access); 180 } 181 syncBacking(backing); 182 return backing; 183 } 184 syncBacking(Backing backing)185 private void syncBacking(Backing backing) { 186 if (backing != null && backing.isDirty() && mCurrentBacking != null) { 187 backing.syncTo(mCurrentBacking); 188 } 189 } 190 getBacking(int mode, int access)191 private Backing getBacking(int mode, int access) { 192 // [Non-iterator looping] 193 for (int i = 0; i < mBackings.size(); ++i) { 194 final Backing backing = mBackings.get(i); 195 196 int backingAccess = 197 (mode == Frame.MODE_WRITE) ? backing.writeAccess() : backing.readAccess(); 198 if ((backingAccess & access) == access) { 199 return backing; 200 } 201 } 202 return null; 203 } 204 attachNewBacking(int mode, int access)205 private Backing attachNewBacking(int mode, int access) { 206 Backing backing = createBacking(mode, access); 207 if (mBackings.size() > 0) { 208 backing.markDirty(); 209 } 210 mBackings.add(backing); 211 return backing; 212 } 213 createBacking(int mode, int access)214 private Backing createBacking(int mode, int access) { 215 // TODO: If the read/write access flags indicate, make/fetch a GraphicBuffer backing. 216 Backing backing = null; 217 int elemSize = mType.getElementSize(); 218 if (shouldFetchCached(access)) { 219 backing = mFrameManager.fetchBacking(mode, access, mDimensions, elemSize); 220 } 221 if (backing == null) { 222 switch (access) { 223 case ACCESS_BYTES: 224 backing = new ByteBufferBacking(); 225 break; 226 case ACCESS_TEXTURE: 227 case ACCESS_RENDERTARGET: 228 backing = new TextureBacking(); 229 break; 230 case ACCESS_OBJECT: 231 backing = new ObjectBacking(); 232 break; 233 case ACCESS_BITMAP: 234 backing = new BitmapBacking(); 235 break; 236 } 237 if (backing == null) { 238 throw new RuntimeException( 239 "Could not create backing for access type " + access + "!"); 240 } 241 if (backing.requiresGpu() && !mFrameManager.getRunner().isOpenGLSupported()) { 242 throw new RuntimeException( 243 "Cannot create backing that requires GPU in a runner that does not " + 244 "support OpenGL!"); 245 } 246 backing.setDimensions(mDimensions); 247 backing.setElementSize(elemSize); 248 backing.setElementId(mType.getElementId()); 249 backing.allocate(mType); 250 mFrameManager.onBackingCreated(backing); 251 } 252 return backing; 253 } 254 importBacking(Backing backing)255 private void importBacking(Backing backing) { 256 // TODO: This actually needs synchronization between the two BackingStore threads for the 257 // general case 258 int access = backing.requiresGpu() ? ACCESS_BYTES : backing.readAccess(); 259 Backing newBacking = createBacking(Frame.MODE_READ, access); 260 newBacking.syncTo(backing); 261 mBackings.add(newBacking); 262 mCurrentBacking = newBacking; 263 } 264 releaseBackings()265 private void releaseBackings() { 266 // [Non-iterator looping] 267 for (int i = 0; i < mBackings.size(); ++i) { 268 releaseBacking(mBackings.get(i)); 269 } 270 mBackings.clear(); 271 mCurrentBacking = null; 272 } 273 releaseBacking(Backing backing)274 private void releaseBacking(Backing backing) { 275 mFrameManager.onBackingAvailable(backing); 276 } 277 lock(Backing backingToLock, int mode)278 private void lock(Backing backingToLock, int mode) { 279 if (mode == Frame.MODE_WRITE) { 280 // Make sure frame is not read-locked 281 if (mReadLocks > 0) { 282 throw new RuntimeException( 283 "Attempting to write-lock the read-locked frame " + this + "!"); 284 } else if (mWriteLocked) { 285 throw new RuntimeException( 286 "Attempting to write-lock the write-locked frame " + this + "!"); 287 } 288 // Mark all other backings dirty 289 // [Non-iterator looping] 290 for (int i = 0; i < mBackings.size(); ++i) { 291 final Backing backing = mBackings.get(i); 292 if (backing != backingToLock) { 293 backing.markDirty(); 294 } 295 } 296 mWriteLocked = true; 297 mCurrentBacking = backingToLock; 298 } else { 299 if (mWriteLocked) { 300 throw new RuntimeException("Attempting to read-lock locked frame " + this + "!"); 301 } 302 ++mReadLocks; 303 } 304 mLockedBacking = backingToLock; 305 } 306 shouldFetchCached(int access)307 private static boolean shouldFetchCached(int access) { 308 return access != ACCESS_OBJECT; 309 } 310 311 312 // Backings //////////////////////////////////////////////////////////////////////////////////// 313 static abstract class Backing { 314 protected int[] mDimensions = null; 315 private int mElementSize; 316 private int mElementID; 317 protected boolean mIsDirty = false; 318 319 int cachePriority = 0; 320 allocate(FrameType frameType)321 public abstract void allocate(FrameType frameType); 322 readAccess()323 public abstract int readAccess(); 324 writeAccess()325 public abstract int writeAccess(); 326 syncTo(Backing backing)327 public abstract void syncTo(Backing backing); 328 lock(int accessType)329 public abstract Object lock(int accessType); 330 getType()331 public abstract int getType(); 332 shouldCache()333 public abstract boolean shouldCache(); 334 requiresGpu()335 public abstract boolean requiresGpu(); 336 destroy()337 public abstract void destroy(); 338 getSize()339 public abstract int getSize(); 340 unlock()341 public void unlock() { 342 // Default implementation does nothing. 343 } 344 setData(Object data)345 public void setData(Object data) { 346 throw new RuntimeException("Internal error: Setting data on frame backing " + this 347 + ", which does not support setting data directly!"); 348 } 349 setDimensions(int[] dimensions)350 public void setDimensions(int[] dimensions) { 351 mDimensions = dimensions; 352 } 353 setElementSize(int elemSize)354 public void setElementSize(int elemSize) { 355 mElementSize = elemSize; 356 } 357 setElementId(int elemId)358 public void setElementId(int elemId) { 359 mElementID = elemId; 360 } 361 getDimensions()362 public int[] getDimensions() { 363 return mDimensions; 364 } 365 getElementSize()366 public int getElementSize() { 367 return mElementSize; 368 } 369 getElementId()370 public int getElementId() { 371 return mElementID; 372 } 373 resize(int[] newDimensions)374 public boolean resize(int[] newDimensions) { 375 return false; 376 } 377 markDirty()378 public void markDirty() { 379 mIsDirty = true; 380 } 381 isDirty()382 public boolean isDirty() { 383 return mIsDirty; 384 } 385 assertImageCompatible(FrameType type)386 protected void assertImageCompatible(FrameType type) { 387 if (type.getElementId() != FrameType.ELEMENT_RGBA8888) { 388 throw new RuntimeException("Cannot allocate texture with non-RGBA data type!"); 389 } else if (mDimensions == null || mDimensions.length != 2) { 390 throw new RuntimeException("Cannot allocate non 2-dimensional texture!"); 391 } 392 } 393 394 } 395 396 static class ObjectBacking extends Backing { 397 398 private Object mObject = null; 399 400 @Override allocate(FrameType frameType)401 public void allocate(FrameType frameType) { 402 mObject = null; 403 } 404 405 @Override readAccess()406 public int readAccess() { 407 return ACCESS_OBJECT; 408 } 409 410 @Override writeAccess()411 public int writeAccess() { 412 return ACCESS_OBJECT; 413 } 414 415 @Override syncTo(Backing backing)416 public void syncTo(Backing backing) { 417 switch (backing.getType()) { 418 case BACKING_OBJECT: 419 mObject = backing.lock(ACCESS_OBJECT); 420 backing.unlock(); 421 break; 422 case BACKING_BITMAP: 423 mObject = backing.lock(ACCESS_BITMAP); 424 backing.unlock(); 425 break; 426 default: 427 mObject = null; 428 } 429 mIsDirty = false; 430 } 431 432 @Override lock(int accessType)433 public Object lock(int accessType) { 434 return mObject; 435 } 436 437 @Override getType()438 public int getType() { 439 return BACKING_OBJECT; 440 } 441 442 @Override shouldCache()443 public boolean shouldCache() { 444 return false; 445 } 446 447 @Override requiresGpu()448 public boolean requiresGpu() { 449 return false; 450 } 451 452 @Override destroy()453 public void destroy() { 454 mObject = null; 455 } 456 457 @Override getSize()458 public int getSize() { 459 return 0; 460 } 461 462 @Override setData(Object data)463 public void setData(Object data) { 464 mObject = data; 465 } 466 467 } 468 469 static class BitmapBacking extends Backing { 470 471 private Bitmap mBitmap = null; 472 473 @Override allocate(FrameType frameType)474 public void allocate(FrameType frameType) { 475 assertImageCompatible(frameType); 476 } 477 478 @Override readAccess()479 public int readAccess() { 480 return ACCESS_BITMAP; 481 } 482 483 @Override writeAccess()484 public int writeAccess() { 485 return ACCESS_BITMAP; 486 } 487 488 @Override syncTo(Backing backing)489 public void syncTo(Backing backing) { 490 int access = backing.readAccess(); 491 if ((access & ACCESS_BITMAP) != 0) { 492 mBitmap = (Bitmap) backing.lock(ACCESS_BITMAP); 493 } else if ((access & ACCESS_BYTES) != 0) { 494 createBitmap(); 495 ByteBuffer buffer = (ByteBuffer) backing.lock(ACCESS_BYTES); 496 mBitmap.copyPixelsFromBuffer(buffer); 497 buffer.rewind(); 498 } else if ((access & ACCESS_TEXTURE) != 0) { 499 createBitmap(); 500 RenderTarget renderTarget = (RenderTarget) backing.lock(ACCESS_RENDERTARGET); 501 mBitmap.copyPixelsFromBuffer( 502 renderTarget.getPixelData(mDimensions[0], mDimensions[1])); 503 } else { 504 throw new RuntimeException("Cannot sync bytebuffer backing!"); 505 } 506 backing.unlock(); 507 mIsDirty = false; 508 } 509 510 @Override lock(int accessType)511 public Object lock(int accessType) { 512 return mBitmap; 513 } 514 515 @Override getType()516 public int getType() { 517 return BACKING_BITMAP; 518 } 519 520 @Override shouldCache()521 public boolean shouldCache() { 522 return false; 523 } 524 525 @Override requiresGpu()526 public boolean requiresGpu() { 527 return false; 528 } 529 530 @Override destroy()531 public void destroy() { 532 // As we share the bitmap with other backings (such as object backings), we must not 533 // recycle it here. 534 mBitmap = null; 535 } 536 537 @Override getSize()538 public int getSize() { 539 return 4 * mDimensions[0] * mDimensions[1]; 540 } 541 542 @Override setData(Object data)543 public void setData(Object data) { 544 // We can assume that data will always be a Bitmap instance. 545 mBitmap = (Bitmap) data; 546 } 547 createBitmap()548 private void createBitmap() { 549 mBitmap = Bitmap.createBitmap(mDimensions[0], mDimensions[1], Bitmap.Config.ARGB_8888); 550 } 551 } 552 553 static class TextureBacking extends Backing { 554 555 private RenderTarget mRenderTarget = null; 556 private TextureSource mTexture = null; 557 558 @Override allocate(FrameType frameType)559 public void allocate(FrameType frameType) { 560 assertImageCompatible(frameType); 561 mTexture = TextureSource.newTexture(); 562 } 563 564 @Override readAccess()565 public int readAccess() { 566 return ACCESS_TEXTURE; 567 } 568 569 @Override writeAccess()570 public int writeAccess() { 571 return ACCESS_RENDERTARGET; 572 } 573 574 @Override syncTo(Backing backing)575 public void syncTo(Backing backing) { 576 int access = backing.readAccess(); 577 if ((access & ACCESS_BYTES) != 0) { 578 ByteBuffer pixels = (ByteBuffer) backing.lock(ACCESS_BYTES); 579 mTexture.allocateWithPixels(pixels, mDimensions[0], mDimensions[1]); 580 } else if ((access & ACCESS_BITMAP) != 0) { 581 Bitmap bitmap = (Bitmap) backing.lock(ACCESS_BITMAP); 582 mTexture.allocateWithBitmapPixels(bitmap); 583 } else if ((access & ACCESS_TEXTURE) != 0) { 584 TextureSource texture = (TextureSource) backing.lock(ACCESS_TEXTURE); 585 int w = mDimensions[0]; 586 int h = mDimensions[1]; 587 ImageShader.renderTextureToTarget(texture, getRenderTarget(), w, h); 588 } else { 589 throw new RuntimeException("Cannot sync bytebuffer backing!"); 590 } 591 backing.unlock(); 592 mIsDirty = false; 593 } 594 595 @Override lock(int accessType)596 public Object lock(int accessType) { 597 switch (accessType) { 598 case ACCESS_TEXTURE: 599 return getTexture(); 600 601 case ACCESS_RENDERTARGET: 602 return getRenderTarget(); 603 604 default: 605 throw new RuntimeException("Illegal access to texture!"); 606 } 607 } 608 609 @Override getType()610 public int getType() { 611 return BACKING_TEXTURE; 612 } 613 614 @Override shouldCache()615 public boolean shouldCache() { 616 return true; 617 } 618 619 @Override requiresGpu()620 public boolean requiresGpu() { 621 return true; 622 } 623 624 @Override destroy()625 public void destroy() { 626 if (mRenderTarget != null) { 627 mRenderTarget.release(); 628 } 629 if (mTexture.isAllocated()) { 630 mTexture.release(); 631 } 632 } 633 634 @Override getSize()635 public int getSize() { 636 return 4 * mDimensions[0] * mDimensions[1]; 637 } 638 getTexture()639 private TextureSource getTexture() { 640 if (!mTexture.isAllocated()) { 641 mTexture.allocate(mDimensions[0], mDimensions[1]); 642 } 643 return mTexture; 644 } 645 getRenderTarget()646 private RenderTarget getRenderTarget() { 647 if (mRenderTarget == null) { 648 int w = mDimensions[0]; 649 int h = mDimensions[1]; 650 mRenderTarget = RenderTarget.currentTarget().forTexture(getTexture(), w, h); 651 } 652 return mRenderTarget; 653 } 654 655 } 656 657 static class ByteBufferBacking extends Backing { 658 659 ByteBuffer mBuffer = null; 660 661 @Override allocate(FrameType frameType)662 public void allocate(FrameType frameType) { 663 int size = frameType.getElementSize(); 664 for (int dim : mDimensions) { 665 size *= dim; 666 } 667 mBuffer = ByteBuffer.allocateDirect(size); 668 } 669 670 @Override readAccess()671 public int readAccess() { 672 return ACCESS_BYTES; 673 } 674 675 @Override writeAccess()676 public int writeAccess() { 677 return ACCESS_BYTES; 678 } 679 680 @Override requiresGpu()681 public boolean requiresGpu() { 682 return false; 683 } 684 685 @Override syncTo(Backing backing)686 public void syncTo(Backing backing) { 687 int access = backing.readAccess(); 688 if ((access & ACCESS_TEXTURE) != 0) { 689 RenderTarget target = (RenderTarget) backing.lock(ACCESS_RENDERTARGET); 690 GLToolbox.readTarget(target, mBuffer, mDimensions[0], mDimensions[1]); 691 } else if ((access & ACCESS_BITMAP) != 0) { 692 Bitmap bitmap = (Bitmap) backing.lock(ACCESS_BITMAP); 693 bitmap.copyPixelsToBuffer(mBuffer); 694 mBuffer.rewind(); 695 } else if ((access & ACCESS_BYTES) != 0) { 696 ByteBuffer otherBuffer = (ByteBuffer) backing.lock(ACCESS_BYTES); 697 mBuffer.put(otherBuffer); 698 otherBuffer.rewind(); 699 } else { 700 throw new RuntimeException("Cannot sync bytebuffer backing!"); 701 } 702 backing.unlock(); 703 mBuffer.rewind(); 704 mIsDirty = false; 705 } 706 707 @Override lock(int accessType)708 public Object lock(int accessType) { 709 return mBuffer.rewind(); 710 } 711 712 @Override unlock()713 public void unlock() { 714 mBuffer.rewind(); 715 } 716 717 @Override getType()718 public int getType() { 719 return BACKING_BYTEBUFFER; 720 } 721 722 @Override shouldCache()723 public boolean shouldCache() { 724 return true; 725 } 726 727 @Override destroy()728 public void destroy() { 729 mBuffer = null; 730 } 731 732 @Override getSize()733 public int getSize() { 734 return mBuffer.remaining(); 735 } 736 737 } 738 } 739