提交 7ce2da3b 编写于 作者: S Simon Fels

Rework client renderer setup

This gets us rid of one unneeded socket based communication point which
we kept around for historic reasons (from Android QEMU) but don't really
need in our case. This allows a simplication of our setup and improves
performance.
上级 aa23c349
......@@ -3,6 +3,7 @@
set(CMAKE_C_FLAGS "-Wall")
include_directories(
${CMAKE_SOURCE_DIR}/src
${CMAKE_SOURCE_DIR}/external/android-emugl/shared
${CMAKE_SOURCE_DIR}/external/android-emugl/host/include
${CMAKE_SOURCE_DIR}/external/android-emugl/shared/OpenglCodecCommon
......
/*
* 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.
*/
#pragma once
#include "OpenglRender/render_api_functions.h"
#include <KHR/khrplatform.h>
/* This header and its declarations must be usable from C code.
*
* If RENDER_API_NO_PROTOTYPES is #defined before including this header, only
* the interface function pointer types will be declared, not the prototypes.
* This allows the client to use those names for its function pointer variables.
*
* All interfaces which can fail return an int, with zero indicating failure
* and anything else indicating success.
*/
#ifdef __cplusplus
extern "C" {
#endif
// Use KHRONOS_APICALL to control visibility, but do not use KHRONOS_APIENTRY
// because we don't need the functions to be __stdcall on Win32.
#define RENDER_APICALL KHRONOS_APICALL
#define RENDER_APIENTRY
#define RENDER_API_DECLARE(return_type, func_name, signature, callargs) \
typedef return_type (RENDER_APIENTRY *func_name ## Fn) signature; \
RENDER_APICALL return_type RENDER_APIENTRY func_name signature;
typedef void (*emugl_logger_func_t)(const char* fmt, ...);
typedef struct {
emugl_logger_func_t coarse;
emugl_logger_func_t fine;
} emugl_logger_struct;
LIST_RENDER_API_FUNCTIONS(RENDER_API_DECLARE)
#ifdef __cplusplus
}
#endif
// Auto-generated with: android/scripts/gen-entries.py --mode=funcargs distrib/android-emugl/host/libs/libOpenglRender/render_api.entries --output=distrib/android-emugl/host/include/OpenglRender/render_api_functions.h
// DO NOT EDIT THIS FILE
#ifndef RENDER_API_FUNCTIONS_H
#define RENDER_API_FUNCTIONS_H
#include "OpenglRender/render_api_platform_types.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <EGL/egl.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);
typedef void (*emugl_crash_func_t)(const char* format, ...);
#define LIST_RENDER_API_FUNCTIONS(X) \
X(int, initLibrary, (), ()) \
X(int, setStreamMode, (int mode), (mode)) \
X(int, initOpenGLRenderer, (EGLNativeDisplayType native_display, char* addr, size_t addrLen, emugl_logger_struct logfuncs, emugl_crash_func_t crashfunc), (native_display, addr, addrLen, logfuncs, crashfunc)) \
X(void, getHardwareStrings, (const char** vendor, const char** renderer, const char** version), (vendor, renderer, version)) \
X(void, setPostCallback, (OnPostFn onPost, void* onPostContext), (onPost, onPostContext)) \
X(bool, showOpenGLSubwindow, (FBNativeWindowType window, int wx, int wy, int ww, int wh, int fbw, int fbh, float dpr, float zRot), (window, wx, wy, ww, wh, fbw, fbh, dpr, zRot)) \
X(bool, destroyOpenGLSubwindow, (), ()) \
X(void, setOpenGLDisplayRotation, (float zRot), (zRot)) \
X(void, setOpenGLDisplayTranslation, (float px, float py), (px, py)) \
X(void, repaintOpenGLDisplay, (), ()) \
X(int, stopOpenGLRenderer, (), ()) \
#endif // RENDER_API_FUNCTIONS_H
/*
* Copyright 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.
*/
#pragma once
// Define FBNativeWindowType which corresponds to the type of native
// host UI window handles.
#if defined(_WIN32) || defined(__VC32__) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) /* Win32 and WinCE */
#include <windows.h>
typedef HWND FBNativeWindowType;
#elif defined(__linux__)
// Really a Window handle, but we don't want to include the X11 headers here.
#include <stdint.h>
typedef uint32_t FBNativeWindowType;
#elif defined(__APPLE__)
typedef void* FBNativeWindowType;
#else
#warning "Unsupported platform"
#endif
......@@ -23,7 +23,6 @@
class IOStream {
public:
IOStream(size_t bufSize) {
m_buf = NULL;
m_bufsize = bufSize;
......@@ -31,19 +30,15 @@ public:
}
virtual void *allocBuffer(size_t minSize) = 0;
virtual int commitBuffer(size_t size) = 0;
virtual const unsigned char *readFully( void *buf, size_t len) = 0;
virtual const unsigned char *read( void *buf, size_t *inout_len) = 0;
virtual int writeFully(const void* buf, size_t len) = 0;
virtual size_t commitBuffer(size_t size) = 0;
virtual const unsigned char *read(void *buf, size_t *inout_len) = 0;
virtual void forceStop() = 0;
virtual ~IOStream() {
// NOTE: m_buf is 'owned' by the child class thus we expect it to be released by it
}
unsigned char *alloc(size_t len) {
if (m_buf && len > m_free) {
if (flush() < 0) {
ERR("Failed to flush in alloc\n");
......@@ -70,7 +65,6 @@ public:
}
int flush() {
if (!m_buf || m_free == m_bufsize) return 0;
int stat = commitBuffer(m_bufsize - m_free);
......@@ -79,12 +73,6 @@ public:
return stat;
}
const unsigned char *readback(void *buf, size_t len) {
flush();
return readFully(buf, len);
}
private:
unsigned char *m_buf;
size_t m_bufsize;
......
......@@ -67,6 +67,8 @@ set(SOURCES
anbox/common/variable_length_array.h
anbox/common/wait_handle.cpp
anbox/common/dispatcher.cpp
anbox/common/small_vector.h
anbox/common/type_traits.h
anbox/container/service.cpp
anbox/container/client.cpp
......@@ -104,6 +106,8 @@ set(SOURCES
anbox/rpc/make_protobuf_object.h
anbox/graphics/opengles_message_processor.cpp
anbox/graphics/buffer_queue.cpp
anbox/graphics/buffered_io_stream.cpp
anbox/graphics/gl_renderer_server.cpp
anbox/graphics/density.h
anbox/graphics/rect.cpp
......@@ -120,16 +124,11 @@ set(SOURCES
anbox/graphics/emugl/RenderApi.cpp
anbox/graphics/emugl/RenderContext.cpp
anbox/graphics/emugl/RenderControl.cpp
anbox/graphics/emugl/RenderServer.cpp
anbox/graphics/emugl/RenderThread.cpp
anbox/graphics/emugl/RenderThreadInfo.cpp
anbox/graphics/emugl/RenderWindow.cpp
anbox/graphics/emugl/SocketStream.cpp
anbox/graphics/emugl/TcpStream.cpp
anbox/graphics/emugl/TextureDraw.cpp
anbox/graphics/emugl/TextureResize.cpp
anbox/graphics/emugl/TimeUtils.cpp
anbox/graphics/emugl/UnixStream.cpp
anbox/graphics/emugl/WindowSurface.cpp
anbox/wm/display.cpp
......
......@@ -128,8 +128,7 @@ anbox::cmds::Run::Run(const BusFactory &bus_factory)
auto qemu_pipe_connector =
std::make_shared<network::PublishedSocketConnector>(
utils::string_format("%s/qemu_pipe", config::socket_path()), rt,
std::make_shared<qemu::PipeConnectionCreator>(
rt, renderer->socket_path(), icon_));
std::make_shared<qemu::PipeConnectionCreator>(rt));
auto bridge_connector = std::make_shared<network::PublishedSocketConnector>(
utils::string_format("%s/anbox_bridge", config::socket_path()), rt,
......
// Copyright 2016 The Android Open Source Project
//
// This software is licensed under the terms of the GNU General Public
// License version 2, as published by the Free Software Foundation, and
// may be copied, distributed, and modified under those terms.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
#pragma once
#include "anbox/common/type_traits.h"
#include <algorithm>
#include <initializer_list>
#include <type_traits>
#include <utility>
#include <stddef.h>
#include <stdlib.h>
//
// SmallVector<T>, SmallFixedVector<T, SmallSize>
//
// This header defines a replacement for a std::vector<> that uses small buffer
// optimization technique - for some preset number of elements |SmallSize| it
// stores them inside of the object, and falls back to the dynamically allocated
// array only if one needs to add more elements.
// This is useful for the performance-critical places where common number of
// processed items is small, but it may still be quite large for a stack array.
//
// SmallFixedVector<> is the class you use to store elements, while
// SmallVector<> is its base class that erases the small size from the type.
//
// NOTE: SmallVector<> cannot guarantee std::vector<>'s iterator invalidation
// rules for move() and swap() operations - std::vector<>s just exchange
// their iterators on swap() and pass the moved ones over, while SmallVector<>
// may leave the iterators pointing to nowhere if they were for the in-place
// array storage.
//
// Currenly only a limited subset of std::vector<>'s operations is implemented,
// but fill free to add the ones you need.
//
namespace anbox {
namespace common {
//
// Forward-declare the 'real' small vector class.
template <class T, size_t S>
class SmallFixedVector;
//
// SmallVector<T> - an interface for a small-buffer-optimized vector.
// It hides the fixed size from its type, so one can use it to pass small
// vectors around and not leak the buffer size to all callers:
//
// void process(SmallVector<Foo>& data);
// ...
// ...
// SmallFixedVector<Foo, 100> aLittleBitOfFoos = ...;
// process(aLittleBitOfFoos);
// ...
// SmallFixedVector<Foo, 1000> moreFoos = ...;
// process(moreFoos);
//
template <class T>
class SmallVector {
// Make them friends so SmallFixedVector is able to refer to SmallVector's
// protected members in static_assert()s.
template <class U, size_t S>
friend class SmallFixedVector;
public:
// Common set of type aliases.
using value_type = T;
using iterator = T*;
using const_iterator = const T*;
using pointer = T*;
using const_pointer = const T*;
using reference = T&;
using const_reference = const T&;
using size_type = size_t;
// It's ok to delete SmallVector<> through the base class - dtor() actually
// takes care of all living elements and the allocated memory.
~SmallVector() { dtor(); }
// std::vector<> interface operations.
iterator begin() { return mBegin; }
const_iterator begin() const { return mBegin; }
const_iterator cbegin() const { return mBegin; }
iterator end() { return mEnd; }
const_iterator end() const { return mEnd; }
const_iterator cend() const { return mEnd; }
size_type size() const { return end() - begin(); }
size_type capacity() const { return mCapacity; }
bool empty() const { return begin() == end(); }
reference operator[](size_t i) { return *(begin() + i); }
const_reference operator[](size_t i) const { return *(cbegin() + i); }
pointer data() { return mBegin; }
const_pointer data() const { return mBegin; }
const_pointer cdata() const { return mBegin; }
template <class... Args>
void emplace_back(Args&&... args) {
grow_for_size(size() + 1);
new (mEnd) T(std::forward<Args>(args)...);
++mEnd;
}
void push_back(const T& t) { emplace_back(t); }
void push_back(T&& t) { emplace_back(std::move(t)); }
void clear() {
destruct(begin(), end());
mEnd = mBegin;
}
void reserve(size_type newCap) {
if (newCap <= this->capacity()) {
return;
}
set_capacity(newCap);
}
void resize(size_type newSize) { resize_impl<true>(newSize); }
// This version of resizing doesn't initialize the newly allocated elements
// Useful for the cases when value-initialization is noticeably slow and
// one wants to directly construct or memcpy the elements into the resized
// place.
void resize_noinit(size_type newSize) { resize_impl<false>(newSize); }
// Returns if the current vector's buffer is dynamically allocated.
bool isAllocated() const { return this->cbegin() != smallBufferStart(); }
protected:
// Hide the default constructor so only SmallFixedVector can be
// instantiated.
SmallVector() = default;
// Destroy all elements in the vector and free the array if it was allocated
// dynamically.
void dtor() {
this->destruct(this->begin(), this->end());
if (isAllocated()) {
free(this->mBegin);
}
}
// Just a convenience setter function to init all members at once.
void init(iterator begin, iterator end, size_type capacity) {
this->mBegin = begin;
this->mEnd = end;
this->mCapacity = capacity;
}
// An implementation of different resizing versions.
template <bool init>
void resize_impl(size_type newSize) {
if (newSize < this->size()) {
const auto newEnd = this->begin() + newSize;
this->destruct(newEnd, this->end());
this->mEnd = newEnd;
} else if (newSize > this->size()) {
grow_for_size(newSize);
const auto newEnd = this->begin() + newSize;
if (init) {
std::uninitialized_fill(this->end(), newEnd, T());
}
this->mEnd = newEnd;
}
}
// Templated append operation for a range of elements.
template <class Iter>
void insert_back(Iter b, Iter e) {
if (b == e) {
return;
}
const auto newSize = this->size() + (e - b);
grow_for_size(newSize);
this->mEnd = std::uninitialized_copy(b, e, this->mEnd);
}
// Multiplicative grow for the internal array so it can hold |newSize|
// elements.
// Doesn't change size(), only capacity().
void grow_for_size(size_type newSize) {
// Grow by 1.5x by default.
if (newSize > capacity()) {
set_capacity(std::max(newSize, capacity() + capacity() / 2));
}
}
// Sets the capacity() to be exacly |newCap|. Allocates the array
// dynamically, moves all elements over and (potentially) deallocates the
// old array.
// Doesn't change size(), only capacity().
void set_capacity(size_type newCap) {
// Here we can only be switching to the dynamic vector, as static one
// always has its capacity on the maximum.
const auto newBegin = (T*)malloc(sizeof(T) * newCap);
if (!newBegin) {
abort(); // what else can we do here?
}
const auto newEnd =
std::uninitialized_copy(std::make_move_iterator(this->begin()),
std::make_move_iterator(this->end()), newBegin);
dtor();
this->mBegin = newBegin;
this->mEnd = newEnd;
this->mCapacity = newCap;
}
// A convenience function to call destructor for a range of elements.
static void destruct(T* b, T* e) {
if (!std::is_trivially_destructible<T>::value) {
for (; b != e; ++b) {
b->~T();
}
}
}
// By design of the class, SmallFixedVector<> will be inheriting from
// SmallVector<>, so its in-place storage array is going to be the very next
// member after the last one here.
// This function returns that address, and SmallFixedVector<> has a static
// assert to make sure it remains correct.
constexpr const void* smallBufferStart() const {
return (const void*)(&mCapacity + 1);
}
// Standard set of members for a vector - begin, end and capacity.
// These point to the currently used chunk of memory, no matter if it's a
// heap-allocated one or an in-place array.
iterator mBegin;
iterator mEnd;
size_type mCapacity;
};
// The implementation of a SmallVector with a fixed in-place size, |SmallSize|.
template <class T, size_t SmallSize>
class SmallFixedVector : public SmallVector<T> {
using base = SmallVector<T>;
public:
// Grab these from the base class.
using value_type = typename base::value_type;
using iterator = typename base::iterator;
using const_iterator = typename base::const_iterator;
using pointer = typename base::pointer;
using const_pointer = typename base::const_pointer;
using reference = typename base::reference;
using const_reference = typename base::const_reference;
using size_type = typename base::size_type;
static constexpr size_type kSmallSize = SmallSize;
// Default constructor - set up an empty vector with capacity at full
// internal array size.
SmallFixedVector() {
// Make sure that the small array starts exactly where base class
// expects it: right after the |mCapacity|.
static_assert(offsetof(base, mCapacity) + sizeof(base::mCapacity) ==
offsetof(SmallFixedVector, mData) &&
offsetof(Data, array) == 0,
"SmallFixedVector<> class layout is wrong, "
"|mData| needs to follow |mCapacity|");
init_inplace();
}
// Ctor from a range of iterators
template <class Iter>
SmallFixedVector(Iter b, Iter e) : SmallFixedVector() {
this->insert_back(b, e);
}
// Ctor from a range - anything that has begin and end.
// Note: template constructor is never a copy/move-ctor.
template <class Range, class = enable_if_c<!std::is_same<Range, T>::value &&
is_range<Range>::value>>
explicit SmallFixedVector(const Range& r)
: SmallFixedVector(std::begin(r), std::end(r)) {}
template <class Range, class = enable_if_c<!std::is_same<Range, T>::value &&
is_range<Range>::value>>
explicit SmallFixedVector(Range&& r)
: SmallFixedVector(std::make_move_iterator(std::begin(r)),
std::make_move_iterator(std::end(r))) {}
template <class U, class = enable_if_convertible<U, T>>
SmallFixedVector(std::initializer_list<U> list)
: SmallFixedVector(std::begin(list), std::end(list)) {}
SmallFixedVector(const SmallFixedVector& other)
: SmallFixedVector(other.begin(), other.end()) {}
SmallFixedVector(SmallFixedVector&& other) {
if (other.isAllocated()) {
// Just steal the allocated memory from the |other|.
this->mBegin = other.mBegin;
this->mEnd = other.mEnd;
this->mCapacity = other.mCapacity;
other.init_inplace();
} else {
// Have to move individual elements.
this->mBegin = mData.array;
this->mEnd = std::uninitialized_copy(
std::make_move_iterator(other.begin()),
std::make_move_iterator(other.end()), this->begin());
this->mCapacity = kSmallSize;
}
}
SmallFixedVector& operator=(const SmallFixedVector& other) {
if (&other != this) {
this->clear();
this->insert_back(other.begin(), other.end());
}
return *this;
}
SmallFixedVector& operator=(SmallFixedVector&& other) {
if (other.isAllocated()) {
// Steal it and we're done.
this->dtor();
this->mBegin = other.mBegin;
this->mEnd = other.mEnd;
this->mCapacity = other.mCapacity;
other.init_inplace();
return *this;
}
if (this->isAllocated() && this->mCapacity < other.size()) {
// Not enough dynamic memory, switch to in-place.
this->dtor();
init_inplace();
} else {
// This could potentially be improved by move-assigning
// only needed items and destroying the rest, but
// destroy-all+construct-all is just simpler. For PODs it actually
// is even faster as it's always a single memcpy().
this->destruct(this->begin(), this->end());
}
// Move the whole |other| into the pre-cleaned memory
const auto newEnd = std::uninitialized_copy(
std::make_move_iterator(other.begin()),
std::make_move_iterator(other.end()), this->mBegin);
this->mEnd = newEnd;
// |other| is valid as-is.
return *this;
}
// Make sure we don't end up trying to move from an interface - it's just
// inefficient with the current code.
SmallFixedVector(base&& other) = delete;
SmallFixedVector& operator=(base&& other) = delete;
private:
// A shortcut for initialization for in-place storage.
void init_inplace() { this->init(mData.array, mData.array, kSmallSize); }
// A union with empty constructor and destructor makes sure that the array
// elements are not default-constructed in ctor and not destructed in dtor:
// the class needs to be able manage their lifetime more precisely.
union Data {
alignas(size_type) T array[kSmallSize];
Data() {}
~Data() {}
} mData;
};
} // namespace common
} // namespace anbox
// Copyright 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 <iterator>
#include <type_traits>
namespace anbox {
namespace common {
namespace details {
// a simple helper class for SFINAE below.
template <class X = void>
struct dummy {
using type = X;
};
} // namespaces details
// add some convenience shortcuts for an overly complex std::enable_if syntax
// Use 'enable_if<Predicate,Type>' instead of
// 'typename std::enable_if<Predicate::value,Type>::type'
template <class Predicate, class Type = void*>
using enable_if = typename std::enable_if<Predicate::value, Type>::type;
// Use 'enable_if_c<BooleanFlag,Type>' instead of
// 'typename std::enable_if<BooleanFlag,Type>::type'
template <bool predicate, class Type = void*>
using enable_if_c = typename std::enable_if<predicate, Type>::type;
// Use 'enable_if_convertible<From,To,Type>' instead of
// 'typename std::enable_if<std::is_convertible<From,To>::value, Type>::type'
template <class From, class To, class Type = void*>
using enable_if_convertible = enable_if<std::is_convertible<From, To>>;
// -----------------------------------------------------------------------------
// A predicate for checking if some object is callable with a specific
// signature. Examples:
//
// is_callable_as<int, void()>::value == false.
// is_callable_as<strcmp, void()>::value == false.
// is_callable_as<strcmp, int(const char*, const char*)>::value == true
//
template <class F, class Signature, class X = void>
struct is_callable_as : std::false_type {};
// This specialization is SFINAE-d out if template arguments can't be combined
// into a call expression F(), or if the result of that call is not |R|
template <class F, class R>
struct is_callable_as<
F, R(), typename std::enable_if<std::is_same<
typename details::dummy<decltype(std::declval<F>()())>::type,
R>::value>::type> : std::true_type {};
// One more specialization, for non empty argument list
template <class F, class R, class... Args>
struct is_callable_as<F, R(Args...),
typename std::enable_if<std::is_same<
typename details::dummy<decltype(std::declval<F>()(
std::declval<Args>()...))>::type,
R>::value>::type> : std::true_type {};
// -----------------------------------------------------------------------------
// Check if a type |T| is any instantiation of a template |U|. Examples:
//
// is_template_instantiation_of<int, std::vector>::value == false
// is_template_instantiation_of<
// std::list<std::vector<int>>, std::vector>::value == false
// is_template_instantiation_of<std::vector<int>, std::vector>::value == true
// is_template_instantiation_of<
// std::vector<std::vector<int>>, std::vector>::value == true
//
template <class T, template <class...> class U>
struct is_template_instantiation_of : std::false_type {};
template <template <class...> class U, class... Args>
struct is_template_instantiation_of<U<Args...>, U> : std::true_type {};
// -----------------------------------------------------------------------------
//
// is_range<T> - check if type |T| is a range-like type.
//
// It makes sure that expressions std::begin(t) and std::end(t) are well-formed
// and those return the same type.
//
// Note: with expression SFINAE from C++14 is_range_helper<> could be renamed to
// is_range<> with no extra code. C++11 needs an extra level of enable_if<>
// to make it work when the type isn't a range.
//
namespace details {
template <class T>
using is_range_helper = std::is_same<
decltype(std::begin(
std::declval<typename std::add_lvalue_reference<T>::type>())),
decltype(
std::end(std::declval<typename std::add_lvalue_reference<T>::type>()))>;
} // namespace details
template <class T, class = void>
struct is_range : std::false_type {};
template <class T>
struct is_range<
T, typename std::enable_if<details::is_range_helper<T>::value>::type>
: std::true_type {};
} // namespace common
} // namespace anbox
/*
* Copyright (C) 2016 Simon Fels <morphis@gravedo.de>
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3, as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranties of
* MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "anbox/graphics/buffer_queue.h"
namespace anbox {
namespace graphics {
BufferQueue::BufferQueue(size_t capacity)
: capacity_(capacity), buffers_(new Buffer[capacity]) {}
BufferQueue::Result BufferQueue::try_push_locked(Buffer &&buffer) {
if (closed_) {
return Result::Error;
}
if (count_ >= capacity_) {
return Result::TryAgain;
}
size_t pos = pos_ + count_;
if (pos >= capacity_) {
pos -= capacity_;
}
buffers_[pos] = std::move(buffer);
if (count_++ == 0) {
can_pop_.notify_one();
}
return Result::Ok;
}
BufferQueue::Result BufferQueue::push_locked(
Buffer &&buffer, std::unique_lock<std::mutex> &lock) {
while (count_ == capacity_) {
if (closed_) {
return Result::Error;
}
can_push_.wait(lock);
}
return try_push_locked(std::move(buffer));
}
BufferQueue::Result BufferQueue::try_pop_locked(Buffer *buffer) {
if (count_ == 0) {
return closed_ ? Result::Error : Result::TryAgain;
}
*buffer = std::move(buffers_[pos_]);
size_t pos = pos_ + 1;
if (pos >= capacity_) {
pos -= capacity_;
}
pos_ = pos;
if (count_-- == capacity_) {
can_push_.notify_one();
}
return Result::Ok;
}
BufferQueue::Result BufferQueue::pop_locked(
Buffer *buffer, std::unique_lock<std::mutex> &lock) {
while (count_ == 0) {
if (closed_) {
// Closed queue is empty.
return Result::Error;
}
can_pop_.wait(lock);
}
return try_pop_locked(buffer);
}
// Close the queue, it is no longer possible to push new items
// to it (i.e. push() will always return Result::Error), or to
// read from an empty queue (i.e. pop() will always return
// Result::Error once the queue becomes empty).
void BufferQueue::close_locked() {
closed_ = true;
// Wake any potential waiters.
if (count_ == capacity_) {
can_push_.notify_all();
}
if (count_ == 0) {
can_pop_.notify_all();
}
}
} // namespace graphics
} // namespace anbox
/*
* Copyright (C) 2016 Simon Fels <morphis@gravedo.de>
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3, as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranties of
* MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ANBOX_GRAPHICS_BUFFER_QUEUE_H_
#define ANBOX_GRAPHICS_BUFFER_QUEUE_H_
#include "anbox/common/small_vector.h"
#include <condition_variable>
#include <memory>
#include <mutex>
namespace anbox {
namespace graphics {
using Buffer = anbox::common::SmallFixedVector<char, 512>;
class BufferQueue {
public:
enum class Result {
Ok = 0,
TryAgain = 1,
Error = 2,
};
BufferQueue(size_t capacity);
Result try_push_locked(Buffer &&buffer);
Result push_locked(Buffer &&buffer, std::unique_lock<std::mutex> &lock);
Result try_pop_locked(Buffer *buffer);
Result pop_locked(Buffer *buffer, std::unique_lock<std::mutex> &lock);
void close_locked();
private:
size_t capacity_ = 0;
size_t pos_ = 0;
size_t count_ = 0;
bool closed_ = false;
std::unique_ptr<Buffer[]> buffers_;
std::condition_variable can_push_;
std::condition_variable can_pop_;
};
} // namespace graphics
} // namespace anbox
#endif
/*
* Copyright (C) 2016 Simon Fels <morphis@gravedo.de>
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3, as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranties of
* MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "anbox/graphics/buffered_io_stream.h"
#include "anbox/logger.h"
namespace anbox {
namespace graphics {
BufferedIOStream::BufferedIOStream(
const std::shared_ptr<anbox::network::SocketMessenger> &messenger,
size_t buffer_size)
: IOStream(buffer_size),
messenger_(messenger),
in_queue_(1024U),
out_queue_(16U),
worker_thread_(&BufferedIOStream::thread_main, this) {
write_buffer_.resize_noinit(buffer_size);
}
BufferedIOStream::~BufferedIOStream() {
forceStop();
if (worker_thread_.joinable()) worker_thread_.join();
}
void *BufferedIOStream::allocBuffer(size_t min_size) {
std::unique_lock<std::mutex> l(out_lock_);
if (write_buffer_.size() < min_size) write_buffer_.resize_noinit(min_size);
return write_buffer_.data();
}
size_t BufferedIOStream::commitBuffer(size_t size) {
std::unique_lock<std::mutex> l(out_lock_);
assert(size <= write_buffer_.size());
if (write_buffer_.isAllocated()) {
write_buffer_.resize(size);
out_queue_.push_locked(std::move(write_buffer_), l);
} else {
out_queue_.push_locked(
Buffer{write_buffer_.data(), write_buffer_.data() + size}, l);
}
return size;
}
const unsigned char *BufferedIOStream::read(void *buf, size_t *inout_len) {
std::unique_lock<std::mutex> l(lock_);
size_t wanted = *inout_len;
size_t count = 0U;
auto dst = static_cast<uint8_t *>(buf);
while (count < wanted) {
if (read_buffer_left_ > 0) {
size_t avail = std::min<size_t>(wanted - count, read_buffer_left_);
memcpy(dst + count,
read_buffer_.data() + (read_buffer_.size() - read_buffer_left_),
avail);
count += avail;
read_buffer_left_ -= avail;
continue;
}
bool blocking = (count == 0);
auto result = BufferQueue::Result::Error;
if (blocking)
result = in_queue_.pop_locked(&read_buffer_, l);
else
result = in_queue_.try_pop_locked(&read_buffer_);
if (result == BufferQueue::Result::Ok) {
read_buffer_left_ = read_buffer_.size();
continue;
}
if (count > 0) break;
// If we end up here something went wrong and we couldn't read
// any valid data.
return nullptr;
}
*inout_len = count;
return static_cast<const unsigned char *>(buf);
}
void BufferedIOStream::forceStop() {
std::lock_guard<std::mutex> l(lock_);
in_queue_.close_locked();
out_queue_.close_locked();
}
void BufferedIOStream::post_data(Buffer &&data) {
std::unique_lock<std::mutex> l(lock_);
in_queue_.push_locked(std::move(data), l);
}
void BufferedIOStream::thread_main() {
while (true) {
std::unique_lock<std::mutex> l(out_lock_);
Buffer buffer;
auto result = out_queue_.pop_locked(&buffer, l);
if (result == BufferQueue::Result::Error) break;
auto bytes_left = buffer.size();
while (bytes_left > 0) {
const auto written = messenger_->send_raw(
buffer.data() + (buffer.size() - bytes_left), bytes_left);
if (written < 0) {
if (errno != EINTR || errno != EAGAIN) {
ERROR("Failed to write data: %s", std::strerror(errno));
break;
}
// Socket is busy, lets try again
} else
bytes_left -= written;
}
}
}
} // namespace graphics
} // namespace anbox
/*
* Copyright (C) 2016 Simon Fels <morphis@gravedo.de>
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3, as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranties of
* MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ANBOX_GRAPHICS_BUFFERED_IO_STREAM_H_
#define ANBOX_GRAPHICS_BUFFERED_IO_STREAM_H_
#include "external/android-emugl/host/include/libOpenglRender/IOStream.h"
#include "anbox/graphics/buffer_queue.h"
#include "anbox/network/socket_messenger.h"
#include <memory>
#include <thread>
namespace anbox {
namespace graphics {
class BufferedIOStream : public IOStream {
public:
static const size_t default_buffer_size{384};
explicit BufferedIOStream(
const std::shared_ptr<anbox::network::SocketMessenger> &messenger,
size_t buffer_size = default_buffer_size);
virtual ~BufferedIOStream();
void *allocBuffer(size_t min_size) override;
size_t commitBuffer(size_t size) override;
const unsigned char *read(void *buf, size_t *inout_len) override;
void forceStop() override;
void post_data(Buffer &&data);
private:
void thread_main();
std::shared_ptr<anbox::network::SocketMessenger> messenger_;
std::mutex lock_;
std::mutex out_lock_;
Buffer write_buffer_;
Buffer read_buffer_;
size_t read_buffer_left_ = 0;
BufferQueue in_queue_;
BufferQueue out_queue_;
std::thread worker_thread_;
};
} // namespace graphics
} // namespace anbox
#endif
......@@ -13,15 +13,8 @@
* 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"
#include "UnixStream.h"
#include "RenderApi.h"
#include "DispatchTables.h"
......@@ -36,205 +29,23 @@
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(
EGLNativeDisplayType native_display, 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;
}
namespace anbox {
namespace graphics {
namespace emugl {
bool initialize(emugl_logger_struct log_funcs, emugl_crash_func_t crash_func) {
set_emugl_crash_reporter(crash_func);
set_emugl_logger(log_funcs.coarse);
set_emugl_cxt_logger(log_funcs.fine);
// 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;
if (!init_egl_dispatch()) return false;
//
// initialize the renderer and listen to connections
// on a thread in the current process.
//
s_renderWindow = new RenderWindow(native_display, kUseThread);
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;
}
if (!gles1_dispatch_init(&s_gles1)) return false;
s_renderThread = RenderServer::create(addr, addrLen);
if (!s_renderThread) {
return false;
}
strncpy(s_renderAddr, addr, sizeof(s_renderAddr));
if (!gles2_dispatch_init(&s_gles2)) return false;
s_renderThread->start();
GL_LOG("OpenGL renderer initialized successfully");
return true;
}
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;
}
#define DEFAULT_STREAM_MODE RENDER_API_STREAM_MODE_UNIX
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 {
stream = new UnixStream(p_stream_buffer_size);
}
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;
}
} // namespace emugl
} // namespace graphics
} // namespace anbox
/*
* Copyright (C) 2011 The Android Open Source Project
* 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.
......@@ -13,22 +13,24 @@
* 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"
#ifndef RENDER_API_H
#define RENDER_API_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);
typedef void (*emugl_logger_func_t)(const char* fmt, ...);
typedef void (*emugl_crash_func_t)(const char* format, ...);
private:
char *bound_socket_path;
UnixStream(int sock, size_t bufSize);
};
typedef struct {
emugl_logger_func_t coarse;
emugl_logger_func_t fine;
} emugl_logger_struct;
namespace anbox {
namespace graphics {
namespace emugl {
bool initialize(emugl_logger_struct log_funcs, emugl_crash_func_t crash_func);
} // namespace emugl
} // namespace graphics
} // namespace anbox
#endif
/*
* 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 <pthread.h>
#include <signal.h>
#include "RenderThread.h"
#include "TcpStream.h"
#include "UnixStream.h"
#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 {
server->m_listenSock = new UnixStream();
}
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;
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;
}
// 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);
}
//
// 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;
}
/*
* 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
......@@ -31,7 +31,7 @@
RenderThread::RenderThread(IOStream *stream, emugl::Mutex *lock)
: emugl::Thread(), m_lock(lock), m_stream(stream) {}
RenderThread::~RenderThread() { delete m_stream; }
RenderThread::~RenderThread() {}
// static
RenderThread *RenderThread::create(IOStream *stream, emugl::Mutex *lock) {
......
// 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 "Renderer.h"
#include "emugl/common/logging.h"
#include "emugl/common/message_channel.h"
#include "emugl/common/mutex.h"
#include "emugl/common/thread.h"
#include <pthread.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#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 {
EGLNativeDisplayType nativeDisplay;
} 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;
Renderer* fb;
bool result = false;
switch (msg.cmd) {
case CMD_INITIALIZE:
D("CMD_INITIALIZE\n");
GL_LOG("RenderWindow: CMD_INITIALIZE");
result = Renderer::initialize(msg.init.nativeDisplay);
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 = Renderer::get()) {
fb->finalize();
}
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");
sigset_t set;
sigfillset(&set);
pthread_sigmask(SIG_SETMASK, &set, NULL);
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(EGLNativeDisplayType native_display, bool use_thread)
: 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.nativeDisplay = native_display;
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.
Renderer* fb = Renderer::get();
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;
}
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();
}
}
// 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(EGLNativeDisplayType native_display, bool use_thread);
// 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);
// 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
......@@ -1006,10 +1006,14 @@ void Renderer::tessellate(std::vector<anbox::graphics::Primitive> &primitives,
rectangle.tex_id = 0;
rectangle.type = GL_TRIANGLE_STRIP;
GLfloat tex_left = static_cast<GLfloat>(renderable.crop().left()) / buf_size.width();
GLfloat tex_top = static_cast<GLfloat>(renderable.crop().top()) / buf_size.height();
GLfloat tex_right = static_cast<GLfloat>(renderable.crop().right()) / buf_size.width();
GLfloat tex_bottom = static_cast<GLfloat>(renderable.crop().bottom()) / buf_size.height();
GLfloat tex_left =
static_cast<GLfloat>(renderable.crop().left()) / buf_size.width();
GLfloat tex_top =
static_cast<GLfloat>(renderable.crop().top()) / buf_size.height();
GLfloat tex_right =
static_cast<GLfloat>(renderable.crop().right()) / buf_size.width();
GLfloat tex_bottom =
static_cast<GLfloat>(renderable.crop().bottom()) / buf_size.height();
auto &vertices = rectangle.vertices;
vertices[0] = {{left, top, 0.0f}, {tex_left, tex_top}};
......
......@@ -23,8 +23,6 @@
#include "WindowSurface.h"
#include "emugl/common/mutex.h"
#include "OpenglRender/render_api.h"
#include "Renderable.h"
#include "anbox/graphics/primitives.h"
......@@ -97,11 +95,6 @@ class Renderer {
// Return the list of configs available from this display.
const RendererConfigList* 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).
......@@ -268,7 +261,6 @@ class Renderer {
static HandleType s_nextHandle;
emugl::Mutex m_lock;
RendererConfigList* m_configs;
FBNativeWindowType m_nativeWindow;
RendererCaps m_caps;
EGLDisplay m_eglDisplay;
RenderContextMap m_contexts;
......
/*
* 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 <netinet/in.h>
#include <netinet/tcp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/un.h>
#include <unistd.h>
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();
if (close(m_sock) < 0) perror("Closing SocketStream failed");
// 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.
::shutdown(m_sock, SHUT_RDWR);
}
/*
* 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 */
/*
* 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 <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#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;
}
/*
* 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
/*
* 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 <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/stat.h>
#include <sys/un.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;
}
......@@ -16,17 +16,30 @@
*/
#include "anbox/graphics/gl_renderer_server.h"
#include "anbox/graphics/emugl/RenderApi.h"
#include "anbox/graphics/emugl/RenderControl.h"
#include "anbox/graphics/emugl/Renderer.h"
#include "anbox/graphics/layer_composer.h"
#include "anbox/logger.h"
#include "anbox/wm/manager.h"
#include "OpenglRender/render_api.h"
#include <boost/throw_exception.hpp>
#include <cstdarg>
#include <stdexcept>
namespace {
void logger_write(const char *format, ...) {
char message[2048];
va_list args;
va_start(args, format);
vsnprintf(message, sizeof(message) - 1, format, args);
va_end(args);
DEBUG("%s", message);
}
}
namespace anbox {
namespace graphics {
GLRendererServer::GLRendererServer(const std::shared_ptr<wm::Manager> &wm)
......@@ -53,45 +66,21 @@ GLRendererServer::GLRendererServer(const std::shared_ptr<wm::Manager> &wm)
0);
}
if (!initLibrary())
BOOST_THROW_EXCEPTION(
std::runtime_error("Failed to initialize OpenGL renderer"));
registerLayerComposer(composer_);
}
GLRendererServer::~GLRendererServer() { stopOpenGLRenderer(); }
void logger_write(const char *format, ...) {
char message[2048];
va_list args;
va_start(args, format);
vsnprintf(message, sizeof(message) - 1, format, args);
va_end(args);
DEBUG("%s", message);
}
void GLRendererServer::start() {
emugl_logger_struct log_funcs;
log_funcs.coarse = logger_write;
log_funcs.fine = logger_write;
char server_addr[256] = {0};
// The width & height we supply here are the dimensions the internal
// framebuffer
// will use. Making this static prevents us for now to resize the window we
// create
// later for the actual display.
if (!initOpenGLRenderer(0, server_addr, sizeof(server_addr), log_funcs,
logger_write))
if (!emugl::initialize(log_funcs, nullptr))
BOOST_THROW_EXCEPTION(
std::runtime_error("Failed to setup OpenGL renderer"));
std::runtime_error("Failed to initialize OpenGL renderer"));
registerLayerComposer(composer_);
}
socket_path_ = server_addr;
GLRendererServer::~GLRendererServer() {
if (const auto r = Renderer::get()) r->finalize();
}
std::string GLRendererServer::socket_path() const { return socket_path_; }
void GLRendererServer::start() { Renderer::initialize(0); }
} // namespace graphics
} // namespace anbox
......@@ -37,10 +37,7 @@ class GLRendererServer {
void start();
std::string socket_path() const;
private:
std::string socket_path_;
std::shared_ptr<wm::Manager> wm_;
std::shared_ptr<LayerComposer> composer_;
};
......
......@@ -69,18 +69,19 @@ void LayerComposer::submit_layers(const RenderableList &renderables) {
// need to recalculate all layer coordinates into relatives ones to the
// window they are drawn into.
auto rect = Rect{
r.screen_position().left() - new_window_frame.left() + r.crop().left(),
r.screen_position().left() - new_window_frame.left() +
r.crop().left(),
r.screen_position().top() - new_window_frame.top() + r.crop().top(),
r.screen_position().right() - new_window_frame.left() + r.crop().left(),
r.screen_position().bottom() - new_window_frame.top() + r.crop().top()
};
r.screen_position().right() - new_window_frame.left() +
r.crop().left(),
r.screen_position().bottom() - new_window_frame.top() +
r.crop().top()};
auto new_renderable = r;
new_renderable.set_screen_position(rect);
final_renderables.push_back(new_renderable);
}
Renderer::get()->draw(
window->native_handle(),
Rect{0, 0, window->frame().width(), window->frame().height()},
......
......@@ -16,44 +16,48 @@
*/
#include "anbox/graphics/opengles_message_processor.h"
#include "anbox/common/small_vector.h"
#include "anbox/graphics/buffered_io_stream.h"
#include "anbox/graphics/emugl/RenderThread.h"
#include "anbox/logger.h"
#include "anbox/network/connections.h"
#include "anbox/network/delegate_message_processor.h"
#include "anbox/network/local_socket_messenger.h"
#include <condition_variable>
#include <functional>
#include <queue>
namespace anbox {
namespace graphics {
emugl::Mutex OpenGlesMessageProcessor::global_lock{};
OpenGlesMessageProcessor::OpenGlesMessageProcessor(
const std::string &renderer_socket_path, const std::shared_ptr<Runtime> &rt,
const std::shared_ptr<network::SocketMessenger> &messenger)
: client_messenger_(messenger) {
connect_and_attach(renderer_socket_path, rt);
}
: messenger_(messenger),
stream_(std::make_shared<BufferedIOStream>(messenger_)) {
// We have to read the client flags first before we can continue
// processing the actual commands
unsigned int client_flags = 0;
auto err = messenger_->receive_msg(
boost::asio::buffer(&client_flags, sizeof(unsigned int)));
if (err) ERROR("%s", err.message());
OpenGlesMessageProcessor::~OpenGlesMessageProcessor() {}
void OpenGlesMessageProcessor::connect_and_attach(
const std::string &socket_path, const std::shared_ptr<Runtime> &rt) {
auto socket = std::make_shared<boost::asio::local::stream_protocol::socket>(
rt->service());
socket->connect(boost::asio::local::stream_protocol::endpoint(socket_path));
renderer_.reset(RenderThread::create(stream_.get(), &global_lock));
if (!renderer_->start())
BOOST_THROW_EXCEPTION(
std::runtime_error("Failed to start renderer thread"));
}
messenger_ = std::make_shared<network::LocalSocketMessenger>(socket);
renderer_ = std::make_shared<network::SocketConnection>(
messenger_, messenger_, 0, nullptr,
std::make_shared<network::DelegateMessageProcessor>(
[&](const std::vector<std::uint8_t> &data) {
client_messenger_->send(reinterpret_cast<const char *>(data.data()),
data.size());
return true;
}));
renderer_->set_name("opengles-renderer");
renderer_->read_next_message();
OpenGlesMessageProcessor::~OpenGlesMessageProcessor() {
renderer_->forceStop();
renderer_->wait(nullptr);
}
bool OpenGlesMessageProcessor::process_data(
const std::vector<std::uint8_t> &data) {
messenger_->send(reinterpret_cast<const char *>(data.data()), data.size());
auto stream = std::static_pointer_cast<BufferedIOStream>(stream_);
Buffer buffer{data.data(), data.data() + data.size()};
stream->post_data(std::move(buffer));
return true;
}
} // namespace graphics
......
......@@ -27,25 +27,27 @@
#include "anbox/network/socket_messenger.h"
#include "anbox/runtime.h"
#include "external/android-emugl/shared/emugl/common/mutex.h"
class IOStream;
class RenderThread;
namespace anbox {
namespace graphics {
class OpenGlesMessageProcessor : public network::MessageProcessor {
public:
OpenGlesMessageProcessor(
const std::string &renderer_socket_path,
const std::shared_ptr<Runtime> &rt,
const std::shared_ptr<network::SocketMessenger> &messenger);
~OpenGlesMessageProcessor();
bool process_data(const std::vector<std::uint8_t> &data) override;
private:
void connect_and_attach(const std::string &socket_path,
const std::shared_ptr<Runtime> &rt);
static emugl::Mutex global_lock;
std::shared_ptr<network::SocketMessenger> client_messenger_;
std::shared_ptr<network::SocketMessenger> messenger_;
std::shared_ptr<network::SocketConnection> renderer_;
std::shared_ptr<IOStream> stream_;
std::shared_ptr<RenderThread> renderer_;
};
} // namespace graphics
} // namespace anbox
......
......@@ -48,7 +48,8 @@ void Rect::resize(const std::int32_t &width, const std::int32_t &height) {
std::ostream &operator<<(std::ostream &out, const Rect &rect) {
return out << "{" << rect.left() << "," << rect.top() << "," << rect.right()
<< "," << rect.bottom() << "} {" << rect.width() << "," << rect.height() << "}";
<< "," << rect.bottom() << "} {" << rect.width() << ","
<< rect.height() << "}";
}
} // namespace graphics
} // namespace anbox
......@@ -73,6 +73,16 @@ Credentials BaseSocketMessenger<stream_protocol>::creds() const {
return {cr.pid, cr.uid, cr.gid};
}
template <typename stream_protocol>
ssize_t BaseSocketMessenger<stream_protocol>::send_raw(char const* data,
size_t length) {
VariableLengthArray<serialization_buffer_size> whole_message{length};
std::copy(data, data + length, whole_message.data());
std::unique_lock<std::mutex> lg(message_lock);
return ::send(socket_fd, data, length, 0);
}
template <typename stream_protocol>
void BaseSocketMessenger<stream_protocol>::send(char const* data,
size_t length) {
......@@ -85,6 +95,7 @@ void BaseSocketMessenger<stream_protocol>::send(char const* data,
ba::write(*socket, ba::buffer(whole_message.data(), whole_message.size()),
boost::asio::transfer_all());
} catch (const boost::system::system_error& err) {
DEBUG("Got error: %s", err.what());
if (err.code() == boost::asio::error::try_again) continue;
}
break;
......
......@@ -39,6 +39,7 @@ class BaseSocketMessenger : public SocketMessenger {
unsigned short local_port() const override;
void send(char const* data, size_t length) override;
ssize_t send_raw(char const* data, size_t length) override;
void async_receive_msg(AnboxReadHandler const& handle,
boost::asio::mutable_buffers_1 const& buffer) override;
boost::system::error_code receive_msg(
......
......@@ -20,12 +20,14 @@
#define ANBOX_NETWORK_MESSAGE_SENDER_H_
#include <sys/types.h>
#include <cstddef>
namespace anbox {
namespace network {
class MessageSender {
public:
virtual void send(char const* data, size_t length) = 0;
virtual ssize_t send_raw(char const* data, size_t length) = 0;
protected:
MessageSender() = default;
......
......@@ -63,15 +63,12 @@ std::string client_type_to_string(
}
namespace anbox {
namespace qemu {
PipeConnectionCreator::PipeConnectionCreator(
const std::shared_ptr<Runtime> &rt, const std::string &renderer_socket_path,
const std::string &boot_animation_icon_path)
PipeConnectionCreator::PipeConnectionCreator(const std::shared_ptr<Runtime> &rt)
: runtime_(rt),
next_connection_id_(0),
connections_(
std::make_shared<network::Connections<network::SocketConnection>>()),
renderer_socket_path_(renderer_socket_path),
boot_animation_icon_path_(boot_animation_icon_path) {}
std::make_shared<network::Connections<network::SocketConnection>>()) {
}
PipeConnectionCreator::~PipeConnectionCreator() {}
......@@ -140,8 +137,7 @@ PipeConnectionCreator::create_processor(
const client_type &type,
const std::shared_ptr<network::SocketMessenger> &messenger) {
if (type == client_type::opengles)
return std::make_shared<graphics::OpenGlesMessageProcessor>(
renderer_socket_path_, runtime_, messenger);
return std::make_shared<graphics::OpenGlesMessageProcessor>(messenger);
else if (type == client_type::qemud_boot_properties)
return std::make_shared<qemu::BootPropertiesMessageProcessor>(messenger);
else if (type == client_type::qemud_hw_control)
......@@ -154,9 +150,6 @@ PipeConnectionCreator::create_processor(
return std::make_shared<qemu::FingerprintMessageProcessor>(messenger);
else if (type == client_type::qemud_gsm)
return std::make_shared<qemu::GsmMessageProcessor>(messenger);
else if (type == client_type::bootanimation)
return std::make_shared<qemu::BootAnimationMessageProcessor>(
messenger, boot_animation_icon_path_);
else if (type == client_type::qemud_adb)
return std::make_shared<qemu::AdbMessageProcessor>(runtime_, messenger);
......
......@@ -34,9 +34,7 @@ namespace qemu {
class PipeConnectionCreator
: public network::ConnectionCreator<boost::asio::local::stream_protocol> {
public:
PipeConnectionCreator(const std::shared_ptr<Runtime> &rt,
const std::string &renderer_socket_path,
const std::string &boot_animation_icon_path);
PipeConnectionCreator(const std::shared_ptr<Runtime> &rt);
~PipeConnectionCreator() noexcept;
void create_connection_for(
......@@ -69,9 +67,6 @@ class PipeConnectionCreator
std::atomic<int> next_connection_id_;
std::shared_ptr<network::Connections<network::SocketConnection>> const
connections_;
std::string renderer_socket_path_;
std::string boot_animation_icon_path_;
};
} // namespace qemu
} // namespace anbox
......
......@@ -42,8 +42,7 @@ PlatformPolicy::PlatformPolicy(
auto display_frame = graphics::Rect::Invalid;
for (auto n = 0; n < SDL_GetNumVideoDisplays(); n++) {
SDL_Rect r;
if (SDL_GetDisplayBounds(n, &r) != 0)
continue;
if (SDL_GetDisplayBounds(n, &r) != 0) continue;
graphics::Rect frame{r.x, r.y, r.x + r.w, r.y + r.h};
......@@ -54,7 +53,8 @@ PlatformPolicy::PlatformPolicy(
}
if (display_frame == graphics::Rect::Invalid)
BOOST_THROW_EXCEPTION(std::runtime_error("No valid display configuration found"));
BOOST_THROW_EXCEPTION(
std::runtime_error("No valid display configuration found"));
display_info_.horizontal_resolution = display_frame.width();
display_info_.vertical_resolution = display_frame.height();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册