Initial work

This commit is contained in:
Simon Fels 2016-05-26 22:38:57 +02:00 • committed by Simon Fels
commit 3222551702
374 changed files with 40290 additions and 0 deletions

View file

@ -0,0 +1,70 @@
LOCAL_PATH := $(call my-dir)
host_OS_SRCS :=
host_common_LDLIBS :=
ifeq ($(BUILD_TARGET_OS),linux)
host_OS_SRCS = NativeSubWindow_x11.cpp
host_common_LDLIBS += -lX11 -lrt
endif
ifeq ($(BUILD_TARGET_OS),darwin)
host_OS_SRCS = NativeSubWindow_cocoa.m
host_common_LDLIBS += -Wl,-framework,AppKit
endif
ifeq ($(BUILD_TARGET_OS),windows)
host_OS_SRCS = NativeSubWindow_win32.cpp
host_common_LDLIBS += -lgdi32
endif
host_common_SRC_FILES := \
$(host_OS_SRCS) \
ColorBuffer.cpp \
FbConfig.cpp \
FrameBuffer.cpp \
ReadBuffer.cpp \
RenderContext.cpp \
RenderControl.cpp \
RenderServer.cpp \
RenderThread.cpp \
RenderThreadInfo.cpp \
render_api.cpp \
RenderWindow.cpp \
SocketStream.cpp \
TcpStream.cpp \
TextureDraw.cpp \
TextureResize.cpp \
TimeUtils.cpp \
WindowSurface.cpp \
ifeq ($(BUILD_TARGET_OS),windows)
host_common_SRC_FILES += Win32PipeStream.cpp
host_common_LDLIBS += -lws2_32 -lpsapi
else
host_common_SRC_FILES += UnixStream.cpp
endif
### host libOpenglRender #################################################
$(call emugl-begin-host-shared-library,lib$(BUILD_TARGET_SUFFIX)OpenglRender)
$(call emugl-import,libGLESv1_dec libGLESv2_dec lib_renderControl_dec libOpenglCodecCommon)
LOCAL_LDLIBS += $(host_common_LDLIBS)
LOCAL_SRC_FILES := $(host_common_SRC_FILES)
$(call emugl-export,C_INCLUDES,$(EMUGL_PATH)/host/include)
$(call emugl-export,C_INCLUDES,$(LOCAL_PATH))
# use Translator's egl/gles headers
LOCAL_C_INCLUDES += $(EMUGL_PATH)/host/libs/Translator/include
LOCAL_C_INCLUDES += $(EMUGL_PATH)/host/libs/libOpenGLESDispatch
LOCAL_STATIC_LIBRARIES += libemugl_common
LOCAL_STATIC_LIBRARIES += libOpenGLESDispatch
LOCAL_SYMBOL_FILE := render_api.entries
$(call emugl-export,CFLAGS,$(EMUGL_USER_CFLAGS))
$(call emugl-end-module)

View file

@ -0,0 +1,37 @@
set(SOURCES
ColorBuffer.cpp
FbConfig.cpp
FrameBuffer.cpp
NativeSubWindow_mir.cpp
ReadBuffer.cpp
RenderContext.cpp
RenderControl.cpp
RenderServer.cpp
RenderThread.cpp
RenderThreadInfo.cpp
render_api.cpp
RenderWindow.cpp
SocketStream.cpp
TcpStream.cpp
TextureDraw.cpp
TextureResize.cpp
TimeUtils.cpp
UnixStream.cpp
WindowSurface.cpp)
include_directories(BEFORE
${MIRCLIENT_INCLUDE_DIRS})
add_library(OpenglRender ${SOURCES})
target_link_libraries(OpenglRender
emugl_common
GLESv1_dec
GLESv2_dec
renderControl_dec
OpenGLESDispatch
OpenglCodecCommon
mir_support
${EGL_LDFLAGS}
${EGL_LIBRARIES}
${MIRCLIENT_LDFLAGS}
${MIRCLIENT_LIBRARIES})

View file

@ -0,0 +1,396 @@
/*
* Copyright (C) 2011 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.
*/
#include "ColorBuffer.h"
#include "DispatchTables.h"
#include "RenderThreadInfo.h"
#include "TextureDraw.h"
#include "TextureResize.h"
#include "OpenGLESDispatch/EGLDispatch.h"
#include <stdio.h>
namespace {
// <EGL/egl.h> defines many types as 'void*' while they're really
// implemented as unsigned integers. These convenience template functions
// help casting between them safely without generating compiler warnings.
inline void* SafePointerFromUInt(unsigned int handle) {
return (void*)(uintptr_t)(handle);
}
inline unsigned int SafeUIntFromPointer(const void* ptr) {
#if 1
// Ignore the assert below to avoid crashing when running older
// system images, which might have buggy encoder libraries. Print
// an error message though.
if ((uintptr_t)(ptr) != (unsigned int)(uintptr_t)(ptr)) {
fprintf(stderr, "EmuGL:WARNING: bad generic pointer %p\n", ptr);
}
#else
// Assertion error if the pointer contains a value that does not fit
// in an unsigned integer!
assert((uintptr_t)(ptr) == (unsigned int)(uintptr_t)(ptr));
#endif
return (unsigned int)(uintptr_t)(ptr);
}
// Lazily create and bind a framebuffer object to the current host context.
// |fbo| is the address of the framebuffer object name.
// |tex| is the name of a texture that is attached to the framebuffer object
// on creation only. I.e. all rendering operations will target it.
// returns true in case of success, false on failure.
bool bindFbo(GLuint* fbo, GLuint tex) {
if (*fbo) {
// fbo already exist - just bind
s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, *fbo);
return true;
}
s_gles2.glGenFramebuffers(1, fbo);
s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, *fbo);
s_gles2.glFramebufferTexture2D(GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0_OES,
GL_TEXTURE_2D, tex, 0);
GLenum status = s_gles2.glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE_OES) {
ERR("ColorBuffer::bindFbo: FBO not complete: %#x\n", status);
s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, 0);
s_gles2.glDeleteFramebuffers(1, fbo);
*fbo = 0;
return false;
}
return true;
}
void unbindFbo() {
s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
// Helper class to use a ColorBuffer::Helper context.
// Usage is pretty simple:
//
// {
// ScopedHelperContext context(m_helper);
// if (!context.isOk()) {
// return false; // something bad happened.
// }
// .... do something ....
// } // automatically calls m_helper->teardownContext();
//
class ScopedHelperContext {
public:
ScopedHelperContext(ColorBuffer::Helper* helper) : mHelper(helper) {
if (!helper->setupContext()) {
mHelper = NULL;
}
}
bool isOk() const { return mHelper != NULL; }
~ScopedHelperContext() {
release();
}
void release() {
if (mHelper) {
mHelper->teardownContext();
mHelper = NULL;
}
}
private:
ColorBuffer::Helper* mHelper;
};
} // namespace
// static
ColorBuffer* ColorBuffer::create(EGLDisplay p_display,
int p_width,
int p_height,
GLenum p_internalFormat,
bool has_eglimage_texture_2d,
Helper* helper) {
GLenum texInternalFormat = 0;
printf("Creating new color buffer width %i height %i\n", p_width, p_height);
switch (p_internalFormat) {
case GL_RGB:
case GL_RGB565_OES:
texInternalFormat = GL_RGB;
break;
case GL_RGBA:
case GL_RGB5_A1_OES:
case GL_RGBA4_OES:
texInternalFormat = GL_RGBA;
break;
default:
return NULL;
break;
}
ScopedHelperContext context(helper);
if (!context.isOk()) {
return NULL;
}
ColorBuffer *cb = new ColorBuffer(p_display, helper);
s_gles2.glGenTextures(1, &cb->m_tex);
s_gles2.glBindTexture(GL_TEXTURE_2D, cb->m_tex);
int nComp = (texInternalFormat == GL_RGB ? 3 : 4);
char* zBuff = static_cast<char*>(::calloc(nComp * p_width * p_height, 1));
s_gles2.glTexImage2D(GL_TEXTURE_2D,
0,
texInternalFormat,
p_width,
p_height,
0,
texInternalFormat,
GL_UNSIGNED_BYTE,
zBuff);
::free(zBuff);
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
//
// create another texture for that colorbuffer for blit
//
s_gles2.glGenTextures(1, &cb->m_blitTex);
s_gles2.glBindTexture(GL_TEXTURE_2D, cb->m_blitTex);
s_gles2.glTexImage2D(GL_TEXTURE_2D,
0,
texInternalFormat,
p_width,
p_height,
0,
texInternalFormat,
GL_UNSIGNED_BYTE,
NULL);
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
cb->m_width = p_width;
cb->m_height = p_height;
cb->m_internalFormat = texInternalFormat;
if (has_eglimage_texture_2d) {
cb->m_eglImage = s_egl.eglCreateImageKHR(
p_display,
s_egl.eglGetCurrentContext(),
EGL_GL_TEXTURE_2D_KHR,
(EGLClientBuffer)SafePointerFromUInt(cb->m_tex),
NULL);
cb->m_blitEGLImage = s_egl.eglCreateImageKHR(
p_display,
s_egl.eglGetCurrentContext(),
EGL_GL_TEXTURE_2D_KHR,
(EGLClientBuffer)SafePointerFromUInt(cb->m_blitTex),
NULL);
}
cb->m_resizer = new TextureResize(p_width, p_height);
return cb;
}
ColorBuffer::ColorBuffer(EGLDisplay display, Helper* helper) :
m_tex(0),
m_blitTex(0),
m_eglImage(NULL),
m_blitEGLImage(NULL),
m_fbo(0),
m_internalFormat(0),
m_display(display),
m_helper(helper) {}
ColorBuffer::~ColorBuffer() {
ScopedHelperContext context(m_helper);
if (m_blitEGLImage) {
s_egl.eglDestroyImageKHR(m_display, m_blitEGLImage);
}
if (m_eglImage) {
s_egl.eglDestroyImageKHR(m_display, m_eglImage);
}
if (m_fbo) {
s_gles2.glDeleteFramebuffers(1, &m_fbo);
}
GLuint tex[2] = {m_tex, m_blitTex};
s_gles2.glDeleteTextures(2, tex);
delete m_resizer;
}
void ColorBuffer::readPixels(int x,
int y,
int width,
int height,
GLenum p_format,
GLenum p_type,
void* pixels) {
ScopedHelperContext context(m_helper);
if (!context.isOk()) {
return;
}
if (bindFbo(&m_fbo, m_tex)) {
s_gles2.glReadPixels(x, y, width, height, p_format, p_type, pixels);
unbindFbo();
}
}
void ColorBuffer::subUpdate(int x,
int y,
int width,
int height,
GLenum p_format,
GLenum p_type,
void* pixels) {
ScopedHelperContext context(m_helper);
if (!context.isOk()) {
return;
}
s_gles2.glBindTexture(GL_TEXTURE_2D, m_tex);
s_gles2.glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
s_gles2.glTexSubImage2D(
GL_TEXTURE_2D, 0, x, y, width, height, p_format, p_type, pixels);
}
bool ColorBuffer::blitFromCurrentReadBuffer()
{
RenderThreadInfo *tInfo = RenderThreadInfo::get();
if (!tInfo->currContext.Ptr()) {
// no Current context
return false;
}
// Copy the content of the current read surface into m_blitEGLImage.
// This is done by creating a temporary texture, bind it to the EGLImage
// then call glCopyTexSubImage2D().
GLuint tmpTex;
GLint currTexBind;
if (tInfo->currContext->isGL2()) {
s_gles2.glGetIntegerv(GL_TEXTURE_BINDING_2D, &currTexBind);
s_gles2.glGenTextures(1,&tmpTex);
s_gles2.glBindTexture(GL_TEXTURE_2D, tmpTex);
s_gles2.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_blitEGLImage);
s_gles2.glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0,
m_width, m_height);
s_gles2.glDeleteTextures(1, &tmpTex);
s_gles2.glBindTexture(GL_TEXTURE_2D, currTexBind);
}
else {
s_gles1.glGetIntegerv(GL_TEXTURE_BINDING_2D, &currTexBind);
s_gles1.glGenTextures(1,&tmpTex);
s_gles1.glBindTexture(GL_TEXTURE_2D, tmpTex);
s_gles1.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_blitEGLImage);
s_gles1.glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0,
m_width, m_height);
s_gles1.glDeleteTextures(1, &tmpTex);
s_gles1.glBindTexture(GL_TEXTURE_2D, currTexBind);
}
ScopedHelperContext context(m_helper);
if (!context.isOk()) {
return false;
}
if (!bindFbo(&m_fbo, m_tex)) {
return false;
}
// Save current viewport and match it to the current colorbuffer size.
GLint vport[4] = { 0, };
s_gles2.glGetIntegerv(GL_VIEWPORT, vport);
s_gles2.glViewport(0, 0, m_width, m_height);
// render m_blitTex
m_helper->getTextureDraw()->draw(m_blitTex, 0., 0, 0);
// Restore previous viewport.
s_gles2.glViewport(vport[0], vport[1], vport[2], vport[3]);
unbindFbo();
return true;
}
bool ColorBuffer::bindToTexture() {
if (!m_eglImage) {
return false;
}
RenderThreadInfo *tInfo = RenderThreadInfo::get();
if (!tInfo->currContext.Ptr()) {
return false;
}
if (tInfo->currContext->isGL2()) {
s_gles2.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_eglImage);
}
else {
s_gles1.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_eglImage);
}
return true;
}
bool ColorBuffer::bindToRenderbuffer() {
if (!m_eglImage) {
return false;
}
RenderThreadInfo *tInfo = RenderThreadInfo::get();
if (!tInfo->currContext.Ptr()) {
return false;
}
if (tInfo->currContext->isGL2()) {
s_gles2.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER_OES, m_eglImage);
}
else {
s_gles1.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER_OES, m_eglImage);
}
return true;
}
bool ColorBuffer::post(float rotation, float dx, float dy) {
// NOTE: Do not call m_helper->setupContext() here!
return m_helper->getTextureDraw()->draw(m_resizer->update(m_tex), rotation, dx, dy);
}
void ColorBuffer::readback(unsigned char* img) {
ScopedHelperContext context(m_helper);
if (!context.isOk()) {
return;
}
if (bindFbo(&m_fbo, m_tex)) {
s_gles2.glReadPixels(
0, 0, m_width, m_height, GL_RGBA, GL_UNSIGNED_BYTE, img);
unbindFbo();
}
}

View file

@ -0,0 +1,161 @@
/*
* Copyright (C) 2011 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 _LIBRENDER_COLORBUFFER_H
#define _LIBRENDER_COLORBUFFER_H
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES/gl.h>
#include "emugl/common/smart_ptr.h"
#include <memory>
class TextureDraw;
class TextureResize;
// A class used to model a guest color buffer, and used to implement several
// related things:
//
// - Every gralloc native buffer with HW read or write requirements will
// allocate a host ColorBuffer instance. When gralloc_lock() is called,
// the guest will use ColorBuffer::readPixels() to read the current content
// of the buffer. When gralloc_unlock() is later called, it will call
// ColorBuffer::subUpdate() to send the updated pixels.
//
// - Every guest window EGLSurface is implemented by a host PBuffer
// (see WindowSurface.h) that can have a ColorBuffer instance attached to
// it (through WindowSurface::attachColorBuffer()). When such an attachment
// exists, WindowSurface::flushColorBuffer() will copy the PBuffer's
// pixel data into the ColorBuffer. The latter can then be displayed
// in the client's UI sub-window with ColorBuffer::post().
//
// - Guest EGLImages are implemented as native gralloc buffers too.
// The guest glEGLImageTargetTexture2DOES() implementations will end up
// calling ColorBuffer::bindToTexture() to bind the current context's
// GL_TEXTURE_2D to the buffer. Similarly, the guest versions of
// glEGLImageTargetRenderbufferStorageOES() will end up calling
// ColorBuffer::bindToRenderbuffer().
//
// This forces the implementation to use a host EGLImage to implement each
// ColorBuffer.
//
// As an additional twist.
class ColorBuffer {
public:
// Helper interface class used during ColorBuffer operations. This is
// introduced to remove coupling from the FrameBuffer class implementation.
class Helper {
public:
Helper() {}
virtual ~Helper() {}
virtual bool setupContext() = 0;
virtual void teardownContext() = 0;
virtual TextureDraw* getTextureDraw() const = 0;
};
// Create a new ColorBuffer instance.
// |p_display| is the host EGLDisplay handle.
// |p_width| and |p_height| are the buffer's dimensions in pixels.
// |p_internalFormat| is the internal pixel format to use, valid values
// are: GL_RGB, GL_RGB565, GL_RGBA, GL_RGB5_A1_OES and GL_RGBA4_OES.
// Implementation is free to use something else though.
// |has_eglimage_texture_2d| should be true iff the display supports
// the EGL_KHR_gl_texture_2D_image extension.
// Returns NULL on failure.
static ColorBuffer* create(EGLDisplay p_display,
int p_width,
int p_height,
GLenum p_internalFormat,
bool has_eglimage_texture_2d,
Helper* helper);
// Destructor.
~ColorBuffer();
// Return ColorBuffer width and height in pixels
GLuint getWidth() const { return m_width; }
GLuint getHeight() const { return m_height; }
// Read the ColorBuffer instance's pixel values into host memory.
void readPixels(int x,
int y,
int width,
int height,
GLenum p_format,
GLenum p_type,
void *pixels);
// Update the ColorBuffer instance's pixel values from host memory.
void subUpdate(int x,
int y,
int width,
int height,
GLenum p_format,
GLenum p_type,
void *pixels);
// Draw a ColorBuffer instance, i.e. blit it to the current guest
// framebuffer object / window surface. This doesn't display anything.
bool draw();
// Post this ColorBuffer to the host native sub-window.
// |rotation| is the rotation angle in degrees, clockwise in the GL
// coordinate space.
bool post(float rotation, float dx, float dy);
// Bind the current context's EGL_TEXTURE_2D texture to this ColorBuffer's
// EGLImage. This is intended to implement glEGLImageTargetTexture2DOES()
// for all GLES versions.
bool bindToTexture();
// Bind the current context's EGL_RENDERBUFFER_OES render buffer to this
// ColorBuffer's EGLImage. This is intended to implement
// glEGLImageTargetRenderbufferStorageOES() for all GLES versions.
bool bindToRenderbuffer();
// Copy the content of the current context's read surface to this
// ColorBuffer. This is used from WindowSurface::flushColorBuffer().
// Return true on success, false on failure (e.g. no current context).
bool blitFromCurrentReadBuffer();
// Read the content of the whole ColorBuffer as 32-bit RGBA pixels.
// |img| must be a buffer large enough (i.e. width * height * 4).
void readback(unsigned char* img);
private:
ColorBuffer(); // no default constructor.
explicit ColorBuffer(EGLDisplay display, Helper* helper);
private:
GLuint m_tex;
GLuint m_blitTex;
EGLImageKHR m_eglImage;
EGLImageKHR m_blitEGLImage;
GLuint m_width;
GLuint m_height;
GLuint m_fbo;
GLenum m_internalFormat;
EGLDisplay m_display;
Helper* m_helper;
TextureResize * m_resizer;
};
typedef emugl::SmartPtr<ColorBuffer> ColorBufferPtr;
#endif

View file

@ -0,0 +1,22 @@
/*
* Copyright (C) 2011-2015 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.
*/
#pragma once
#include "OpenGLESDispatch/GLESv1Dispatch.h"
#include "OpenGLESDispatch/GLESv2Dispatch.h"
extern GLESv2Dispatch s_gles2;
extern GLESv1Dispatch s_gles1;

View file

