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