1/* 2 * Copyright (C) 2012 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.android.rs.imagejb; 18 19import android.app.Activity; 20 21import android.content.Intent; 22import android.os.Bundle; 23import android.os.Handler; 24import android.os.Message; 25import android.widget.AdapterView; 26import android.widget.ArrayAdapter; 27import android.widget.SeekBar; 28import android.widget.Spinner; 29import android.widget.TextView; 30import android.view.View; 31import android.view.TextureView; 32import android.view.Surface; 33import android.graphics.SurfaceTexture; 34import android.graphics.Point; 35 36import android.util.Log; 37import android.renderscript.ScriptC; 38import android.renderscript.RenderScript; 39import android.renderscript.Type; 40import android.renderscript.Allocation; 41import android.renderscript.Element; 42import android.renderscript.Script; 43 44 45public class ImageProcessingActivityJB extends Activity 46 implements SeekBar.OnSeekBarChangeListener, 47 TextureView.SurfaceTextureListener { 48 private final String TAG = "Img"; 49 50 private Spinner mSpinner; 51 private SeekBar mBar1; 52 private SeekBar mBar2; 53 private SeekBar mBar3; 54 private SeekBar mBar4; 55 private SeekBar mBar5; 56 57 private int mBars[] = new int[5]; 58 private int mBarsOld[] = new int[5]; 59 60 private TextView mText1; 61 private TextView mText2; 62 private TextView mText3; 63 private TextView mText4; 64 private TextView mText5; 65 private SizedTV mDisplayView; 66 67 private int mTestList[]; 68 private float mTestResults[]; 69 70 private boolean mToggleIO; 71 private boolean mToggleDVFS; 72 private boolean mToggleLong; 73 private boolean mTogglePause; 74 private boolean mToggleAnimate; 75 private boolean mToggleDisplay; 76 private int mBitmapWidth; 77 private int mBitmapHeight; 78 private boolean mDemoMode; 79 80 // Updates pending is a counter of how many kernels have been 81 // sent to RS for processing 82 // 83 // In benchmark this is incremented each time a kernel is launched and 84 // decremented each time a kernel completes 85 // 86 // In demo mode, each UI input increments the counter and it is zeroed 87 // when the latest settings are sent to RS for processing. 88 private int mUpdatesPending; 89 90 // In demo mode this is used to count updates in the pipeline. It's 91 // incremented when work is submitted to RS and decremented when invalidate is 92 // called to display a result. 93 private int mShowsPending; 94 95 96 static public class SizedTV extends TextureView { 97 int mWidth; 98 int mHeight; 99 100 public SizedTV(android.content.Context c) { 101 super(c); 102 mWidth = 800; 103 mHeight = 450; 104 } 105 106 public SizedTV(android.content.Context c, android.util.AttributeSet attrs) { 107 super(c, attrs); 108 mWidth = 800; 109 mHeight = 450; 110 } 111 112 public SizedTV(android.content.Context c, android.util.AttributeSet attrs, int f) { 113 super(c, attrs, f); 114 mWidth = 800; 115 mHeight = 450; 116 } 117 118 protected void onMeasure(int w, int h) { 119 setMeasuredDimension(mWidth, mHeight); 120 } 121 } 122 123 ///////////////////////////////////////////////////////////////////////// 124 125 // Message processor to handle notifications for when kernel completes 126 private class MessageProcessor extends RenderScript.RSMessageHandler { 127 MessageProcessor() { 128 } 129 130 public void run() { 131 synchronized(mProcessor) { 132 // In demo mode, decrement the pending displays and notify the 133 // UI processor it can now enqueue more work if additional updates 134 // are blocked by a full pipeline. 135 if (mShowsPending > 0) { 136 mShowsPending --; 137 mProcessor.notifyAll(); 138 } 139 } 140 } 141 } 142 143 144 ///////////////////////////////////////////////////////////////////////// 145 // Processor is a helper thread for running the work without 146 // blocking the UI thread. 147 class Processor extends Thread { 148 RenderScript mRS; 149 Allocation mInPixelsAllocation; 150 Allocation mInPixelsAllocation2; 151 Allocation mOutDisplayAllocation; 152 Allocation mOutPixelsAllocation; 153 154 private Surface mOutSurface; 155 private float mLastResult; 156 private boolean mRun = true; 157 private boolean mDoingBenchmark; 158 private TestBase mTest; 159 private TextureView mDisplayView; 160 161 private boolean mBenchmarkMode; 162 163 // We don't want to call the "changed" methods excessively as this 164 // can cause extra work for drivers. Before running a test update 165 // any bars which have changed. 166 void runTest() { 167 if (mBars[0] != mBarsOld[0]) { 168 mTest.onBar1Changed(mBars[0]); 169 mBarsOld[0] = mBars[0]; 170 } 171 if (mBars[1] != mBarsOld[1]) { 172 mTest.onBar2Changed(mBars[1]); 173 mBarsOld[1] = mBars[1]; 174 } 175 if (mBars[2] != mBarsOld[2]) { 176 mTest.onBar3Changed(mBars[2]); 177 mBarsOld[2] = mBars[2]; 178 } 179 if (mBars[3] != mBarsOld[3]) { 180 mTest.onBar4Changed(mBars[3]); 181 mBarsOld[3] = mBars[3]; 182 } 183 if (mBars[4] != mBarsOld[4]) { 184 mTest.onBar5Changed(mBars[4]); 185 mBarsOld[4] = mBars[4]; 186 } 187 mTest.runTest(); 188 } 189 190 Processor(RenderScript rs, TextureView v, boolean benchmarkMode) { 191 mRS = rs; 192 mDisplayView = v; 193 194 mRS.setMessageHandler(new MessageProcessor()); 195 196 switch(mBitmapWidth) { 197 case 3840: 198 mInPixelsAllocation = Allocation.createFromBitmapResource( 199 mRS, getResources(), R.drawable.img3840x2160a); 200 mInPixelsAllocation2 = Allocation.createFromBitmapResource( 201 mRS, getResources(), R.drawable.img3840x2160b); 202 break; 203 case 1920: 204 mInPixelsAllocation = Allocation.createFromBitmapResource( 205 mRS, getResources(), R.drawable.img1920x1080a); 206 mInPixelsAllocation2 = Allocation.createFromBitmapResource( 207 mRS, getResources(), R.drawable.img1920x1080b); 208 break; 209 case 1280: 210 mInPixelsAllocation = Allocation.createFromBitmapResource( 211 mRS, getResources(), R.drawable.img1280x720a); 212 mInPixelsAllocation2 = Allocation.createFromBitmapResource( 213 mRS, getResources(), R.drawable.img1280x720b); 214 break; 215 case 800: 216 mInPixelsAllocation = Allocation.createFromBitmapResource( 217 mRS, getResources(), R.drawable.img800x450a); 218 mInPixelsAllocation2 = Allocation.createFromBitmapResource( 219 mRS, getResources(), R.drawable.img800x450b); 220 break; 221 } 222 223 // We create the output allocation using USAGE_IO_OUTPUT so we can share the 224 // bits with a TextureView. This is more efficient than using a bitmap. 225 mOutDisplayAllocation = Allocation.createTyped(mRS, mInPixelsAllocation.getType(), 226 Allocation.MipmapControl.MIPMAP_NONE, 227 Allocation.USAGE_SCRIPT | 228 Allocation.USAGE_IO_OUTPUT); 229 mOutPixelsAllocation = mOutDisplayAllocation; 230 231 if (!mToggleIO) { 232 // Not using USAGE_IO for the script so create a non-io kernel to copy from 233 mOutPixelsAllocation = Allocation.createTyped(mRS, mInPixelsAllocation.getType(), 234 Allocation.MipmapControl.MIPMAP_NONE, 235 Allocation.USAGE_SCRIPT); 236 } 237 238 mBenchmarkMode = benchmarkMode; 239 start(); 240 } 241 242 class Result { 243 float totalTime; 244 int itterations; 245 } 246 247 // Run one loop of kernels for at least the specified minimum time. 248 // The function returns the average time in ms for the test run 249 private Result runBenchmarkLoop(float minTime) { 250 mUpdatesPending = 0; 251 Result r = new Result(); 252 253 long t = java.lang.System.currentTimeMillis(); 254 do { 255 synchronized(this) { 256 // Shows pending is used to track the number of kernels in the RS pipeline 257 // We throttle it to 2. This provide some buffering to allow a kernel to be started 258 // before we are nofitied the previous finished. However, larger numbers are uncommon 259 // in interactive apps as they introduce 'lag' between user input and display. 260 mShowsPending++; 261 if (mShowsPending > 2) { 262 try { 263 this.wait(); 264 } catch(InterruptedException e) { 265 } 266 } 267 } 268 269 // If animations are enabled update the test state. 270 if (mToggleAnimate) { 271 mTest.animateBars(r.totalTime); 272 } 273 274 // Run the kernel 275 mTest.runTest(); 276 r.itterations ++; 277 278 if (mToggleDisplay) { 279 // If we are not outputting directly to the TextureView we need to copy from 280 // our temporary buffer. 281 if (mOutDisplayAllocation != mOutPixelsAllocation) { 282 mOutDisplayAllocation.copyFrom(mOutPixelsAllocation); 283 } 284 285 // queue the update of the TextureView with the allocation contents 286 mOutDisplayAllocation.ioSend(); 287 } 288 289 // Send our RS message handler a message so we know when this work has completed 290 mRS.sendMessage(0, null); 291 292 long t2 = java.lang.System.currentTimeMillis(); 293 r.totalTime += (t2 - t) / 1000.f; 294 t = t2; 295 } while (r.totalTime < minTime); 296 297 // Wait for any stray operations to complete and update the final time 298 mRS.finish(); 299 long t2 = java.lang.System.currentTimeMillis(); 300 r.totalTime += (t2 - t) / 1000.f; 301 t = t2; 302 return r; 303 } 304 305 306 // Get a benchmark result for a specific test 307 private float getBenchmark() { 308 mDoingBenchmark = true; 309 mUpdatesPending = 0; 310 311 long result = 0; 312 float runtime = 1.f; 313 if (mToggleLong) { 314 runtime = 10.f; 315 } 316 317 if (mToggleDVFS) { 318 mDvfsWar.go(); 319 } 320 321 // We run a short bit of work before starting the actual test 322 // this is to let any power management do its job and respond 323 runBenchmarkLoop(0.3f); 324 325 // Run the actual benchmark 326 Result r = runBenchmarkLoop(runtime); 327 328 Log.v("rs", "Test: time=" + r.totalTime +"s, frames=" + r.itterations + 329 ", avg=" + r.totalTime / r.itterations * 1000.f); 330 331 mDoingBenchmark = false; 332 return r.totalTime / r.itterations * 1000.f; 333 } 334 335 public void run() { 336 Surface lastSurface = null; 337 while (mRun) { 338 // Our loop for launching tests or benchmarks 339 synchronized(this) { 340 // If we have no work to do, or we have displays pending, wait 341 if ((mUpdatesPending == 0) || (mShowsPending != 0)) { 342 try { 343 this.wait(); 344 } catch(InterruptedException e) { 345 } 346 } 347 348 // We may have been asked to exit while waiting 349 if (!mRun) return; 350 351 // During startup we may not have a surface yet to display, if 352 // this is the case, wait. 353 if ((mOutSurface == null) || (mOutPixelsAllocation == null)) { 354 continue; 355 } 356 357 // Our display surface changed, set it. 358 if (lastSurface != mOutSurface) { 359 mOutDisplayAllocation.setSurface(mOutSurface); 360 lastSurface = mOutSurface; 361 } 362 } 363 364 if (mBenchmarkMode) { 365 // Loop over the tests we want to benchmark 366 for (int ct=0; (ct < mTestList.length) && mRun; ct++) { 367 368 // For reproducibility we wait a short time for any sporadic work 369 // created by the user touching the screen to launch the test to pass. 370 // Also allows for things to settle after the test changes. 371 mRS.finish(); 372 try { 373 sleep(250); 374 } catch(InterruptedException e) { 375 } 376 377 // If we just ran a test, we destroy it here to relieve some memory pressure 378 if (mTest != null) { 379 mTest.destroy(); 380 } 381 382 // Select the next test 383 mTest = changeTest(mTestList[ct], false); 384 385 // If the user selected the "long pause" option, wait 386 if (mTogglePause) { 387 for (int i=0; (i < 100) && mRun; i++) { 388 try { 389 sleep(100); 390 } catch(InterruptedException e) { 391 } 392 } 393 } 394 395 // Run the test 396 mTestResults[ct] = getBenchmark(); 397 } 398 onBenchmarkFinish(mRun); 399 } else { 400 boolean update = false; 401 synchronized(this) { 402 // If we have updates to process and are not blocked by pending shows, 403 // start the next kernel 404 if ((mUpdatesPending > 0) && (mShowsPending == 0)) { 405 mUpdatesPending = 0; 406 update = true; 407 mShowsPending++; 408 } 409 } 410 411 if (update) { 412 // Run the kernel 413 runTest(); 414 415 // If we are not outputting directly to the TextureView we need to copy from 416 // our temporary buffer. 417 if (mOutDisplayAllocation != mOutPixelsAllocation) { 418 mOutDisplayAllocation.copyFrom(mOutPixelsAllocation); 419 } 420 421 // queue the update of the TextureView with the allocation contents 422 mOutDisplayAllocation.ioSend(); 423 424 // Send our RS message handler a message so we know when this work has completed 425 mRS.sendMessage(0, null); 426 } 427 } 428 } 429 430 } 431 432 public void update() { 433 // something UI related has changed, enqueue an update if one is not 434 // already pending. Wake the worker if needed 435 synchronized(this) { 436 if (mUpdatesPending < 2) { 437 mUpdatesPending++; 438 notifyAll(); 439 } 440 } 441 } 442 443 public void setSurface(Surface s) { 444 mOutSurface = s; 445 update(); 446 } 447 448 public void exit() { 449 mRun = false; 450 451 synchronized(this) { 452 notifyAll(); 453 } 454 455 try { 456 this.join(); 457 } catch(InterruptedException e) { 458 } 459 460 mInPixelsAllocation.destroy(); 461 mInPixelsAllocation2.destroy(); 462 if (mOutPixelsAllocation != mOutDisplayAllocation) { 463 mOutPixelsAllocation.destroy(); 464 } 465 466 if (mTest != null) { 467 mTest.destroy(); 468 mTest = null; 469 } 470 mOutDisplayAllocation.destroy(); 471 mRS.destroy(); 472 473 mInPixelsAllocation = null; 474 mInPixelsAllocation2 = null; 475 mOutPixelsAllocation = null; 476 mOutDisplayAllocation = null; 477 mRS = null; 478 } 479 } 480 481 /////////////////////////////////////////////////////////////////////////////////////// 482 483 static class DVFSWorkaround { 484 static class spinner extends Thread { 485 boolean mRun = true; 486 long mNextSleep; 487 488 spinner() { 489 setPriority(MIN_PRIORITY); 490 start(); 491 } 492 493 public void run() { 494 while (mRun) { 495 Thread.yield(); 496 synchronized(this) { 497 long t = java.lang.System.currentTimeMillis(); 498 if (t > mNextSleep) { 499 try { 500 this.wait(); 501 } catch(InterruptedException e) { 502 } 503 } 504 } 505 } 506 } 507 508 public void go(long t) { 509 synchronized(this) { 510 mNextSleep = t; 511 notifyAll(); 512 } 513 } 514 } 515 516 spinner s1; 517 DVFSWorkaround() { 518 s1 = new spinner(); 519 } 520 521 void go() { 522 long t = java.lang.System.currentTimeMillis() + 2000; 523 s1.go(t); 524 } 525 526 void destroy() { 527 synchronized(this) { 528 s1.mRun = false; 529 notifyAll(); 530 } 531 } 532 } 533 DVFSWorkaround mDvfsWar = new DVFSWorkaround(); 534 535 /////////////////////////////////////////////////////////// 536 537 538 private boolean mDoingBenchmark; 539 public Processor mProcessor; 540 541 TestBase changeTest(IPTestListJB.TestName t, boolean setupUI) { 542 TestBase tb = IPTestListJB.newTest(t); 543 544 tb.createBaseTest(this); 545 if (setupUI) { 546 setupBars(tb); 547 } 548 return tb; 549 } 550 551 TestBase changeTest(int id, boolean setupUI) { 552 IPTestListJB.TestName t = IPTestListJB.TestName.values()[id]; 553 return changeTest(t, setupUI); 554 } 555 556 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 557 if (fromUser) { 558 if (seekBar == mBar1) { 559 mBars[0] = progress; 560 } else if (seekBar == mBar2) { 561 mBars[1] = progress; 562 } else if (seekBar == mBar3) { 563 mBars[2] = progress; 564 } else if (seekBar == mBar4) { 565 mBars[3] = progress; 566 } else if (seekBar == mBar5) { 567 mBars[4] = progress; 568 } 569 mProcessor.update(); 570 } 571 } 572 573 public void onStartTrackingTouch(SeekBar seekBar) { 574 } 575 576 public void onStopTrackingTouch(SeekBar seekBar) { 577 } 578 579 void setupBars(TestBase t) { 580 mSpinner.setVisibility(View.VISIBLE); 581 t.onSpinner1Setup(mSpinner); 582 583 mBar1.setVisibility(View.VISIBLE); 584 mText1.setVisibility(View.VISIBLE); 585 t.onBar1Setup(mBar1, mText1); 586 587 mBar2.setVisibility(View.VISIBLE); 588 mText2.setVisibility(View.VISIBLE); 589 t.onBar2Setup(mBar2, mText2); 590 591 mBar3.setVisibility(View.VISIBLE); 592 mText3.setVisibility(View.VISIBLE); 593 t.onBar3Setup(mBar3, mText3); 594 595 mBar4.setVisibility(View.VISIBLE); 596 mText4.setVisibility(View.VISIBLE); 597 t.onBar4Setup(mBar4, mText4); 598 599 mBar5.setVisibility(View.VISIBLE); 600 mText5.setVisibility(View.VISIBLE); 601 t.onBar5Setup(mBar5, mText5); 602 } 603 604 void hideBars() { 605 mSpinner.setVisibility(View.INVISIBLE); 606 607 mBar1.setVisibility(View.INVISIBLE); 608 mText1.setVisibility(View.INVISIBLE); 609 610 mBar2.setVisibility(View.INVISIBLE); 611 mText2.setVisibility(View.INVISIBLE); 612 613 mBar3.setVisibility(View.INVISIBLE); 614 mText3.setVisibility(View.INVISIBLE); 615 616 mBar4.setVisibility(View.INVISIBLE); 617 mText4.setVisibility(View.INVISIBLE); 618 619 mBar5.setVisibility(View.INVISIBLE); 620 mText5.setVisibility(View.INVISIBLE); 621 } 622 623 @Override 624 protected void onCreate(Bundle savedInstanceState) { 625 super.onCreate(savedInstanceState); 626 setContentView(R.layout.main); 627 628 mDisplayView = (SizedTV) findViewById(R.id.display); 629 630 mSpinner = (Spinner) findViewById(R.id.spinner1); 631 632 mBar1 = (SeekBar) findViewById(R.id.slider1); 633 mBar2 = (SeekBar) findViewById(R.id.slider2); 634 mBar3 = (SeekBar) findViewById(R.id.slider3); 635 mBar4 = (SeekBar) findViewById(R.id.slider4); 636 mBar5 = (SeekBar) findViewById(R.id.slider5); 637 638 mBar1.setOnSeekBarChangeListener(this); 639 mBar2.setOnSeekBarChangeListener(this); 640 mBar3.setOnSeekBarChangeListener(this); 641 mBar4.setOnSeekBarChangeListener(this); 642 mBar5.setOnSeekBarChangeListener(this); 643 644 mText1 = (TextView) findViewById(R.id.slider1Text); 645 mText2 = (TextView) findViewById(R.id.slider2Text); 646 mText3 = (TextView) findViewById(R.id.slider3Text); 647 mText4 = (TextView) findViewById(R.id.slider4Text); 648 mText5 = (TextView) findViewById(R.id.slider5Text); 649 } 650 651 @Override 652 protected void onPause() { 653 super.onPause(); 654 mProcessor.exit(); 655 } 656 657 public void onBenchmarkFinish(boolean ok) { 658 if (ok) { 659 Intent intent = new Intent(); 660 intent.putExtra("tests", mTestList); 661 intent.putExtra("results", mTestResults); 662 setResult(RESULT_OK, intent); 663 } else { 664 setResult(RESULT_CANCELED); 665 } 666 finish(); 667 } 668 669 670 void startProcessor() { 671 if (!mDemoMode) { 672 hideBars(); 673 } 674 675 Point size = new Point(); 676 getWindowManager().getDefaultDisplay().getSize(size); 677 678 int mScreenWidth = size.x; 679 int mScreenHeight = size.y; 680 681 int tw = mBitmapWidth; 682 int th = mBitmapHeight; 683 684 if (tw > mScreenWidth || th > mScreenHeight) { 685 float s1 = (float)tw / (float)mScreenWidth; 686 float s2 = (float)th / (float)mScreenHeight; 687 688 if (s1 > s2) { 689 tw /= s1; 690 th /= s1; 691 } else { 692 tw /= s2; 693 th /= s2; 694 } 695 } 696 697 android.util.Log.v("rs", "TV sizes " + tw + ", " + th); 698 699 mDisplayView.mWidth = tw; 700 mDisplayView.mHeight = th; 701 //mDisplayView.setTransform(new android.graphics.Matrix()); 702 703 mProcessor = new Processor(RenderScript.create(this), mDisplayView, !mDemoMode); 704 mDisplayView.setSurfaceTextureListener(this); 705 706 if (mDemoMode) { 707 mProcessor.mTest = changeTest(mTestList[0], true); 708 } 709 } 710 711 @Override 712 protected void onResume() { 713 super.onResume(); 714 Intent i = getIntent(); 715 mTestList = i.getIntArrayExtra("tests"); 716 717 mToggleIO = i.getBooleanExtra("enable io", false); 718 mToggleDVFS = i.getBooleanExtra("enable dvfs", false); 719 mToggleLong = i.getBooleanExtra("enable long", false); 720 mTogglePause = i.getBooleanExtra("enable pause", false); 721 mToggleAnimate = i.getBooleanExtra("enable animate", false); 722 mToggleDisplay = i.getBooleanExtra("enable display", false); 723 mBitmapWidth = i.getIntExtra("resolution X", 0); 724 mBitmapHeight = i.getIntExtra("resolution Y", 0); 725 mDemoMode = i.getBooleanExtra("demo", false); 726 727 mTestResults = new float[mTestList.length]; 728 729 startProcessor(); 730 } 731 732 protected void onDestroy() { 733 super.onDestroy(); 734 } 735 736 737 @Override 738 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { 739 mProcessor.setSurface(new Surface(surface)); 740 } 741 742 @Override 743 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { 744 mProcessor.setSurface(new Surface(surface)); 745 } 746 747 @Override 748 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { 749 mProcessor.setSurface(null); 750 return true; 751 } 752 753 @Override 754 public void onSurfaceTextureUpdated(SurfaceTexture surface) { 755 } 756} 757