@ -0,0 +1,275 @@
// Copyright (C) 2015 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.
#include "FbConfig.h"
#include "OpenGLESDispatch/EGLDispatch.h"
#include <stdio.h>
#include <string.h>
namespace {
#define E(...) fprintf(stderr, __VA_ARGS__)
const GLuint kConfigAttributes[] = {
EGL_DEPTH_SIZE, // must be first - see getDepthSize()
EGL_STENCIL_SIZE, // must be second - see getStencilSize()
EGL_RENDERABLE_TYPE,// must be third - see getRenderableType()
EGL_SURFACE_TYPE, // must be fourth - see getSurfaceType()
EGL_CONFIG_ID, // must be fifth - see chooseConfig()
EGL_BUFFER_SIZE,
EGL_ALPHA_SIZE,
EGL_BLUE_SIZE,
EGL_GREEN_SIZE,
EGL_RED_SIZE,
EGL_CONFIG_CAVEAT,
EGL_LEVEL,
EGL_MAX_PBUFFER_HEIGHT,
EGL_MAX_PBUFFER_PIXELS,
EGL_MAX_PBUFFER_WIDTH,
EGL_NATIVE_RENDERABLE,
EGL_NATIVE_VISUAL_ID,
EGL_NATIVE_VISUAL_TYPE,
EGL_SAMPLES,
EGL_SAMPLE_BUFFERS,
EGL_TRANSPARENT_TYPE,
EGL_TRANSPARENT_BLUE_VALUE,
EGL_TRANSPARENT_GREEN_VALUE,
EGL_TRANSPARENT_RED_VALUE,
EGL_BIND_TO_TEXTURE_RGB,
EGL_BIND_TO_TEXTURE_RGBA,
EGL_MIN_SWAP_INTERVAL,
EGL_MAX_SWAP_INTERVAL,
EGL_LUMINANCE_SIZE,
EGL_ALPHA_MASK_SIZE,
EGL_COLOR_BUFFER_TYPE,
//EGL_MATCH_NATIVE_PIXMAP,
EGL_CONFORMANT
};
const size_t kConfigAttributesLen =
sizeof(kConfigAttributes) / sizeof(kConfigAttributes[0]);
bool isCompatibleHostConfig(EGLConfig config, EGLDisplay display) {
// Filter out configs which do not support pbuffers, since they
// are used to implement window surfaces.
EGLint surfaceType;
s_egl.eglGetConfigAttrib(
display, config, EGL_SURFACE_TYPE, &surfaceType);
if (!(surfaceType & EGL_PBUFFER_BIT)) {
return false;
}
// Filter out configs that do not support RGB pixel values.
EGLint redSize = 0, greenSize = 0, blueSize = 0, alphaSize = 0;
s_egl.eglGetConfigAttrib(
display, config,EGL_RED_SIZE, &redSize);
s_egl.eglGetConfigAttrib(
display, config, EGL_GREEN_SIZE, &greenSize);
s_egl.eglGetConfigAttrib(
display, config, EGL_BLUE_SIZE, &blueSize);
s_egl.eglGetConfigAttrib(
display, config, EGL_ALPHA_SIZE, &alphaSize);
if (!redSize || !greenSize || !blueSize || !alphaSize) {
return false;
}
return true;
}
} // namespace
FbConfig::~FbConfig() {
delete [] mAttribValues;
}
FbConfig::FbConfig(EGLConfig hostConfig, EGLDisplay hostDisplay) :
mEglConfig(hostConfig), mAttribValues(NULL) {
mAttribValues = new GLint[kConfigAttributesLen];
for (size_t i = 0; i < kConfigAttributesLen; ++i) {
mAttribValues[i] = 0;
s_egl.eglGetConfigAttrib(hostDisplay,
hostConfig,
kConfigAttributes[i],
&mAttribValues[i]);
// This implementation supports guest window surfaces by wrapping
// them around host Pbuffers, so always report it to the guest.
if (kConfigAttributes[i] == EGL_SURFACE_TYPE) {
mAttribValues[i] |= EGL_WINDOW_BIT;
}
}
}
FbConfigList::FbConfigList(EGLDisplay display) :
mCount(0), mConfigs(NULL), mDisplay(display) {
if (display == EGL_NO_DISPLAY) {
E("%s: Invalid display value %p (EGL_NO_DISPLAY)\n",
__FUNCTION__, (void*)display);
return;
}
EGLint numHostConfigs = 0;
if (!s_egl.eglGetConfigs(display, NULL, 0, &numHostConfigs)) {
E("%s: Could not get number of host EGL configs\n", __FUNCTION__);
return;
}
EGLConfig* hostConfigs = new EGLConfig[numHostConfigs];
s_egl.eglGetConfigs(display, hostConfigs, numHostConfigs, &numHostConfigs);
mConfigs = new FbConfig*[numHostConfigs];
for (EGLint i = 0; i < numHostConfigs; ++i) {
// Filter out configs that are not compatible with our implementation.
if (!isCompatibleHostConfig(hostConfigs[i], display)) {
continue;
}
mConfigs[mCount] = new FbConfig(hostConfigs[i], display);
mCount++;
}
delete [] hostConfigs;
}
FbConfigList::~FbConfigList() {
for (int n = 0; n < mCount; ++n) {
delete mConfigs[n];
}
delete [] mConfigs;
}
int FbConfigList::chooseConfig(const EGLint* attribs,
EGLint* configs,
EGLint configsSize) const {
EGLint numHostConfigs = 0;
if (!s_egl.eglGetConfigs(mDisplay, NULL, 0, &numHostConfigs)) {
E("%s: Could not get number of host EGL configs\n", __FUNCTION__);
return 0;
}
EGLConfig* matchedConfigs = new EGLConfig[numHostConfigs];
// If EGL_SURFACE_TYPE appears in |attribs|, the value passed to
// eglChooseConfig should be forced to EGL_PBUFFER_BIT because that's
// what it used by the current implementation, exclusively. This forces
// the rewrite of |attribs| into a new array.
bool hasSurfaceType = false;
bool mustReplaceSurfaceType = false;
int numAttribs = 0;
while (attribs[numAttribs] != EGL_NONE) {
if (attribs[numAttribs] == EGL_SURFACE_TYPE) {
hasSurfaceType = true;
if (attribs[numAttribs + 1] != EGL_PBUFFER_BIT) {
mustReplaceSurfaceType = true;
}
}
numAttribs += 2;
}
EGLint* newAttribs = NULL;
if (mustReplaceSurfaceType) {
// There is at least on EGL_SURFACE_TYPE in |attribs|. Copy the
// array and replace all values with EGL_PBUFFER_BIT
newAttribs = new GLint[numAttribs + 1];
memcpy(newAttribs, attribs, numAttribs * sizeof(GLint));
newAttribs[numAttribs] = EGL_NONE;
for (int n = 0; n < numAttribs; n += 2) {
if (newAttribs[n] == EGL_SURFACE_TYPE) {
newAttribs[n + 1] = EGL_PBUFFER_BIT;
}
}
} else if (!hasSurfaceType) {
// There is no EGL_SURFACE_TYPE in |attribs|, then add one entry
// with the value EGL_PBUFFER_BIT.
newAttribs = new GLint[numAttribs + 3];
memcpy(newAttribs, attribs, numAttribs * sizeof(GLint));
newAttribs[numAttribs] = EGL_SURFACE_TYPE;
newAttribs[numAttribs + 1] = EGL_PBUFFER_BIT;
newAttribs[numAttribs + 2] = EGL_NONE;
}
if (!s_egl.eglChooseConfig(mDisplay,
newAttribs ? newAttribs : attribs,
matchedConfigs,
numHostConfigs,
&numHostConfigs)) {
numHostConfigs = 0;
}
delete [] newAttribs;
int result = 0;
for (int n = 0; n < numHostConfigs; ++n) {
// Don't count or write more than |configsSize| items if |configs|
// is not NULL.
if (configs && configsSize > 0 && result >= configsSize) {
break;
}
// Skip incompatible host configs.
if (!isCompatibleHostConfig(matchedConfigs[n], mDisplay)) {
continue;
}
// Find the FbConfig with the same EGL_CONFIG_ID
EGLint hostConfigId;
s_egl.eglGetConfigAttrib(
mDisplay, matchedConfigs[n], EGL_CONFIG_ID, &hostConfigId);
for (int k = 0; k < mCount; ++k) {
int guestConfigId = mConfigs[k]->getConfigId();
if (guestConfigId == hostConfigId) {
// There is a match. Write it to |configs| if it is not NULL.
if (configs && result < configsSize) {
configs[result] = (uint32_t)k;
}
result ++;
break;
}
}
}
delete [] matchedConfigs;
return result;
}
void FbConfigList::getPackInfo(EGLint* numConfigs,
EGLint* numAttributes) const {
if (numConfigs) {
*numConfigs = mCount;
}
if (numAttributes) {
*numAttributes = static_cast<EGLint>(kConfigAttributesLen);
}
}
EGLint FbConfigList::packConfigs(GLuint bufferByteSize, GLuint* buffer) const {
GLuint numAttribs = static_cast<GLuint>(kConfigAttributesLen);
GLuint kGLuintSize = static_cast<GLuint>(sizeof(GLuint));
GLuint neededByteSize = (mCount + 1) * numAttribs * kGLuintSize;
if (!buffer || bufferByteSize < neededByteSize) {
return -neededByteSize;
}
// Write to the buffer the config attribute ids, followed for each one
// of the configs, their values.
memcpy(buffer, kConfigAttributes, kConfigAttributesLen * kGLuintSize);
for (int i = 0; i < mCount; ++i) {
memcpy(buffer + (i + 1) * kConfigAttributesLen,
mConfigs[i]->mAttribValues,
kConfigAttributesLen * kGLuintSize);
}
return mCount;
}

View file

@ -0,0 +1,163 @@
// Copyright (C) 2015 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 _LIBRENDER_FB_CONFIG_H
#define _LIBRENDER_FB_CONFIG_H
#include <EGL/egl.h>
#include <GLES/gl.h>
#include <stddef.h>
// A class used to model a guest EGL config.
// This really wraps a host EGLConfig handle, and provides a few cached
// attributes that can be retrieved through direct accessors, like
// getDepthSize().
//
// Each FbConfig is identified by a unique id which is its index in
// an FbConfigList instance (as declared below). It is not related to
// the host EGLConfig value or its EGL_CONFIG_ID.
//
// One doesn't create an FbConfig instance. Instead, create and initialize
// an FbConfigList from the host EGLDisplay, and use its size() and get()
// methods to access it.
class FbConfig {
public:
// Destructor
~FbConfig();
// Retrieve host EGLConfig.
EGLConfig getEglConfig() const { return mEglConfig; }
// Get depth size in bits.
GLuint getDepthSize() const { return getAttribValue(0); }
// Get stencil size in bits.
GLuint getStencilSize() const { return getAttribValue(1); }
// Get renderable type mask.
GLuint getRenderableType() const { return getAttribValue(2); }
// Get surface type mask.
GLuint getSurfaceType() const { return getAttribValue(3); }
// Get the EGL_CONFIG_ID value. This is the same as the one of the
// underlying host EGLConfig handle.
GLint getConfigId() const { return (GLint)getAttribValue(4); }
private:
FbConfig();
FbConfig(FbConfig& other);
explicit FbConfig(EGLConfig hostConfig, EGLDisplay hostDisplay);
friend class FbConfigList;
GLuint getAttribValue(int n) const {
return mAttribValues ? mAttribValues[n] : 0U;
}
EGLConfig mEglConfig;
GLint* mAttribValues;
};
// A class to model the list of FbConfig for a given EGLDisplay, this is
// built from the list of host EGLConfig handles, filtered to only accept
// configs that are useful by the rendering library (e.g. they must support
// PBuffers and RGB pixel values).
//
// Usage is the following:
//
// 1) Create new instance by passing host EGLDisplay value.
//
// 2) Call empty() to check that the list is not empty, which would indicate
// an error during creation.
//
// 3) FbConfig instances are identified by numbers in 0..(N-1) range, where
// N is the result of the size() method.
//
// 4) Convert an FbConfig id into an FbConfig instance with get().
//
// 5) Use getPackInfo() and packConfigs() to retrieve information about
// available configs to the guest.
class FbConfigList {
public:
// Create a new list of FbConfig instance, by querying all compatible
// host configs from |display|. A compatible config is one that supports
// Pbuffers and RGB pixel values.
//
// After construction, call empty() to check if there are items.
// An empty list means there was an error during construction.
explicit FbConfigList(EGLDisplay display);
// Destructor.
~FbConfigList();
// Return true iff the list is empty. true means there was an error
// during construction.
bool empty() const { return mCount == 0; }
// Return the number of FbConfig instances in the list.
// Each instance is identified by a number from 0 to N-1,
// where N is the result of this function.
size_t size() const { return static_cast<size_t>(mCount); }
// Retrieve the FbConfig instance associated with |guestId|,
// which must be an integer between 0 and |size() - 1|. Returns
// NULL in case of failure.
const FbConfig* get(int guestId) const {
if (guestId >= 0 && guestId < mCount) {
return mConfigs[guestId];
} else {
return NULL;
}
}
// Use |attribs| a list of EGL attribute name/values terminated by
// EGL_NONE, to select a set of matching FbConfig instances.
//
// On success, returns the number of matching instances.
// If |configs| is not NULL, it will be populated with the guest IDs
// of the matched FbConfig instances.
//
// |configsSize| is the number of entries in the |configs| array. The
// function will never write more than |configsSize| entries into
// |configsSize|.
EGLint chooseConfig(const EGLint* attribs,
EGLint* configs,
EGLint configsSize) const;
// Retrieve information that can be sent to the guest before packed
// config list information. If |numConfigs| is NULL, then |*numConfigs|
// will be set on return to the number of config instances.
// If |numAttribs| is not NULL, then |*numAttribs| will be set on return
// to the number of attribute values cached by each FbConfig instance.
void getPackInfo(EGLint* mumConfigs, EGLint* numAttribs) const;
// Write the full list information into an array of EGLuint items.
// |buffer| is the output buffer that will receive the data.
// |bufferByteSize| is teh buffer size in bytes.
// On success, this returns
EGLint packConfigs(GLuint bufferByteSize, GLuint* buffer) const;
private:
FbConfigList();
FbConfigList(const FbConfigList& other);
int mCount;
FbConfig** mConfigs;
EGLDisplay mDisplay;
};
#endif // _LIBRENDER_FB_CONFIG_H

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,360 @@
/*
* Copyright (C) 2011-2015 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 _LIBRENDER_FRAMEBUFFER_H
#define _LIBRENDER_FRAMEBUFFER_H
#include "ColorBuffer.h"
#include "emugl/common/mutex.h"
#include "FbConfig.h"
#include "RenderContext.h"
#include "TextureDraw.h"
#include "WindowSurface.h"
#include "OpenglRender/render_api.h"
#include <EGL/egl.h>
#include <map>
#include <stdint.h>
// Type of handles, a.k.a. "object names" in the GL specification.
// These are integers used to uniquely identify a resource of a given type.
typedef uint32_t HandleType;
struct ColorBufferRef {
ColorBufferPtr cb;
uint32_t refcount; // number of client-side references
};
typedef std::map<HandleType, RenderContextPtr> RenderContextMap;
typedef std::map<HandleType, std::pair<WindowSurfacePtr, HandleType> > WindowSurfaceMap;
typedef std::map<HandleType, ColorBufferRef> ColorBufferMap;
// A structure used to list the capabilities of the underlying EGL
// implementation that the FrameBuffer instance depends on.
// |has_eglimage_texture_2d| is true iff the EGL_KHR_gl_texture_2D_image
// extension is supported.
// |has_eglimage_renderbuffer| is true iff the EGL_KHR_gl_renderbuffer_image
// extension is supported.
// |eglMajor| and |eglMinor| are the major and minor version numbers of
// the underlying EGL implementation.
struct FrameBufferCaps {
bool has_eglimage_texture_2d;
bool has_eglimage_renderbuffer;
EGLint eglMajor;
EGLint eglMinor;
};
// The FrameBuffer class holds the global state of the emulation library on
// top of the underlying EGL/GLES implementation. It should probably be
// named "Display" instead of "FrameBuffer".
//
// There is only one global instance, that can be retrieved with getFB(),
// and which must be previously setup by calling initialize().
//
class FrameBuffer {
public:
// Initialize the global instance.
// |width| and |height| are the dimensions of the emulator GPU display
// in pixels. |useSubWindow| is true to indicate that the caller
// will use setupSubWindow() to let EmuGL display the GPU content in its
// own sub-windows. If false, this means the caller will use
// setPostCallback() instead to retrieve the content.
// Returns true on success, false otherwise.
static bool initialize(int width, int height, bool useSubWindow);
// Setup a sub-window to display the content of the emulated GPU
// on-top of an existing UI window. |p_window| is the platform-specific
// parent window handle. |wx|, |wy|, |ww| and |wh| are the
// dimensions in pixels of the sub-window, relative to the parent window's
// coordinate. |fbw| and |fbh| are the dimensions used to initialize
// the framebuffer, which may be different from the dimensions of the
// sub-window (in which case scaling will be applied automatically).
// |dpr| is the device pixel ratio of the monitor, which is needed for
// proper panning on high-density displays (like retina)
// |zRot| is a rotation angle in degrees, (clockwise in the Y-upwards GL
// coordinate space).
//
// If a sub-window already exists, this function updates the subwindow
// and framebuffer properties to match the given values.
//
// Return true on success, false otherwise.
//
// NOTE: This can return false for software-only EGL engines like OSMesa.
bool setupSubWindow(FBNativeWindowType p_window,
int wx, int wy,
int ww, int wh,
int fbw, int fbh, float dpr, float zRot);
// Remove the sub-window created by setupSubWindow(), if any.
// Return true on success, false otherwise.
bool removeSubWindow();
// Finalize the instance.
void finalize();
// Return a pointer to the global instance. initialize() must be called
// previously, or this will return NULL.
static FrameBuffer *getFB() { return s_theFrameBuffer; }
// Return the capabilities of the underlying display.
const FrameBufferCaps &getCaps() const { return m_caps; }
// Return the emulated GPU display width in pixels.
int getWidth() const { return m_framebufferWidth; }
// Return the emulated GPU display height in pixels.
int getHeight() const { return m_framebufferHeight; }
// Return the list of configs available from this display.
const FbConfigList* getConfigs() const { return m_configs; }
// Set a callback that will be called each time the emulated GPU content
// is updated. This can be relatively slow with host-based GPU emulation,
// so only do this when you need to.
void setPostCallback(OnPostFn onPost, void* onPostContext);
// Retrieve the GL strings of the underlying EGL/GLES implementation.
// On return, |*vendor|, |*renderer| and |*version| will point to strings
// that are owned by the instance (and must not be freed by the caller).
void getGLStrings(const char** vendor,
const char** renderer,
const char** version) const {
*vendor = m_glVendor;
*renderer = m_glRenderer;
*version = m_glVersion;
}
// Create a new RenderContext instance for this display instance.
// |p_config| is the index of one of the configs returned by getConfigs().
// |p_share| is either EGL_NO_CONTEXT or the handle of a shared context.
// |p_isGL2| is true to create a GLES 2.x context, or false for a GLES 1.x
// one.
// Return a new handle value, which will be 0 in case of error.
HandleType createRenderContext(
int p_config, HandleType p_share, bool p_isGL2 = false);
// Create a new WindowSurface instance from this display instance.
// |p_config| is the index of one of the configs returned by getConfigs().
// |p_width| and |p_height| are the window dimensions in pixels.
// Return a new handle value, or 0 in case of error.
HandleType createWindowSurface(int p_config, int p_width, int p_height);
// Create a new ColorBuffer instance from this display instance.
// |p_width| and |p_height| are its dimensions in pixels.
// |p_internalFormat| is the pixel format. See ColorBuffer::create() for
// list of valid values. Note that ColorBuffer instances are reference-
// counted. Use openColorBuffer / closeColorBuffer to operate on the
// internal count.
HandleType createColorBuffer(
int p_width, int p_height, GLenum p_internalFormat);
// Call this function when a render thread terminates to destroy all
// the remaining contexts it created. Necessary to avoid leaking host
// contexts when a guest application crashes, for example.
void drainRenderContext();
// Call this function when a render thread terminates to destroy all
// remaining window surfqce it created. Necessary to avoid leaking
// host buffers when a guest application crashes, for example.
void drainWindowSurface();
// Destroy a given RenderContext instance. |p_context| is its handle
// value as returned by createRenderContext().
void DestroyRenderContext(HandleType p_context);
// Destroy a given WindowSurface instance. |p_surcace| is its handle
// value as returned by createWindowSurface().
void DestroyWindowSurface(HandleType p_surface);
// Increment the reference count associated with a given ColorBuffer
// instance. |p_colorbuffer| is its handle value as returned by
// createColorBuffer().
int openColorBuffer(HandleType p_colorbuffer);
// Decrement the reference count associated with a given ColorBuffer
// instance. |p_colorbuffer| is its handle value as returned by
// createColorBuffer(). Note that if the reference count reaches 0,
// the instance is destroyed automatically.
void closeColorBuffer(HandleType p_colorbuffer);
// Equivalent for eglMakeCurrent() for the current display.
// |p_context|, |p_drawSurface| and |p_readSurface| are the handle values
// of the context, the draw surface and the read surface, respectively.
// Returns true on success, false on failure.
// Note: if all handle values are 0, this is an unbind operation.
bool bindContext(HandleType p_context,
HandleType p_drawSurface,
HandleType p_readSurface);
// Attach a ColorBuffer to a WindowSurface instance.
// See the documentation for WindowSurface::setColorBuffer().
// |p_surface| is the target WindowSurface's handle value.
// |p_colorbuffer| is the ColorBuffer handle value.
// Returns true on success, false otherwise.
bool setWindowSurfaceColorBuffer(
HandleType p_surface, HandleType p_colorbuffer);
// Copy the content of a WindowSurface's Pbuffer to its attached
// ColorBuffer. See the documentation for WindowSurface::flushColorBuffer()
// |p_surface| is the target WindowSurface's handle value.
// Returns true on success, false on failure.
bool flushWindowSurfaceColorBuffer(HandleType p_surface);
// Bind the current context's EGL_TEXTURE_2D texture to a ColorBuffer
// instance's EGLImage. This is intended to implement
// glEGLImageTargetTexture2DOES() for all GLES versions.
// |p_colorbuffer| is the ColorBuffer's handle value.
// Returns true on success, false on failure.
bool bindColorBufferToTexture(HandleType p_colorbuffer);
// Bind the current context's EGL_RENDERBUFFER_OES render buffer to this
// ColorBuffer's EGLImage. This is intended to implement
// glEGLImageTargetRenderbufferStorageOES() for all GLES versions.
// |p_colorbuffer| is the ColorBuffer's handle value.
// Returns true on success, false on failure.
bool bindColorBufferToRenderbuffer(HandleType p_colorbuffer);
// Read the content of a given ColorBuffer into client memory.
// |p_colorbuffer| is the ColorBuffer's handle value. Similar
// to glReadPixels(), this can be a slow operation.
// |x|, |y|, |width| and |height| are the position and dimensions of
// a rectangle whose pixel values will be transfered to the host.
// |format| indicates the format of the pixel data, e.g. GL_RGB or GL_RGBA.
// |type| is the type of pixel data, e.g. GL_UNSIGNED_BYTE.
// |pixels| is the address of a caller-provided buffer that will be filled
// with the pixel data.
void readColorBuffer(HandleType p_colorbuffer,
int x, int y, int width, int height,
GLenum format, GLenum type, void *pixels);
// Update the content of a given ColorBuffer from client data.
// |p_colorbuffer| is the ColorBuffer's handle value. Similar
// to glReadPixels(), this can be a slow operation.
// |x|, |y|, |width| and |height| are the position and dimensions of
// a rectangle whose pixel values will be transfered to the GPU
// |format| indicates the format of the pixel data, e.g. GL_RGB or GL_RGBA.
// |type| is the type of pixel data, e.g. GL_UNSIGNED_BYTE.
// |pixels| is the address of a buffer containing the new pixel data.
// Returns true on success, false otherwise.
bool updateColorBuffer(HandleType p_colorbuffer,
int x, int y, int width, int height,
GLenum format, GLenum type, void *pixels);
// Display the content of a given ColorBuffer into the framebuffer's
// sub-window. |p_colorbuffer| is a handle value.
// |needLock| is used to indicate whether the operation requires
// acquiring/releasing the FrameBuffer instance's lock. It should be
// false only when called internally.
bool post(HandleType p_colorbuffer, bool needLock = true);
// Re-post the last ColorBuffer that was displayed through post().
// This is useful if you detect that the sub-window content needs to
// be re-displayed for any reason.
bool repost();
// Return the host EGLDisplay used by this instance.
EGLDisplay getDisplay() const { return m_eglDisplay; }
// Change the rotation of the displayed GPU sub-window.
void setDisplayRotation(float zRot) {
m_zRot = zRot;
repost();
}
// Changes what coordinate of this framebuffer will be displayed at the
// corner of the GPU sub-window. Specifically, |px| and |py| = 0 means
// align the bottom-left of the framebuffer with the bottom-left of the
// sub-window, and |px| and |py| = 1 means align the top right of the
// framebuffer with the top right of the sub-window. Intermediate values
// interpolate between these states.
void setDisplayTranslation(float px, float py) {
// Sanity check the values to ensure they are between 0 and 1
m_px = px > 1 ? 1 : (px < 0 ? 0 : px);
m_py = py > 1 ? 1 : (py < 0 ? 0 : py);
repost();
}
// Return a TextureDraw instance that can be used with this surfaces
// and windows created by this instance.
TextureDraw* getTextureDraw() const { return m_textureDraw; }
HandleType createClientImage(HandleType context, EGLenum target, GLuint buffer);
EGLBoolean destroyClientImage(HandleType image);
// Used internally.
bool bind_locked();
bool unbind_locked();
private:
FrameBuffer(int p_width, int p_height, bool useSubWindow);
~FrameBuffer();
HandleType genHandle();
bool bindSubwin_locked();
private:
static FrameBuffer *s_theFrameBuffer;
static HandleType s_nextHandle;
int m_x;
int m_y;
int m_framebufferWidth;
int m_framebufferHeight;
int m_windowWidth;
int m_windowHeight;
float m_dpr;
bool m_useSubWindow;
emugl::Mutex m_lock;
FbConfigList* m_configs;
FBNativeWindowType m_nativeWindow;
FrameBufferCaps m_caps;
EGLDisplay m_eglDisplay;
RenderContextMap m_contexts;
WindowSurfaceMap m_windows;
ColorBufferMap m_colorbuffers;
ColorBuffer::Helper* m_colorBufferHelper;
EGLSurface m_eglSurface;
EGLContext m_eglContext;
EGLSurface m_pbufSurface;
EGLContext m_pbufContext;
EGLContext m_prevContext;
EGLSurface m_prevReadSurf;
EGLSurface m_prevDrawSurf;
EGLNativeWindowType m_subWin;
TextureDraw* m_textureDraw;
EGLConfig m_eglConfig;
HandleType m_lastPostedColorBuffer;
float m_zRot;
float m_px;
float m_py;
bool m_eglContextInitialized;
int m_statsNumFrames;
long long m_statsStartTime;
bool m_fpsStats;
OnPostFn m_onPost;
void* m_onPostContext;
unsigned char* m_fbImage;
const char* m_glVendor;
const char* m_glRenderer;
const char* m_glVersion;
};
#endif

View file

@ -0,0 +1,70 @@
/*
* Copyright (C) 2011 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 NATIVE_SUB_WINDOW_H
#define NATIVE_SUB_WINDOW_H
#include "OpenglRender/render_api_platform_types.h"
#include <EGL/egl.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*SubWindowRepaintCallback)(void*);
// Create a new sub-window that will be used to display the content of the
// emulated GPU on top of the regular UI window.
// |p_window| is the platform-specific handle to the main UI window.
// |x|, |y| is the position sub-window relative to the top-left corner of the
// main window.
// |width| and |height| are the dimensions of the sub-window, as well as of
// the emulated framebuffer.
// |repaint_callback| may be invoked every time the window has to be repainted
// (such as receiving a WM_PAINT event on Windows). If the provided argument is
// NULL, nothing will be invoked.
// |repaint_callback_param| an additional parameter that will be passed to the
// repaint callback when/if it's invoked.
// On success, return a new platform-specific window handle, cast as an
// EGLNativeWindowType. Or 0/NULL in case of failure.
EGLNativeWindowType createSubWindow(FBNativeWindowType p_window,
int x,
int y,
int width,
int height,
SubWindowRepaintCallback repaint_callback,
void* repaint_callback_param);
// Destroy a sub-window previously created through createSubWindow() above.
void destroySubWindow(EGLNativeWindowType win);
// Moves a sub-window previously created through createSubWindow() above.
// |p_parent_window| is the platform-specific handle to the main UI window.
// |p_sub_window| is the platform-specific handle to the EGL subwindow.
// |x|,|y|,|width|,|height| are the new location and dimensions of the
// subwindow.
int moveSubWindow(FBNativeWindowType p_parent_window,
EGLNativeWindowType p_sub_window,
int x,
int y,
int width,
int height);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,101 @@
/*
* Copyright (C) 2011 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.
*/
#include "NativeSubWindow.h"
#include <Cocoa/Cocoa.h>
/*
* EmuGLView inherit from NSView and override the isOpaque
* method to return YES. That prevents drawing of underlying window/view
* when the view needs to be redrawn.
*/
@interface EmuGLView : NSView {
} @end
@implementation EmuGLView
- (BOOL)isOpaque {
return YES;
}
@end
EGLNativeWindowType createSubWindow(FBNativeWindowType p_window,
int x,
int y,
int width,
int height,
SubWindowRepaintCallback repaint_callback,
void* repaint_callback_param) {
NSWindow* win = (NSWindow *)p_window;
if (!win) {
return NULL;
}
/* (x,y) assume an upper-left origin, but Cocoa uses a lower-left origin */
NSRect content_rect = [win contentRectForFrameRect:[win frame]];
int cocoa_y = (int)content_rect.size.height - (y + height);
NSRect contentRect = NSMakeRect(x, cocoa_y, width, height);
NSView *glView = [[EmuGLView alloc] initWithFrame:contentRect];
if (glView) {
[glView setWantsBestResolutionOpenGLSurface:YES];
[[win contentView] addSubview:glView];
[win makeKeyAndOrderFront:nil];
}
return (EGLNativeWindowType)glView;
}
void destroySubWindow(EGLNativeWindowType win) {
if(win){
NSView *glView = (NSView *)win;
[glView removeFromSuperview];
[glView release];
}
}
int moveSubWindow(FBNativeWindowType p_parent_window,
EGLNativeWindowType p_sub_window,
int x,
int y,
int width,
int height) {
NSWindow *win = (NSWindow *)p_parent_window;
if (!win) {
return 0;
}
NSView *glView = (NSView *)p_sub_window;
if (!glView) {
return 0;
}
/* The view must be removed from the hierarchy to be properly resized */
[glView removeFromSuperview];
/* (x,y) assume an upper-left origin, but Cocoa uses a lower-left origin */
NSRect content_rect = [win contentRectForFrameRect:[win frame]];
int cocoa_y = (int)content_rect.size.height - (y + height);
NSRect newFrame = NSMakeRect(x, cocoa_y, width, height);
[glView setFrame:newFrame];
/* Re-add the sub-window to the view hierarchy */
[[win contentView] addSubview:glView];
[win makeKeyAndOrderFront:nil];
return 1;
}

