gecko/gfx/layers/LayerScope.cpp
Boris Chiou 4b888d9c25 Bug 1096139 - Create websocket dynamically after we enable Layerscope. r=dglastonbury
We have to create the websocket if it doesn't exist after enabling
Layerscope. Therefore, we don't have to reboot the device anymore.

1. Remove the Init and DeInit in CreateCompositor and DestroyCompositor
   to prevent some unwanted deInits on the browser. (Our browser often
   calls DestroyCompositor)
2. Initize websocket only when we need it.
2014-11-20 02:00:00 +01:00

1047 lines
30 KiB
C++

/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* This must occur *after* layers/PLayers.h to avoid typedefs conflicts. */
#include "LayerScope.h"
#include "Composer2D.h"
#include "Effects.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/Preferences.h"
#include "mozilla/Endian.h"
#include "TexturePoolOGL.h"
#include "mozilla/layers/CompositorOGL.h"
#include "mozilla/layers/CompositorParent.h"
#include "mozilla/layers/LayerManagerComposite.h"
#include "mozilla/layers/TextureHostOGL.h"
#include "gfxColor.h"
#include "gfxContext.h"
#include "gfxUtils.h"
#include "gfxPrefs.h"
#include "nsIWidget.h"
#include "GLContext.h"
#include "GLContextProvider.h"
#include "GLReadTexImageHelper.h"
#include "nsIServiceManager.h"
#include "nsIConsoleService.h"
#include <memory>
#include "mozilla/LinkedList.h"
#include "mozilla/Base64.h"
#include "mozilla/SHA1.h"
#include "mozilla/StaticPtr.h"
#include "nsThreadUtils.h"
#include "nsISocketTransport.h"
#include "nsIServerSocket.h"
#include "nsReadLine.h"
#include "nsNetCID.h"
#include "nsIOutputStream.h"
#include "nsIAsyncInputStream.h"
#include "nsIEventTarget.h"
#include "nsProxyRelease.h"
// Undo the damage done by mozzconf.h
#undef compress
#include "mozilla/Compression.h"
// Protocol buffer (generated automatically)
#include "protobuf/LayerScopePacket.pb.h"
namespace mozilla {
namespace layers {
using namespace mozilla::Compression;
using namespace mozilla::gfx;
using namespace mozilla::gl;
using namespace mozilla;
using namespace layerscope;
class DebugDataSender;
class DebugGLData;
/* This class handle websocket protocol which included
* handshake and data frame's header
*/
class LayerScopeWebSocketHandler : public nsIInputStreamCallback {
public:
NS_DECL_THREADSAFE_ISUPPORTS
enum SocketStateType {
NoHandshake,
HandshakeSuccess,
HandshakeFailed
};
LayerScopeWebSocketHandler()
: mState(NoHandshake)
{ }
private:
virtual ~LayerScopeWebSocketHandler()
{
if (mTransport) {
mTransport->Close(NS_OK);
}
}
public:
void OpenStream(nsISocketTransport* aTransport) {
MOZ_ASSERT(aTransport);
mTransport = aTransport;
mTransport->OpenOutputStream(nsITransport::OPEN_BLOCKING,
0,
0,
getter_AddRefs(mOutputStream));
nsCOMPtr<nsIInputStream> debugInputStream;
mTransport->OpenInputStream(0,
0,
0,
getter_AddRefs(debugInputStream));
mInputStream = do_QueryInterface(debugInputStream);
mInputStream->AsyncWait(this, 0, 0, NS_GetCurrentThread());
}
bool WriteToStream(void *ptr, uint32_t size) {
if (mState == NoHandshake) {
// Not yet handshake, just return true in case of
// LayerScope remove this handle
return true;
} else if (mState == HandshakeFailed) {
return false;
}
// Generate WebSocket header
uint8_t wsHeader[10];
int wsHeaderSize = 0;
const uint8_t opcode = 0x2;
wsHeader[0] = 0x80 | (opcode & 0x0f); // FIN + opcode;
if (size <= 125) {
wsHeaderSize = 2;
wsHeader[1] = size;
} else if (size < 65536) {
wsHeaderSize = 4;
wsHeader[1] = 0x7E;
NetworkEndian::writeUint16(wsHeader + 2, size);
} else {
wsHeaderSize = 10;
wsHeader[1] = 0x7F;
NetworkEndian::writeUint64(wsHeader + 2, size);
}
// Send WebSocket header
nsresult rv;
uint32_t cnt;
rv = mOutputStream->Write(reinterpret_cast<char*>(wsHeader),
wsHeaderSize, &cnt);
if (NS_FAILED(rv))
return false;
uint32_t written = 0;
while (written < size) {
uint32_t cnt;
rv = mOutputStream->Write(reinterpret_cast<char*>(ptr) + written,
size - written, &cnt);
if (NS_FAILED(rv))
return false;
written += cnt;
}
return true;
}
// nsIInputStreamCallback
NS_IMETHODIMP OnInputStreamReady(nsIAsyncInputStream *stream) MOZ_OVERRIDE
{
nsTArray<nsCString> protocolString;
ReadInputStreamData(protocolString);
if (WebSocketHandshake(protocolString)) {
mState = HandshakeSuccess;
} else {
mState = HandshakeFailed;
}
return NS_OK;
}
private:
void ReadInputStreamData(nsTArray<nsCString>& aProtocolString)
{
nsLineBuffer<char> lineBuffer;
nsCString line;
bool more = true;
do {
NS_ReadLine(mInputStream.get(), &lineBuffer, line, &more);
if (line.Length() > 0) {
aProtocolString.AppendElement(line);
}
} while (more && line.Length() > 0);
}
bool WebSocketHandshake(nsTArray<nsCString>& aProtocolString)
{
nsresult rv;
bool isWebSocket = false;
nsCString version;
nsCString wsKey;
nsCString protocol;
// Validate WebSocket client request.
if (aProtocolString.Length() == 0)
return false;
// Check that the HTTP method is GET
const char* HTTP_METHOD = "GET ";
if (strncmp(aProtocolString[0].get(), HTTP_METHOD, strlen(HTTP_METHOD)) != 0) {
return false;
}
for (uint32_t i = 1; i < aProtocolString.Length(); ++i) {
const char* line = aProtocolString[i].get();
const char* prop_pos = strchr(line, ':');
if (prop_pos != nullptr) {
nsCString key(line, prop_pos - line);
nsCString value(prop_pos + 2);
if (key.EqualsIgnoreCase("upgrade") &&
value.EqualsIgnoreCase("websocket")) {
isWebSocket = true;
} else if (key.EqualsIgnoreCase("sec-websocket-version")) {
version = value;
} else if (key.EqualsIgnoreCase("sec-websocket-key")) {
wsKey = value;
} else if (key.EqualsIgnoreCase("sec-websocket-protocol")) {
protocol = value;
}
}
}
if (!isWebSocket) {
return false;
}
if (!(version.EqualsLiteral("7") ||
version.EqualsLiteral("8") ||
version.EqualsLiteral("13"))) {
return false;
}
if (!(protocol.EqualsIgnoreCase("binary"))) {
return false;
}
// Client request is valid. Start to generate and send server response.
nsAutoCString guid("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
nsAutoCString res;
SHA1Sum sha1;
nsCString combined(wsKey + guid);
sha1.update(combined.get(), combined.Length());
uint8_t digest[SHA1Sum::kHashSize]; // SHA1 digests are 20 bytes long.
sha1.finish(digest);
nsCString newString(reinterpret_cast<char*>(digest), SHA1Sum::kHashSize);
Base64Encode(newString, res);
nsCString response("HTTP/1.1 101 Switching Protocols\r\n");
response.AppendLiteral("Upgrade: websocket\r\n");
response.AppendLiteral("Connection: Upgrade\r\n");
response.Append(nsCString("Sec-WebSocket-Accept: ") + res + nsCString("\r\n"));
response.AppendLiteral("Sec-WebSocket-Protocol: binary\r\n\r\n");
uint32_t written = 0;
uint32_t size = response.Length();
while (written < size) {
uint32_t cnt;
rv = mOutputStream->Write(const_cast<char*>(response.get()) + written,
size - written, &cnt);
if (NS_FAILED(rv))
return false;
written += cnt;
}
mOutputStream->Flush();
return true;
}
nsCOMPtr<nsIOutputStream> mOutputStream;
nsCOMPtr<nsIAsyncInputStream> mInputStream;
nsCOMPtr<nsISocketTransport> mTransport;
SocketStateType mState;
};
NS_IMPL_ISUPPORTS(LayerScopeWebSocketHandler, nsIInputStreamCallback);
class LayerScopeWebSocketManager {
public:
LayerScopeWebSocketManager();
~LayerScopeWebSocketManager();
void AddConnection(nsISocketTransport *aTransport)
{
MOZ_ASSERT(aTransport);
nsRefPtr<LayerScopeWebSocketHandler> temp = new LayerScopeWebSocketHandler();
temp->OpenStream(aTransport);
mHandlers.AppendElement(temp.get());
}
void RemoveConnection(uint32_t aIndex)
{
MOZ_ASSERT(aIndex < mHandlers.Length());
mHandlers.RemoveElementAt(aIndex);
}
void RemoveAllConnections()
{
mHandlers.Clear();
}
bool WriteAll(void *ptr, uint32_t size)
{
for (int32_t i = mHandlers.Length() - 1; i >= 0; --i) {
if (!mHandlers[i]->WriteToStream(ptr, size)) {
// Send failed, remove this handler
RemoveConnection(i);
}
}
return true;
}
bool IsConnected()
{
return (mHandlers.Length() != 0) ? true : false;
}
void AppendDebugData(DebugGLData *aDebugData);
void CleanDebugData();
void DispatchDebugData();
private:
nsTArray<nsRefPtr<LayerScopeWebSocketHandler> > mHandlers;
nsCOMPtr<nsIThread> mDebugSenderThread;
nsRefPtr<DebugDataSender> mCurrentSender;
nsCOMPtr<nsIServerSocket> mServerSocket;
};
// Static class to create and destory LayerScopeWebSocketManager object
class WebSocketHelper
{
public:
static void CreateServerSocket()
{
// Create Web Server Socket (which has to be on the main thread)
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
if (!sWebSocketManager) {
sWebSocketManager = new LayerScopeWebSocketManager();
}
}
static void DestroyServerSocket()
{
// Destroy Web Server Socket
if (sWebSocketManager) {
sWebSocketManager->RemoveAllConnections();
}
}
static LayerScopeWebSocketManager* GetSocketManager()
{
return sWebSocketManager;
}
private:
static StaticAutoPtr<LayerScopeWebSocketManager> sWebSocketManager;
};
StaticAutoPtr<LayerScopeWebSocketManager> WebSocketHelper::sWebSocketManager;
/*
* DebugGLData is the base class of
* 1. DebugGLFrameStatusData (Frame start/end packet)
* 2. DebugGLColorData (Color data packet)
* 3. DebugGLTextureData (Texture data packet)
*/
class DebugGLData: public LinkedListElement<DebugGLData> {
public:
explicit DebugGLData(Packet::DataType aDataType)
: mDataType(aDataType)
{ }
virtual ~DebugGLData() { }
Packet::DataType GetDataType() const { return mDataType; }
virtual bool Write() = 0;
static bool WriteToStream(Packet& aPacket) {
if (!WebSocketHelper::GetSocketManager())
return true;
uint32_t size = aPacket.ByteSize();
auto data = MakeUnique<uint8_t[]>(size);
aPacket.SerializeToArray(data.get(), size);
return WebSocketHelper::GetSocketManager()->WriteAll(data.get(), size);
}
protected:
Packet::DataType mDataType;
};
class DebugGLFrameStatusData : public DebugGLData
{
public:
DebugGLFrameStatusData(Packet::DataType aDataType,
int64_t aValue)
: DebugGLData(aDataType),
mFrameStamp(aValue)
{ }
explicit DebugGLFrameStatusData(Packet::DataType aDataType)
: DebugGLData(aDataType),
mFrameStamp(0)
{ }
int64_t GetFrameStamp() const { return mFrameStamp; }
virtual bool Write() MOZ_OVERRIDE {
Packet packet;
packet.set_type(mDataType);
FramePacket* fp = packet.mutable_frame();
fp->set_value(static_cast<uint64_t>(mFrameStamp));
if (!WriteToStream(packet))
return false;
return true;
}
protected:
int64_t mFrameStamp;
};
class DebugGLTextureData : public DebugGLData {
public:
DebugGLTextureData(GLContext* cx,
void* layerRef,
GLenum target,
GLuint name,
DataSourceSurface* img)
: DebugGLData(Packet::TEXTURE),
mLayerRef(layerRef),
mTarget(target),
mName(name),
mContextAddress(reinterpret_cast<intptr_t>(cx)),
mDatasize(0)
{
// pre-packing
// DataSourceSurface may have locked buffer,
// so we should compress now, and then it could
// be unlocked outside.
pack(img);
}
const void* GetLayerRef() const { return mLayerRef; }
GLuint GetName() const { return mName; }
GLenum GetTextureTarget() const { return mTarget; }
intptr_t GetContextAddress() const { return mContextAddress; }
uint32_t GetDataSize() const { return mDatasize; }
virtual bool Write() MOZ_OVERRIDE {
if (!WriteToStream(mPacket))
return false;
return true;
}
private:
void pack(DataSourceSurface* aImage) {
mPacket.set_type(mDataType);
TexturePacket* tp = mPacket.mutable_texture();
tp->set_layerref(reinterpret_cast<uint64_t>(mLayerRef));
tp->set_name(mName);
tp->set_target(mTarget);
tp->set_dataformat(LOCAL_GL_RGBA);
tp->set_glcontext(static_cast<uint64_t>(mContextAddress));
if (aImage) {
tp->set_width(aImage->GetSize().width);
tp->set_height(aImage->GetSize().height);
tp->set_stride(aImage->Stride());
mDatasize = aImage->GetSize().height * aImage->Stride();
auto compresseddata = MakeUnique<char[]>(LZ4::maxCompressedSize(mDatasize));
if (compresseddata) {
int ndatasize = LZ4::compress((char*)aImage->GetData(),
mDatasize,
compresseddata.get());
if (ndatasize > 0) {
mDatasize = ndatasize;
tp->set_dataformat((1 << 16 | tp->dataformat()));
tp->set_data(compresseddata.get(), mDatasize);
} else {
NS_WARNING("Compress data failed");
tp->set_data(aImage->GetData(), mDatasize);
}
} else {
NS_WARNING("Couldn't new compressed data.");
tp->set_data(aImage->GetData(), mDatasize);
}
} else {
tp->set_width(0);
tp->set_height(0);
tp->set_stride(0);
}
}
protected:
void* mLayerRef;
GLenum mTarget;
GLuint mName;
intptr_t mContextAddress;
uint32_t mDatasize;
// Packet data
Packet mPacket;
};
class DebugGLColorData : public DebugGLData {
public:
DebugGLColorData(void* layerRef,
const gfxRGBA& color,
int width,
int height)
: DebugGLData(Packet::COLOR),
mLayerRef(layerRef),
mColor(color.Packed()),
mSize(width, height)
{ }
const void* GetLayerRef() const { return mLayerRef; }
uint32_t GetColor() const { return mColor; }
const nsIntSize& GetSize() const { return mSize; }
virtual bool Write() MOZ_OVERRIDE {
Packet packet;
packet.set_type(mDataType);
ColorPacket* cp = packet.mutable_color();
cp->set_layerref(reinterpret_cast<uint64_t>(mLayerRef));
cp->set_color(mColor);
cp->set_width(mSize.width);
cp->set_height(mSize.height);
if (!WriteToStream(packet))
return false;
return true;
}
protected:
void* mLayerRef;
uint32_t mColor;
nsIntSize mSize;
};
class DebugGLLayersData : public DebugGLData {
public:
explicit DebugGLLayersData(UniquePtr<Packet> aPacket)
: DebugGLData(Packet::LAYERS),
mPacket(Move(aPacket))
{ }
virtual bool Write() MOZ_OVERRIDE {
mPacket->set_type(mDataType);
if (!WriteToStream(*mPacket))
return false;
return true;
}
protected:
UniquePtr<Packet> mPacket;
};
class DebugListener : public nsIServerSocketListener
{
virtual ~DebugListener() { }
public:
NS_DECL_THREADSAFE_ISUPPORTS
DebugListener() { }
/* nsIServerSocketListener */
NS_IMETHODIMP OnSocketAccepted(nsIServerSocket *aServ,
nsISocketTransport *aTransport)
{
if (!WebSocketHelper::GetSocketManager())
return NS_OK;
printf_stderr("*** LayerScope: Accepted connection\n");
WebSocketHelper::GetSocketManager()->AddConnection(aTransport);
return NS_OK;
}
NS_IMETHODIMP OnStopListening(nsIServerSocket *aServ,
nsresult aStatus)
{
return NS_OK;
}
};
NS_IMPL_ISUPPORTS(DebugListener, nsIServerSocketListener);
class DebugDataSender : public nsIRunnable
{
virtual ~DebugDataSender() {
Cleanup();
}
public:
NS_DECL_THREADSAFE_ISUPPORTS
DebugDataSender() { }
void Append(DebugGLData *d) {
mList.insertBack(d);
}
void Cleanup() {
if (mList.isEmpty())
return;
DebugGLData *d;
while ((d = mList.popFirst()) != nullptr)
delete d;
}
/* nsIRunnable impl; send the data */
NS_IMETHODIMP Run() {
DebugGLData *d;
nsresult rv = NS_OK;
while ((d = mList.popFirst()) != nullptr) {
UniquePtr<DebugGLData> cleaner(d);
if (!d->Write()) {
rv = NS_ERROR_FAILURE;
break;
}
}
Cleanup();
if (NS_FAILED(rv)) {
WebSocketHelper::DestroyServerSocket();
}
return NS_OK;
}
protected:
LinkedList<DebugGLData> mList;
};
NS_IMPL_ISUPPORTS(DebugDataSender, nsIRunnable);
class CreateServerSocketRunnable : public nsRunnable
{
public:
NS_IMETHOD Run() {
WebSocketHelper::CreateServerSocket();
return NS_OK;
}
};
/*
* LayerScope SendXXX Structure
* 1. SendLayer
* 2. SendEffectChain
* 1. SendTexturedEffect
* -> SendTextureSource
* 2. SendYCbCrEffect
* -> SendTextureSource
* 3. SendColor
*/
class SenderHelper
{
// Sender public APIs
public:
static void SendLayer(LayerComposite* aLayer,
int aWidth,
int aHeight);
static void SendEffectChain(gl::GLContext* aGLContext,
const EffectChain& aEffectChain,
int aWidth = 0,
int aHeight = 0);
// Sender private functions
private:
static void SendColor(void* aLayerRef,
const gfxRGBA& aColor,
int aWidth,
int aHeight);
static void SendTextureSource(GLContext* aGLContext,
void* aLayerRef,
TextureSourceOGL* aSource,
bool aFlipY);
static void SendTexturedEffect(GLContext* aGLContext,
void* aLayerRef,
const TexturedEffect* aEffect);
static void SendYCbCrEffect(GLContext* aGLContext,
void* aLayerRef,
const EffectYCbCr* aEffect);
};
// ----------------------------------------------
// SenderHelper implementation
// ----------------------------------------------
void
SenderHelper::SendLayer(LayerComposite* aLayer,
int aWidth,
int aHeight)
{
MOZ_ASSERT(aLayer && aLayer->GetLayer());
if (!aLayer || !aLayer->GetLayer()) {
return;
}
switch (aLayer->GetLayer()->GetType()) {
case Layer::TYPE_COLOR: {
EffectChain effect;
aLayer->GenEffectChain(effect);
SenderHelper::SendEffectChain(nullptr, effect, aWidth, aHeight);
break;
}
case Layer::TYPE_IMAGE:
case Layer::TYPE_CANVAS:
case Layer::TYPE_PAINTED: {
// Get CompositableHost and Compositor
CompositableHost* compHost = aLayer->GetCompositableHost();
Compositor* comp = compHost->GetCompositor();
// Send EffectChain only for CompositorOGL
if (LayersBackend::LAYERS_OPENGL == comp->GetBackendType()) {
CompositorOGL* compOGL = static_cast<CompositorOGL*>(comp);
EffectChain effect;
// Generate primary effect (lock and gen)
AutoLockCompositableHost lock(compHost);
aLayer->GenEffectChain(effect);
SenderHelper::SendEffectChain(compOGL->gl(), effect);
}
break;
}
case Layer::TYPE_CONTAINER:
default:
break;
}
}
void
SenderHelper::SendColor(void* aLayerRef,
const gfxRGBA& aColor,
int aWidth,
int aHeight)
{
WebSocketHelper::GetSocketManager()->AppendDebugData(
new DebugGLColorData(aLayerRef, aColor, aWidth, aHeight));
}
void
SenderHelper::SendTextureSource(GLContext* aGLContext,
void* aLayerRef,
TextureSourceOGL* aSource,
bool aFlipY)
{
MOZ_ASSERT(aGLContext);
if (!aGLContext) {
return;
}
GLenum textureTarget = aSource->GetTextureTarget();
ShaderConfigOGL config = ShaderConfigFromTargetAndFormat(textureTarget,
aSource->GetFormat());
int shaderConfig = config.mFeatures;
aSource->BindTexture(LOCAL_GL_TEXTURE0, gfx::Filter::LINEAR);
GLuint textureId = 0;
// This is horrid hack. It assumes that aGLContext matches the context
// aSource has bound to.
if (textureTarget == LOCAL_GL_TEXTURE_2D) {
aGLContext->GetUIntegerv(LOCAL_GL_TEXTURE_BINDING_2D, &textureId);
} else if (textureTarget == LOCAL_GL_TEXTURE_EXTERNAL) {
aGLContext->GetUIntegerv(LOCAL_GL_TEXTURE_BINDING_EXTERNAL, &textureId);
} else if (textureTarget == LOCAL_GL_TEXTURE_RECTANGLE) {
aGLContext->GetUIntegerv(LOCAL_GL_TEXTURE_BINDING_RECTANGLE, &textureId);
}
gfx::IntSize size = aSource->GetSize();
// By sending 0 to ReadTextureImage rely upon aSource->BindTexture binding
// texture correctly. textureId is used for tracking in DebugGLTextureData.
RefPtr<DataSourceSurface> img =
aGLContext->ReadTexImageHelper()->ReadTexImage(0, textureTarget,
size,
shaderConfig, aFlipY);
WebSocketHelper::GetSocketManager()->AppendDebugData(
new DebugGLTextureData(aGLContext, aLayerRef, textureTarget,
textureId, img));
}
void
SenderHelper::SendTexturedEffect(GLContext* aGLContext,
void* aLayerRef,
const TexturedEffect* aEffect)
{
TextureSourceOGL* source = aEffect->mTexture->AsSourceOGL();
if (!source)
return;
bool flipY = false;
SendTextureSource(aGLContext, aLayerRef, source, flipY);
}
void
SenderHelper::SendYCbCrEffect(GLContext* aGLContext,
void* aLayerRef,
const EffectYCbCr* aEffect)
{
TextureSource* sourceYCbCr = aEffect->mTexture;
if (!sourceYCbCr)
return;
const int Y = 0, Cb = 1, Cr = 2;
TextureSourceOGL* sourceY = sourceYCbCr->GetSubSource(Y)->AsSourceOGL();
TextureSourceOGL* sourceCb = sourceYCbCr->GetSubSource(Cb)->AsSourceOGL();
TextureSourceOGL* sourceCr = sourceYCbCr->GetSubSource(Cr)->AsSourceOGL();
bool flipY = false;
SendTextureSource(aGLContext, aLayerRef, sourceY, flipY);
SendTextureSource(aGLContext, aLayerRef, sourceCb, flipY);
SendTextureSource(aGLContext, aLayerRef, sourceCr, flipY);
}
void
SenderHelper::SendEffectChain(GLContext* aGLContext,
const EffectChain& aEffectChain,
int aWidth,
int aHeight)
{
const Effect* primaryEffect = aEffectChain.mPrimaryEffect;
switch (primaryEffect->mType) {
case EffectTypes::RGB: {
const TexturedEffect* texturedEffect =
static_cast<const TexturedEffect*>(primaryEffect);
SendTexturedEffect(aGLContext, aEffectChain.mLayerRef, texturedEffect);
break;
}
case EffectTypes::YCBCR: {
const EffectYCbCr* yCbCrEffect =
static_cast<const EffectYCbCr*>(primaryEffect);
SendYCbCrEffect(aGLContext, aEffectChain.mLayerRef, yCbCrEffect);
break;
}
case EffectTypes::SOLID_COLOR: {
const EffectSolidColor* solidColorEffect =
static_cast<const EffectSolidColor*>(primaryEffect);
gfxRGBA color(solidColorEffect->mColor.r,
solidColorEffect->mColor.g,
solidColorEffect->mColor.b,
solidColorEffect->mColor.a);
SendColor(aEffectChain.mLayerRef, color, aWidth, aHeight);
break;
}
case EffectTypes::COMPONENT_ALPHA:
case EffectTypes::RENDER_TARGET:
default:
break;
}
//const Effect* secondaryEffect = aEffectChain.mSecondaryEffects[EffectTypes::MASK];
// TODO:
}
// ----------------------------------------------
// LayerScopeWebSocketManager implementation
// ----------------------------------------------
LayerScopeWebSocketManager::LayerScopeWebSocketManager()
{
NS_NewThread(getter_AddRefs(mDebugSenderThread));
mServerSocket = do_CreateInstance(NS_SERVERSOCKET_CONTRACTID);
int port = gfxPrefs::LayerScopePort();
mServerSocket->Init(port, false, -1);
mServerSocket->AsyncListen(new DebugListener);
}
LayerScopeWebSocketManager::~LayerScopeWebSocketManager()
{
}
void
LayerScopeWebSocketManager::AppendDebugData(DebugGLData *aDebugData)
{
if (!mCurrentSender) {
mCurrentSender = new DebugDataSender();
}
mCurrentSender->Append(aDebugData);
}
void
LayerScopeWebSocketManager::CleanDebugData()
{
if (mCurrentSender) {
mCurrentSender->Cleanup();
}
}
void
LayerScopeWebSocketManager::DispatchDebugData()
{
mDebugSenderThread->Dispatch(mCurrentSender, NS_DISPATCH_NORMAL);
mCurrentSender = nullptr;
}
// ----------------------------------------------
// LayerScope implementation
// ----------------------------------------------
void
LayerScope::Init()
{
if (!gfxPrefs::LayerScopeEnabled()) {
return;
}
if (NS_IsMainThread()) {
WebSocketHelper::CreateServerSocket();
} else {
// Dispatch creation to main thread, and make sure we
// dispatch this only once after booting
static bool dispatched = false;
if (dispatched) {
return;
}
DebugOnly<nsresult> rv = NS_DispatchToMainThread(new CreateServerSocketRunnable());
MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed to dispatch WebSocket Creation to main thread");
dispatched = true;
}
}
void
LayerScope::SendEffectChain(gl::GLContext* aGLContext,
const EffectChain& aEffectChain,
int aWidth,
int aHeight)
{
// Protect this public function
if (!CheckSendable()) {
return;
}
SenderHelper::SendEffectChain(aGLContext, aEffectChain, aWidth, aHeight);
}
void
LayerScope::SendLayer(LayerComposite* aLayer,
int aWidth,
int aHeight)
{
// Protect this public function
if (!CheckSendable()) {
return;
}
SenderHelper::SendLayer(aLayer, aWidth, aHeight);
}
void
LayerScope::SendLayerDump(UniquePtr<Packet> aPacket)
{
// Protect this public function
if (!CheckSendable()) {
return;
}
WebSocketHelper::GetSocketManager()->AppendDebugData(
new DebugGLLayersData(Move(aPacket)));
}
bool
LayerScope::CheckSendable()
{
// Only compositor threads check LayerScope status
MOZ_ASSERT(CompositorParent::IsInCompositorThread());
if (!gfxPrefs::LayerScopeEnabled()) {
return false;
}
if (!WebSocketHelper::GetSocketManager()) {
Init();
return false;
}
if (!WebSocketHelper::GetSocketManager()->IsConnected()) {
return false;
}
return true;
}
void
LayerScope::CleanLayer()
{
if (CheckSendable()) {
WebSocketHelper::GetSocketManager()->CleanDebugData();
}
}
// ----------------------------------------------
// LayerScopeAutoFrame implementation
// ----------------------------------------------
LayerScopeAutoFrame::LayerScopeAutoFrame(int64_t aFrameStamp)
{
// Do Begin Frame
BeginFrame(aFrameStamp);
}
LayerScopeAutoFrame::~LayerScopeAutoFrame()
{
// Do End Frame
EndFrame();
}
void
LayerScopeAutoFrame::BeginFrame(int64_t aFrameStamp)
{
if (!LayerScope::CheckSendable()) {
return;
}
WebSocketHelper::GetSocketManager()->AppendDebugData(
new DebugGLFrameStatusData(Packet::FRAMESTART, aFrameStamp));
}
void
LayerScopeAutoFrame::EndFrame()
{
if (!LayerScope::CheckSendable()) {
return;
}
WebSocketHelper::GetSocketManager()->AppendDebugData(
new DebugGLFrameStatusData(Packet::FRAMEEND));
WebSocketHelper::GetSocketManager()->DispatchDebugData();
}
} /* layers */
} /* mozilla */