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