View file

@ -0,0 +1,201 @@
/*
* Copyright (C) 2011 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 MIR_EGL_PLATFORM
#include "mir_support/shared_state.h"
#include "NativeSubWindow.h"
#include "OpenglCodecCommon/ErrorLog.h"
#include <stdexcept>
#include <string>
#include <map>
#include <mirclient/mir_toolkit/mir_client_library.h>
namespace {
std::map<EGLNativeWindowType,MirSurface*> surfaces;
static const MirDisplayOutput *find_active_output(
const MirDisplayConfiguration *conf)
{
const MirDisplayOutput *output = NULL;
int d;
for (d = 0; d < (int)conf->num_outputs; d++)
{
const MirDisplayOutput *out = conf->outputs + d;
if (out->used &&
out->connected &&
out->num_modes &&
out->current_mode < out->num_modes)
{
output = out;
break;
}
}
return output;
}
}
MirPixelFormat defaultPixelFormatFor(MirConnection *connection)
{
MirPixelFormat format;
unsigned int nformats;
mir_connection_get_available_surface_formats(connection, &format, 1, &nformats);
return format;
}
void handleSurfaceEvent(const MirSurfaceEvent *event) {
MirSurfaceAttrib attrib = mir_surface_event_get_attribute(event);
if (attrib != mir_surface_attrib_visibility)
return;
switch (mir_surface_event_get_attribute(event)) {
case mir_surface_visibility_exposed:
ERR("Surface exposed\n");
break;
case mir_surface_visibility_occluded:
ERR("Surface occluded\n");
break;
default:
break;
}
}
void surfaceEventHandler(MirSurface *surface, MirEvent const *event, void *context) {
switch (mir_event_get_type(event)) {
case mir_event_type_input:
break;
case mir_event_type_surface:
handleSurfaceEvent(mir_event_get_surface_event(event));
break;
case mir_event_type_resize:
{
MirResizeEvent const *resize_event = mir_event_get_resize_event(event);
ERR("Resized to %dx%d\n",
mir_resize_event_get_width(resize_event),
mir_resize_event_get_height(resize_event));
}
break;
case mir_event_type_close_surface:
break;
case mir_event_type_surface_output:
{
MirSurfaceOutputEvent const *output_event = mir_event_get_surface_output_event(event);
ERR("dpi %i output id %d\n",
mir_surface_output_event_get_dpi(output_event),
mir_surface_output_event_get_output_id(output_event));
}
break;
default:
break;
}
}
EGLNativeWindowType createSubWindow(FBNativeWindowType p_window,
int x,
int y,
int width,
int height,
SubWindowRepaintCallback repaint_callback,
void* repaint_callback_param) {
(void) p_window;
(void) x;
(void) y;
(void) width;
(void) height;
(void) repaint_callback;
(void) repaint_callback_param;
mir::support::SharedState::get()->ensure_connection();
const auto pixel_format = defaultPixelFormatFor(mir::support::SharedState::get()->connection());
ERR("Selected pixel format %d\n", pixel_format);
MirDisplayConfiguration* display_config =
mir_connection_create_display_config(mir::support::SharedState::get()->connection());
const MirDisplayOutput *output = find_active_output(display_config);
if (!output)
throw new std::runtime_error("Failed to find active output display");
const MirDisplayMode *mode = &output->modes[output->current_mode];
auto spec = mir_connection_create_spec_for_normal_surface(
mir::support::SharedState::get()->connection(),
mode->vertical_resolution,
mode->horizontal_resolution,
pixel_format);
mir_surface_spec_set_name(spec, "anbox");
mir_surface_spec_set_event_handler(spec, surfaceEventHandler, nullptr);
ERR("Selecting output id %d", output->output_id);
mir_surface_spec_set_fullscreen_on_output(spec, output->output_id);
mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_hardware);
mir_display_config_destroy(display_config);
auto surface = mir_surface_create_sync(spec);
mir_surface_spec_release(spec);
if (!mir_surface_is_valid(surface)) {
ERR("surface error: %s\n", mir_surface_get_error_message(surface));
throw std::runtime_error("Failed to create a Mir surface");
}
MirSurfaceParameters parameters;
mir_surface_get_parameters(surface, &parameters);
ERR("width %i height %i output id %i\n",
parameters.width,
parameters.height,
parameters.output_id);
auto surface_buffer_stream = mir_surface_get_buffer_stream(surface);
auto native_window = reinterpret_cast<EGLNativeWindowType>(
mir_buffer_stream_get_egl_native_window(surface_buffer_stream));
surfaces.insert({ native_window, surface });
return native_window;
}
void destroySubWindow(EGLNativeWindowType win) {
if (surfaces.find(win) == surfaces.end())
return;
auto surface = surfaces[win];
surfaces.erase(win);
mir_surface_release_sync(surface);
}
int moveSubWindow(FBNativeWindowType p_parent_window,
EGLNativeWindowType p_sub_window,
int x,
int y,
int width,
int height) {
ERR("%s: Not implemented", __func__);
return true;
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (C) 2011 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.
*/
#include "NativeSubWindow.h"
EGLNativeWindowType createSubWindow(FBNativeWindowType p_window,
int x,
int y,
int width,
int height,
SubWindowRepaintCallback repaint_callback,
void* repaint_callback_param) {
return NULL;
}
void destroySubWindow(EGLNativeWindowType win) {
}
int moveSubWindow(FBNativeWindowType p_parent_window,
EGLNativeWindowType p_sub_window,
int x,
int y,
int width,
int height) {
return false;
}

View file

