Skip to content
Snippets Groups Projects
Commit ef4e8650 authored by Steve Kondik's avatar Steve Kondik
Browse files

vnc: Use CpuConsumer

There's no need to use a GLConsumer unless we really want to draw
on the surface or send it to a hardware encoder. Get rid of all
this stuff and just use CpuConsumer.
parent 7225c7ff
No related branches found
No related tags found
No related merge requests found
......@@ -3,9 +3,7 @@ LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
src/EglWindow.cpp \
src/InputDevice.cpp \
src/Program.cpp \
src/VirtualDisplay.cpp \
src/VNCFlinger.cpp \
src/main.cpp
......@@ -25,14 +23,12 @@ LOCAL_SHARED_LIBRARIES := \
libssl \
libui \
libutils \
libz \
libEGL \
libGLESv3
libz
LOCAL_STATIC_LIBRARIES += \
libvncserver
LOCAL_CFLAGS := -Ofast -Werror
LOCAL_CFLAGS := -Ofast -Werror -std=c++11
LOCAL_CFLAGS += -DLOG_NDEBUG=0
#LOCAL_CXX := /usr/bin/include-what-you-use
......
/*
* Copyright 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "VNC-EglWindow"
//#define LOG_NDEBUG 0
#include <utils/Log.h>
#define EGL_EGLEXT_PROTOTYPES
#include <gui/BufferQueue.h>
#include <gui/GraphicBufferAlloc.h>
#include <gui/Surface.h>
#include "EglWindow.h"
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <assert.h>
using namespace android;
status_t EglWindow::createWindow(const sp<IGraphicBufferProducer>& surface) {
if (mEglSurface != EGL_NO_SURFACE) {
ALOGE("surface already created");
return UNKNOWN_ERROR;
}
status_t err = eglSetupContext(false);
if (err != NO_ERROR) {
return err;
}
// Cache the current dimensions. We're not expecting these to change.
surface->query(NATIVE_WINDOW_WIDTH, &mWidth);
surface->query(NATIVE_WINDOW_HEIGHT, &mHeight);
// Output side (EGL surface to draw on).
sp<ANativeWindow> anw = new Surface(surface);
mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, anw.get(),
NULL);
if (mEglSurface == EGL_NO_SURFACE) {
ALOGE("eglCreateWindowSurface error: %#x", eglGetError());
eglRelease();
return UNKNOWN_ERROR;
}
return NO_ERROR;
}
status_t EglWindow::createPbuffer(int width, int height) {
if (mEglSurface != EGL_NO_SURFACE) {
ALOGE("surface already created");
return UNKNOWN_ERROR;
}
status_t err = eglSetupContext(true);
if (err != NO_ERROR) {
return err;
}
mWidth = width;
mHeight = height;
EGLint pbufferAttribs[] = {
EGL_WIDTH, width,
EGL_HEIGHT, height,
EGL_NONE
};
mEglSurface = eglCreatePbufferSurface(mEglDisplay, mEglConfig, pbufferAttribs);
if (mEglSurface == EGL_NO_SURFACE) {
ALOGE("eglCreatePbufferSurface error: %#x", eglGetError());
eglRelease();
return UNKNOWN_ERROR;
}
return NO_ERROR;
}
status_t EglWindow::makeCurrent() const {
if (!eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
ALOGE("eglMakeCurrent failed: %#x", eglGetError());
return UNKNOWN_ERROR;
}
return NO_ERROR;
}
status_t EglWindow::eglSetupContext(bool forPbuffer) {
EGLBoolean result;
mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (mEglDisplay == EGL_NO_DISPLAY) {
ALOGE("eglGetDisplay failed: %#x", eglGetError());
return UNKNOWN_ERROR;
}
EGLint majorVersion, minorVersion;
result = eglInitialize(mEglDisplay, &majorVersion, &minorVersion);
if (result != EGL_TRUE) {
ALOGE("eglInitialize failed: %#x", eglGetError());
return UNKNOWN_ERROR;
}
ALOGV("Initialized EGL v%d.%d", majorVersion, minorVersion);
EGLint numConfigs = 0;
EGLint windowConfigAttribs[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_RECORDABLE_ANDROID, 1,
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
};
EGLint pbufferConfigAttribs[] = {
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_NONE
};
result = eglChooseConfig(mEglDisplay,
forPbuffer ? pbufferConfigAttribs : windowConfigAttribs,
&mEglConfig, 1, &numConfigs);
if (result != EGL_TRUE) {
ALOGE("eglChooseConfig error: %#x", eglGetError());
return UNKNOWN_ERROR;
}
EGLint contextAttribs[] = {
EGL_CONTEXT_CLIENT_VERSION, 3,
EGL_NONE
};
mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT,
contextAttribs);
if (mEglContext == EGL_NO_CONTEXT) {
ALOGE("eglCreateContext error: %#x", eglGetError());
return UNKNOWN_ERROR;
}
return NO_ERROR;
}
void EglWindow::eglRelease() {
ALOGV("EglWindow::eglRelease");
if (mEglDisplay != EGL_NO_DISPLAY) {
eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
EGL_NO_CONTEXT);
if (mEglContext != EGL_NO_CONTEXT) {
eglDestroyContext(mEglDisplay, mEglContext);
}
if (mEglSurface != EGL_NO_SURFACE) {
eglDestroySurface(mEglDisplay, mEglSurface);
}
}
mEglDisplay = EGL_NO_DISPLAY;
mEglContext = EGL_NO_CONTEXT;
mEglSurface = EGL_NO_SURFACE;
mEglConfig = NULL;
eglReleaseThread();
}
// Sets the presentation time on the current EGL buffer.
void EglWindow::presentationTime(nsecs_t whenNsec) const {
eglPresentationTimeANDROID(mEglDisplay, mEglSurface, whenNsec);
}
// Swaps the EGL buffer.
void EglWindow::swapBuffers() const {
eglSwapBuffers(mEglDisplay, mEglSurface);
}
/*
* Copyright 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SCREENRECORD_EGL_WINDOW_H
#define SCREENRECORD_EGL_WINDOW_H
#include <gui/BufferQueue.h>
#include <utils/Errors.h>
#include <EGL/egl.h>
namespace android {
/*
* Wraps EGL display, context, surface, config for a window surface.
*
* Not thread safe.
*/
class EglWindow {
public:
EglWindow() :
mEglDisplay(EGL_NO_DISPLAY),
mEglContext(EGL_NO_CONTEXT),
mEglSurface(EGL_NO_SURFACE),
mEglConfig(NULL),
mWidth(0),
mHeight(0)
{}
~EglWindow() { eglRelease(); }
// Creates an EGL window for the supplied surface.
status_t createWindow(const sp<IGraphicBufferProducer>& surface);
// Creates an EGL pbuffer surface.
status_t createPbuffer(int width, int height);
// Return width and height values (obtained from IGBP).
int getWidth() const { return mWidth; }
int getHeight() const { return mHeight; }
// Release anything we created.
void release() { eglRelease(); }
// Make this context current.
status_t makeCurrent() const;
// Sets the presentation time on the current EGL buffer.
void presentationTime(nsecs_t whenNsec) const;
// Swaps the EGL buffer.
void swapBuffers() const;
private:
EglWindow(const EglWindow&);
EglWindow& operator=(const EglWindow&);
// Init display, create config and context.
status_t eglSetupContext(bool forPbuffer);
void eglRelease();
// Basic EGL goodies.
EGLDisplay mEglDisplay;
EGLContext mEglContext;
EGLSurface mEglSurface;
EGLConfig mEglConfig;
// Surface dimensions.
int mWidth;
int mHeight;
};
}; // namespace android
#endif /*SCREENRECORD_EGL_WINDOW_H*/
/*
* Copyright 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "VNC"
//#define LOG_NDEBUG 0
#include <utils/Log.h>
#include "Program.h"
#include <GLES3/gl3.h>
#include <GLES3/gl3ext.h>
#include <GLES2/gl2ext.h>
#include <assert.h>
using namespace android;
// 4x4 identity matrix
const float Program::kIdentity[] = {
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
// Simple vertex shader. Texture coord calc includes matrix for GLConsumer
// transform.
static const char* kVertexShader =
"uniform mat4 uMVPMatrix;\n"
"uniform mat4 uGLCMatrix;\n"
"attribute vec4 aPosition;\n"
"attribute vec4 aTextureCoord;\n"
"varying vec2 vTextureCoord;\n"
"void main() {\n"
" gl_Position = uMVPMatrix * aPosition;\n"
" vTextureCoord = (uGLCMatrix * aTextureCoord).xy;\n"
"}\n";
// Trivial fragment shader for external texture.
static const char* kExtFragmentShader =
"#extension GL_OES_EGL_image_external : require\n"
"precision mediump float;\n"
"varying vec2 vTextureCoord;\n"
"uniform samplerExternalOES uTexture;\n"
"void main() {\n"
" gl_FragColor = texture2D(uTexture, vTextureCoord);\n"
"}\n";
// Trivial fragment shader for mundane texture.
static const char* kFragmentShader =
"precision mediump float;\n"
"varying vec2 vTextureCoord;\n"
"uniform sampler2D uTexture;\n"
"void main() {\n"
" gl_FragColor = texture2D(uTexture, vTextureCoord);\n"
//" gl_FragColor = vec4(0.2, 1.0, 0.2, 1.0);\n"
"}\n";
status_t Program::setup(ProgramType type) {
ALOGV("Program::setup type=%d", type);
status_t err;
mProgramType = type;
GLuint program;
if (type == PROGRAM_TEXTURE_2D) {
err = createProgram(&program, kVertexShader, kFragmentShader);
} else {
err = createProgram(&program, kVertexShader, kExtFragmentShader);
}
if (err != NO_ERROR) {
return err;
}
assert(program != 0);
maPositionLoc = glGetAttribLocation(program, "aPosition");
maTextureCoordLoc = glGetAttribLocation(program, "aTextureCoord");
muMVPMatrixLoc = glGetUniformLocation(program, "uMVPMatrix");
muGLCMatrixLoc = glGetUniformLocation(program, "uGLCMatrix");
muTextureLoc = glGetUniformLocation(program, "uTexture");
if ((maPositionLoc | maTextureCoordLoc | muMVPMatrixLoc |
muGLCMatrixLoc | muTextureLoc) == -1) {
ALOGE("Attrib/uniform lookup failed: %#x", glGetError());
glDeleteProgram(program);
return UNKNOWN_ERROR;
}
mProgram = program;
return NO_ERROR;
}
void Program::release() {
ALOGV("Program::release");
if (mProgram != 0) {
glDeleteProgram(mProgram);
mProgram = 0;
}
}
status_t Program::createProgram(GLuint* outPgm, const char* vertexShader,
const char* fragmentShader) {
GLuint vs, fs;
status_t err;
err = compileShader(GL_VERTEX_SHADER, vertexShader, &vs);
if (err != NO_ERROR) {
return err;
}
err = compileShader(GL_FRAGMENT_SHADER, fragmentShader, &fs);
if (err != NO_ERROR) {
glDeleteShader(vs);
return err;
}
GLuint program;
err = linkShaderProgram(vs, fs, &program);
glDeleteShader(vs);
glDeleteShader(fs);
if (err == NO_ERROR) {
*outPgm = program;
}
return err;
}
status_t Program::compileShader(GLenum shaderType, const char* src,
GLuint* outShader) {
GLuint shader = glCreateShader(shaderType);
if (shader == 0) {
ALOGE("glCreateShader error: %#x", glGetError());
return UNKNOWN_ERROR;
}
glShaderSource(shader, 1, &src, NULL);
glCompileShader(shader);
GLint compiled = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
if (!compiled) {
ALOGE("Compile of shader type %d failed", shaderType);
GLint infoLen = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
if (infoLen) {
char* buf = new char[infoLen];
if (buf) {
glGetShaderInfoLog(shader, infoLen, NULL, buf);
ALOGE("Compile log: %s", buf);
delete[] buf;
}
}
glDeleteShader(shader);
return UNKNOWN_ERROR;
}
*outShader = shader;
return NO_ERROR;
}
status_t Program::linkShaderProgram(GLuint vs, GLuint fs, GLuint* outPgm) {
GLuint program = glCreateProgram();
if (program == 0) {
ALOGE("glCreateProgram error: %#x", glGetError());
return UNKNOWN_ERROR;
}
glAttachShader(program, vs);
glAttachShader(program, fs);
glLinkProgram(program);
GLint linkStatus = GL_FALSE;
glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
if (linkStatus != GL_TRUE) {
ALOGE("glLinkProgram failed");
GLint bufLength = 0;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
if (bufLength) {
char* buf = new char[bufLength];
if (buf) {
glGetProgramInfoLog(program, bufLength, NULL, buf);
ALOGE("Link log: %s", buf);
delete[] buf;
}
}
glDeleteProgram(program);
return UNKNOWN_ERROR;
}
*outPgm = program;
return NO_ERROR;
}
status_t Program::blit(GLuint texName, const float* texMatrix,
int32_t x, int32_t y, int32_t w, int32_t h, bool invert) const {
ALOGV("Program::blit %d xy=%d,%d wh=%d,%d", texName, x, y, w, h);
const float pos[] = {
float(x), float(y+h),
float(x+w), float(y+h),
float(x), float(y),
float(x+w), float(y),
};
const float uv[] = {
0.0f, 0.0f,
1.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f,
};
status_t err;
err = beforeDraw(texName, texMatrix, pos, uv, invert);
if (err == NO_ERROR) {
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
err = afterDraw();
}
return err;
}
status_t Program::drawTriangles(GLuint texName, const float* texMatrix,
const float* vertices, const float* texes, size_t count) const {
ALOGV("Program::drawTriangles texName=%d", texName);
status_t err;
err = beforeDraw(texName, texMatrix, vertices, texes, false);
if (err == NO_ERROR) {
glDrawArrays(GL_TRIANGLES, 0, count);
err = afterDraw();
}
return err;
}
status_t Program::beforeDraw(GLuint texName, const float* texMatrix,
const float* vertices, const float* texes, bool invert) const {
// Create an orthographic projection matrix based on viewport size.
GLint vp[4];
glGetIntegerv(GL_VIEWPORT, vp);
float screenToNdc[16] = {
2.0f/float(vp[2]), 0.0f, 0.0f, 0.0f,
0.0f, -2.0f/float(vp[3]), 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 0.0f, 1.0f,
};
if (invert) {
screenToNdc[5] = -screenToNdc[5];
screenToNdc[13] = -screenToNdc[13];
}
glUseProgram(mProgram);
glVertexAttribPointer(maPositionLoc, 2, GL_FLOAT, GL_FALSE, 0, vertices);
glVertexAttribPointer(maTextureCoordLoc, 2, GL_FLOAT, GL_FALSE, 0, texes);
glEnableVertexAttribArray(maPositionLoc);
glEnableVertexAttribArray(maTextureCoordLoc);
glUniformMatrix4fv(muMVPMatrixLoc, 1, GL_FALSE, screenToNdc);
glUniformMatrix4fv(muGLCMatrixLoc, 1, GL_FALSE, texMatrix);
glActiveTexture(GL_TEXTURE0);
switch (mProgramType) {
case PROGRAM_EXTERNAL_TEXTURE:
glBindTexture(GL_TEXTURE_EXTERNAL_OES, texName);
break;
case PROGRAM_TEXTURE_2D:
glBindTexture(GL_TEXTURE_2D, texName);
break;
default:
ALOGE("unexpected program type %d", mProgramType);
return UNKNOWN_ERROR;
}
glUniform1i(muTextureLoc, 0);
GLenum glErr;
if ((glErr = glGetError()) != GL_NO_ERROR) {
ALOGE("GL error before draw: %#x", glErr);
glDisableVertexAttribArray(maPositionLoc);
glDisableVertexAttribArray(maTextureCoordLoc);
return UNKNOWN_ERROR;
}
return NO_ERROR;
}
status_t Program::afterDraw() const {
glDisableVertexAttribArray(maPositionLoc);
glDisableVertexAttribArray(maTextureCoordLoc);
GLenum glErr;
if ((glErr = glGetError()) != GL_NO_ERROR) {
ALOGE("GL error after draw: %#x", glErr);
return UNKNOWN_ERROR;
}
return NO_ERROR;
}
/*
* Copyright 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SCREENRECORD_PROGRAM_H
#define SCREENRECORD_PROGRAM_H
#include <utils/Errors.h>
#include <EGL/egl.h>
#include <GLES3/gl3.h>
namespace android {
/*
* Utility class for GLES rendering.
*
* Not thread-safe.
*/
class Program {
public:
enum ProgramType { PROGRAM_UNKNOWN=0, PROGRAM_EXTERNAL_TEXTURE,
PROGRAM_TEXTURE_2D };
Program() :
mProgramType(PROGRAM_UNKNOWN),
mProgram(0),
maPositionLoc(0),
maTextureCoordLoc(0),
muMVPMatrixLoc(0),
muGLCMatrixLoc(0),
muTextureLoc(0)
{}
~Program() { release(); }
// Initialize the program for use with the specified texture type.
status_t setup(ProgramType type);
// Release the program and associated resources.
void release();
// Blit the specified texture to { x, y, x+w, y+h }. Inverts the
// content if "invert" is set.
status_t blit(GLuint texName, const float* texMatrix,
int32_t x, int32_t y, int32_t w, int32_t h,
bool invert = false) const;
// Draw a number of triangles.
status_t drawTriangles(GLuint texName, const float* texMatrix,
const float* vertices, const float* texes, size_t count) const;
static const float kIdentity[];
private:
Program(const Program&);
Program& operator=(const Program&);
// Common code for draw functions.
status_t beforeDraw(GLuint texName, const float* texMatrix,
const float* vertices, const float* texes, bool invert) const;
status_t afterDraw() const;
// GLES 2 shader utilities.
status_t createProgram(GLuint* outPgm, const char* vertexShader,
const char* fragmentShader);
static status_t compileShader(GLenum shaderType, const char* src,
GLuint* outShader);
static status_t linkShaderProgram(GLuint vs, GLuint fs, GLuint* outPgm);
ProgramType mProgramType;
GLuint mProgram;
GLint maPositionLoc;
GLint maTextureCoordLoc;
GLint muMVPMatrixLoc;
GLint muGLCMatrixLoc;
GLint muTextureLoc;
};
}; // namespace android
#endif /*SCREENRECORD_PROGRAM_H*/
......@@ -14,6 +14,8 @@
using namespace android;
Mutex VNCFlinger::sUpdateMutex;
status_t VNCFlinger::start() {
Mutex::Autolock _l(mMutex);
......@@ -74,19 +76,20 @@ status_t VNCFlinger::setup_l() {
mVNCScreen->newClientHook = (rfbNewClientHookPtr) VNCFlinger::onNewClient;
mVNCScreen->kbdAddEvent = InputDevice::keyEvent;
mVNCScreen->ptrAddEvent = InputDevice::pointerEvent;
mVNCScreen->displayHook = (rfbDisplayHookPtr) VNCFlinger::onFrameStart;
mVNCScreen->displayFinishedHook = (rfbDisplayFinishedHookPtr) VNCFlinger::onFrameDone;
mVNCScreen->serverFormat.trueColour = true;
mVNCScreen->serverFormat.bitsPerPixel = 32;
mVNCScreen->handleEventsEagerly = true;
mVNCScreen->deferUpdateTime = 16;
mVNCScreen->deferUpdateTime = 0;
mVNCScreen->screenData = this;
rfbInitServer(mVNCScreen);
/* Mark as dirty since we haven't sent any updates at all yet. */
rfbMarkRectAsModified(mVNCScreen, 0, 0, mWidth, mHeight);
mVirtualDisplay = new VirtualDisplay(mVNCScreen);
mVirtualDisplay = new VirtualDisplay(mVNCScreen, &sUpdateMutex);
return err;
}
......@@ -147,6 +150,23 @@ enum rfbNewClientAction VNCFlinger::onNewClient(rfbClientPtr cl) {
return RFB_CLIENT_ACCEPT;
}
void VNCFlinger::onFrameStart(rfbClientPtr /* cl */) {
sUpdateMutex.lock();
ALOGV("frame start");
}
void VNCFlinger::onFrameDone(rfbClientPtr /* cl */, int status) {
sUpdateMutex.unlock();
ALOGV("frame done! %d", status);
}
void VNCFlinger::markFrame(void* frame, size_t stride) {
Mutex::Autolock _l(sUpdateMutex);
mVNCScreen->frameBuffer = (char *)frame;
mVNCScreen->paddedWidthInBytes = stride * 4;
rfbMarkRectAsModified(mVNCScreen, 0, 0, mWidth, mHeight);
}
void VNCFlinger::rfbLogger(const char *format, ...) {
va_list args;
char buf[256];
......
......@@ -27,6 +27,8 @@ public:
virtual size_t addClient();
virtual size_t removeClient();
virtual void markFrame(void* frame, size_t stride);
private:
virtual status_t setup_l();
......@@ -34,6 +36,8 @@ private:
static ClientGoneHookPtr onClientGone(rfbClientPtr cl);
static enum rfbNewClientAction onNewClient(rfbClientPtr cl);
static void onFrameStart(rfbClientPtr cl);
static void onFrameDone(rfbClientPtr cl, int result);
static void rfbLogger(const char *format, ...);
Condition mCondition;
......@@ -48,6 +52,7 @@ private:
DisplayInfo mMainDpyInfo;
Mutex mMutex;
static Mutex sUpdateMutex;
sp<VirtualDisplay> mVirtualDisplay;
......
......@@ -106,10 +106,10 @@ status_t VirtualDisplay::setDisplayProjection(const sp<IBinder>& dpy,
Rect displayRect(offX, offY, offX + outWidth, offY + outHeight);
if (mRotate) {
printf("Rotated content area is %ux%u at offset x=%d y=%d\n",
ALOGV("Rotated content area is %ux%u at offset x=%d y=%d\n",
outHeight, outWidth, offY, offX);
} else {
printf("Content area is %ux%u at offset x=%d y=%d\n",
ALOGV("Content area is %ux%u at offset x=%d y=%d\n",
outWidth, outHeight, offX, offY);
}
......@@ -123,6 +123,7 @@ status_t VirtualDisplay::start(const DisplayInfo& mainDpyInfo) {
Mutex::Autolock _l(mMutex);
ALOGV("Orientation: %d", mainDpyInfo.orientation);
mRotate = isDeviceRotated(mainDpyInfo.orientation);
mWidth = mRotate ? mainDpyInfo.h : mainDpyInfo.w;
mHeight = mRotate ? mainDpyInfo.w : mainDpyInfo.h;
......@@ -195,51 +196,14 @@ bool VirtualDisplay::threadLoop() {
status_t VirtualDisplay::setup_l() {
status_t err;
err = mEglWindow.createPbuffer(mWidth, mHeight);
if (err != NO_ERROR) {
return err;
}
mEglWindow.makeCurrent();
glViewport(0, 0, mWidth, mHeight);
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
// Shader for rendering the external texture.
err = mExtTexProgram.setup(Program::PROGRAM_EXTERNAL_TEXTURE);
if (err != NO_ERROR) {
return err;
}
// Input side (buffers from virtual display).
glGenTextures(1, &mExtTextureName);
if (mExtTextureName == 0) {
ALOGE("glGenTextures failed: %#x", glGetError());
return UNKNOWN_ERROR;
}
mBufSize = mWidth * mHeight * kGlBytesPerPixel;
// pixel buffer for image copy
mPBO = new GLuint[NUM_PBO];
glGenBuffers(NUM_PBO, mPBO);
for (unsigned int i = 0; i < NUM_PBO; i++) {
glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO[i]);
glBufferData(GL_PIXEL_PACK_BUFFER, mBufSize, 0, GL_DYNAMIC_READ);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
}
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&mProducer, &consumer);
mGlConsumer = new GLConsumer(consumer, mExtTextureName,
GL_TEXTURE_EXTERNAL_OES, true, false);
mGlConsumer->setName(String8("virtual display"));
mGlConsumer->setDefaultBufferSize(mWidth, mHeight);
mCpuConsumer = new CpuConsumer(consumer, 1);
mCpuConsumer->setName(String8("vds-to-cpu"));
mCpuConsumer->setDefaultBufferSize(mWidth, mHeight);
mProducer->setMaxDequeuedBufferCount(4);
mGlConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_TEXTURE);
mGlConsumer->setFrameAvailableListener(this);
mCpuConsumer->setFrameAvailableListener(this);
ALOGD("VirtualDisplay::setup_l OK");
return NO_ERROR;
......@@ -248,60 +212,42 @@ status_t VirtualDisplay::setup_l() {
void* VirtualDisplay::processFrame_l() {
ALOGD("processFrame_l\n");
float texMatrix[16];
mGlConsumer->updateTexImage();
mGlConsumer->getTransformMatrix(texMatrix);
mUpdateMutex->lock();
int64_t startWhen, blitWhen, readWhen, mapWhen, memcpyWhen, markWhen;
startWhen = systemTime(CLOCK_MONOTONIC);
CpuConsumer::LockedBuffer imgBuffer;
status_t res = mCpuConsumer->lockNextBuffer(&imgBuffer);
if (res != OK) {
ALOGE("Failed to lock next buffer: %s (%d)", strerror(-res), res);
return nullptr;
}
// The data is in an external texture, so we need to render it to the
// pbuffer to get access to RGB pixel data. We also want to flip it
// upside-down for easy conversion to a bitmap.
int width = mEglWindow.getWidth();
int height = mEglWindow.getHeight();
mExtTexProgram.blit(mExtTextureName, texMatrix, 0, 0, mWidth, mHeight, true);
ALOGV("imgBuffer ptr: %p format: %x (%dx%d, stride=%d)", imgBuffer.data, imgBuffer.format, imgBuffer.width, imgBuffer.height, imgBuffer.stride);
blitWhen = systemTime(CLOCK_MONOTONIC);
void* vncbuf = mVNCScreen->frameBuffer;
void* imgbuf = imgBuffer.data;
GLenum glErr;
glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO[mIndex]);
glReadPixels(0, 0, mWidth, mHeight, GL_RGBA, GL_UNSIGNED_BYTE, 0);
if ((glErr = glGetError()) != GL_NO_ERROR) {
ALOGE("glReadPixels failed: %#x", glErr);
return NULL;
for (size_t y = 0; y < mHeight; y++) {
memcpy(vncbuf, imgbuf, mWidth * 4);
vncbuf = (void *)((char *)vncbuf + mWidth * 4);
imgbuf = (void *)((char *)imgbuf + imgBuffer.stride * 4);
}
readWhen = systemTime(CLOCK_MONOTONIC);
void* ptr = glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, mBufSize, GL_MAP_READ_BIT);
mapWhen = systemTime(CLOCK_MONOTONIC);
//memcpy(mVNCScreen->frameBuffer, ptr, mBufSize);
mVNCScreen->frameBuffer = (char *)ptr;
memcpyWhen = systemTime(CLOCK_MONOTONIC);
ALOGD("buf copied");
mVNCScreen->frameBuffer = (char *)imgBuffer.data;
mVNCScreen->paddedWidthInBytes = imgBuffer.stride * 4;
rfbMarkRectAsModified(mVNCScreen, 0, 0, mWidth, mHeight);
markWhen = systemTime(CLOCK_MONOTONIC);
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
ALOGV("processFrame: blit=%.3fms read=%.3fms map=%.3fms memcpy=%.3fms mark=%.3fms",
(blitWhen - startWhen) / 1000000.0,
(readWhen - blitWhen) / 1000000.0,
(mapWhen - readWhen) / 1000000.0,
(memcpyWhen - mapWhen) / 1000000.0,
(markWhen - memcpyWhen) / 1000000.0);
mCpuConsumer->unlockBuffer(imgBuffer);
mUpdateMutex->unlock();
mIndex = (mIndex + 1) % NUM_PBO;
return mVNCScreen->frameBuffer;
return nullptr;
}
void VirtualDisplay::release_l() {
ALOGD("release_l");
mGlConsumer.clear();
mCpuConsumer.clear();
mProducer.clear();
mExtTexProgram.release();
mEglWindow.release();
SurfaceComposerClient::destroyDisplay(mDpy);
}
......
......@@ -17,11 +17,8 @@
#ifndef VDS_H
#define VDS_H
#include "Program.h"
#include "EglWindow.h"
#include <gui/BufferQueue.h>
#include <gui/GLConsumer.h>
#include <gui/CpuConsumer.h>
#include <gui/IGraphicBufferProducer.h>
#include <ui/DisplayInfo.h>
#include <utils/Thread.h>
......@@ -35,13 +32,13 @@ namespace android {
/*
* Support for "frames" output format.
*/
class VirtualDisplay : public GLConsumer::FrameAvailableListener, Thread {
class VirtualDisplay : public CpuConsumer::FrameAvailableListener, Thread {
public:
VirtualDisplay(rfbScreenInfoPtr vncScreen) : Thread(false),
VirtualDisplay(rfbScreenInfoPtr vncScreen, Mutex *updateMutex) : Thread(false),
mVNCScreen(vncScreen),
mUpdateMutex(updateMutex),
mThreadResult(UNKNOWN_ERROR),
mState(UNINITIALIZED),
mIndex(0)
mState(UNINITIALIZED)
{}
// Create an "input surface", similar in purpose to a MediaCodec input
......@@ -81,6 +78,7 @@ private:
void* processFrame_l();
rfbScreenInfoPtr mVNCScreen;
Mutex *mUpdateMutex;
uint32_t mHeight, mWidth;
bool mRotate;
......@@ -107,22 +105,7 @@ private:
sp<IGraphicBufferProducer> mProducer;
// This receives frames from the virtual display and makes them available
// as an external texture.
sp<GLConsumer> mGlConsumer;
// EGL display / context / surface.
EglWindow mEglWindow;
// GL rendering support.
Program mExtTexProgram;
// External texture, updated by GLConsumer.
GLuint mExtTextureName;
// Pixel data buffers.
size_t mBufSize;
GLuint* mPBO;
unsigned int mIndex;
sp<CpuConsumer> mCpuConsumer;
sp<IBinder> mDpy;
};
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment