SRGB_test.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  1. /*
  2. * Copyright 2013 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. #define LOG_TAG "SRGB_test"
  17. //#define LOG_NDEBUG 0
  18. // Ignore for this file because it flags every instance of
  19. // ASSERT_EQ(GL_NO_ERROR, glGetError());
  20. #pragma clang diagnostic ignored "-Wsign-compare"
  21. #include "GLTest.h"
  22. #include <math.h>
  23. #include <gui/CpuConsumer.h>
  24. #include <gui/Surface.h>
  25. #include <gui/SurfaceComposerClient.h>
  26. #include <EGL/egl.h>
  27. #include <EGL/eglext.h>
  28. #include <GLES3/gl3.h>
  29. #include <android/native_window.h>
  30. #include <gtest/gtest.h>
  31. namespace android {
  32. class SRGBTest : public ::testing::Test {
  33. protected:
  34. // Class constants
  35. enum {
  36. DISPLAY_WIDTH = 512,
  37. DISPLAY_HEIGHT = 512,
  38. PIXEL_SIZE = 4, // bytes or components
  39. DISPLAY_SIZE = DISPLAY_WIDTH * DISPLAY_HEIGHT * PIXEL_SIZE,
  40. ALPHA_VALUE = 223, // should be in [0, 255]
  41. TOLERANCE = 1,
  42. };
  43. static const char SHOW_DEBUG_STRING[];
  44. SRGBTest() :
  45. mInputSurface(), mCpuConsumer(), mLockedBuffer(),
  46. mEglDisplay(EGL_NO_DISPLAY), mEglConfig(),
  47. mEglContext(EGL_NO_CONTEXT), mEglSurface(EGL_NO_SURFACE),
  48. mComposerClient(), mSurfaceControl(), mOutputSurface() {
  49. }
  50. virtual ~SRGBTest() {
  51. if (mEglDisplay != EGL_NO_DISPLAY) {
  52. if (mEglSurface != EGL_NO_SURFACE) {
  53. eglDestroySurface(mEglDisplay, mEglSurface);
  54. }
  55. if (mEglContext != EGL_NO_CONTEXT) {
  56. eglDestroyContext(mEglDisplay, mEglContext);
  57. }
  58. eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
  59. EGL_NO_CONTEXT);
  60. eglTerminate(mEglDisplay);
  61. }
  62. }
  63. virtual void SetUp() {
  64. sp<IGraphicBufferProducer> producer;
  65. sp<IGraphicBufferConsumer> consumer;
  66. BufferQueue::createBufferQueue(&producer, &consumer);
  67. ASSERT_EQ(NO_ERROR, consumer->setDefaultBufferSize(
  68. DISPLAY_WIDTH, DISPLAY_HEIGHT));
  69. mCpuConsumer = new CpuConsumer(consumer, 1);
  70. String8 name("CpuConsumer_for_SRGBTest");
  71. mCpuConsumer->setName(name);
  72. mInputSurface = new Surface(producer);
  73. ASSERT_NO_FATAL_FAILURE(createEGLSurface(mInputSurface.get()));
  74. ASSERT_NO_FATAL_FAILURE(createDebugSurface());
  75. }
  76. virtual void TearDown() {
  77. ASSERT_NO_FATAL_FAILURE(copyToDebugSurface());
  78. ASSERT_TRUE(mLockedBuffer.data != NULL);
  79. ASSERT_EQ(NO_ERROR, mCpuConsumer->unlockBuffer(mLockedBuffer));
  80. }
  81. static float linearToSRGB(float l) {
  82. if (l <= 0.0031308f) {
  83. return l * 12.92f;
  84. } else {
  85. return 1.055f * pow(l, (1 / 2.4f)) - 0.055f;
  86. }
  87. }
  88. static float srgbToLinear(float s) {
  89. if (s <= 0.04045) {
  90. return s / 12.92f;
  91. } else {
  92. return pow(((s + 0.055f) / 1.055f), 2.4f);
  93. }
  94. }
  95. static uint8_t srgbToLinear(uint8_t u) {
  96. float f = u / 255.0f;
  97. return static_cast<uint8_t>(srgbToLinear(f) * 255.0f + 0.5f);
  98. }
  99. void fillTexture(bool writeAsSRGB) {
  100. uint8_t* textureData = new uint8_t[DISPLAY_SIZE];
  101. for (int y = 0; y < DISPLAY_HEIGHT; ++y) {
  102. for (int x = 0; x < DISPLAY_WIDTH; ++x) {
  103. float realValue = static_cast<float>(x) / (DISPLAY_WIDTH - 1);
  104. realValue *= ALPHA_VALUE / 255.0f; // Premultiply by alpha
  105. if (writeAsSRGB) {
  106. realValue = linearToSRGB(realValue);
  107. }
  108. int offset = (y * DISPLAY_WIDTH + x) * PIXEL_SIZE;
  109. for (int c = 0; c < 3; ++c) {
  110. uint8_t intValue = static_cast<uint8_t>(
  111. realValue * 255.0f + 0.5f);
  112. textureData[offset + c] = intValue;
  113. }
  114. textureData[offset + 3] = ALPHA_VALUE;
  115. }
  116. }
  117. glTexImage2D(GL_TEXTURE_2D, 0, writeAsSRGB ? GL_SRGB8_ALPHA8 : GL_RGBA8,
  118. DISPLAY_WIDTH, DISPLAY_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE,
  119. textureData);
  120. ASSERT_EQ(GL_NO_ERROR, glGetError());
  121. delete[] textureData;
  122. }
  123. void initShaders() {
  124. static const char vertexSource[] =
  125. "attribute vec4 vPosition;\n"
  126. "varying vec2 texCoords;\n"
  127. "void main() {\n"
  128. " texCoords = 0.5 * (vPosition.xy + vec2(1.0, 1.0));\n"
  129. " gl_Position = vPosition;\n"
  130. "}\n";
  131. static const char fragmentSource[] =
  132. "precision mediump float;\n"
  133. "uniform sampler2D texSampler;\n"
  134. "varying vec2 texCoords;\n"
  135. "void main() {\n"
  136. " gl_FragColor = texture2D(texSampler, texCoords);\n"
  137. "}\n";
  138. GLuint program;
  139. {
  140. SCOPED_TRACE("Creating shader program");
  141. ASSERT_NO_FATAL_FAILURE(GLTest::createProgram(
  142. vertexSource, fragmentSource, &program));
  143. }
  144. GLint positionHandle = glGetAttribLocation(program, "vPosition");
  145. ASSERT_EQ(GL_NO_ERROR, glGetError());
  146. ASSERT_NE(-1, positionHandle);
  147. GLint samplerHandle = glGetUniformLocation(program, "texSampler");
  148. ASSERT_EQ(GL_NO_ERROR, glGetError());
  149. ASSERT_NE(-1, samplerHandle);
  150. static const GLfloat vertices[] = {
  151. -1.0f, 1.0f,
  152. -1.0f, -1.0f,
  153. 1.0f, -1.0f,
  154. 1.0f, 1.0f,
  155. };
  156. glVertexAttribPointer(positionHandle, 2, GL_FLOAT, GL_FALSE, 0, vertices);
  157. ASSERT_EQ(GL_NO_ERROR, glGetError());
  158. glEnableVertexAttribArray(positionHandle);
  159. ASSERT_EQ(GL_NO_ERROR, glGetError());
  160. glUseProgram(program);
  161. ASSERT_EQ(GL_NO_ERROR, glGetError());
  162. glUniform1i(samplerHandle, 0);
  163. ASSERT_EQ(GL_NO_ERROR, glGetError());
  164. GLuint textureHandle;
  165. glGenTextures(1, &textureHandle);
  166. ASSERT_EQ(GL_NO_ERROR, glGetError());
  167. glBindTexture(GL_TEXTURE_2D, textureHandle);
  168. ASSERT_EQ(GL_NO_ERROR, glGetError());
  169. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  170. ASSERT_EQ(GL_NO_ERROR, glGetError());
  171. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  172. ASSERT_EQ(GL_NO_ERROR, glGetError());
  173. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  174. ASSERT_EQ(GL_NO_ERROR, glGetError());
  175. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  176. ASSERT_EQ(GL_NO_ERROR, glGetError());
  177. }
  178. void drawTexture(bool asSRGB, GLint x, GLint y, GLsizei width,
  179. GLsizei height) {
  180. ASSERT_NO_FATAL_FAILURE(fillTexture(asSRGB));
  181. glViewport(x, y, width, height);
  182. ASSERT_EQ(GL_NO_ERROR, glGetError());
  183. glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
  184. ASSERT_EQ(GL_NO_ERROR, glGetError());
  185. }
  186. void checkLockedBuffer(PixelFormat format, android_dataspace dataSpace) {
  187. ASSERT_EQ(mLockedBuffer.format, format);
  188. ASSERT_EQ(mLockedBuffer.width, DISPLAY_WIDTH);
  189. ASSERT_EQ(mLockedBuffer.height, DISPLAY_HEIGHT);
  190. ASSERT_EQ(mLockedBuffer.dataSpace, dataSpace);
  191. }
  192. static bool withinTolerance(int a, int b) {
  193. int diff = a - b;
  194. return diff >= 0 ? diff <= TOLERANCE : -diff <= TOLERANCE;
  195. }
  196. // Primary producer and consumer
  197. sp<Surface> mInputSurface;
  198. sp<CpuConsumer> mCpuConsumer;
  199. CpuConsumer::LockedBuffer mLockedBuffer;
  200. EGLDisplay mEglDisplay;
  201. EGLConfig mEglConfig;
  202. EGLContext mEglContext;
  203. EGLSurface mEglSurface;
  204. // Auxiliary display output
  205. sp<SurfaceComposerClient> mComposerClient;
  206. sp<SurfaceControl> mSurfaceControl;
  207. sp<Surface> mOutputSurface;
  208. private:
  209. void createEGLSurface(Surface* inputSurface) {
  210. mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
  211. ASSERT_EQ(EGL_SUCCESS, eglGetError());
  212. ASSERT_NE(EGL_NO_DISPLAY, mEglDisplay);
  213. EXPECT_TRUE(eglInitialize(mEglDisplay, NULL, NULL));
  214. ASSERT_EQ(EGL_SUCCESS, eglGetError());
  215. static const EGLint configAttribs[] = {
  216. EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
  217. EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR,
  218. EGL_RED_SIZE, 8,
  219. EGL_GREEN_SIZE, 8,
  220. EGL_BLUE_SIZE, 8,
  221. EGL_ALPHA_SIZE, 8,
  222. EGL_NONE };
  223. EGLint numConfigs = 0;
  224. EXPECT_TRUE(eglChooseConfig(mEglDisplay, configAttribs, &mEglConfig, 1,
  225. &numConfigs));
  226. ASSERT_EQ(EGL_SUCCESS, eglGetError());
  227. ASSERT_GT(numConfigs, 0);
  228. static const EGLint contextAttribs[] = {
  229. EGL_CONTEXT_CLIENT_VERSION, 3,
  230. EGL_NONE } ;
  231. mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT,
  232. contextAttribs);
  233. ASSERT_EQ(EGL_SUCCESS, eglGetError());
  234. ASSERT_NE(EGL_NO_CONTEXT, mEglContext);
  235. mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig,
  236. inputSurface, NULL);
  237. ASSERT_EQ(EGL_SUCCESS, eglGetError());
  238. ASSERT_NE(EGL_NO_SURFACE, mEglSurface);
  239. EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
  240. mEglContext));
  241. ASSERT_EQ(EGL_SUCCESS, eglGetError());
  242. }
  243. void createDebugSurface() {
  244. if (getenv(SHOW_DEBUG_STRING) == NULL) return;
  245. mComposerClient = new SurfaceComposerClient;
  246. ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
  247. mSurfaceControl = mComposerClient->createSurface(
  248. String8("SRGBTest Surface"), DISPLAY_WIDTH, DISPLAY_HEIGHT,
  249. PIXEL_FORMAT_RGBA_8888);
  250. ASSERT_TRUE(mSurfaceControl != NULL);
  251. ASSERT_TRUE(mSurfaceControl->isValid());
  252. SurfaceComposerClient::openGlobalTransaction();
  253. ASSERT_EQ(NO_ERROR, mSurfaceControl->setLayer(0x7FFFFFFF));
  254. ASSERT_EQ(NO_ERROR, mSurfaceControl->show());
  255. SurfaceComposerClient::closeGlobalTransaction();
  256. ANativeWindow_Buffer outBuffer;
  257. ARect inOutDirtyBounds;
  258. mOutputSurface = mSurfaceControl->getSurface();
  259. mOutputSurface->lock(&outBuffer, &inOutDirtyBounds);
  260. uint8_t* bytePointer = reinterpret_cast<uint8_t*>(outBuffer.bits);
  261. for (int y = 0; y < outBuffer.height; ++y) {
  262. int rowOffset = y * outBuffer.stride; // pixels
  263. for (int x = 0; x < outBuffer.width; ++x) {
  264. int colOffset = (rowOffset + x) * PIXEL_SIZE; // bytes
  265. for (int c = 0; c < PIXEL_SIZE; ++c) {
  266. int offset = colOffset + c;
  267. bytePointer[offset] = ((c + 1) * 56) - 1;
  268. }
  269. }
  270. }
  271. mOutputSurface->unlockAndPost();
  272. }
  273. void copyToDebugSurface() {
  274. if (!mOutputSurface.get()) return;
  275. size_t bufferSize = mLockedBuffer.height * mLockedBuffer.stride *
  276. PIXEL_SIZE;
  277. ANativeWindow_Buffer outBuffer;
  278. ARect outBufferBounds;
  279. mOutputSurface->lock(&outBuffer, &outBufferBounds);
  280. ASSERT_EQ(mLockedBuffer.width, static_cast<uint32_t>(outBuffer.width));
  281. ASSERT_EQ(mLockedBuffer.height, static_cast<uint32_t>(outBuffer.height));
  282. ASSERT_EQ(mLockedBuffer.stride, static_cast<uint32_t>(outBuffer.stride));
  283. if (mLockedBuffer.format == outBuffer.format) {
  284. memcpy(outBuffer.bits, mLockedBuffer.data, bufferSize);
  285. } else {
  286. ASSERT_EQ(mLockedBuffer.format, PIXEL_FORMAT_RGBA_8888);
  287. ASSERT_EQ(mLockedBuffer.dataSpace, HAL_DATASPACE_SRGB);
  288. ASSERT_EQ(outBuffer.format, PIXEL_FORMAT_RGBA_8888);
  289. uint8_t* outPointer = reinterpret_cast<uint8_t*>(outBuffer.bits);
  290. for (int y = 0; y < outBuffer.height; ++y) {
  291. int rowOffset = y * outBuffer.stride; // pixels
  292. for (int x = 0; x < outBuffer.width; ++x) {
  293. int colOffset = (rowOffset + x) * PIXEL_SIZE; // bytes
  294. // RGB are converted
  295. for (int c = 0; c < (PIXEL_SIZE - 1); ++c) {
  296. outPointer[colOffset + c] = srgbToLinear(
  297. mLockedBuffer.data[colOffset + c]);
  298. }
  299. // Alpha isn't converted
  300. outPointer[colOffset + 3] =
  301. mLockedBuffer.data[colOffset + 3];
  302. }
  303. }
  304. }
  305. mOutputSurface->unlockAndPost();
  306. int sleepSeconds = atoi(getenv(SHOW_DEBUG_STRING));
  307. sleep(sleepSeconds);
  308. }
  309. };
  310. const char SRGBTest::SHOW_DEBUG_STRING[] = "DEBUG_OUTPUT_SECONDS";
  311. TEST_F(SRGBTest, GLRenderFromSRGBTexture) {
  312. ASSERT_NO_FATAL_FAILURE(initShaders());
  313. // The RGB texture is displayed in the top half
  314. ASSERT_NO_FATAL_FAILURE(drawTexture(false, 0, DISPLAY_HEIGHT / 2,
  315. DISPLAY_WIDTH, DISPLAY_HEIGHT / 2));
  316. // The SRGB texture is displayed in the bottom half
  317. ASSERT_NO_FATAL_FAILURE(drawTexture(true, 0, 0,
  318. DISPLAY_WIDTH, DISPLAY_HEIGHT / 2));
  319. eglSwapBuffers(mEglDisplay, mEglSurface);
  320. ASSERT_EQ(EGL_SUCCESS, eglGetError());
  321. // Lock
  322. ASSERT_EQ(NO_ERROR, mCpuConsumer->lockNextBuffer(&mLockedBuffer));
  323. ASSERT_NO_FATAL_FAILURE(
  324. checkLockedBuffer(PIXEL_FORMAT_RGBA_8888, HAL_DATASPACE_UNKNOWN));
  325. // Compare a pixel in the middle of each texture
  326. int midSRGBOffset = (DISPLAY_HEIGHT / 4) * mLockedBuffer.stride *
  327. PIXEL_SIZE;
  328. int midRGBOffset = midSRGBOffset * 3;
  329. midRGBOffset += (DISPLAY_WIDTH / 2) * PIXEL_SIZE;
  330. midSRGBOffset += (DISPLAY_WIDTH / 2) * PIXEL_SIZE;
  331. for (int c = 0; c < PIXEL_SIZE; ++c) {
  332. int expectedValue = mLockedBuffer.data[midRGBOffset + c];
  333. int actualValue = mLockedBuffer.data[midSRGBOffset + c];
  334. ASSERT_PRED2(withinTolerance, expectedValue, actualValue);
  335. }
  336. // mLockedBuffer is unlocked in TearDown so we can copy data from it to
  337. // the debug surface if necessary
  338. }
  339. // XXX: Disabled since we don't currently expect this to work
  340. TEST_F(SRGBTest, DISABLED_RenderToSRGBSurface) {
  341. ASSERT_NO_FATAL_FAILURE(initShaders());
  342. // By default, the first buffer we write into will be RGB
  343. // Render an RGB texture across the whole surface
  344. ASSERT_NO_FATAL_FAILURE(drawTexture(false, 0, 0,
  345. DISPLAY_WIDTH, DISPLAY_HEIGHT));
  346. eglSwapBuffers(mEglDisplay, mEglSurface);
  347. ASSERT_EQ(EGL_SUCCESS, eglGetError());
  348. // Lock
  349. ASSERT_EQ(NO_ERROR, mCpuConsumer->lockNextBuffer(&mLockedBuffer));
  350. ASSERT_NO_FATAL_FAILURE(
  351. checkLockedBuffer(PIXEL_FORMAT_RGBA_8888, HAL_DATASPACE_UNKNOWN));
  352. // Save the values of the middle pixel for later comparison against SRGB
  353. uint8_t values[PIXEL_SIZE] = {};
  354. int middleOffset = (DISPLAY_HEIGHT / 2) * mLockedBuffer.stride *
  355. PIXEL_SIZE;
  356. middleOffset += (DISPLAY_WIDTH / 2) * PIXEL_SIZE;
  357. for (int c = 0; c < PIXEL_SIZE; ++c) {
  358. values[c] = mLockedBuffer.data[middleOffset + c];
  359. }
  360. // Unlock
  361. ASSERT_EQ(NO_ERROR, mCpuConsumer->unlockBuffer(mLockedBuffer));
  362. // Switch to SRGB window surface
  363. #define EGL_GL_COLORSPACE_KHR EGL_VG_COLORSPACE
  364. #define EGL_GL_COLORSPACE_SRGB_KHR EGL_VG_COLORSPACE_sRGB
  365. static const int srgbAttribs[] = {
  366. EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR,
  367. EGL_NONE,
  368. };
  369. EXPECT_TRUE(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
  370. mEglContext));
  371. ASSERT_EQ(EGL_SUCCESS, eglGetError());
  372. EXPECT_TRUE(eglDestroySurface(mEglDisplay, mEglSurface));
  373. ASSERT_EQ(EGL_SUCCESS, eglGetError());
  374. mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig,
  375. mInputSurface.get(), srgbAttribs);
  376. ASSERT_EQ(EGL_SUCCESS, eglGetError());
  377. ASSERT_NE(EGL_NO_SURFACE, mEglSurface);
  378. EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
  379. mEglContext));
  380. ASSERT_EQ(EGL_SUCCESS, eglGetError());
  381. // Render the texture again
  382. ASSERT_NO_FATAL_FAILURE(drawTexture(false, 0, 0,
  383. DISPLAY_WIDTH, DISPLAY_HEIGHT));
  384. eglSwapBuffers(mEglDisplay, mEglSurface);
  385. ASSERT_EQ(EGL_SUCCESS, eglGetError());
  386. // Lock
  387. ASSERT_EQ(NO_ERROR, mCpuConsumer->lockNextBuffer(&mLockedBuffer));
  388. // Make sure we actually got the SRGB buffer on the consumer side
  389. ASSERT_NO_FATAL_FAILURE(
  390. checkLockedBuffer(PIXEL_FORMAT_RGBA_8888, HAL_DATASPACE_SRGB));
  391. // Verify that the stored value is the same, accounting for RGB/SRGB
  392. for (int c = 0; c < PIXEL_SIZE; ++c) {
  393. // The alpha value should be equivalent before linear->SRGB
  394. float rgbAsSRGB = (c == 3) ? values[c] / 255.0f :
  395. linearToSRGB(values[c] / 255.0f);
  396. int expectedValue = rgbAsSRGB * 255.0f + 0.5f;
  397. int actualValue = mLockedBuffer.data[middleOffset + c];
  398. ASSERT_PRED2(withinTolerance, expectedValue, actualValue);
  399. }
  400. // mLockedBuffer is unlocked in TearDown so we can copy data from it to
  401. // the debug surface if necessary
  402. }
  403. } // namespace android