blob: 7a95042d872fb52ff523cb2fd5330122b8b6b339 [file] [log] [blame]
Steve Kondik55db0532017-06-12 11:27:18 -07001/*
2 * Copyright 2014 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
17#define LOG_TAG "VNC-VirtualDisplay"
18//#define LOG_NDEBUG 0
19#include <utils/Log.h>
20
21#include <binder/IPCThreadState.h>
22#include <binder/ProcessState.h>
23
24#include <gui/SurfaceComposerClient.h>
25
Steve Kondik55db0532017-06-12 11:27:18 -070026#include <GLES3/gl3.h>
Steve Kondikdda11002017-06-13 08:20:27 -070027#include <GLES3/gl3ext.h>
28#include <GLES2/gl2ext.h>
Steve Kondik55db0532017-06-12 11:27:18 -070029
30#include <ui/Rect.h>
31
32#include "VirtualDisplay.h"
33
34using namespace android;
35
36static const int kGlBytesPerPixel = 4; // GL_RGBA
37
38
39/*
40 * Returns "true" if the device is rotated 90 degrees.
41 */
42bool VirtualDisplay::isDeviceRotated(int orientation) {
43 return orientation != DISPLAY_ORIENTATION_0 &&
44 orientation != DISPLAY_ORIENTATION_180;
45}
46
47/*
48 * Sets the display projection, based on the display dimensions, video size,
49 * and device orientation.
50 */
51status_t VirtualDisplay::setDisplayProjection(const sp<IBinder>& dpy,
52 const DisplayInfo& mMainDpyInfo) {
53 status_t err;
54
55 // Set the region of the layer stack we're interested in, which in our
56 // case is "all of it". If the app is rotated (so that the width of the
57 // app is based on the height of the display), reverse width/height.
58 bool deviceRotated = isDeviceRotated(mMainDpyInfo.orientation);
59 uint32_t sourceWidth, sourceHeight;
60 if (!deviceRotated) {
61 sourceWidth = mMainDpyInfo.w;
62 sourceHeight = mMainDpyInfo.h;
63 } else {
64 ALOGV("using rotated width/height");
65 sourceHeight = mMainDpyInfo.w;
66 sourceWidth = mMainDpyInfo.h;
67 }
68 Rect layerStackRect(sourceWidth, sourceHeight);
69
70 // We need to preserve the aspect ratio of the display.
71 float displayAspect = (float) sourceHeight / (float) sourceWidth;
72
73
74 // Set the way we map the output onto the display surface (which will
75 // be e.g. 1280x720 for a 720p video). The rect is interpreted
76 // post-rotation, so if the display is rotated 90 degrees we need to
77 // "pre-rotate" it by flipping width/height, so that the orientation
78 // adjustment changes it back.
79 //
80 // We might want to encode a portrait display as landscape to use more
81 // of the screen real estate. (If players respect a 90-degree rotation
82 // hint, we can essentially get a 720x1280 video instead of 1280x720.)
83 // In that case, we swap the configured video width/height and then
84 // supply a rotation value to the display projection.
85 uint32_t videoWidth, videoHeight;
86 uint32_t outWidth, outHeight;
87 if (!mRotate) {
88 videoWidth = mWidth;
89 videoHeight = mHeight;
90 } else {
91 videoWidth = mHeight;
92 videoHeight = mWidth;
93 }
94 if (videoHeight > (uint32_t)(videoWidth * displayAspect)) {
95 // limited by narrow width; reduce height
96 outWidth = videoWidth;
97 outHeight = (uint32_t)(videoWidth * displayAspect);
98 } else {
99 // limited by short height; restrict width
100 outHeight = videoHeight;
101 outWidth = (uint32_t)(videoHeight / displayAspect);
102 }
103 uint32_t offX, offY;
104 offX = (videoWidth - outWidth) / 2;
105 offY = (videoHeight - outHeight) / 2;
106 Rect displayRect(offX, offY, offX + outWidth, offY + outHeight);
107
108 if (mRotate) {
109 printf("Rotated content area is %ux%u at offset x=%d y=%d\n",
110 outHeight, outWidth, offY, offX);
111 } else {
112 printf("Content area is %ux%u at offset x=%d y=%d\n",
113 outWidth, outHeight, offX, offY);
114 }
115
116 SurfaceComposerClient::setDisplayProjection(dpy,
117 mRotate ? DISPLAY_ORIENTATION_90 : DISPLAY_ORIENTATION_0,
118 layerStackRect, displayRect);
119 return NO_ERROR;
120}
121
122status_t VirtualDisplay::start(const DisplayInfo& mainDpyInfo, EventQueue *queue) {
123
124 Mutex::Autolock _l(mMutex);
125
126 mQueue = queue;
127
128 mRotate = isDeviceRotated(mainDpyInfo.orientation);
129 mWidth = mRotate ? mainDpyInfo.h : mainDpyInfo.w;
130 mHeight = mRotate ? mainDpyInfo.w : mainDpyInfo.h;
131
132 sp<ProcessState> self = ProcessState::self();
133 self->startThreadPool();
134
135 run("vnc-virtualdisplay");
136
137 mState = INIT;
138 while (mState == INIT) {
139 mStartCond.wait(mMutex);
140 }
141
142 if (mThreadResult != NO_ERROR) {
143 ALOGE("Failed to start VDS thread: err=%d", mThreadResult);
144 return mThreadResult;
145 }
146 assert(mState == RUNNING);
147
148 mDpy = SurfaceComposerClient::createDisplay(
149 String8("VNCFlinger"), false /*secure*/);
150
151 SurfaceComposerClient::openGlobalTransaction();
152 SurfaceComposerClient::setDisplaySurface(mDpy, mProducer);
153 setDisplayProjection(mDpy, mainDpyInfo);
154 SurfaceComposerClient::setDisplayLayerStack(mDpy, 0); // default stack
155 SurfaceComposerClient::closeGlobalTransaction();
156
157 ALOGV("VirtualDisplay::start successful");
158 return NO_ERROR;
159}
160
161status_t VirtualDisplay::stop() {
162 Mutex::Autolock _l(mMutex);
163 mState = STOPPING;
164 mEventCond.signal();
165 return NO_ERROR;
166}
167
168bool VirtualDisplay::threadLoop() {
169 Mutex::Autolock _l(mMutex);
170
171 mThreadResult = setup_l();
172
173 if (mThreadResult != NO_ERROR) {
174 ALOGW("Aborting VDS thread");
175 mState = STOPPED;
176 release_l();
177 mStartCond.broadcast();
178 return false;
179 }
180
181 ALOGV("VDS thread running");
182 mState = RUNNING;
183 mStartCond.broadcast();
184
185 while (mState == RUNNING) {
186 mEventCond.wait(mMutex);
187 ALOGD("Awake, frame available");
188 void* ptr = processFrame_l();
Steve Kondikdda11002017-06-13 08:20:27 -0700189
190 //const Event ev(EVENT_BUFFER_READY, ptr);
191 //mQueue->enqueue(ev);
Steve Kondik55db0532017-06-12 11:27:18 -0700192 }
193
194 ALOGV("VDS thread stopping");
195 release_l();
196 mState = STOPPED;
197 return false; // stop
198}
199
200status_t VirtualDisplay::setup_l() {
201 status_t err;
202
203 err = mEglWindow.createPbuffer(mWidth, mHeight);
204 if (err != NO_ERROR) {
205 return err;
206 }
207 mEglWindow.makeCurrent();
208
209 glViewport(0, 0, mWidth, mHeight);
210 glDisable(GL_DEPTH_TEST);
211 glDisable(GL_CULL_FACE);
212
213 // Shader for rendering the external texture.
214 err = mExtTexProgram.setup(Program::PROGRAM_EXTERNAL_TEXTURE);
215 if (err != NO_ERROR) {
216 return err;
217 }
218
219 // Input side (buffers from virtual display).
220 glGenTextures(1, &mExtTextureName);
221 if (mExtTextureName == 0) {
222 ALOGE("glGenTextures failed: %#x", glGetError());
223 return UNKNOWN_ERROR;
224 }
225
226 mBufSize = mWidth * mHeight * kGlBytesPerPixel;
Steve Kondikdda11002017-06-13 08:20:27 -0700227
Steve Kondik55db0532017-06-12 11:27:18 -0700228 // pixel buffer for image copy
229 mPBO = new GLuint[NUM_PBO];
230 glGenBuffers(NUM_PBO, mPBO);
231
232 for (unsigned int i = 0; i < NUM_PBO; i++) {
233 glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO[i]);
234 glBufferData(GL_PIXEL_PACK_BUFFER, mBufSize, 0, GL_DYNAMIC_READ);
235 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
236 }
237
238 sp<IGraphicBufferConsumer> consumer;
239 BufferQueue::createBufferQueue(&mProducer, &consumer);
240 mGlConsumer = new GLConsumer(consumer, mExtTextureName,
241 GL_TEXTURE_EXTERNAL_OES, true, false);
242 mGlConsumer->setName(String8("virtual display"));
243 mGlConsumer->setDefaultBufferSize(mWidth, mHeight);
244 mProducer->setMaxDequeuedBufferCount(4);
245 mGlConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_TEXTURE);
246
247 mGlConsumer->setFrameAvailableListener(this);
248
249 ALOGD("VirtualDisplay::setup_l OK");
250 return NO_ERROR;
251}
252
253void* VirtualDisplay::processFrame_l() {
254 ALOGD("processFrame_l\n");
255
256 float texMatrix[16];
257 mGlConsumer->updateTexImage();
258 mGlConsumer->getTransformMatrix(texMatrix);
259
Steve Kondikdda11002017-06-13 08:20:27 -0700260 int64_t startWhen, blitWhen, readWhen, mapWhen, memcpyWhen, markWhen;
261 startWhen = systemTime(CLOCK_MONOTONIC);
262
Steve Kondik55db0532017-06-12 11:27:18 -0700263 // The data is in an external texture, so we need to render it to the
264 // pbuffer to get access to RGB pixel data. We also want to flip it
265 // upside-down for easy conversion to a bitmap.
266 int width = mEglWindow.getWidth();
267 int height = mEglWindow.getHeight();
268 mExtTexProgram.blit(mExtTextureName, texMatrix, 0, 0, mWidth, mHeight, true);
269
Steve Kondikdda11002017-06-13 08:20:27 -0700270 blitWhen = systemTime(CLOCK_MONOTONIC);
271
Steve Kondik55db0532017-06-12 11:27:18 -0700272 GLenum glErr;
273 glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO[mIndex]);
274 glReadPixels(0, 0, mWidth, mHeight, GL_RGBA, GL_UNSIGNED_BYTE, 0);
275 if ((glErr = glGetError()) != GL_NO_ERROR) {
276 ALOGE("glReadPixels failed: %#x", glErr);
277 return NULL;
278 }
279
Steve Kondikdda11002017-06-13 08:20:27 -0700280 readWhen = systemTime(CLOCK_MONOTONIC);
281
Steve Kondik55db0532017-06-12 11:27:18 -0700282 void* ptr = glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, mBufSize, GL_MAP_READ_BIT);
Steve Kondikdda11002017-06-13 08:20:27 -0700283 mapWhen = systemTime(CLOCK_MONOTONIC);
284 //memcpy(mVNCScreen->frameBuffer, ptr, mBufSize);
285 mVNCScreen->frameBuffer = (char *)ptr;
286 memcpyWhen = systemTime(CLOCK_MONOTONIC);
287 rfbMarkRectAsModified(mVNCScreen, 0, 0, mWidth, mHeight);
288 markWhen = systemTime(CLOCK_MONOTONIC);
289
Steve Kondik55db0532017-06-12 11:27:18 -0700290 glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
291 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
292
Steve Kondikdda11002017-06-13 08:20:27 -0700293 ALOGV("processFrame: blit=%.3fms read=%.3fms map=%.3fms memcpy=%.3fms mark=%.3fms",
294 (blitWhen - startWhen) / 1000000.0,
295 (readWhen - blitWhen) / 1000000.0,
296 (mapWhen - readWhen) / 1000000.0,
297 (memcpyWhen - mapWhen) / 1000000.0,
298 (markWhen - memcpyWhen) / 1000000.0);
299
300 mIndex = (mIndex + 1) % NUM_PBO;
301 return mVNCScreen->frameBuffer;
Steve Kondik55db0532017-06-12 11:27:18 -0700302}
303
304void VirtualDisplay::release_l() {
305 ALOGD("release_l");
306 mGlConsumer.clear();
307 mProducer.clear();
308 mExtTexProgram.release();
309 mEglWindow.release();
310 SurfaceComposerClient::destroyDisplay(mDpy);
311}
312
313// Callback; executes on arbitrary thread.
314void VirtualDisplay::onFrameAvailable(const BufferItem& item) {
315 Mutex::Autolock _l(mMutex);
316 mEventCond.signal();
317 ALOGD("mTimestamp=%ld mFrameNumber=%ld", item.mTimestamp, item.mFrameNumber);
318}