@ -0,0 +1,90 @@
/*
* Copyright (C) 2011 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.
*/
#include "NativeSubWindow.h"
struct SubWindowUserData {
SubWindowRepaintCallback repaint_callback;
void* repaint_callback_param;
};
static LRESULT CALLBACK subWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
if (uMsg == WM_PAINT) {
auto user_data =
(SubWindowUserData*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if (user_data && user_data->repaint_callback) {
user_data->repaint_callback(user_data->repaint_callback_param);
}
} else if (uMsg == WM_NCDESTROY) {
SubWindowUserData* user_data =
(SubWindowUserData*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
delete user_data;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
EGLNativeWindowType createSubWindow(FBNativeWindowType p_window,
int x, int y,int width, int height,
SubWindowRepaintCallback repaint_callback,
void* repaint_callback_param){
static const char className[] = "subWin";
WNDCLASS wc = {};
if (!GetClassInfo(GetModuleHandle(NULL), className, &wc)) {
wc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;// redraw if size changes
wc.lpfnWndProc = &subWindowProc; // points to window procedure
wc.cbWndExtra = sizeof(void*) ; // save extra window memory
wc.lpszClassName = className; // name of window class
RegisterClass(&wc);
}
EGLNativeWindowType ret = CreateWindowEx(
WS_EX_NOPARENTNOTIFY, // do not bother our parent window
className,
"sub",
WS_CHILD|WS_DISABLED,
x,y,width,height,
p_window,
NULL,
NULL,
NULL);
auto user_data = new SubWindowUserData();
user_data->repaint_callback = repaint_callback;
user_data->repaint_callback_param = repaint_callback_param;
SetWindowLongPtr(ret, GWLP_USERDATA, (LONG_PTR)user_data);
ShowWindow(ret, SW_SHOW);
return ret;
}
void destroySubWindow(EGLNativeWindowType win){
PostMessage(win, WM_CLOSE, 0, 0);
}
int moveSubWindow(FBNativeWindowType p_parent_window,
EGLNativeWindowType p_sub_window,
int x,
int y,
int width,
int height) {
BOOL ret = MoveWindow(p_sub_window,
x,
y,
width,
height,
TRUE);
return ret;
}

View file

@ -0,0 +1,105 @@
/*
* Copyright (C) 2011 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.
*/
#include "NativeSubWindow.h"
#include <stdio.h>
static Bool WaitForMapNotify(Display *d, XEvent *e, char *arg) {
if (e->type == MapNotify && e->xmap.window == (Window)arg) {
return 1;
}
return 0;
}
static Bool WaitForConfigureNotify(Display *d, XEvent *e, char *arg) {
if (e->type == ConfigureNotify && e->xmap.window == (Window)arg) {
return 1;
}
return 0;
}
static Display *s_display = NULL;
EGLNativeWindowType createSubWindow(FBNativeWindowType p_window,
int x,
int y,
int width,
int height,
SubWindowRepaintCallback repaint_callback,
void* repaint_callback_param) {
// The call to this function is protected by a lock
// in FrameBuffer so it is safe to check and initialize s_display here
if (!s_display) {
s_display = XOpenDisplay(NULL);
}
XSetWindowAttributes wa;
wa.event_mask = StructureNotifyMask;
wa.override_redirect = True;
Window win = XCreateWindow(s_display,
p_window,
x,
y,
width,
height,
0,
CopyFromParent,
CopyFromParent,
CopyFromParent,
CWEventMask,
&wa);
XMapWindow(s_display,win);
XSetWindowBackground(s_display, win, BlackPixel(s_display, 0));
XEvent e;
XIfEvent(s_display, &e, WaitForMapNotify, (char *)win);
return win;
}
void destroySubWindow(EGLNativeWindowType win) {
if (!s_display) {
return;
}
XDestroyWindow(s_display, win);
}
int moveSubWindow(FBNativeWindowType p_parent_window,
EGLNativeWindowType p_sub_window,
int x,
int y,
int width,
int height) {
// This value is set during create, so if it is still null, simply
// return because the global state is corrupted
if (!s_display) {
return false;
}
// This prevents flicker on resize.
XSetWindowBackgroundPixmap(s_display, p_sub_window, None);
int ret = XMoveResizeWindow(
s_display,
p_sub_window,
x,
y,
width,
height);
XEvent e;
XIfEvent(s_display, &e, WaitForConfigureNotify, (char *)p_sub_window);
return ret;
}

View file

@ -0,0 +1,74 @@
/*
* Copyright (C) 2011 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.
*/
#include "ReadBuffer.h"
#include <string.h>
#include <assert.h>
#include <limits.h>
#include "ErrorLog.h"
ReadBuffer::ReadBuffer(size_t bufsize)
{
m_size = bufsize;
m_buf = (unsigned char*)malloc(m_size*sizeof(unsigned char));
m_validData = 0;
m_readPtr = m_buf;
}
ReadBuffer::~ReadBuffer()
{
free(m_buf);
}
int ReadBuffer::getData(IOStream *stream)
{
if(stream == NULL)
return -1;
if ((m_validData > 0) && (m_readPtr > m_buf)) {
memmove(m_buf, m_readPtr, m_validData);
}
// get fresh data into the buffer;
size_t len = m_size - m_validData;
if (len==0) {
//we need to inc our buffer
size_t new_size = m_size*2;
unsigned char* new_buf;
if (new_size < m_size) { // overflow check
new_size = INT_MAX;
}
new_buf = (unsigned char*)realloc(m_buf, new_size);
if (!new_buf) {
ERR("Failed to alloc %zu bytes for ReadBuffer\n", new_size);
return -1;
}
m_size = new_size;
m_buf = new_buf;
len = m_size - m_validData;
}
m_readPtr = m_buf;
if (NULL != stream->read(m_buf + m_validData, &len)) {
m_validData += len;
return len;
}
return -1;
}
void ReadBuffer::consume(size_t amount)
{
assert(amount <= m_validData);
m_validData -= amount;
m_readPtr += amount;
}

View file

@ -0,0 +1,35 @@
/*
* Copyright (C) 2011 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 _READ_BUFFER_H
#define _READ_BUFFER_H
#include "IOStream.h"
class ReadBuffer {
public:
ReadBuffer(size_t bufSize);
~ReadBuffer();
int getData(IOStream *stream); // get fresh data from the stream
unsigned char *buf() { return m_readPtr; } // return the next read location
size_t validData() { return m_validData; } // return the amount of valid data in readptr
void consume(size_t amount); // notify that 'amount' data has been consumed;
private:
unsigned char *m_buf;
unsigned char *m_readPtr;
size_t m_size;
size_t m_validData;
};
#endif

View file

@ -0,0 +1,49 @@
/*
* Copyright (C) 2011 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.
*/
#include "RenderContext.h"
#include "OpenGLESDispatch/EGLDispatch.h"
RenderContext* RenderContext::create(EGLDisplay display,
EGLConfig config,
EGLContext sharedContext,
bool isGl2) {
const EGLint contextAttribs[] = {
EGL_CONTEXT_CLIENT_VERSION, isGl2 ? 2 : 1,
EGL_NONE
};
EGLContext context = s_egl.eglCreateContext(
display, config, sharedContext, contextAttribs);
if (context == EGL_NO_CONTEXT) {
return NULL;
}
return new RenderContext(display, context, isGl2);
}
RenderContext::RenderContext(EGLDisplay display,
EGLContext context,
bool isGl2) :
mDisplay(display),
mContext(context),
mIsGl2(isGl2),
mContextData() {}
RenderContext::~RenderContext() {
if (mContext != EGL_NO_CONTEXT) {
s_egl.eglDestroyContext(mDisplay, mContext);
}
}

View file

@ -0,0 +1,69 @@
/*
* Copyright (C) 2011 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 _LIBRENDER_RENDER_CONTEXT_H
#define _LIBRENDER_RENDER_CONTEXT_H
#include "emugl/common/smart_ptr.h"
#include "GLDecoderContextData.h"
#include <EGL/egl.h>
// A class used to model a guest EGLContext. This simply wraps a host
// EGLContext, associated with an GLDecoderContextData instance that is
// used to store copies of guest-side arrays.
class RenderContext {
public:
// Create a new RenderContext instance.
// |display| is the host EGLDisplay handle.
// |config| is the host EGLConfig to use.
// |sharedContext| is either EGL_NO_CONTEXT of a host EGLContext handle.
// |isGl2| is true iff the new context will be used with GLESv2, or
// GLESv1 otherwise.
static RenderContext *create(EGLDisplay display,
EGLConfig config,
EGLContext sharedContext,
bool isGL2 = false);
// Destructor.
~RenderContext();
// Retrieve host EGLContext value.
EGLContext getEGLContext() const { return mContext; }
// Return true iff this is a GLESv2 context.
bool isGL2() const { return mIsGl2; }
// Retrieve GLDecoderContextData instance reference for this
// RenderContext instance.
GLDecoderContextData& decoderContextData() { return mContextData; }
private:
RenderContext();
RenderContext(EGLDisplay display,
EGLContext context,
bool isGl2);
private:
EGLDisplay mDisplay;
EGLContext mContext;
bool mIsGl2;
GLDecoderContextData mContextData;
};
typedef emugl::SmartPtr<RenderContext> RenderContextPtr;
#endif // _LIBRENDER_RENDER_CONTEXT_H

View file

@ -0,0 +1,417 @@
/*
* Copyright (C) 2011 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.
*/
#include "RenderControl.h"
#include "DispatchTables.h"
#include "FbConfig.h"
#include "FrameBuffer.h"
#include "RenderThreadInfo.h"
#include "ChecksumCalculatorThreadInfo.h"
#include "OpenGLESDispatch/EGLDispatch.h"
static const GLint rendererVersion = 1;
static GLint rcGetRendererVersion()
{
return rendererVersion;
}
static EGLint rcGetEGLVersion(EGLint* major, EGLint* minor)
{
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb) {
return EGL_FALSE;
}
*major = (EGLint)fb->getCaps().eglMajor;
*minor = (EGLint)fb->getCaps().eglMinor;
return EGL_TRUE;
}
static EGLint rcQueryEGLString(EGLenum name, void* buffer, EGLint bufferSize)
{
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb) {
return 0;
}
const char *str = s_egl.eglQueryString(fb->getDisplay(), name);
if (!str) {
return 0;
}
int len = strlen(str) + 1;
if (!buffer || len > bufferSize) {
return -len;
}
strcpy((char *)buffer, str);
return len;
}
static EGLint rcGetGLString(EGLenum name, void* buffer, EGLint bufferSize)
{
RenderThreadInfo *tInfo = RenderThreadInfo::get();
const char *str = NULL;
int len = 0;
if (tInfo && tInfo->currContext.Ptr()) {
if (tInfo->currContext->isGL2()) {
str = (const char *)s_gles2.glGetString(name);
}
else {
str = (const char *)s_gles1.glGetString(name);
}
if (str) {
len = strlen(str) + 1;
}
}
// We add the maximum supported GL protocol number into GL_EXTENSIONS
const char* glProtocolStr = NULL;
if (name == GL_EXTENSIONS) {
glProtocolStr = ChecksumCalculatorThreadInfo::getMaxVersionString();
if (len==0) len = 1; // the last byte
len += strlen(glProtocolStr) + 1;
}
if (!buffer || len > bufferSize) {
return -len;
}
if (name == GL_EXTENSIONS) {
snprintf((char *)buffer, bufferSize, "%s%s ", str ? str : "", glProtocolStr);
} else if (str) {
strcpy((char *)buffer, str);
} else {
if (bufferSize >= 1) {
((char*)buffer)[0] = '\0';
}
len = 0;
}
return len;
}
static EGLint rcGetNumConfigs(uint32_t* p_numAttribs)
{
int numConfigs = 0, numAttribs = 0;
FrameBuffer::getFB()->getConfigs()->getPackInfo(&numConfigs, &numAttribs);
if (p_numAttribs) {
*p_numAttribs = static_cast<uint32_t>(numAttribs);
}
return numConfigs;
}
static EGLint rcGetConfigs(uint32_t bufSize, GLuint* buffer)
{
GLuint bufferSize = (GLuint)bufSize;
return FrameBuffer::getFB()->getConfigs()->packConfigs(bufferSize, buffer);
}
static EGLint rcChooseConfig(EGLint *attribs,
uint32_t attribs_size,
uint32_t *configs,
uint32_t configs_size)
{
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb || attribs_size==0) {
return 0;
}
return fb->getConfigs()->chooseConfig(
attribs, (EGLint*)configs, (EGLint)configs_size);
}
static EGLint rcGetFBParam(EGLint param)
{
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb) {
return 0;
}
EGLint ret = 0;
switch(param) {
case FB_WIDTH:
ret = fb->getWidth();
break;
case FB_HEIGHT:
ret = fb->getHeight();
break;
case FB_XDPI:
ret = 72; // XXX: should be implemented
break;
case FB_YDPI:
ret = 72; // XXX: should be implemented
break;
case FB_FPS:
ret = 60;
break;
case FB_MIN_SWAP_INTERVAL:
ret = 1; // XXX: should be implemented
break;
case FB_MAX_SWAP_INTERVAL:
ret = 1; // XXX: should be implemented
break;
default:
break;
}
return ret;
}
static uint32_t rcCreateContext(uint32_t config,
uint32_t share, uint32_t glVersion)
{
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb) {
return 0;
}
// To make it consistent with the guest, create GLES2 context when GL
// version==2 or 3
HandleType ret = fb->createRenderContext(config, share, glVersion == 2 || glVersion == 3);
return ret;
}
static void rcDestroyContext(uint32_t context)
{
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb) {
return;
}
fb->DestroyRenderContext(context);
}
static uint32_t rcCreateWindowSurface(uint32_t config,
uint32_t width, uint32_t height)
{
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb) {
return 0;
}
return fb->createWindowSurface(config, width, height);
}
static void rcDestroyWindowSurface(uint32_t windowSurface)
{
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb) {
return;
}
fb->DestroyWindowSurface( windowSurface );
}
static uint32_t rcCreateColorBuffer(uint32_t width,
uint32_t height, GLenum internalFormat)
{
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb) {
return 0;
}
return fb->createColorBuffer(width, height, internalFormat);
}
static int rcOpenColorBuffer2(uint32_t colorbuffer)
{
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb) {
return -1;
}
return fb->openColorBuffer( colorbuffer );
}
// Deprecated, kept for compatibility with old system images only.
// Use rcOpenColorBuffer2 instead.
static void rcOpenColorBuffer(uint32_t colorbuffer)
{
(void) rcOpenColorBuffer2(colorbuffer);
}
static void rcCloseColorBuffer(uint32_t colorbuffer)
{
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb) {
return;
}
fb->closeColorBuffer( colorbuffer );
}
static int rcFlushWindowColorBuffer(uint32_t windowSurface)
{
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb) {
return -1;
}
if (!fb->flushWindowSurfaceColorBuffer(windowSurface)) {
return -1;
}
return 0;
}
static void rcSetWindowColorBuffer(uint32_t windowSurface,
uint32_t colorBuffer)
{
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb) {
return;
}
fb->setWindowSurfaceColorBuffer(windowSurface, colorBuffer);
}
static EGLint rcMakeCurrent(uint32_t context,
uint32_t drawSurf, uint32_t readSurf)
{
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb) {
return EGL_FALSE;
}
bool ret = fb->bindContext(context, drawSurf, readSurf);
return (ret ? EGL_TRUE : EGL_FALSE);
}
static void rcFBPost(uint32_t colorBuffer)
{
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb) {
return;
}
fb->post(colorBuffer);
}
static void rcFBSetSwapInterval(EGLint interval)
{
// XXX: TBD - should be implemented
}
static void rcBindTexture(uint32_t colorBuffer)
{
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb) {
return;
}
fb->bindColorBufferToTexture(colorBuffer);
}
static void rcBindRenderbuffer(uint32_t colorBuffer)
{
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb) {
return;
}
fb->bindColorBufferToRenderbuffer(colorBuffer);
}
static EGLint rcColorBufferCacheFlush(uint32_t colorBuffer,
EGLint postCount, int forRead)
{
// XXX: TBD - should be implemented
return 0;
}
static void rcReadColorBuffer(uint32_t colorBuffer,
GLint x, GLint y,
GLint width, GLint height,
GLenum format, GLenum type, void* pixels)
{
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb) {
return;
}
fb->readColorBuffer(colorBuffer, x, y, width, height, format, type, pixels);
}
static int rcUpdateColorBuffer(uint32_t colorBuffer,
GLint x, GLint y,
GLint width, GLint height,
GLenum format, GLenum type, void* pixels)
{
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb) {
return -1;
}
fb->updateColorBuffer(colorBuffer, x, y, width, height, format, type, pixels);
return 0;
}
static uint32_t rcCreateClientImage(uint32_t context, EGLenum target, GLuint buffer)
{
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb) {
return 0;
}
return fb->createClientImage(context, target, buffer);
}
static int rcDestroyClientImage(uint32_t image)
{
FrameBuffer *fb = FrameBuffer::getFB();
if (!fb) {
return 0;
}
return fb->destroyClientImage(image);
}
static void rcSelectChecksumCalculator(uint32_t protocol, uint32_t reserved) {
ChecksumCalculatorThreadInfo::setVersion(protocol);
}
void initRenderControlContext(renderControl_decoder_context_t *dec)
{
dec->rcGetRendererVersion = rcGetRendererVersion;
dec->rcGetEGLVersion = rcGetEGLVersion;
dec->rcQueryEGLString = rcQueryEGLString;
dec->rcGetGLString = rcGetGLString;
dec->rcGetNumConfigs = rcGetNumConfigs;
dec->rcGetConfigs = rcGetConfigs;
dec->rcChooseConfig = rcChooseConfig;
dec->rcGetFBParam = rcGetFBParam;
dec->rcCreateContext = rcCreateContext;
dec->rcDestroyContext = rcDestroyContext;
dec->rcCreateWindowSurface = rcCreateWindowSurface;
dec->rcDestroyWindowSurface = rcDestroyWindowSurface;
dec->rcCreateColorBuffer = rcCreateColorBuffer;
dec->rcOpenColorBuffer = rcOpenColorBuffer;
dec->rcCloseColorBuffer = rcCloseColorBuffer;
dec->rcSetWindowColorBuffer = rcSetWindowColorBuffer;
dec->rcFlushWindowColorBuffer = rcFlushWindowColorBuffer;
dec->rcMakeCurrent = rcMakeCurrent;
dec->rcFBPost = rcFBPost;
dec->rcFBSetSwapInterval = rcFBSetSwapInterval;
dec->rcBindTexture = rcBindTexture;
dec->rcBindRenderbuffer = rcBindRenderbuffer;
dec->rcColorBufferCacheFlush = rcColorBufferCacheFlush;
dec->rcReadColorBuffer = rcReadColorBuffer;
dec->rcUpdateColorBuffer = rcUpdateColorBuffer;
dec->rcOpenColorBuffer2 = rcOpenColorBuffer2;
dec->rcCreateClientImage = rcCreateClientImage;
dec->rcDestroyClientImage = rcDestroyClientImage;
dec->rcSelectChecksumCalculator = rcSelectChecksumCalculator;
}

View file

@ -0,0 +1,23 @@
/*
* Copyright (C) 2011 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 _RENDER_CONTROL_H
#define _RENDER_CONTROL_H
#include "renderControl_dec.h"
void initRenderControlContext(renderControl_decoder_context_t *dec);
#endif

View file

@ -0,0 +1,170 @@
/*
* Copyright (C) 2011 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.
*/
#include "RenderServer.h"
#include "RenderThread.h"
#include "TcpStream.h"
#ifndef _WIN32
#include "UnixStream.h"
#include <signal.h>
#include <pthread.h>
#endif
#ifdef _WIN32
#include "Win32PipeStream.h"
#endif
#include "OpenglRender/render_api.h"
#include <set>
#include <string.h>
typedef std::set<RenderThread *> RenderThreadsSet;
RenderServer::RenderServer() :
m_lock(),
m_listenSock(NULL),
m_exiting(false)
{
}
RenderServer::~RenderServer()
{
delete m_listenSock;
}
extern "C" int gRendererStreamMode;
RenderServer *RenderServer::create(char* addr, size_t addrLen)
{
RenderServer *server = new RenderServer();
if (!server) {
return NULL;
}
if (gRendererStreamMode == RENDER_API_STREAM_MODE_TCP) {
server->m_listenSock = new TcpStream();
} else {
#ifdef _WIN32
server->m_listenSock = new Win32PipeStream();
#else
server->m_listenSock = new UnixStream();
#endif
}
char addrstr[SocketStream::MAX_ADDRSTR_LEN];
if (server->m_listenSock->listen(addrstr) < 0) {
ERR("RenderServer::create failed to listen\n");
delete server;
return NULL;
}
size_t len = strlen(addrstr) + 1;
if (len > addrLen) {
ERR("RenderServer address name too big for provided buffer: %zu > %zu\n",
len, addrLen);
delete server;
return NULL;
}
memcpy(addr, addrstr, len);
return server;
}
intptr_t RenderServer::main()
{
RenderThreadsSet threads;
#ifndef _WIN32
sigset_t set;
sigfillset(&set);
pthread_sigmask(SIG_SETMASK, &set, NULL);
#endif
while(1) {
SocketStream *stream = m_listenSock->accept();
if (!stream) {
fprintf(stderr,"Error accepting gles connection, ignoring.\n");
continue;
}
unsigned int clientFlags;
if (!stream->readFully(&clientFlags, sizeof(unsigned int))) {
fprintf(stderr,"Error reading clientFlags\n");
delete stream;
continue;
}
DBG("RenderServer: Got new stream!\n");
// check if we have been requested to exit while waiting on accept
if ((clientFlags & IOSTREAM_CLIENT_EXIT_SERVER) != 0) {
m_exiting = true;
delete stream;
break;
}
RenderThread *rt = RenderThread::create(stream, &m_lock);
if (!rt) {
fprintf(stderr,"Failed to create RenderThread\n");
delete stream;
} else if (!rt->start()) {
fprintf(stderr,"Failed to start RenderThread\n");
delete rt;
delete stream;
}
//
// remove from the threads list threads which are
// no longer running
//
for (RenderThreadsSet::iterator n,t = threads.begin();
t != threads.end();
t = n) {
// first find next iterator
n = t;
n++;
// delete and erase the current iterator
// if thread is no longer running
if ((*t)->isFinished()) {
delete (*t);
threads.erase(t);
}
}
// if the thread has been created and started, insert it to the list
if (rt) {
threads.insert(rt);
DBG("Started new RenderThread\n");
}
}
//
// Wait for all threads to finish
//
for (RenderThreadsSet::iterator t = threads.begin();
t != threads.end();
t++) {
(*t)->forceStop();
(*t)->wait(NULL);
delete (*t);
}
threads.clear();
return 0;
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (C) 2011 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 _LIB_OPENGL_RENDER_RENDER_SERVER_H
#define _LIB_OPENGL_RENDER_RENDER_SERVER_H
#include "SocketStream.h"
#include "emugl/common/mutex.h"
#include "emugl/common/thread.h"
class RenderServer : public emugl::Thread
{
public:
static RenderServer *create(char* addr, size_t addrLen);
virtual ~RenderServer();
virtual intptr_t main();
bool isExiting() const { return m_exiting; }
private:
RenderServer();
private:
emugl::Mutex m_lock;
SocketStream *m_listenSock;
bool m_exiting;
};
#endif

View file

@ -0,0 +1,165 @@
/*
* Copyright (C) 2011 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.
*/
#include "RenderThread.h"
#include "FrameBuffer.h"
#include "ReadBuffer.h"
#include "RenderControl.h"
#include "RenderThreadInfo.h"
#include "TimeUtils.h"
#include "OpenGLESDispatch/EGLDispatch.h"
#include "OpenGLESDispatch/GLESv2Dispatch.h"
#include "OpenGLESDispatch/GLESv1Dispatch.h"
#include "../../../shared/OpenglCodecCommon/ChecksumCalculatorThreadInfo.h"
#define STREAM_BUFFER_SIZE 4*1024*1024
RenderThread::RenderThread(IOStream *stream, emugl::Mutex *lock) :
emugl::Thread(),
m_lock(lock),
m_stream(stream) {}
RenderThread::~RenderThread() {
delete m_stream;
}
// static
RenderThread* RenderThread::create(IOStream *stream, emugl::Mutex *lock) {
return new RenderThread(stream, lock);
}
void RenderThread::forceStop() {
m_stream->forceStop();
}
intptr_t RenderThread::main() {
RenderThreadInfo tInfo;
ChecksumCalculatorThreadInfo tChecksumInfo;
//
// initialize decoders
//
tInfo.m_glDec.initGL(gles1_dispatch_get_proc_func, NULL);
tInfo.m_gl2Dec.initGL(gles2_dispatch_get_proc_func, NULL);
initRenderControlContext(&tInfo.m_rcDec);
ReadBuffer readBuf(STREAM_BUFFER_SIZE);
int stats_totalBytes = 0;
long long stats_t0 = GetCurrentTimeMS();
//
// open dump file if RENDER_DUMP_DIR is defined
//
const char *dump_dir = getenv("RENDERER_DUMP_DIR");
FILE *dumpFP = NULL;
if (dump_dir) {
size_t bsize = strlen(dump_dir) + 32;
char *fname = new char[bsize];
snprintf(fname,bsize,"%s/stream_%p", dump_dir, this);
dumpFP = fopen(fname, "wb");
if (!dumpFP) {
fprintf(stderr,"Warning: stream dump failed to open file %s\n",fname);
}
delete [] fname;
}
while (1) {
int stat = readBuf.getData(m_stream);
if (stat <= 0) {
break;
}
//
// log received bandwidth statistics
//
stats_totalBytes += readBuf.validData();
long long dt = GetCurrentTimeMS() - stats_t0;
if (dt > 1000) {
float dts = (float)dt / 1000.0f;
printf("Used Bandwidth %5.3f MB/s\n", ((float)stats_totalBytes / dts) / (1024.0f*1024.0f));
stats_totalBytes = 0;
stats_t0 = GetCurrentTimeMS();
}
//
// dump stream to file if needed
//
if (dumpFP) {
int skip = readBuf.validData() - stat;
fwrite(readBuf.buf()+skip, 1, readBuf.validData()-skip, dumpFP);
fflush(dumpFP);
}
bool progress;
do {
progress = false;
m_lock->lock();
//
// try to process some of the command buffer using the GLESv1 decoder
//
size_t last = tInfo.m_glDec.decode(readBuf.buf(), readBuf.validData(), m_stream);
if (last > 0) {
progress = true;
readBuf.consume(last);
}
//
// try to process some of the command buffer using the GLESv2 decoder
//
last = tInfo.m_gl2Dec.decode(readBuf.buf(), readBuf.validData(), m_stream);
if (last > 0) {
progress = true;
readBuf.consume(last);
}
//
// try to process some of the command buffer using the
// renderControl decoder
//
last = tInfo.m_rcDec.decode(readBuf.buf(), readBuf.validData(), m_stream);
if (last > 0) {
readBuf.consume(last);
progress = true;
}
m_lock->unlock();
} while( progress );
}
if (dumpFP) {
fclose(dumpFP);
}
//
// Release references to the current thread's context/surfaces if any
//
FrameBuffer::getFB()->bindContext(0, 0, 0);
if (tInfo.currContext || tInfo.currDrawSurf || tInfo.currReadSurf) {
fprintf(stderr, "ERROR: RenderThread exiting with current context/surfaces\n");
}
FrameBuffer::getFB()->drainWindowSurface();
FrameBuffer::getFB()->drainRenderContext();
return 0;
}

View file

@ -0,0 +1,58 @@
/*
* Copyright (C) 2011 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 _LIB_OPENGL_RENDER_RENDER_THREAD_H
#define _LIB_OPENGL_RENDER_RENDER_THREAD_H
#include "IOStream.h"
#include "emugl/common/mutex.h"
#include "emugl/common/thread.h"
// A class used to model a thread of the RenderServer. Each one of them
// handles a single guest client / protocol byte stream.
class RenderThread : public emugl::Thread {
public:
// Create a new RenderThread instance.
// |stream| is an input stream that will be read from the thread,
// and deleted by it when it exits.
// |mutex| is a pointer to a shared mutex used to serialize
// decoding operations between all threads.
// TODO(digit): Why is this needed here? Shouldn't this be handled
// by the decoders themselves or at a lower-level?
static RenderThread* create(IOStream* stream, emugl::Mutex* mutex);
// Destructor.
virtual ~RenderThread();
// Returns true iff the thread has finished.
// Note that this also means that the thread's stack has been
bool isFinished() { return tryWait(NULL); }
// Force a thread to stop.
void forceStop();
private:
RenderThread(); // No default constructor
RenderThread(IOStream* stream, emugl::Mutex* mutex);
virtual intptr_t main();
emugl::Mutex* m_lock;
IOStream* m_stream;
};
#endif

View file

@ -0,0 +1,43 @@
/*
* Copyright (C) 2011 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.
*/
#include "RenderThreadInfo.h"
#include "emugl/common/lazy_instance.h"
#include "emugl/common/thread_store.h"
namespace {
class ThreadInfoStore : public ::emugl::ThreadStore {
public:
ThreadInfoStore() : ::emugl::ThreadStore(NULL) {}
};
} // namespace
static ::emugl::LazyInstance<ThreadInfoStore> s_tls = LAZY_INSTANCE_INIT;
RenderThreadInfo::RenderThreadInfo() {
s_tls->set(this);
}
RenderThreadInfo::~RenderThreadInfo() {
s_tls->set(NULL);
}
RenderThreadInfo* RenderThreadInfo::get() {
return static_cast<RenderThreadInfo*>(s_tls->get());
}

View file

@ -0,0 +1,60 @@
/*
* Copyright (C) 2011 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 _LIB_OPENGL_RENDER_THREAD_INFO_H
#define _LIB_OPENGL_RENDER_THREAD_INFO_H
#include "RenderContext.h"
#include "WindowSurface.h"
#include "GLESv1Decoder.h"
#include "GLESv2Decoder.h"
#include "renderControl_dec.h"
#include <set>
typedef uint32_t HandleType;
typedef std::set<HandleType> ThreadContextSet;
typedef std::set<HandleType> WindowSurfaceSet;
// A class used to model the state of each RenderThread related
struct RenderThreadInfo {
// Create new instance. Only call this once per thread.
// Future callls to get() will return this instance until
// it is destroyed.
RenderThreadInfo();
// Destructor.
~RenderThreadInfo();
// Return the current thread's instance, if any, or NULL.
static RenderThreadInfo* get();
// Current EGL context, draw surface and read surface.
RenderContextPtr currContext;
WindowSurfacePtr currDrawSurf;
WindowSurfacePtr currReadSurf;
// Decoder states.
GLESv1Decoder m_glDec;
GLESv2Decoder m_gl2Dec;
renderControl_decoder_context_t m_rcDec;
// all the contexts that are created by this render thread
ThreadContextSet m_contextSet;
// all the window surfaces that are created by this render thread
WindowSurfaceSet m_windowSet;
};
#endif

View file

@ -0,0 +1,454 @@
// Copyright 2014-2015 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.
#include "RenderWindow.h"
#include "emugl/common/logging.h"
#include "emugl/common/message_channel.h"
#include "emugl/common/mutex.h"
#include "emugl/common/thread.h"
#include "FrameBuffer.h"
#include <stdarg.h>
#include <stdio.h>
#ifndef _WIN32
#include <signal.h>
#include <pthread.h>
#endif
#define DEBUG 0
#if DEBUG
# define D(...) my_debug(__PRETTY_FUNCTION__, __LINE__, __VA_ARGS__)
#else
# define D(...) ((void)0)
#endif
namespace {
#if DEBUG
void my_debug(const char* function, int line, const char* format, ...) {
static ::emugl::Mutex mutex;
va_list args;
va_start(args, format);
mutex.lock();
fprintf(stderr, "%s:%d:", function, line);
vfprintf(stderr, format, args);
mutex.unlock();
va_end(args);
}
#endif
// List of possible commands to send to the render window thread from
// the main one.
enum Command {
CMD_INITIALIZE,
CMD_SET_POST_CALLBACK,
CMD_SETUP_SUBWINDOW,
CMD_REMOVE_SUBWINDOW,
CMD_SET_ROTATION,
CMD_SET_TRANSLATION,
CMD_REPAINT,
CMD_FINALIZE,
};
} // namespace
// A single message sent from the main thread to the render window thread.
// |cmd| determines which fields are valid to read.
struct RenderWindowMessage {
Command cmd;
union {
// CMD_INITIALIZE
struct {
int width;
int height;
bool useSubWindow;
} init;
// CMD_SET_POST_CALLBACK
struct {
OnPostFn on_post;
void* on_post_context;
} set_post_callback;
// CMD_SETUP_SUBWINDOW
struct {
FBNativeWindowType parent;
int wx;
int wy;
int ww;
int wh;
int fbw;
int fbh;
float dpr;
float rotation;
} subwindow;
// CMD_SET_TRANSLATION;
struct {
float px;
float py;
} trans;
// CMD_SET_ROTATION
float rotation;
// result of operations.
bool result;
};
// Process the current message, and updates its |result| field.
// Returns true on success, or false on failure.
bool process() const {
const RenderWindowMessage& msg = *this;
FrameBuffer* fb;
bool result = false;
switch (msg.cmd) {
case CMD_INITIALIZE:
D("CMD_INITIALIZE w=%d h=%d\n", msg.init.width, msg.init.height);
GL_LOG("RenderWindow: CMD_INITIALIZE w=%d h=%d",
msg.init.width, msg.init.height);
result = FrameBuffer::initialize(msg.init.width,
msg.init.height,
msg.init.useSubWindow);
break;
case CMD_FINALIZE:
D("CMD_FINALIZE\n");
// this command may be issued even when frame buffer is not
// yet created (e.g. if CMD_INITIALIZE failed),
// so make sure we check if it is there before finalizing
if (const auto fb = FrameBuffer::getFB()) {
fb->finalize();
}
result = true;
break;
case CMD_SET_POST_CALLBACK:
D("CMD_SET_POST_CALLBACK\n");
fb = FrameBuffer::getFB();
fb->setPostCallback(msg.set_post_callback.on_post,
msg.set_post_callback.on_post_context);
result = true;
break;
case CMD_SETUP_SUBWINDOW:
D("CMD_SETUP_SUBWINDOW: parent=%p wx=%d wy=%d ww=%d wh=%d fbw=%d fbh=%d dpr=%f rotation=%f\n",
(void*)msg.subwindow.parent,
msg.subwindow.wx,
msg.subwindow.wy,
msg.subwindow.ww,
msg.subwindow.wh,
msg.subwindow.fbw,
msg.subwindow.fbh,
msg.subwindow.dpr,
msg.subwindow.rotation);
result = FrameBuffer::getFB()->setupSubWindow(
msg.subwindow.parent,
msg.subwindow.wx,
msg.subwindow.wy,
msg.subwindow.ww,
msg.subwindow.wh,
msg.subwindow.fbw,
msg.subwindow.fbh,
msg.subwindow.dpr,
msg.subwindow.rotation);
break;
case CMD_REMOVE_SUBWINDOW:
D("CMD_REMOVE_SUBWINDOW\n");
result = FrameBuffer::getFB()->removeSubWindow();
break;
case CMD_SET_ROTATION:
D("CMD_SET_ROTATION rotation=%f\n", msg.rotation);
fb = FrameBuffer::getFB();
if (fb) {
fb->setDisplayRotation(msg.rotation);
result = true;
}
break;
case CMD_SET_TRANSLATION:
D("CMD_SET_TRANSLATION translation=%f,%f\n", msg.trans.px, msg.trans.py);
fb = FrameBuffer::getFB();
if (fb) {
fb->setDisplayTranslation(msg.trans.px, msg.trans.py);
result = true;
}
break;
case CMD_REPAINT:
D("CMD_REPAINT\n");
fb = FrameBuffer::getFB();
if (fb) {
fb->repost();
result = true;
}
break;
default:
;
}
return result;
}
};
// Simple synchronization structure used to exchange data between the
// main and render window threads. Usage is the following:
//
// The main thread does the following in a loop:
//
// canWriteCmd.wait()
// updates |message| by writing a new |cmd| value and appropriate
// parameters.
// canReadCmd.signal()
// canReadResult.wait()
// reads |message.result|
// canWriteResult.signal()
//
// The render window thread will do the following:
//
// canReadCmd.wait()
// reads |message.cmd| and acts upon it.
// canWriteResult.wait()
// writes |message.result|
// canReadResult.signal()
// canWriteCmd.signal()
//
class RenderWindowChannel {
public:
RenderWindowChannel() : mIn(), mOut() {}
~RenderWindowChannel() {}
// Send a message from the main thread.
// Note that the content of |msg| is copied into the channel.
// Returns with the command's result (true or false).
bool sendMessageAndGetResult(const RenderWindowMessage& msg) {
D("msg.cmd=%d\n", msg.cmd);
mIn.send(msg);
D("waiting for result\n");
bool result = false;
mOut.receive(&result);
D("result=%s\n", result ? "success" : "failure");
return result;
}
// Receive a message from the render window thread.
// On exit, |*msg| gets a copy of the message. The caller
// must always call sendResult() after processing the message.
void receiveMessage(RenderWindowMessage* msg) {
D("entering\n");
mIn.receive(msg);
D("message cmd=%d\n", msg->cmd);
}
// Send result from the render window thread to the main one.
// Must always be called after receiveMessage().
void sendResult(bool result) {
D("waiting to send result (%s)\n", result ? "success" : "failure");
mOut.send(result);
D("result sent\n");
}
private:
emugl::MessageChannel<RenderWindowMessage, 16U> mIn;
emugl::MessageChannel<bool, 16U> mOut;
};
namespace {
// This class implements the window render thread.
// Its purpose is to listen for commands from the main thread in a loop,
// process them, then return a boolean result for each one of them.
//
// The thread ends with a CMD_FINALIZE.
//
class RenderWindowThread : public emugl::Thread {
public:
RenderWindowThread(RenderWindowChannel* channel) : mChannel(channel) {}
virtual intptr_t main() {
D("Entering render window thread thread\n");
#ifndef _WIN32
sigset_t set;
sigfillset(&set);
pthread_sigmask(SIG_SETMASK, &set, NULL);
#endif
bool running = true;
while (running) {
RenderWindowMessage msg;
D("Waiting for message from main thread\n");
mChannel->receiveMessage(&msg);
bool result = msg.process();
if (msg.cmd == CMD_FINALIZE) {
running = false;
}
D("Sending result (%s) to main thread\n", result ? "success" : "failure");
mChannel->sendResult(result);
}
D("Exiting thread\n");
return 0;
}
private:
RenderWindowChannel* mChannel;
};
} // namespace
RenderWindow::RenderWindow(int width,
int height,
bool use_thread,
bool use_sub_window) :
mValid(false),
mHasSubWindow(false),
mThread(NULL),
mChannel(NULL) {
if (use_thread) {
mChannel = new RenderWindowChannel();
mThread = new RenderWindowThread(mChannel);
mThread->start();
}
RenderWindowMessage msg;
msg.cmd = CMD_INITIALIZE;
msg.init.width = width;
msg.init.height = height;
msg.init.useSubWindow = use_sub_window;
mValid = processMessage(msg);
}
RenderWindow::~RenderWindow() {
D("Entering\n");
removeSubWindow();
D("Sending CMD_FINALIZE\n");
RenderWindowMessage msg;
msg.cmd = CMD_FINALIZE;
(void) processMessage(msg);
if (mThread) {
mThread->wait(NULL);
delete mThread;
delete mChannel;
}
}
bool RenderWindow::getHardwareStrings(const char** vendor,
const char** renderer,
const char** version) {
D("Entering\n");
// TODO(digit): Move this to render window thread.
FrameBuffer* fb = FrameBuffer::getFB();
if (!fb) {
D("No framebuffer!\n");
return false;
}
fb->getGLStrings(vendor, renderer, version);
D("Exiting vendor=[%s] renderer=[%s] version=[%s]\n",
*vendor, *renderer, *version);
return true;
}
void RenderWindow::setPostCallback(OnPostFn onPost, void* onPostContext) {
D("Entering\n");
RenderWindowMessage msg;
msg.cmd = CMD_SET_POST_CALLBACK;
msg.set_post_callback.on_post = onPost;
msg.set_post_callback.on_post_context = onPostContext;
(void) processMessage(msg);
D("Exiting\n");
}
bool RenderWindow::setupSubWindow(FBNativeWindowType window,
int wx,
int wy,
int ww,
int wh,
int fbw,
int fbh,
float dpr,
float zRot) {
D("Entering mHasSubWindow=%s\n", mHasSubWindow ? "true" : "false");
RenderWindowMessage msg;
msg.cmd = CMD_SETUP_SUBWINDOW;
msg.subwindow.parent = window;
msg.subwindow.wx = wx;
msg.subwindow.wy = wy;
msg.subwindow.ww = ww;
msg.subwindow.wh = wh;
msg.subwindow.fbw = fbw;
msg.subwindow.fbh = fbh;
msg.subwindow.dpr = dpr;
msg.subwindow.rotation = zRot;
mHasSubWindow = processMessage(msg);
D("Exiting mHasSubWindow=%s\n", mHasSubWindow ? "true" : "false");
return mHasSubWindow;
}
bool RenderWindow::removeSubWindow() {
D("Entering mHasSubWindow=%s\n", mHasSubWindow ? "true" : "false");
if (!mHasSubWindow) {
return false;
}
mHasSubWindow = false;
RenderWindowMessage msg;
msg.cmd = CMD_REMOVE_SUBWINDOW;
bool result = processMessage(msg);
D("Exiting result=%s\n", result ? "success" : "failure");
return result;
}
void RenderWindow::setRotation(float zRot) {
D("Entering rotation=%f\n", zRot);
RenderWindowMessage msg;
msg.cmd = CMD_SET_ROTATION;
msg.rotation = zRot;
(void) processMessage(msg);
D("Exiting\n");
}
void RenderWindow::setTranslation(float px, float py) {
D("Entering translation=%f,%f\n", px, py);
RenderWindowMessage msg;
msg.cmd = CMD_SET_TRANSLATION;
msg.trans.px = px;
msg.trans.py = py;
(void) processMessage(msg);
D("Exiting\n");
}
void RenderWindow::repaint() {
D("Entering\n");
RenderWindowMessage msg;
msg.cmd = CMD_REPAINT;
(void) processMessage(msg);
D("Exiting\n");
}
bool RenderWindow::processMessage(const RenderWindowMessage& msg) {
if (mChannel) {
return mChannel->sendMessageAndGetResult(msg);
} else {
return msg.process();
}
}

View file

@ -0,0 +1,135 @@
// Copyright 2014-2015 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 ANDROID_EMUGL_LIBRENDER_RENDER_WINDOW_H
#define ANDROID_EMUGL_LIBRENDER_RENDER_WINDOW_H
#include "OpenglRender/render_api.h"
namespace emugl {
class Thread;
} // namespace emugl
class RenderWindowChannel;
struct RenderWindowMessage;
// Helper class used to manage the sub-window that displays the emulated GPU
// output. To use it, do the following:
//
// 1) Create a new instance, passing the size of the emulated accelerated
// framebuffer in pixels you need.
//
// 2) Check isValid() after construction. If false, the library could not
// initialize the class properly, and one should abort.
//
// 3) Optional: call setPostCallback() to specify a callback function which
// will be called everytime a new frame is drawn.
//
// 4) Call setupSubWindow() to setup a new sub-window within the UI window.
// One can call removeSubWindow() to remove it, and one can call
// setupSubWindow() + removeSubWindow() any number of time (e.g. for
// changing the position / rotation of the subwindow).
//
// 5) Optional: call setRotation() to only change the display rotation of
// the sub-window content.
//
// 6) Call repaint() to force a repaint().
//
class RenderWindow {
public:
// Create new instance. |width| and |height| are the dimensions of the
// emulated accelerated framebuffer. |use_thread| can be true to force
// the use of a separate thread, which might be required on some platforms
// to avoid GL-realted corruption issues in the main window. Call
// isValid() after construction to verify that it worked properly.
//
// |use_sub_window| is true if the client will call setupSubWindow(),
// and false if it will call setPostCallback().
//
// Note that this call doesn't display anything, it just initializes
// the library, use setupSubWindow() to display something.
RenderWindow(int width, int height, bool use_thread, bool use_sub_window);
// Destructor. This will automatically call removeSubWindow() is needed.
~RenderWindow();
// Returns true if the RenderWindow instance is valid, which really
// means that the constructor succeeded.
bool isValid() const { return mValid; }
// Return misc. GL strings to the caller. On success, return true and sets
// |*vendor| to the GL vendor string, |*renderer| to the GL renderer one,
// and |*version| to the GL version one. On failure, return false.
bool getHardwareStrings(const char** vendor,
const char** renderer,
const char** version);
// Specify a function that will be called everytime a new frame is
// displayed. This is relatively slow but allows one to capture the
// output.
void setPostCallback(OnPostFn onPost, void* onPostContext);
// Start displaying the emulated framebuffer using a sub-window of a
// parent |window| id. |wx|, |wy|, |ww| and |wh| are the position
// and dimension of the sub-window, relative to its parent.
// |fbw| and |fbh| are the dimensions of the underlying guest framebuffer.
// |dpr| is the device pixel ratio for the monitor, which is required for
// higher-density displays (such as retina).
// |rotation| is a clockwise-rotation for the content. Only multiples of
// 90. are accepted. Returns true on success, false otherwise.
//
// If the subwindow already exists, this function will update
// the dimensions of the subwindow, backing framebuffer, and rendering
// pipeline to reflect the new values.
//
// One can call removeSubWindow() to remove the sub-window.
bool setupSubWindow(FBNativeWindowType window,
int wx,
int wy,
int ww,
int wh,
int fbw,
int fbh,
float dpr,
float rotation);
// Remove the sub-window created by calling setupSubWindow().
// Note that this doesn't discard the content of the emulated framebuffer,
// it just hides it from the main window. Returns true on success, false
// otherwise.
bool removeSubWindow();
// Change the display rotation on the fly. |zRot| is a clockwise rotation
// angle in degrees. Only multiples of 90. are accepted.
void setRotation(float zRot);
// Change the display translation. |px|,|py| are numbers between 0 and 1,
// with (0,0) indicating "align the bottom left of the framebuffer with the
// bottom left of the subwindow", and (1,1) indicating "align the top right of
// the framebuffer with the top right of the subwindow."
void setTranslation(float px, float py);
// Force a repaint of the whole content into the sub-window.
void repaint();
private:
bool processMessage(const RenderWindowMessage& msg);
bool mValid;
bool mHasSubWindow;
emugl::Thread* mThread;
RenderWindowChannel* mChannel;
};
#endif // ANDROID_EMUGL_LIBRENDER_RENDER_WINDOW_H

View file

@ -0,0 +1,183 @@
/*
* Copyright (C) 2011 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.
*/
#include "SocketStream.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#ifndef _WIN32
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/un.h>
#else
#include <ws2tcpip.h>
#endif
SocketStream::SocketStream(size_t bufSize) :
IOStream(bufSize),
m_sock(-1),
m_bufsize(bufSize),
m_buf(NULL)
{
}
SocketStream::SocketStream(int sock, size_t bufSize) :
IOStream(bufSize),
m_sock(sock),
m_bufsize(bufSize),
m_buf(NULL)
{
}
SocketStream::~SocketStream()
{
if (m_sock >= 0) {
forceStop();
#ifndef _WIN32
if(close(m_sock) < 0)
perror("Closing SocketStream failed");
#endif
// DBG("SocketStream::~close @ %d \n", m_sock);
m_sock = -1;
}
if (m_buf != NULL) {
free(m_buf);
m_buf = NULL;
}
}
void *SocketStream::allocBuffer(size_t minSize)
{
size_t allocSize = (m_bufsize < minSize ? minSize : m_bufsize);
if (!m_buf) {
m_buf = (unsigned char *)malloc(allocSize);
}
else if (m_bufsize < allocSize) {
unsigned char *p = (unsigned char *)realloc(m_buf, allocSize);
if (p != NULL) {
m_buf = p;
m_bufsize = allocSize;
} else {
ERR("%s: realloc (%zu) failed\n", __FUNCTION__, allocSize);
free(m_buf);
m_buf = NULL;
m_bufsize = 0;
}
}
return m_buf;
};
int SocketStream::commitBuffer(size_t size)
{
return writeFully(m_buf, size);
}
int SocketStream::writeFully(const void* buffer, size_t size)
{
if (!valid()) return -1;
size_t res = size;
int retval = 0;
while (res > 0) {
ssize_t stat = ::send(m_sock, (const char *)buffer + (size - res), res, 0);
if (stat < 0) {
if (errno != EINTR) {
retval = stat;
ERR("%s: failed: %s\n", __FUNCTION__, strerror(errno));
break;
}
} else {
res -= stat;
}
}
return retval;
}
const unsigned char *SocketStream::readFully(void *buf, size_t len)
{
if (!valid()) return NULL;
if (!buf) {
return NULL; // do not allow NULL buf in that implementation
}
size_t res = len;
while (res > 0) {
ssize_t stat = ::recv(m_sock, (char *)(buf) + len - res, res, 0);
if (stat > 0) {
res -= stat;
continue;
}
if (stat == 0 || errno != EINTR) { // client shutdown or error
return NULL;
}
}
return (const unsigned char *)buf;
}
const unsigned char *SocketStream::read( void *buf, size_t *inout_len)
{
if (!valid()) return NULL;
if (!buf) {
return NULL; // do not allow NULL buf in that implementation
}
int n;
do {
n = this->recv(buf, *inout_len);
} while( n < 0 && errno == EINTR );
if (n > 0) {
*inout_len = n;
return (const unsigned char *)buf;
}
return NULL;
}
int SocketStream::recv(void *buf, size_t len)
{
if (!valid()) return int(ERR_INVALID_SOCKET);
int res = 0;
while(true) {
res = ::recv(m_sock, (char *)buf, len, 0);
if (res < 0) {
if (errno == EINTR) {
continue;
}
}
break;
}
return res;
}
void SocketStream::forceStop() {
// Shutdown socket to force read/write errors.
#ifdef _WIN32
::shutdown(m_sock, SD_BOTH);
// As documented by programmers on MSDN, shutdown implementation in Windows does
// NOT result to unblocking threads that are blocked on a recv on the socket
// being shut down. The only way to actually implement this behavior (expected
// by this forceStop() implementation) is to rudely close the socket.
::closesocket(m_sock);
m_sock = -1;
#else
::shutdown(m_sock, SHUT_RDWR);
#endif
}

View file

@ -0,0 +1,53 @@
/*
* Copyright (C) 2011 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 __SOCKET_STREAM_H
#define __SOCKET_STREAM_H
#include <stdlib.h>
#include "IOStream.h"
class SocketStream : public IOStream {
public:
typedef enum { ERR_INVALID_SOCKET = -1000 } SocketStreamError;
static const size_t MAX_ADDRSTR_LEN = 256;
explicit SocketStream(size_t bufsize = 10000);
virtual ~SocketStream();
virtual int listen(char addrstr[MAX_ADDRSTR_LEN]) = 0;
virtual SocketStream *accept() = 0;
virtual int connect(const char* addr) = 0;
virtual void *allocBuffer(size_t minSize);
virtual int commitBuffer(size_t size);
virtual const unsigned char *readFully(void *buf, size_t len);
virtual const unsigned char *read(void *buf, size_t *inout_len);
bool valid() { return m_sock >= 0; }
virtual int recv(void *buf, size_t len);
virtual int writeFully(const void *buf, size_t len);
virtual void forceStop();
protected:
int m_sock;
size_t m_bufsize;
unsigned char *m_buf;
SocketStream(int sock, size_t bufSize);
};
#endif /* __SOCKET_STREAM_H */

View file

@ -0,0 +1,78 @@
/*
* Copyright (C) 2011 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.
*/
#include "TcpStream.h"
#include "emugl/common/sockets.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#ifndef _WIN32
#include <netinet/in.h>
#include <netinet/tcp.h>
#else
#include <ws2tcpip.h>
#endif
#define LISTEN_BACKLOG 4
TcpStream::TcpStream(size_t bufSize) : SocketStream(bufSize) {}
TcpStream::TcpStream(int sock, size_t bufSize) :
SocketStream(sock, bufSize) {
// disable Nagle algorithm to improve bandwidth of small
// packets which are quite common in our implementation.
emugl::socketTcpDisableNagle(sock);
}
int TcpStream::listen(char addrstr[MAX_ADDRSTR_LEN]) {
m_sock = emugl::socketTcpLoopbackServer(0, SOCK_STREAM);
if (!valid())
return int(ERR_INVALID_SOCKET);
int port = emugl::socketGetPort(m_sock);
if (port < 0) {
::close(m_sock);
return int(ERR_INVALID_SOCKET);
}
snprintf(addrstr, MAX_ADDRSTR_LEN - 1, "%hu", port);
addrstr[MAX_ADDRSTR_LEN-1] = '\0';
return 0;
}
SocketStream * TcpStream::accept() {
int clientSock = emugl::socketAccept(m_sock);
if (clientSock < 0)
return NULL;
return new TcpStream(clientSock, m_bufsize);
}
int TcpStream::connect(const char* addr) {
int port = atoi(addr);
m_sock = emugl::socketTcpLoopbackClient(port, SOCK_STREAM);
return valid() ? 0 : -1;
}
int TcpStream::connect(const char* hostname, unsigned short port)
{
m_sock = emugl::socketTcpClient(hostname, port, SOCK_STREAM);
return valid() ? 0 : -1;
}

View file

@ -0,0 +1,32 @@
/*
* Copyright (C) 2011 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 __TCP_STREAM_H
#define __TCP_STREAM_H
#include "SocketStream.h"
class TcpStream : public SocketStream {
public:
explicit TcpStream(size_t bufsize = 10000);
virtual int listen(char addrstr[MAX_ADDRSTR_LEN]);
virtual SocketStream *accept();
virtual int connect(const char* addr);
int connect(const char* hostname, unsigned short port);
private:
TcpStream(int sock, size_t bufSize);
};
#endif

View file

@ -0,0 +1,287 @@
// Copyright (C) 2015 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.
#include "TextureDraw.h"
#include "DispatchTables.h"
#include <math.h>
#include <string.h>
#include <vector>
#include <stdio.h>
#define ERR(...) fprintf(stderr, __VA_ARGS__)
// M_PI isn't defined in C++ (when strict ISO compliance is enabled)
#ifndef M_PI
#define M_PI 3.14159265358979323846264338327
#endif
namespace {
// Helper function to create a new shader.
// |shaderType| is the shader type (e.g. GL_VERTEX_SHADER).
// |shaderText| is a 0-terminated C string for the shader source to use.
// On success, return the handle of the new compiled shader, or 0 on failure.
GLuint createShader(GLint shaderType, const char* shaderText) {
// Create new shader handle and attach source.
GLuint shader = s_gles2.glCreateShader(shaderType);
if (!shader) {
return 0;
}
const GLchar* text = static_cast<const GLchar*>(shaderText);
const GLint textLen = ::strlen(shaderText);
s_gles2.glShaderSource(shader, 1, &text, &textLen);
// Compiler the shader.
GLint success;
s_gles2.glCompileShader(shader);
s_gles2.glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (success == GL_FALSE) {
s_gles2.glDeleteShader(shader);
return 0;
}
return shader;
}
// No scaling / projection since we want to fill the whole viewport with
// the texture, hence a trivial vertex shader that only supports clockwise
// rotation.
const char kVertexShaderSource[] =
"#version 100\n"
"const int _translator_gl_MaxVertexUniformVectors = 256;\n"
"const int _translator_gl_MaxFragmentUniformVectors = 256;\n"
"const int _translator_gl_MaxVaryingVectors = 15;\n"
"#define gl_MaxVertexUniformVectors _translator_gl_MaxVertexUniformVectors\n"
"#define gl_MaxFragmentUniformVectors _translator_gl_MaxFragmentUniformVectors\n"
"#define gl_MaxVaryingVectors _translator_gl_MaxVaryingVectors\n"
"#line 1\n"
"attribute vec4 position;\n"
"attribute vec2 inCoord;\n"
"varying lowp vec2 outCoord;\n"
"uniform float rotation;\n"
"uniform vec2 translation;\n"
"void main(void) {\n"
" float cs = cos(rotation);\n"
" float sn = sin(rotation);\n"
" gl_Position.x = position.x * cs + position.y * sn - translation.x;\n"
" gl_Position.y = position.y * cs - position.x * sn - translation.y;\n"
" gl_Position.zw = position.zw;\n"
" outCoord = inCoord;\n"
"}\n";
// Similarly, just interpolate texture coordinates.
const char kFragmentShaderSource[] =
"#version 100\n"
"const int _translator_gl_MaxVertexUniformVectors = 256;\n"
"const int _translator_gl_MaxFragmentUniformVectors = 256;\n"
"const int _translator_gl_MaxVaryingVectors = 15;\n"
"#define gl_MaxVertexUniformVectors _translator_gl_MaxVertexUniformVectors\n"
"#define gl_MaxFragmentUniformVectors _translator_gl_MaxFragmentUniformVectors\n"
"#define gl_MaxVaryingVectors _translator_gl_MaxVaryingVectors\n"
"#line 1\n"
"varying lowp vec2 outCoord;\n"
"uniform sampler2D texture;\n"
"void main(void) {\n"
" gl_FragColor = texture2D(texture, outCoord);\n"
"}\n";
// Hard-coded arrays of vertex information.
struct Vertex {
float pos[3];
float coord[2];
};
const Vertex kVertices[] = {
{{ +1, -1, +0 }, { +1, +1 }},
{{ +1, +1, +0 }, { +1, +0 }},
{{ -1, +1, +0 }, { +0, +0 }},
{{ -1, -1, +0 }, { +0, +1 }},
};
const GLubyte kIndices[] = { 0, 1, 2, 2, 3, 0 };
const GLint kIndicesLen = sizeof(kIndices) / sizeof(kIndices[0]);
} // namespace
TextureDraw::TextureDraw(EGLDisplay display) :
mDisplay(display),
mVertexShader(0),
mFragmentShader(0),
mProgram(0),
mPositionSlot(-1),
mInCoordSlot(-1),
mTextureSlot(-1),
mRotationSlot(-1),
mTranslationSlot(-1) {
// Create shaders and program.
mVertexShader = createShader(GL_VERTEX_SHADER, kVertexShaderSource);
mFragmentShader = createShader(GL_FRAGMENT_SHADER, kFragmentShaderSource);
mProgram = s_gles2.glCreateProgram();
s_gles2.glAttachShader(mProgram, mVertexShader);
s_gles2.glAttachShader(mProgram, mFragmentShader);
GLint success;
s_gles2.glLinkProgram(mProgram);
s_gles2.glGetProgramiv(mProgram, GL_LINK_STATUS, &success);
if (success == GL_FALSE) {
GLchar messages[256];
s_gles2.glGetProgramInfoLog(
mProgram, sizeof(messages), 0, &messages[0]);
ERR("%s: Could not create/link program: %s\n", __FUNCTION__, messages);
s_gles2.glDeleteProgram(mProgram);
mProgram = 0;
return;
}
s_gles2.glUseProgram(mProgram);
// Retrieve attribute/uniform locations.
mPositionSlot = s_gles2.glGetAttribLocation(mProgram, "position");
s_gles2.glEnableVertexAttribArray(mPositionSlot);
mInCoordSlot = s_gles2.glGetAttribLocation(mProgram, "inCoord");
s_gles2.glEnableVertexAttribArray(mInCoordSlot);
mRotationSlot = s_gles2.glGetUniformLocation(mProgram, "rotation");
mTranslationSlot = s_gles2.glGetUniformLocation(mProgram, "translation");
mTextureSlot = s_gles2.glGetUniformLocation(mProgram, "texture");
#if 0
printf("SLOTS position=%d inCoord=%d texture=%d rotation=%d\n",
mPositionSlot, mInCoordSlot, mTextureSlot, mRotationSlot);
#endif
// Create vertex and index buffers.
s_gles2.glGenBuffers(1, &mVertexBuffer);
s_gles2.glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
s_gles2.glBufferData(
GL_ARRAY_BUFFER, sizeof(kVertices), kVertices, GL_STATIC_DRAW);
s_gles2.glGenBuffers(1, &mIndexBuffer);
s_gles2.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer);
s_gles2.glBufferData(GL_ELEMENT_ARRAY_BUFFER,
sizeof(kIndices),
kIndices,
GL_STATIC_DRAW);
}
bool TextureDraw::draw(GLuint texture, float rotation, float dx, float dy) {
if (!mProgram) {
ERR("%s: no program\n", __FUNCTION__);
return false;
}
// TODO(digit): Save previous program state.
GLenum err;
s_gles2.glUseProgram(mProgram);
err = s_gles2.glGetError();
if (err != GL_NO_ERROR) {
ERR("%s: Could not use program error=0x%x\n",
__FUNCTION__, err);
}
// Setup the |position| attribute values.
s_gles2.glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
err = s_gles2.glGetError();
if (err != GL_NO_ERROR) {
ERR("%s: Could not bind GL_ARRAY_BUFFER error=0x%x\n",
__FUNCTION__, err);
}
s_gles2.glEnableVertexAttribArray(mPositionSlot);
s_gles2.glVertexAttribPointer(mPositionSlot,
3,
GL_FLOAT,
GL_FALSE,
sizeof(Vertex),
0);
err = s_gles2.glGetError();
if (err != GL_NO_ERROR) {
ERR("%s: Could glVertexAttribPointer with mPositionSlot error=0x%x\n",
__FUNCTION__, err);
}
// Setup the |inCoord| attribute values.
s_gles2.glEnableVertexAttribArray(mInCoordSlot);
s_gles2.glVertexAttribPointer(mInCoordSlot,
2,
GL_FLOAT,
GL_FALSE,
sizeof(Vertex),
reinterpret_cast<GLvoid*>(
static_cast<uintptr_t>(
sizeof(float) * 3)));
// setup the |texture| uniform value.
s_gles2.glActiveTexture(GL_TEXTURE0);
s_gles2.glBindTexture(GL_TEXTURE_2D, texture);
s_gles2.glUniform1i(mTextureSlot, 0);
// setup the |rotation| uniform value.
s_gles2.glUniform1f(mRotationSlot, rotation * M_PI / 180.);
s_gles2.glUniform2f(mTranslationSlot, dx, dy);
#if 1
// Validate program, just to be sure.
s_gles2.glValidateProgram(mProgram);
GLint validState = 0;
s_gles2.glGetProgramiv(mProgram, GL_VALIDATE_STATUS, &validState);
if (validState == GL_FALSE) {
GLchar messages[256];
s_gles2.glGetProgramInfoLog(
mProgram, sizeof(messages), 0, &messages[0]);
ERR("%s: Could not run program: %s\n", __FUNCTION__, messages);
return false;
}
#endif
// Do the rendering.
s_gles2.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer);
err = s_gles2.glGetError();
if (err != GL_NO_ERROR) {
ERR("%s: Could not glBindBuffer(GL_ELEMENT_ARRAY_BUFFER) error=0x%x\n",
__FUNCTION__, err);
}
s_gles2.glDrawElements(GL_TRIANGLES, kIndicesLen, GL_UNSIGNED_BYTE, 0);
err = s_gles2.glGetError();
if (err != GL_NO_ERROR) {
ERR("%s: Could not glDrawElements() error=0x%x\n",
__FUNCTION__, err);
}
// TODO(digit): Restore previous program state.
return true;
}
TextureDraw::~TextureDraw() {
s_gles2.glDeleteBuffers(1, &mIndexBuffer);
s_gles2.glDeleteBuffers(1, &mVertexBuffer);
if (mFragmentShader) {
s_gles2.glDeleteShader(mFragmentShader);
}
if (mVertexShader) {
s_gles2.glDeleteShader(mVertexShader);
}
}

View file

@ -0,0 +1,61 @@
// Copyright (C) 2015 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 TEXTURE_DRAW_H
#define TEXTURE_DRAW_H
#include <EGL/egl.h>
#include <GLES2/gl2.h>
// Helper class used to draw a simple texture to the current framebuffer.
// Usage is pretty simple:
//
// 1) Create a TextureDraw instance, passing the current EGLDisplay to it.
//
// 2) Each time you want to draw a texture, call draw(texture, rotation),
// where |texture| is the name of a GLES 2.x texture object, and
// |rotation| is an angle in degrees describing the clockwise rotation
// in the GL y-upwards coordinate space. This function fills the whole
// framebuffer with texture content.
//
class TextureDraw {
public:
// Create a new instance.
TextureDraw(EGLDisplay display);
// Destructor
~TextureDraw();
// Fill the current framebuffer with the content of |texture|, which must
// be the name of a GLES 2.x texture object. |rotationDegrees| is a
// clockwise rotation angle in degrees (clockwise in the GL Y-upwards
// coordinate space). |dx,dy| is the translation of the image towards the
// origin.
bool draw(GLuint texture, float rotationDegrees, float dx, float dy);
private:
EGLDisplay mDisplay;
GLuint mVertexShader;
GLuint mFragmentShader;
GLuint mProgram;
GLint mPositionSlot;
GLint mInCoordSlot;
GLint mTextureSlot;
GLint mRotationSlot;
GLint mTranslationSlot;
GLuint mVertexBuffer;
GLuint mIndexBuffer;
};
#endif // TEXTURE_DRAW_H

View file

@ -0,0 +1,344 @@
/*
* Copyright (C) 2015 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.
*/
#include "TextureResize.h"
#include "DispatchTables.h"
#include <stdio.h>
#include <sstream>
#include <string>
#include <utility>
#define ERR(...) fprintf(stderr, __VA_ARGS__)
#define MAX_FACTOR_POWER 4
static const char kCommonShaderSource[] =
"precision mediump float;\n"
"varying vec2 vUV00, vUV01;\n"
"#if FACTOR > 2\n"
"varying vec2 vUV02, vUV03;\n"
"#if FACTOR > 4\n"
"varying vec2 vUV04, vUV05, vUV06, vUV07;\n"
"#if FACTOR > 8\n"
"varying vec2 vUV08, vUV09, vUV10, vUV11, vUV12, vUV13, vUV14, vUV15;\n"
"#endif\n"
"#endif\n"
"#endif\n";
static const char kVertexShaderSource[] =
"attribute vec2 aPosition;\n"
"void main() {\n"
" gl_Position = vec4(aPosition, 0, 1);\n"
" vec2 uv = ((aPosition + 1.0) / 2.0) + 0.5 / kDimension;\n"
" vUV00 = uv;\n"
" #ifdef HORIZONTAL\n"
" vUV01 = uv + vec2( 1.0 / kDimension.x, 0);\n"
" #if FACTOR > 2\n"
" vUV02 = uv + vec2( 2.0 / kDimension.x, 0);\n"
" vUV03 = uv + vec2( 3.0 / kDimension.x, 0);\n"
" #if FACTOR > 4\n"
" vUV04 = uv + vec2( 4.0 / kDimension.x, 0);\n"
" vUV05 = uv + vec2( 5.0 / kDimension.x, 0);\n"
" vUV06 = uv + vec2( 6.0 / kDimension.x, 0);\n"
" vUV07 = uv + vec2( 7.0 / kDimension.x, 0);\n"
" #if FACTOR > 8\n"
" vUV08 = uv + vec2( 8.0 / kDimension.x, 0);\n"
" vUV09 = uv + vec2( 9.0 / kDimension.x, 0);\n"
" vUV10 = uv + vec2(10.0 / kDimension.x, 0);\n"
" vUV11 = uv + vec2(11.0 / kDimension.x, 0);\n"
" vUV12 = uv + vec2(12.0 / kDimension.x, 0);\n"
" vUV13 = uv + vec2(13.0 / kDimension.x, 0);\n"
" vUV14 = uv + vec2(14.0 / kDimension.x, 0);\n"
" vUV15 = uv + vec2(15.0 / kDimension.x, 0);\n"
" #endif\n" // FACTOR > 8
" #endif\n" // FACTOR > 4
" #endif\n" // FACTOR > 2
" #else\n"
" vUV01 = uv + vec2(0, 1.0 / kDimension.y);\n"
" #if FACTOR > 2\n"
" vUV02 = uv + vec2(0, 2.0 / kDimension.y);\n"
" vUV03 = uv + vec2(0, 3.0 / kDimension.y);\n"
" #if FACTOR > 4\n"
" vUV04 = uv + vec2(0, 4.0 / kDimension.y);\n"
" vUV05 = uv + vec2(0, 5.0 / kDimension.y);\n"
" vUV06 = uv + vec2(0, 6.0 / kDimension.y);\n"
" vUV07 = uv + vec2(0, 7.0 / kDimension.y);\n"
" #if FACTOR > 8\n"
" vUV08 = uv + vec2(0, 8.0 / kDimension.y);\n"
" vUV09 = uv + vec2(0, 9.0 / kDimension.y);\n"
" vUV10 = uv + vec2(0, 10.0 / kDimension.y);\n"
" vUV11 = uv + vec2(0, 11.0 / kDimension.y);\n"
" vUV12 = uv + vec2(0, 12.0 / kDimension.y);\n"
" vUV13 = uv + vec2(0, 13.0 / kDimension.y);\n"
" vUV14 = uv + vec2(0, 14.0 / kDimension.y);\n"
" vUV15 = uv + vec2(0, 15.0 / kDimension.y);\n"
" #endif\n" // FACTOR > 8
" #endif\n" // FACTOR > 4
" #endif\n" // FACTOR > 2
" #endif\n" // HORIZONTAL/VERTICAL
"}\n";
const char kFragmentShaderSource[] =
"uniform sampler2D uTexture;\n"
"vec4 read(vec2 uv) {\n"
" vec4 r = texture2D(uTexture, uv);\n"
" #ifdef HORIZONTAL\n"
" r.rgb = pow(r.rgb, vec3(2.2));\n"
" #endif\n"
" return r;\n"
"}\n"
"void main() {\n"
" vec4 sum = read(vUV00) + read(vUV01);\n"
" #if FACTOR > 2\n"
" sum += read(vUV02) + read(vUV03);\n"
" #if FACTOR > 4\n"
" sum += read(vUV04) + read(vUV05) + read(vUV06) + read(vUV07);\n"
" #if FACTOR > 8\n"
" sum += read(vUV08) + read(vUV09) + read(vUV10) + read(vUV11) +"
" read(vUV12) + read(vUV13) + read(vUV14) + read(vUV15);\n"
" #endif\n"
" #endif\n"
" #endif\n"
" sum /= float(FACTOR);\n"
" #ifdef VERTICAL\n"
" sum.rgb = pow(sum.rgb, vec3(1.0 / 2.2));\n"
" #endif\n"
" gl_FragColor = sum;\n"
"}\n";
static const float kVertexData[] = {-1, -1, 3, -1, -1, 3};
static void detachShaders(GLuint program) {
GLuint shaders[2] = {};
GLsizei count = 0;
s_gles2.glGetAttachedShaders(program, 2, &count, shaders);
if (s_gles2.glGetError() == GL_NO_ERROR) {
for (GLsizei i = 0; i < count; i++) {
s_gles2.glDetachShader(program, shaders[i]);
s_gles2.glDeleteShader(shaders[i]);
}
}
}
static GLuint createShader(GLenum type, const std::initializer_list<const char*>& source) {
GLint success, infoLength;
GLuint shader = s_gles2.glCreateShader(type);
if (shader) {
s_gles2.glShaderSource(shader, source.size(), source.begin(), nullptr);
s_gles2.glCompileShader(shader);
s_gles2.glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (success == GL_FALSE) {
s_gles2.glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLength);
std::string infoLog(infoLength + 1, '\0');
s_gles2.glGetShaderInfoLog(shader, infoLength, nullptr, &infoLog[0]);
ERR("%s shader compile failed:\n%s\n",
(type == GL_VERTEX_SHADER) ? "Vertex" : "Fragment",
infoLog.c_str());
s_gles2.glDeleteShader(shader);
shader = 0;
}
}
return shader;
}
static void attachShaders(TextureResize::Framebuffer* fb, const char* factorDefine,
const char* dimensionDefine, GLuint width, GLuint height) {
std::ostringstream dimensionConst;
dimensionConst << "const vec2 kDimension = vec2(" << width << ", " << height << ");\n";
GLuint vShader = createShader(GL_VERTEX_SHADER, {
factorDefine, dimensionDefine,
kCommonShaderSource, dimensionConst.str().c_str(), kVertexShaderSource
});
GLuint fShader = createShader(GL_FRAGMENT_SHADER, {
factorDefine, dimensionDefine, kCommonShaderSource, kFragmentShaderSource
});
if (!vShader || !fShader) {
return;
}
s_gles2.glAttachShader(fb->program, vShader);
s_gles2.glAttachShader(fb->program, fShader);
s_gles2.glLinkProgram(fb->program);
s_gles2.glUseProgram(fb->program);
fb->aPosition = s_gles2.glGetAttribLocation(fb->program, "aPosition");
fb->uTexture = s_gles2.glGetUniformLocation(fb->program, "uTexture");
}
TextureResize::TextureResize(GLuint width, GLuint height) :
mWidth(width),
mHeight(height),
mFactor(1),
mFBWidth({0,}),
mFBHeight({0,}) {
s_gles2.glGenTextures(1, &mFBWidth.texture);
s_gles2.glBindTexture(GL_TEXTURE_2D, mFBWidth.texture);
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
s_gles2.glGenTextures(1, &mFBHeight.texture);
s_gles2.glBindTexture(GL_TEXTURE_2D, mFBHeight.texture);
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
s_gles2.glGenFramebuffers(1, &mFBWidth.framebuffer);
s_gles2.glGenFramebuffers(1, &mFBHeight.framebuffer);
mFBWidth.program = s_gles2.glCreateProgram();
mFBHeight.program = s_gles2.glCreateProgram();
s_gles2.glGenBuffers(1, &mVertexBuffer);
s_gles2.glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
s_gles2.glBufferData(GL_ARRAY_BUFFER, sizeof(kVertexData), kVertexData, GL_STATIC_DRAW);
}
TextureResize::~TextureResize() {
GLuint fb[2] = {mFBWidth.framebuffer, mFBHeight.framebuffer};
s_gles2.glDeleteFramebuffers(2, fb);
GLuint tex[2] = {mFBWidth.texture, mFBHeight.texture};
s_gles2.glDeleteTextures(2, tex);
s_gles2.glDeleteProgram(mFBWidth.program);
s_gles2.glDeleteProgram(mFBHeight.program);
s_gles2.glDeleteBuffers(1, &mVertexBuffer);
}
GLuint TextureResize::update(GLuint texture) {
// Store the viewport. The viewport is clobbered due to the framebuffers.
GLint vport[4] = { 0, };
s_gles2.glGetIntegerv(GL_VIEWPORT, vport);
// Correctly deal with rotated screens.
GLint tWidth = vport[2], tHeight = vport[3];
if ((mWidth < mHeight) != (tWidth < tHeight)) {
std::swap(tWidth, tHeight);
}
// Compute the scaling factor needed to get an image just larger than the target viewport.
unsigned int factor = 1;
for (int i = 0, w = mWidth / 2, h = mHeight / 2;
i < MAX_FACTOR_POWER && w >= tWidth && h >= tHeight;
i++, w /= 2, h /= 2, factor *= 2) {
}
// No resizing needed.
if (factor == 1) {
return texture;
}
s_gles2.glGetError(); // Clear any GL errors.
setupFramebuffers(factor);
resize(texture);
s_gles2.glViewport(vport[0], vport[1], vport[2], vport[3]); // Restore the viewport.
// If there was an error while resizing, just use the unscaled texture.
GLenum error = s_gles2.glGetError();
if (error != GL_NO_ERROR) {
ERR("GL error while resizing: 0x%x (ignored)\n", error);
return texture;
}
return mFBHeight.texture;
}
void TextureResize::setupFramebuffers(unsigned int factor) {
if (factor == mFactor) {
// The factor hasn't changed, no need to update the framebuffers.
return;
}
// Update the framebuffer sizes to match the new factor.
s_gles2.glBindTexture(GL_TEXTURE_2D, mFBWidth.texture);
s_gles2.glTexImage2D(
GL_TEXTURE_2D, 0, GL_RGBA, mWidth / factor, mHeight, 0, GL_RGBA, GL_FLOAT, nullptr);
s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, mFBWidth.framebuffer);
s_gles2.glFramebufferTexture2D(
GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mFBWidth.texture, 0);
s_gles2.glBindTexture(GL_TEXTURE_2D, mFBHeight.texture);
s_gles2.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, mWidth / factor, mHeight / factor, 0, GL_RGBA,
GL_UNSIGNED_BYTE, nullptr);
s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, mFBHeight.framebuffer);
s_gles2.glFramebufferTexture2D(
GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mFBHeight.texture, 0);
// Update the shaders to the new factor. First detach the old shaders...
detachShaders(mFBWidth.program);
detachShaders(mFBHeight.program);
// ... then attach the new ones.
std::ostringstream factorDefine;
factorDefine << "#define FACTOR " << factor << "\n";
attachShaders(&mFBWidth, factorDefine.str().c_str(), "#define HORIZONTAL\n", mWidth, mHeight);
attachShaders(&mFBHeight, factorDefine.str().c_str(), "#define VERTICAL\n", mWidth, mHeight);
mFactor = factor;
}
void TextureResize::resize(GLuint texture) {
s_gles2.glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
s_gles2.glActiveTexture(GL_TEXTURE0);
// First scale the horizontal dimension by rendering the input texture to a scaled framebuffer.
s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, mFBWidth.framebuffer);
s_gles2.glViewport(0, 0, mWidth / mFactor, mHeight);
s_gles2.glUseProgram(mFBWidth.program);
s_gles2.glEnableVertexAttribArray(mFBWidth.aPosition);
s_gles2.glVertexAttribPointer(mFBWidth.aPosition, 2, GL_FLOAT, GL_FALSE, 0, 0);
s_gles2.glBindTexture(GL_TEXTURE_2D, texture);
// Store the current texture filters and set to nearest for scaling.
GLint mag_filter, min_filter;
s_gles2.glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, &mag_filter);
s_gles2.glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, &min_filter);
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
s_gles2.glUniform1i(mFBWidth.uTexture, 0);
s_gles2.glDrawArrays(GL_TRIANGLES, 0, sizeof(kVertexData) / (2 * sizeof(float)));
// Restore the previous texture filters.
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter);
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter);
// Secondly, scale the vertical dimension using the second framebuffer.
s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, mFBHeight.framebuffer);
s_gles2.glViewport(0, 0, mWidth / mFactor, mHeight / mFactor);
s_gles2.glUseProgram(mFBHeight.program);
s_gles2.glEnableVertexAttribArray(mFBHeight.aPosition);
s_gles2.glVertexAttribPointer(mFBHeight.aPosition, 2, GL_FLOAT, GL_FALSE, 0, 0);
s_gles2.glBindTexture(GL_TEXTURE_2D, mFBWidth.texture);
s_gles2.glUniform1i(mFBHeight.uTexture, 0);
s_gles2.glDrawArrays(GL_TRIANGLES, 0, sizeof(kVertexData) / (2 * sizeof(float)));
// Clear the bindings.
s_gles2.glBindBuffer(GL_ARRAY_BUFFER, 0);
s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, 0);
s_gles2.glBindTexture(GL_TEXTURE_2D, 0);
}

View file

@ -0,0 +1,51 @@
/*
* Copyright (C) 2015 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 _LIBRENDER_TEXTURERESIZE_H
#define _LIBRENDER_TEXTURERESIZE_H
#include <GLES2/gl2.h>
class TextureResize {
public:
TextureResize(GLuint width, GLuint height);
~TextureResize();
// Scales the given texture for the current viewport and returns the scaled
// texture. May return the input if no scaling is required.
GLuint update(GLuint texture);
struct Framebuffer {
GLuint texture;
GLuint framebuffer;
GLuint program;
GLuint aPosition;
GLuint uTexture;
};
private:
void setupFramebuffers(unsigned int factor);
void resize(GLuint texture);
private:
GLuint mWidth;
GLuint mHeight;
unsigned int mFactor;
Framebuffer mFBWidth;
Framebuffer mFBHeight;
GLuint mVertexBuffer;
};
#endif

View file

@ -0,0 +1,69 @@
/*
* Copyright (C) 2011 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.
*/
#include "TimeUtils.h"
#ifdef _WIN32
#include <windows.h>
#include <time.h>
#include <stdio.h>
#elif defined(__linux__)
#include <stdlib.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#else
#include <sys/time.h>
#include <unistd.h>
#endif
long long GetCurrentTimeMS()
{
#ifdef _WIN32
static LARGE_INTEGER freq;
static bool bNotInit = true;
if ( bNotInit ) {
bNotInit = (QueryPerformanceFrequency( &freq ) == FALSE);
}
LARGE_INTEGER currVal;
QueryPerformanceCounter( &currVal );
return currVal.QuadPart / (freq.QuadPart / 1000);
#elif defined(__linux__)
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
long long iDiff = (now.tv_sec * 1000LL) + now.tv_nsec/1000000LL;
return iDiff;
#else /* Others, e.g. OS X */
struct timeval now;
gettimeofday(&now, NULL);
long long iDiff = (now.tv_sec * 1000LL) + now.tv_usec/1000LL;
return iDiff;
#endif
}
void TimeSleepMS(int p_mili)
{
#ifdef _WIN32
Sleep(p_mili);
#else
usleep(p_mili * 1000);
#endif
}

View file

@ -0,0 +1,22 @@
/*
* Copyright (C) 2011 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 _TIME_UTILS_H
#define _TIME_UTILS_H
long long GetCurrentTimeMS();
void TimeSleepMS(int p_mili);
#endif

View file

@ -0,0 +1,174 @@
/*
* Copyright (C) 2011 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.
*/
#include "UnixStream.h"
#include "emugl/common/sockets.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/un.h>
#include <sys/stat.h>
/* Not all systems define PATH_MAX, those who don't generally don't
* have a limit on the maximum path size, so use a value that is
* large enough for our very limited needs.
*/
#ifndef PATH_MAX
#define PATH_MAX 128
#endif
UnixStream::UnixStream(size_t bufSize) :
SocketStream(bufSize),
bound_socket_path(NULL)
{
}
UnixStream::UnixStream(int sock, size_t bufSize) :
SocketStream(sock, bufSize),
bound_socket_path(NULL)
{
}
UnixStream::~UnixStream()
{
if (bound_socket_path != NULL) {
int ret = 0;
do {
ret = unlink(bound_socket_path);
} while (ret < 0 && errno == EINTR);
if(ret != 0) {
ERR("Failed to unlink UNIX socket at \"%s\"\n", bound_socket_path);
perror("UNIX socket could not be unlinked");
}
free(bound_socket_path);
}
}
/* Initialize a sockaddr_un with the appropriate values corresponding
* to a given 'virtual port'. Returns 0 on success, -1 on error.
*/
static int
make_unix_path(char *path, size_t pathlen, int port_number)
{
char tmp[PATH_MAX]; // temp directory
int ret = 0;
// First, create user-specific temp directory if needed
const char* user = getenv("XDG_RUNTIME_DIR");
if (user != NULL) {
struct stat st;
snprintf(tmp, sizeof(tmp), "%s/anbox", user);
do {
ret = ::lstat(tmp, &st);
} while (ret < 0 && errno == EINTR);
if (ret < 0 && errno == ENOENT) {
do {
ret = ::mkdir(tmp, 0766);
} while (ret < 0 && errno == EINTR);
if (ret < 0) {
ERR("Could not create temp directory: %s", tmp);
user = NULL; // will fall-back to /tmp
}
}
else if (ret < 0) {
user = NULL; // will fallback to /tmp
}
}
if (user == NULL) { // fallback to /tmp in case of error
snprintf(tmp, sizeof(tmp), "/tmp");
}
// Now, initialize it properly
snprintf(path, pathlen, "%s/qemu-gles-%d", tmp, port_number);
// If the emulator is killed, it can leave the socket file behind.
// Since the filename has PID in it, we can be sure that this socket
// is not supposed to be here and delete it, to prevent EADDRINUSE
// later in bind()
if (::access(path, F_OK) == 0) {
ret = ::remove(path);
if (ret < 0) {
ERR("Failed to remove stale socket file at %s: %s\n", path, strerror(errno));
} else {
DBG("Stale socket file at %s was removed.\n", path);
}
}
return 0;
}
int UnixStream::listen(char addrstr[MAX_ADDRSTR_LEN])
{
if (make_unix_path(addrstr, MAX_ADDRSTR_LEN, getpid()) < 0) {
return -1;
}
m_sock = emugl::socketLocalServer(addrstr, SOCK_STREAM);
if (!valid())
return int(ERR_INVALID_SOCKET);
bound_socket_path = strdup(addrstr);
if(bound_socket_path == NULL) {
ERR("WARNING: UNIX socket at \"%s\" should be manually removed \n",
addrstr);
return -1;
}
return 0;
}
SocketStream * UnixStream::accept()
{
int clientSock = -1;
while (true) {
struct sockaddr_un addr;
socklen_t len = sizeof(addr);
clientSock = ::accept(m_sock, (sockaddr *)&addr, &len);
// DBG("UnixStream::accept @ %d \n", clientSock);
if (clientSock < 0 && errno == EINTR) {
continue;
}
break;
}
UnixStream *clientStream = NULL;
if (clientSock >= 0) {
clientStream = new UnixStream(clientSock, m_bufsize);
}
return clientStream;
}
int UnixStream::connect(const char* addr)
{
m_sock = emugl::socketLocalClient(addr, SOCK_STREAM);
// DBG("UnixStream::connect @ %d \n", m_sock);
if (!valid()) return -1;
return 0;
}

View file

@ -0,0 +1,33 @@
/*
* Copyright (C) 2011 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 __UNIX_STREAM_H
#define __UNIX_STREAM_H
#include "SocketStream.h"
class UnixStream : public SocketStream {
public:
explicit UnixStream(size_t bufsize = 10000);
~UnixStream();
virtual int listen(char addrstr[MAX_ADDRSTR_LEN]);
virtual SocketStream *accept();
virtual int connect(const char* addr);
private:
char *bound_socket_path;
UnixStream(int sock, size_t bufSize);
};
#endif

View file

@ -0,0 +1,241 @@
/*
* Copyright (C) 2011 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.
*/
#include "Win32PipeStream.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <windows.h>
#ifndef _WIN32
#error ONLY BUILD THIS SOURCE FILE FOR WINDOWS!
#endif
/* The official documentation states that the name of a given named
* pipe cannot be more than 256 characters long.
*/
#define NAMED_PIPE_MAX 256
Win32PipeStream::Win32PipeStream(size_t bufSize) :
SocketStream(bufSize),
m_pipe(INVALID_HANDLE_VALUE)
{
}
Win32PipeStream::Win32PipeStream(HANDLE pipe, size_t bufSize) :
SocketStream(-1, bufSize),
m_pipe(pipe)
{
}
Win32PipeStream::~Win32PipeStream()
{
if (m_pipe != INVALID_HANDLE_VALUE) {
CloseHandle(m_pipe);
m_pipe = INVALID_HANDLE_VALUE;
}
}
/* Initialize the pipe name corresponding to a given port
*/
static void
make_pipe_name(char *path, size_t pathlen, int port_number)
{
snprintf(path, pathlen, "\\\\.\\pipe\\qemu-gles-%d", port_number);
}
/* Technical note: Named pipes work differently from BSD Sockets.
* One does not create/bind a pipe, and collect a new handle each
* time a client connects with accept().
*
* Instead, the server creates a new pipe instance each time it wants
* to get a new client connection, then calls ConnectNamedPipe() to
* wait for a connection.
*
* So listen() is a no-op, and accept() really creates the pipe handle.
*
* Also, connect() must create a pipe handle with CreateFile() and
* wait for a server instance with WaitNamedPipe()
*/
int Win32PipeStream::listen(char addrstr[MAX_ADDRSTR_LEN])
{
m_port = GetCurrentProcessId();
make_pipe_name(addrstr, MAX_ADDRSTR_LEN, m_port);
return 0;
}
SocketStream * Win32PipeStream::accept()
{
char path[NAMED_PIPE_MAX+1];
SocketStream* clientStream;
HANDLE pipe;
make_pipe_name(path, sizeof(path), m_port);
pipe = ::CreateNamedPipe(
path, // pipe name
PIPE_ACCESS_DUPLEX, // read-write access
PIPE_TYPE_BYTE | // byte-oriented writes
PIPE_READMODE_BYTE | // byte-oriented reads
PIPE_WAIT, // blocking operations
PIPE_UNLIMITED_INSTANCES, // no limit on clients
4096, // input buffer size
4096, // output buffer size
0, // client time-out
NULL); // default security attributes
if (pipe == INVALID_HANDLE_VALUE) {
ERR("%s: CreateNamedPipe failed %d\n", __FUNCTION__, (int)GetLastError());
return NULL;
}
// Stupid Win32 API design: If a client is already connected, then
// ConnectNamedPipe will return 0, and GetLastError() will return
// ERROR_PIPE_CONNECTED. This is not an error! It just means that the
// function didn't have to wait.
//
if (::ConnectNamedPipe(pipe, NULL) == 0 && GetLastError() != ERROR_PIPE_CONNECTED) {
ERR("%s: ConnectNamedPipe failed: %d\n", __FUNCTION__, (int)GetLastError());
CloseHandle(pipe);
return NULL;
}
clientStream = new Win32PipeStream(pipe, m_bufsize);
return clientStream;
}
int Win32PipeStream::connect(const char* addr)
{
HANDLE pipe;
int tries = 10;
/* We're going to loop in order to wait for the pipe server to
* be setup properly.
*/
for (; tries > 0; tries--) {
pipe = ::CreateFile(
addr, // pipe name
GENERIC_READ | GENERIC_WRITE, // read & write
0, // no sharing
NULL, // default security attrs
OPEN_EXISTING, // open existing pipe
0, // default attributes
NULL); // no template file
/* If we have a valid pipe handle, break from the loop */
if (pipe != INVALID_HANDLE_VALUE) {
break;
}
/* We can get here if the pipe is busy, i.e. if the server hasn't
* create a new pipe instance to service our request. In which case
* GetLastError() will return ERROR_PIPE_BUSY.
*
* If so, then use WaitNamedPipe() to wait for a decent time
* to try again.
*/
if (GetLastError() != ERROR_PIPE_BUSY) {
/* Not ERROR_PIPE_BUSY */
ERR("%s: CreateFile failed: %d\n", __FUNCTION__, (int)GetLastError());
errno = EINVAL;
return -1;
}
/* Wait for 5 seconds */
if ( !WaitNamedPipe(addr, 5000) ) {
ERR("%s: WaitNamedPipe failed: %d\n", __FUNCTION__, (int)GetLastError());
errno = EINVAL;
return -1;
}
}
m_pipe = pipe;
return 0;
}
/* Special buffer methods, since we can't use socket functions here */
int Win32PipeStream::commitBuffer(size_t size)
{
if (m_pipe == INVALID_HANDLE_VALUE)
return -1;
size_t res = size;
int retval = 0;
while (res > 0) {
DWORD written;
if (! ::WriteFile(m_pipe, (const char *)m_buf + (size - res), res, &written, NULL)) {
retval = -1;
ERR("%s: failed: %d\n", __FUNCTION__, (int)GetLastError());
break;
}
res -= written;
}
return retval;
}
const unsigned char *Win32PipeStream::readFully(void *buf, size_t len)
{
if (m_pipe == INVALID_HANDLE_VALUE)
return NULL;
if (!buf) {
return NULL; // do not allow NULL buf in that implementation
}
size_t res = len;
while (res > 0) {
DWORD readcount = 0;
if (! ::ReadFile(m_pipe, (char *)buf + (len - res), res, &readcount, NULL) || readcount == 0) {
errno = (int)GetLastError();
return NULL;
}
res -= readcount;
}
return (const unsigned char *)buf;
}
const unsigned char *Win32PipeStream::read( void *buf, size_t *inout_len)
{
size_t len = *inout_len;
DWORD readcount;
if (m_pipe == INVALID_HANDLE_VALUE)
return NULL;
if (!buf) {
return NULL; // do not allow NULL buf in that implementation
}
if (!::ReadFile(m_pipe, (char *)buf, len, &readcount, NULL)) {
errno = (int)GetLastError();
return NULL;
}
*inout_len = (size_t)readcount;
return (const unsigned char *)buf;
}
void Win32PipeStream::forceStop()
{
HANDLE handle = m_pipe;
m_pipe = INVALID_HANDLE_VALUE;
CloseHandle(handle);
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (C) 2011 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 __WIN32_PIPE_STREAM_H
#define __WIN32_PIPE_STREAM_H
#include "SocketStream.h"
#include <windows.h>
class Win32PipeStream : public SocketStream {
public:
explicit Win32PipeStream(size_t bufsize = 10000);
virtual ~Win32PipeStream();
virtual int listen(char addrstr[MAX_ADDRSTR_LEN]);
virtual SocketStream *accept();
virtual int connect(const char* addr);
virtual int commitBuffer(size_t size);
virtual const unsigned char *readFully(void *buf, size_t len);
virtual const unsigned char *read(void *buf, size_t *inout_len);
virtual void forceStop();
private:
Win32PipeStream(HANDLE pipe, size_t bufSize);
HANDLE m_pipe;
int m_port;
};
#endif

View file

@ -0,0 +1,187 @@
/*
* Copyright (C) 2011 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.
*/
#include "WindowSurface.h"
#include "ErrorLog.h"
#include "FbConfig.h"
#include "OpenGLESDispatch/EGLDispatch.h"
#include <GLES/glext.h>
#include <stdio.h>
#include <string.h>
WindowSurface::WindowSurface(EGLDisplay display,
EGLConfig config) :
mSurface(NULL),
mAttachedColorBuffer(NULL),
mReadContext(NULL),
mDrawContext(NULL),
mWidth(0),
mHeight(0),
mConfig(config),
mDisplay(display) {}
WindowSurface::~WindowSurface() {
if (mSurface) {
s_egl.eglDestroySurface(mDisplay, mSurface);
}
}
WindowSurface *WindowSurface::create(EGLDisplay display,
EGLConfig config,
int p_width,
int p_height) {
// allocate space for the WindowSurface object
WindowSurface *win = new WindowSurface(display, config);
if (!win) {
return NULL;
}
// Create a pbuffer to be used as the egl surface
// for that window.
if (!win->resize(p_width, p_height)) {
delete win;
return NULL;
}
return win;
}
void WindowSurface::setColorBuffer(ColorBufferPtr p_colorBuffer) {
mAttachedColorBuffer = p_colorBuffer;
// resize the window if the attached color buffer is of different
// size.
unsigned int cbWidth = mAttachedColorBuffer->getWidth();
unsigned int cbHeight = mAttachedColorBuffer->getHeight();
if (cbWidth != mWidth || cbHeight != mHeight) {
resize(cbWidth, cbHeight);
}
}
void WindowSurface::bind(RenderContextPtr p_ctx, BindType p_bindType) {
if (p_bindType == BIND_READ) {
mReadContext = p_ctx;
} else if (p_bindType == BIND_DRAW) {
mDrawContext = p_ctx;
} else if (p_bindType == BIND_READDRAW) {
mReadContext = p_ctx;
mDrawContext = p_ctx;
}
}
bool WindowSurface::flushColorBuffer() {
if (!mAttachedColorBuffer.Ptr()) {
return true;
}
if (!mWidth || !mHeight) {
return false;
}
if (mAttachedColorBuffer->getWidth() != mWidth ||
mAttachedColorBuffer->getHeight() != mHeight) {
// XXX: should never happen - how this needs to be handled?
fprintf(stderr, "Dimensions do not match\n");
return false;
}
if (!mDrawContext.Ptr()) {
fprintf(stderr, "Draw context is NULL\n");
return false;
}
// Make the surface current
EGLContext prevContext = s_egl.eglGetCurrentContext();
EGLSurface prevReadSurf = s_egl.eglGetCurrentSurface(EGL_READ);
EGLSurface prevDrawSurf = s_egl.eglGetCurrentSurface(EGL_DRAW);
if (!s_egl.eglMakeCurrent(mDisplay,
mSurface,
mSurface,
mDrawContext->getEGLContext())) {
fprintf(stderr, "Error making draw context current\n");
return false;
}
mAttachedColorBuffer->blitFromCurrentReadBuffer();
// restore current context/surface
s_egl.eglMakeCurrent(mDisplay, prevDrawSurf, prevReadSurf, prevContext);
return true;
}
bool WindowSurface::resize(unsigned int p_width, unsigned int p_height)
{
if (mSurface && mWidth == p_width && mHeight == p_height) {
// no need to resize
return true;
}
EGLContext prevContext = s_egl.eglGetCurrentContext();
EGLSurface prevReadSurf = s_egl.eglGetCurrentSurface(EGL_READ);
EGLSurface prevDrawSurf = s_egl.eglGetCurrentSurface(EGL_DRAW);
EGLSurface prevPbuf = mSurface;
bool needRebindContext = mSurface &&
(prevReadSurf == mSurface ||
prevDrawSurf == mSurface);
if (needRebindContext) {
s_egl.eglMakeCurrent(
mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
}
//
// Destroy previous surface
//
if (mSurface) {
s_egl.eglDestroySurface(mDisplay, mSurface);
mSurface = NULL;
}
//
// Create pbuffer surface.
//
const EGLint pbufAttribs[5] = {
EGL_WIDTH, (EGLint) p_width, EGL_HEIGHT, (EGLint) p_height, EGL_NONE,
};
mSurface = s_egl.eglCreatePbufferSurface(mDisplay,
mConfig,
pbufAttribs);
if (mSurface == EGL_NO_SURFACE) {
fprintf(stderr, "Renderer error: failed to create/resize pbuffer!!\n");
return false;
}
mWidth = p_width;
mHeight = p_height;
if (needRebindContext) {
s_egl.eglMakeCurrent(
mDisplay,
(prevDrawSurf == prevPbuf) ? mSurface : prevDrawSurf,
(prevReadSurf == prevPbuf) ? mSurface : prevReadSurf,
prevContext);
}
return true;
}

View file

@ -0,0 +1,103 @@
/*
* Copyright (C) 2011 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 _LIBRENDER_WINDOW_SURFACE_H
#define _LIBRENDER_WINDOW_SURFACE_H
#include "ColorBuffer.h"
#include "RenderContext.h"
#include "emugl/common/smart_ptr.h"
#include <EGL/egl.h>
#include <GLES/gl.h>
// A class used to model a guest-side window surface. The implementation
// uses a host Pbuffer to act as the EGL rendering surface instead.
class WindowSurface {
public:
// Create a new WindowSurface instance.
// |display| is the host EGLDisplay value.
// |config| is the host EGLConfig value.
// |width| and |height| are the initial size of the Pbuffer.
// Return a new WindowSurface instance on success, or NULL on failure.
static WindowSurface* create(EGLDisplay display,
EGLConfig config,
int width,
int height);
// Destructor.
~WindowSurface();
// Retrieve the host EGLSurface of the WindowSurface's Pbuffer.
EGLSurface getEGLSurface() const { return mSurface; }
// Attach a ColorBuffer to this WindowSurface.
// Once attached, calling flushColorBuffer() will copy the Pbuffer's
// pixels to the color buffer.
//
// IMPORTANT: This automatically resizes the Pbuffer's to the ColorBuffer's
// dimensions. Potentially losing pixel values in the process.
void setColorBuffer(ColorBufferPtr p_colorBuffer);
// Copy the Pbuffer's pixels to the attached color buffer.
// Returns true on success, or false on error (e.g. if there is no
// attached color buffer).
bool flushColorBuffer();
// Used by bind() below.
enum BindType {
BIND_READ,
BIND_DRAW,
BIND_READDRAW
};
// TODO(digit): What is this used for exactly? For example, the
// mReadContext is never used by this class. The mDrawContext is only
// used temporarily during flushColorBuffer() operation, and could be
// passed as a parameter to the function instead. Maybe this is only used
// to increment reference counts on the smart pointers.
//
// Bind a context to the WindowSurface (huh? Normally you would bind a
// surface to the context, not the other way around)
//
// |p_ctx| is a RenderContext pointer.
// |p_bindType| is the type of bind. For BIND_READ, this assigns |p_ctx|
// to mReadContext, for BIND_DRAW, it assigns it to mDrawContext, and for
// for BIND_READDRAW, it assigns it to both.
void bind(RenderContextPtr p_ctx, BindType p_bindType);
private:
WindowSurface();
WindowSurface(const WindowSurface& other);
explicit WindowSurface(EGLDisplay display, EGLConfig config);
bool resize(unsigned int p_width, unsigned int p_height);
private:
EGLSurface mSurface;
ColorBufferPtr mAttachedColorBuffer;
RenderContextPtr mReadContext;
RenderContextPtr mDrawContext;
GLuint mWidth;
GLuint mHeight;
EGLConfig mConfig;
EGLDisplay mDisplay;
};
typedef emugl::SmartPtr<WindowSurface> WindowSurfacePtr;
#endif // _LIBRENDER_WINDOW_SURFACE_H

View file

@ -0,0 +1,338 @@
/*
* Copyright (C) 2011-2015 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.
*/
#include "OpenglRender/render_api.h"
#include "IOStream.h"
#include "RenderServer.h"
#include "RenderWindow.h"
#include "TimeUtils.h"
#include "TcpStream.h"
#ifdef _WIN32
#include "Win32PipeStream.h"
#else
#include "UnixStream.h"
#endif
#include "DispatchTables.h"
#include "OpenGLESDispatch/EGLDispatch.h"
#include "OpenGLESDispatch/GLESv1Dispatch.h"
#include "OpenGLESDispatch/GLESv2Dispatch.h"
#include "emugl/common/crash_reporter.h"
#include "emugl/common/logging.h"
#include <string.h>
GLESv2Dispatch s_gles2;
GLESv1Dispatch s_gles1;
static RenderServer* s_renderThread = NULL;
static char s_renderAddr[256];
static RenderWindow* s_renderWindow = NULL;
static IOStream *createRenderThread(int p_stream_buffer_size,
unsigned int clientFlags);
RENDER_APICALL int RENDER_APIENTRY initLibrary(void)
{
//
// Load EGL Plugin
//
if (!init_egl_dispatch()) {
// Failed to load EGL
printf("Failed to init_egl_dispatch\n");
return false;
}
//
// Load GLES Plugin
//
if (!gles1_dispatch_init(&s_gles1)) {
// Failed to load GLES
ERR("Failed to gles1_dispatch_init\n");
return false;
}
/* failure to init the GLES2 dispatch table is not fatal */
if (!gles2_dispatch_init(&s_gles2)) {
ERR("Failed to gles2_dispatch_init\n");
return false;
}
return true;
}
RENDER_APICALL int RENDER_APIENTRY initOpenGLRenderer(
int width, int height, bool useSubWindow, char* addr, size_t addrLen,
emugl_logger_struct logfuncs, emugl_crash_func_t crashfunc) {
set_emugl_crash_reporter(crashfunc);
set_emugl_logger(logfuncs.coarse);
set_emugl_cxt_logger(logfuncs.fine);
//
// Fail if renderer is already initialized
//
if (s_renderThread) {
return false;
}
// kUseThread is used to determine whether the RenderWindow should use
// a separate thread to manage its subwindow GL/GLES context.
// For now, this feature is disabled entirely for the following
// reasons:
//
// - It must be disabled on Windows at all times, otherwise the main window becomes
// unresponsive after a few seconds of user interaction (e.g. trying to
// move it over the desktop). Probably due to the subtle issues around
// input on this platform (input-queue is global, message-queue is
// per-thread). Also, this messes considerably the display of the
// main window when running the executable under Wine.
//
// - On Linux/XGL and OSX/Cocoa, this used to be necessary to avoid corruption
// issues with the GL state of the main window when using the SDL UI.
// After the switch to Qt, this is no longer necessary and may actually cause
// undesired interactions between the UI thread and the RenderWindow thread:
// for example, in a multi-monitor setup the context might be recreated when
// dragging the window between monitors, triggering a Qt-specific callback
// in the context of RenderWindow thread, which will become blocked on the UI
// thread, which may in turn be blocked on something else.
bool kUseThread = false;
//
// initialize the renderer and listen to connections
// on a thread in the current process.
//
s_renderWindow = new RenderWindow(width, height, kUseThread, useSubWindow);
if (!s_renderWindow) {
ERR("Could not create rendering window class");
GL_LOG("Could not create rendering window class");
return false;
}
if (!s_renderWindow->isValid()) {
ERR("Could not initialize emulated framebuffer\n");
delete s_renderWindow;
s_renderWindow = NULL;
return false;
}
s_renderThread = RenderServer::create(addr, addrLen);
if (!s_renderThread) {
return false;
}
strncpy(s_renderAddr, addr, sizeof(s_renderAddr));
s_renderThread->start();
GL_LOG("OpenGL renderer initialized successfully");
return true;
}
RENDER_APICALL void RENDER_APIENTRY setPostCallback(
OnPostFn onPost, void* onPostContext) {
if (s_renderWindow) {
s_renderWindow->setPostCallback(onPost, onPostContext);
} else {
ERR("Calling setPostCallback() before creating render window!");
}
}
RENDER_APICALL void RENDER_APIENTRY getHardwareStrings(
const char** vendor,
const char** renderer,
const char** version) {
if (s_renderWindow &&
s_renderWindow->getHardwareStrings(vendor, renderer, version)) {
return;
}
*vendor = *renderer = *version = NULL;
}
RENDER_APICALL int RENDER_APIENTRY stopOpenGLRenderer(void)
{
bool ret = false;
// open a dummy connection to the renderer to make it
// realize the exit request.
// (send the exit request in clientFlags)
IOStream *dummy = createRenderThread(8, IOSTREAM_CLIENT_EXIT_SERVER);
if (!dummy) return false;
if (s_renderThread) {
// wait for the thread to exit
ret = s_renderThread->wait(NULL);
delete s_renderThread;
s_renderThread = NULL;
}
if (s_renderWindow != NULL) {
delete s_renderWindow;
s_renderWindow = NULL;
}
delete dummy;
return ret;
}
RENDER_APICALL bool RENDER_APIENTRY showOpenGLSubwindow(
FBNativeWindowType window_id,
int wx,
int wy,
int ww,
int wh,
int fbw,
int fbh,
float dpr,
float zRot)
{
RenderWindow* window = s_renderWindow;
if (window) {
return window->setupSubWindow(window_id,wx,wy,ww,wh,fbw,fbh,dpr,zRot);
}
// XXX: should be implemented by sending the renderer process
// a request
ERR("%s not implemented for separate renderer process !!!\n",
__FUNCTION__);
return false;
}
RENDER_APICALL bool RENDER_APIENTRY destroyOpenGLSubwindow(void)
{
RenderWindow* window = s_renderWindow;
if (window) {
return window->removeSubWindow();
}
// XXX: should be implemented by sending the renderer process
// a request
ERR("%s not implemented for separate renderer process !!!\n",
__FUNCTION__);
return false;
}
RENDER_APICALL void RENDER_APIENTRY setOpenGLDisplayRotation(float zRot)
{
RenderWindow* window = s_renderWindow;
if (window) {
window->setRotation(zRot);
return;
}
// XXX: should be implemented by sending the renderer process
// a request
ERR("%s not implemented for separate renderer process !!!\n",
__FUNCTION__);
}
RENDER_APICALL void RENDER_APIENTRY setOpenGLDisplayTranslation(float px, float py)
{
RenderWindow* window = s_renderWindow;
if (window) {
window->setTranslation(px, py);
return;
}
// XXX: should be implemented by sending the renderer process
// a request
ERR("%s not implemented for separate renderer process !!!\n",
__FUNCTION__);
}
RENDER_APICALL void RENDER_APIENTRY repaintOpenGLDisplay(void)
{
RenderWindow* window = s_renderWindow;
if (window) {
window->repaint();
return;
}
// XXX: should be implemented by sending the renderer process
// a request
ERR("%s not implemented for separate renderer process !!!\n",
__FUNCTION__);
}
/* NOTE: For now, always use TCP mode by default, until the emulator
* has been updated to support Unix and Win32 pipes
*/
#define DEFAULT_STREAM_MODE RENDER_API_STREAM_MODE_TCP
int gRendererStreamMode = DEFAULT_STREAM_MODE;
IOStream *createRenderThread(int p_stream_buffer_size, unsigned int clientFlags)
{
SocketStream* stream = NULL;
if (gRendererStreamMode == RENDER_API_STREAM_MODE_TCP) {
stream = new TcpStream(p_stream_buffer_size);
} else {
#ifdef _WIN32
stream = new Win32PipeStream(p_stream_buffer_size);
#else /* !_WIN32 */
stream = new UnixStream(p_stream_buffer_size);
#endif
}
if (!stream) {
ERR("createRenderThread failed to create stream\n");
return NULL;
}
if (stream->connect(s_renderAddr) < 0) {
ERR("createRenderThread failed to connect\n");
delete stream;
return NULL;
}
//
// send clientFlags to the renderer
//
unsigned int *pClientFlags =
(unsigned int *)stream->allocBuffer(sizeof(unsigned int));
*pClientFlags = clientFlags;
stream->commitBuffer(sizeof(unsigned int));
return stream;
}
RENDER_APICALL int RENDER_APIENTRY setStreamMode(int mode)
{
switch (mode) {
case RENDER_API_STREAM_MODE_DEFAULT:
mode = DEFAULT_STREAM_MODE;
break;
case RENDER_API_STREAM_MODE_TCP:
break;
#ifndef _WIN32
case RENDER_API_STREAM_MODE_UNIX:
break;
#else /* _WIN32 */
case RENDER_API_STREAM_MODE_PIPE:
break;
#endif /* _WIN32 */
default:
// Invalid stream mode
return false;
}
gRendererStreamMode = mode;
return true;
}

View file

@ -0,0 +1,129 @@
!render_api
%#include "OpenglRender/render_api_platform_types.h"
%
%#include <stdbool.h>
%#include <stddef.h>
%#include <stdint.h>
%
%/* list of constants to be passed to setStreamMode */
%#define RENDER_API_STREAM_MODE_DEFAULT 0
%#define RENDER_API_STREAM_MODE_TCP 1
%#define RENDER_API_STREAM_MODE_UNIX 2
%#define RENDER_API_STREAM_MODE_PIPE 3
%
%typedef void (*OnPostFn)(void* context, int width, int height, int ydir,
% int format, int type, unsigned char* pixels);
# Initialize the library and tries to load the corresponding EGL/GLES
# translation libraries. Must be called before anything else to ensure that
# everything works. Returns 0 on success, error code otherwise.
# If it returns an error, you cannot use the library at all.
int initLibrary(void);
# Change the stream mode. This must be called before initOpenGLRenderer()
# |mode| is one of STREAM_DEFAULT, STREAM_UNIX, STREAM_TCP or STREAM_PIPE.
int setStreamMode(int mode);
# initOpenGLRenderer - initialize the OpenGL renderer process.
#
# width and height are the framebuffer dimensions that will be reported to the
# guest display driver.
#
# useSubWindow is true to indicate that createOpenGLSubWindow() will be called
# later. If false, only setPostCallback() is supported.
#
# addr is a buffer of addrLen bytes that will receive the address that clients
# should connect to. The interpretation depends on the transport:
# - TCP: The buffer contains the port number as a string. The server is
# listening only on the loopback address.
# - Win32 and UNIX named pipes: The buffer contains the full path clients
# should connect to.
#
# This function is *NOT* thread safe and should be called first
# to initialize the renderer after initLibrary().
int initOpenGLRenderer(int width, int height, bool useSubWindow, char* addr, size_t addrLen, emugl_logger_struct logfuncs);
# getHardwareStrings - describe the GPU hardware and driver.
# The underlying GL's vendor/renderer/version strings are returned to the
# caller. The pointers become invalid after a call to stopOpenGLRenderer().
void getHardwareStrings(const char** vendor, const char** renderer, const char** version);
# A per-frame callback can be registered with setPostCallback(); to remove it
# pass NULL for both parameters. While a callback is registered, the renderer
# will call it just before each new frame is displayed, providing a copy of
# the framebuffer contents.
#
# The callback will be called from one of the renderer's threads, so will
# probably need synchronization on any data structures it modifies. The
# pixels buffer may be overwritten as soon as the callback returns; if it
# needs the pixels afterwards it must copy them.
#
# The pixels buffer is intentionally not const: the callback may modify the
# data without copying to another buffer if it wants, e.g. in-place RGBA to
# RGB conversion, or in-place y-inversion.
#
# Parameters are:
# context The pointer optionally provided when the callback was
# registered. The client can use this to pass whatever
# information it wants to the callback.
# width, height Dimensions of the image, in pixels. Rows are tightly
# packed; there is no inter-row padding.
# ydir Indicates row order: 1 means top-to-bottom order, -1 means
# bottom-to-top order.
# format, type Format and type GL enums, as used in glTexImage2D() or
# glReadPixels(), describing the pixel format.
# pixels The framebuffer image.
#
# In the first implementation, ydir is always -1 (bottom to top), format and
# type are always GL_RGBA and GL_UNSIGNED_BYTE, and the width and height will
# always be the same as the ones passed to initOpenGLRenderer().
void setPostCallback(OnPostFn onPost, void* onPostContext);
# showOpenGLSubwindow -
# Create or modify a native subwindow which is a child of 'window'
# to be used for framebuffer display. If a subwindow already exists,
# its properties will be updated to match the given parameters.
# wx,wy is the top left corner of the rendering subwindow.
# ww,wh are the dimensions of the rendering subwindow.
# fbw,fbh are the dimensions of the underlying guest framebuffer.
# dpr is the device pixel ratio, which is needed for higher density
# displays like retina.
# zRot is the rotation to apply on the framebuffer display image.
#
# Return true on success, false on failure, which can happen when using
# a software-only renderer like OSMesa. In this case, the client should
# call setPostCallback to get the content of each new frame when it is
# posted, and will be responsible for displaying it.
bool showOpenGLSubwindow(FBNativeWindowType window, int wx, int wy, int ww, int wh, int fbw, int fbh, float dpr, float zRot);
# destroyOpenGLSubwindow -
# destroys the created native subwindow. Once destroyed,
# Framebuffer content will not be visible until a new
# subwindow will be created.
# Return true on success, false otherwise.
bool destroyOpenGLSubwindow(void);
# setOpenGLDisplayRotation -
# set the framebuffer display image rotation in units
# of degrees around the z axis
void setOpenGLDisplayRotation(float zRot);
# setOpenGLDisplayTranslation
# change what coordinate of the guest framebuffer will be displayed at the
# corner of the GPU sub-window. Specifically, |px| and |py| = 0 means
# align the bottom-left of the framebuffer with the bottom-left of the
# sub-window, and |px| and |py| = 1 means align the top right of the
# framebuffer with the top right of the sub-window. Intermediate values
# interpolate between these states.
void setOpenGLDisplayTranslation(float px, float py);
# repaintOpenGLDisplay -
# causes the OpenGL subwindow to get repainted with the
# latest framebuffer content.
void repaintOpenGLDisplay(void);
# stopOpenGLRenderer - stops the OpenGL renderer process.
# This functions is#NOT* thread safe and should be called
# only if previous initOpenGLRenderer has returned true.
int stopOpenGLRenderer(void);