diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /gfx/layers/LayerScope.cpp | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/layers/LayerScope.cpp')
-rw-r--r-- | gfx/layers/LayerScope.cpp | 1633 |
1 files changed, 1633 insertions, 0 deletions
diff --git a/gfx/layers/LayerScope.cpp b/gfx/layers/LayerScope.cpp new file mode 100644 index 0000000000..69c0e9eaff --- /dev/null +++ b/gfx/layers/LayerScope.cpp @@ -0,0 +1,1633 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=4 sw=2 sts=2 et: */ +/* 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 "nsAppRunner.h" +#include "Effects.h" +#include "Layers.h" +#include "mozilla/EndianUtils.h" +#include "mozilla/MathAlgorithms.h" +#include "mozilla/Preferences.h" +#include "mozilla/StaticPrefs_gfx.h" +#include "mozilla/TimeStamp.h" + +#include "mozilla/layers/CompositorOGL.h" +#include "mozilla/layers/CompositorThread.h" +#include "mozilla/layers/LayerManagerComposite.h" +#include "mozilla/layers/TextureHostOGL.h" + +#include "gfxContext.h" +#include "gfxUtils.h" + +#include "nsIWidget.h" + +#include "GLContext.h" +#include "GLContextProvider.h" +#include "GLReadTexImageHelper.h" + +#include <memory> +#include "mozilla/LinkedList.h" +#include "mozilla/Base64.h" +#include "mozilla/SHA1.h" +#include "mozilla/StaticPtr.h" +#include "nsComponentManagerUtils.h" +#include "nsThreadUtils.h" +#include "nsISocketTransport.h" +#include "nsIServerSocket.h" +#include "nsReadLine.h" +#include "nsNetCID.h" +#include "nsIOutputStream.h" +#include "nsIAsyncInputStream.h" +#include "nsProxyRelease.h" +#include <list> + +// Undo the damage done by mozzconf.h +#undef compress +#include "mozilla/Compression.h" + +// Undo the damage done by X11 +#ifdef Status +# undef Status +#endif +// 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; + +/* + * Manage Websocket connections + */ +class LayerScopeWebSocketManager { + public: + LayerScopeWebSocketManager(); + ~LayerScopeWebSocketManager(); + + void RemoveAllConnections() { + MOZ_ASSERT(NS_IsMainThread()); + + MutexAutoLock lock(mHandlerMutex); + 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() { + // This funtion can be called in both main thread and compositor thread. + MutexAutoLock lock(mHandlerMutex); + return (mHandlers.Length() != 0) ? true : false; + } + + void AppendDebugData(DebugGLData* aDebugData); + void CleanDebugData(); + void DispatchDebugData(); + + private: + void AddConnection(nsISocketTransport* aTransport) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aTransport); + + MutexAutoLock lock(mHandlerMutex); + + RefPtr<SocketHandler> temp = new SocketHandler(); + temp->OpenStream(aTransport); + mHandlers.AppendElement(temp.get()); + } + + void RemoveConnection(uint32_t aIndex) { + // TBD: RemoveConnection is executed on the compositor thread and + // AddConntection is executed on the main thread, which might be + // a problem if a user disconnect and connect readlly quickly at + // viewer side. + + // We should dispatch RemoveConnection onto main thead. + MOZ_ASSERT(aIndex < mHandlers.Length()); + + MutexAutoLock lock(mHandlerMutex); + mHandlers.RemoveElementAt(aIndex); + } + + friend class SocketListener; + class SocketListener : public nsIServerSocketListener { + public: + NS_DECL_THREADSAFE_ISUPPORTS + + SocketListener() = default; + + /* nsIServerSocketListener */ + NS_IMETHOD OnSocketAccepted(nsIServerSocket* aServ, + nsISocketTransport* aTransport) override; + NS_IMETHOD OnStopListening(nsIServerSocket* aServ, + nsresult aStatus) override { + return NS_OK; + } + + private: + virtual ~SocketListener() = default; + }; + + /* + * This class handle websocket protocol which included + * handshake and data frame's header + */ + class SocketHandler : public nsIInputStreamCallback { + public: + NS_DECL_THREADSAFE_ISUPPORTS + + SocketHandler() : mState(NoHandshake), mConnected(false) {} + + void OpenStream(nsISocketTransport* aTransport); + bool WriteToStream(void* aPtr, uint32_t aSize); + + // nsIInputStreamCallback + NS_IMETHOD OnInputStreamReady(nsIAsyncInputStream* aStream) override; + + private: + virtual ~SocketHandler() { CloseConnection(); } + + void ReadInputStreamData(nsTArray<nsCString>& aProtocolString); + bool WebSocketHandshake(nsTArray<nsCString>& aProtocolString); + void ApplyMask(uint32_t aMask, uint8_t* aData, uint64_t aLen); + bool HandleDataFrame(uint8_t* aData, uint32_t aSize); + void CloseConnection(); + + nsresult HandleSocketMessage(nsIAsyncInputStream* aStream); + nsresult ProcessInput(uint8_t* aBuffer, uint32_t aCount); + + private: + enum SocketStateType { NoHandshake, HandshakeSuccess, HandshakeFailed }; + SocketStateType mState; + + nsCOMPtr<nsIOutputStream> mOutputStream; + nsCOMPtr<nsIAsyncInputStream> mInputStream; + nsCOMPtr<nsISocketTransport> mTransport; + bool mConnected; + }; + + nsTArray<RefPtr<SocketHandler> > mHandlers; + nsCOMPtr<nsIThread> mDebugSenderThread; + RefPtr<DebugDataSender> mCurrentSender; + nsCOMPtr<nsIServerSocket> mServerSocket; + + // Keep mHandlers accessing thread safe. + Mutex mHandlerMutex; +}; + +NS_IMPL_ISUPPORTS(LayerScopeWebSocketManager::SocketListener, + nsIServerSocketListener); +NS_IMPL_ISUPPORTS(LayerScopeWebSocketManager::SocketHandler, + nsIInputStreamCallback); + +class DrawSession { + public: + DrawSession() : mOffsetX(0.0), mOffsetY(0.0), mRects(0) {} + + float mOffsetX; + float mOffsetY; + gfx::Matrix4x4 mMVMatrix; + size_t mRects; + gfx::Rect mLayerRects[4]; + gfx::Rect mTextureRects[4]; + std::list<GLuint> mTexIDs; +}; + +class ContentMonitor { + public: + using THArray = nsTArray<const TextureHost*>; + + // Notify the content of a TextureHost was changed. + void SetChangedHost(const TextureHost* host) { + if (THArray::NoIndex == mChangedHosts.IndexOf(host)) { + mChangedHosts.AppendElement(host); + } + } + + // Clear changed flag of a host. + void ClearChangedHost(const TextureHost* host) { + if (THArray::NoIndex != mChangedHosts.IndexOf(host)) { + mChangedHosts.RemoveElement(host); + } + } + + // Return true iff host is a new one or the content of it had been changed. + bool IsChangedOrNew(const TextureHost* host) { + if (THArray::NoIndex == mSeenHosts.IndexOf(host)) { + mSeenHosts.AppendElement(host); + return true; + } + + if (decltype(mChangedHosts)::NoIndex != mChangedHosts.IndexOf(host)) { + return true; + } + + return false; + } + + void Empty() { + mSeenHosts.SetLength(0); + mChangedHosts.SetLength(0); + } + + private: + THArray mSeenHosts; + THArray mChangedHosts; +}; + +/* + * Hold all singleton objects used by LayerScope. + */ +class LayerScopeManager { + public: + void CreateServerSocket() { + // WebSocketManager must be created on the main thread. + if (NS_IsMainThread()) { + mWebSocketManager = mozilla::MakeUnique<LayerScopeWebSocketManager>(); + } 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(this)); + MOZ_ASSERT(NS_SUCCEEDED(rv), + "Failed to dispatch WebSocket Creation to main thread"); + dispatched = true; + } + } + + void DestroyServerSocket() { + // Destroy Web Server Socket + if (mWebSocketManager) { + mWebSocketManager->RemoveAllConnections(); + } + } + + LayerScopeWebSocketManager* GetSocketManager() { + return mWebSocketManager.get(); + } + + ContentMonitor* GetContentMonitor() { + if (!mContentMonitor.get()) { + mContentMonitor = mozilla::MakeUnique<ContentMonitor>(); + } + + return mContentMonitor.get(); + } + + void NewDrawSession() { mSession = mozilla::MakeUnique<DrawSession>(); } + + DrawSession& CurrentSession() { return *mSession; } + + void SetPixelScale(double scale) { mScale = scale; } + + double GetPixelScale() const { return mScale; } + + LayerScopeManager() : mScale(1.0) {} + + private: + friend class CreateServerSocketRunnable; + class CreateServerSocketRunnable : public Runnable { + public: + explicit CreateServerSocketRunnable(LayerScopeManager* aLayerScopeManager) + : Runnable("layers::LayerScopeManager::CreateServerSocketRunnable"), + mLayerScopeManager(aLayerScopeManager) {} + NS_IMETHOD Run() override { + mLayerScopeManager->mWebSocketManager = + mozilla::MakeUnique<LayerScopeWebSocketManager>(); + return NS_OK; + } + + private: + LayerScopeManager* mLayerScopeManager; + }; + + mozilla::UniquePtr<LayerScopeWebSocketManager> mWebSocketManager; + mozilla::UniquePtr<DrawSession> mSession; + mozilla::UniquePtr<ContentMonitor> mContentMonitor; + double mScale; +}; + +LayerScopeManager gLayerScopeManager; + +/* + * The static helper functions that set data into the packet + * 1. DumpRect + * 2. DumpFilter + */ +template <typename T> +static void DumpRect(T* aPacketRect, const Rect& aRect) { + aPacketRect->set_x(aRect.X()); + aPacketRect->set_y(aRect.Y()); + aPacketRect->set_w(aRect.Width()); + aPacketRect->set_h(aRect.Height()); +} + +static void DumpFilter(TexturePacket* aTexturePacket, + const SamplingFilter aSamplingFilter) { + switch (aSamplingFilter) { + case SamplingFilter::GOOD: + aTexturePacket->set_mfilter(TexturePacket::GOOD); + break; + case SamplingFilter::LINEAR: + aTexturePacket->set_mfilter(TexturePacket::LINEAR); + break; + case SamplingFilter::POINT: + aTexturePacket->set_mfilter(TexturePacket::POINT); + break; + default: + MOZ_ASSERT(false, + "Can't dump unexpected mSamplingFilter to texture packet!"); + break; + } +} + +/* + * DebugGLData is the base class of + * 1. DebugGLFrameStatusData (Frame start/end packet) + * 2. DebugGLColorData (Color data packet) + * 3. DebugGLTextureData (Texture data packet) + * 4. DebugGLLayersData (Layers Tree data packet) + * 5. DebugGLMetaData (Meta data packet) + */ +class DebugGLData : public LinkedListElement<DebugGLData> { + public: + explicit DebugGLData(Packet::DataType aDataType) : mDataType(aDataType) {} + + virtual ~DebugGLData() = default; + + virtual bool Write() = 0; + + protected: + static bool WriteToStream(Packet& aPacket) { + if (!gLayerScopeManager.GetSocketManager()) return true; + + size_t size = aPacket.ByteSizeLong(); + auto data = MakeUnique<uint8_t[]>(size); + aPacket.SerializeToArray(data.get(), size); + return gLayerScopeManager.GetSocketManager()->WriteAll(data.get(), size); + } + + Packet::DataType mDataType; +}; + +class DebugGLFrameStatusData final : public DebugGLData { + public: + DebugGLFrameStatusData(Packet::DataType aDataType, int64_t aValue) + : DebugGLData(aDataType), mFrameStamp(aValue) {} + + explicit DebugGLFrameStatusData(Packet::DataType aDataType) + : DebugGLData(aDataType), mFrameStamp(0) {} + + bool Write() override { + Packet packet; + packet.set_type(mDataType); + + FramePacket* fp = packet.mutable_frame(); + fp->set_value(static_cast<uint64_t>(mFrameStamp)); + + fp->set_scale(gLayerScopeManager.GetPixelScale()); + + return WriteToStream(packet); + } + + protected: + int64_t mFrameStamp; +}; + +class DebugGLTextureData final : public DebugGLData { + public: + DebugGLTextureData(GLContext* cx, void* layerRef, GLenum target, GLuint name, + DataSourceSurface* img, bool aIsMask, + UniquePtr<Packet> aPacket) + : DebugGLData(Packet::TEXTURE), + mLayerRef(reinterpret_cast<uint64_t>(layerRef)), + mTarget(target), + mName(name), + mContextAddress(reinterpret_cast<intptr_t>(cx)), + mDatasize(0), + mIsMask(aIsMask), + mPacket(std::move(aPacket)) { + // pre-packing + // DataSourceSurface may have locked buffer, + // so we should compress now, and then it could + // be unlocked outside. + pack(img); + } + + bool Write() override { return WriteToStream(*mPacket); } + + private: + void pack(DataSourceSurface* aImage) { + mPacket->set_type(mDataType); + + TexturePacket* tp = mPacket->mutable_texture(); + tp->set_layerref(mLayerRef); + tp->set_name(mName); + tp->set_target(mTarget); + tp->set_dataformat(LOCAL_GL_RGBA); + tp->set_glcontext(static_cast<uint64_t>(mContextAddress)); + tp->set_ismask(mIsMask); + + if (aImage) { + DataSourceSurface::ScopedMap map(aImage, DataSourceSurface::READ); + tp->set_width(aImage->GetSize().width); + tp->set_height(aImage->GetSize().height); + tp->set_stride(map.GetStride()); + + mDatasize = aImage->GetSize().height * map.GetStride(); + + auto compresseddata = + MakeUnique<char[]>(LZ4::maxCompressedSize(mDatasize)); + if (compresseddata) { + int ndatasize = LZ4::compress((char*)map.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(map.GetData(), mDatasize); + } + } else { + NS_WARNING("Couldn't new compressed data."); + tp->set_data(map.GetData(), mDatasize); + } + } else { + tp->set_width(0); + tp->set_height(0); + tp->set_stride(0); + } + } + + protected: + uint64_t mLayerRef; + GLenum mTarget; + GLuint mName; + intptr_t mContextAddress; + uint32_t mDatasize; + bool mIsMask; + + // Packet data + UniquePtr<Packet> mPacket; +}; + +class DebugGLColorData final : public DebugGLData { + public: + DebugGLColorData(void* layerRef, const DeviceColor& color, int width, + int height) + : DebugGLData(Packet::COLOR), + mLayerRef(reinterpret_cast<uint64_t>(layerRef)), + mColor(color.ToABGR()), + mSize(width, height) {} + + bool Write() override { + Packet packet; + packet.set_type(mDataType); + + ColorPacket* cp = packet.mutable_color(); + cp->set_layerref(mLayerRef); + cp->set_color(mColor); + cp->set_width(mSize.width); + cp->set_height(mSize.height); + + return WriteToStream(packet); + } + + protected: + uint64_t mLayerRef; + uint32_t mColor; + IntSize mSize; +}; + +class DebugGLLayersData final : public DebugGLData { + public: + explicit DebugGLLayersData(UniquePtr<Packet> aPacket) + : DebugGLData(Packet::LAYERS), mPacket(std::move(aPacket)) {} + + bool Write() override { + mPacket->set_type(mDataType); + return WriteToStream(*mPacket); + } + + protected: + UniquePtr<Packet> mPacket; +}; + +class DebugGLMetaData final : public DebugGLData { + public: + DebugGLMetaData(Packet::DataType aDataType, bool aValue) + : DebugGLData(aDataType), mComposedByHwc(aValue) {} + + explicit DebugGLMetaData(Packet::DataType aDataType) + : DebugGLData(aDataType), mComposedByHwc(false) {} + + bool Write() override { + Packet packet; + packet.set_type(mDataType); + + MetaPacket* mp = packet.mutable_meta(); + mp->set_composedbyhwc(mComposedByHwc); + + return WriteToStream(packet); + } + + protected: + bool mComposedByHwc; +}; + +class DebugGLDrawData final : public DebugGLData { + public: + DebugGLDrawData(float aOffsetX, float aOffsetY, + const gfx::Matrix4x4& aMVMatrix, size_t aRects, + const gfx::Rect* aLayerRects, const gfx::Rect* aTextureRects, + const std::list<GLuint>& aTexIDs, void* aLayerRef) + : DebugGLData(Packet::DRAW), + mOffsetX(aOffsetX), + mOffsetY(aOffsetY), + mMVMatrix(aMVMatrix), + mRects(aRects), + mTexIDs(aTexIDs), + mLayerRef(reinterpret_cast<uint64_t>(aLayerRef)) { + for (size_t i = 0; i < mRects; i++) { + mLayerRects[i] = aLayerRects[i]; + mTextureRects[i] = aTextureRects[i]; + } + } + + bool Write() override { + Packet packet; + packet.set_type(mDataType); + + DrawPacket* dp = packet.mutable_draw(); + dp->set_layerref(mLayerRef); + + dp->set_offsetx(mOffsetX); + dp->set_offsety(mOffsetY); + + auto element = reinterpret_cast<Float*>(&mMVMatrix); + for (int i = 0; i < 16; i++) { + dp->add_mvmatrix(*element++); + } + dp->set_totalrects(mRects); + + MOZ_ASSERT(mRects > 0 && mRects < 4); + for (size_t i = 0; i < mRects; i++) { + // Vertex + DumpRect(dp->add_layerrect(), mLayerRects[i]); + // UV + DumpRect(dp->add_texturerect(), mTextureRects[i]); + } + + for (GLuint texId : mTexIDs) { + dp->add_texids(texId); + } + + return WriteToStream(packet); + } + + protected: + float mOffsetX; + float mOffsetY; + gfx::Matrix4x4 mMVMatrix; + size_t mRects; + gfx::Rect mLayerRects[4]; + gfx::Rect mTextureRects[4]; + std::list<GLuint> mTexIDs; + uint64_t mLayerRef; +}; + +class DebugDataSender { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DebugDataSender) + + // Append a DebugData into mList on mThread + class AppendTask : public nsIRunnable { + public: + NS_DECL_THREADSAFE_ISUPPORTS + + AppendTask(DebugDataSender* host, DebugGLData* d) : mData(d), mHost(host) {} + + NS_IMETHOD Run() override { + mHost->mList.insertBack(mData); + return NS_OK; + } + + private: + virtual ~AppendTask() = default; + + DebugGLData* mData; + // Keep a strong reference to DebugDataSender to prevent this object + // accessing mHost on mThread, when it's been destroyed on the main + // thread. + RefPtr<DebugDataSender> mHost; + }; + + // Clear all DebugData in mList on mThead. + class ClearTask : public nsIRunnable { + public: + NS_DECL_THREADSAFE_ISUPPORTS + explicit ClearTask(DebugDataSender* host) : mHost(host) {} + + NS_IMETHOD Run() override { + mHost->RemoveData(); + return NS_OK; + } + + private: + virtual ~ClearTask() = default; + + RefPtr<DebugDataSender> mHost; + }; + + // Send all DebugData in mList via websocket, and then, clean up + // mList on mThread. + class SendTask : public nsIRunnable { + public: + NS_DECL_THREADSAFE_ISUPPORTS + + explicit SendTask(DebugDataSender* host) : mHost(host) {} + + NS_IMETHOD Run() override { + // Sendout all appended debug data. + DebugGLData* d = nullptr; + while ((d = mHost->mList.popFirst()) != nullptr) { + UniquePtr<DebugGLData> cleaner(d); + if (!d->Write()) { + gLayerScopeManager.DestroyServerSocket(); + break; + } + } + + // Cleanup. + mHost->RemoveData(); + return NS_OK; + } + + private: + virtual ~SendTask() = default; + + RefPtr<DebugDataSender> mHost; + }; + + explicit DebugDataSender(nsIThread* thread) : mThread(thread) {} + + void Append(DebugGLData* d) { + mThread->Dispatch(new AppendTask(this, d), NS_DISPATCH_NORMAL); + } + + void Cleanup() { mThread->Dispatch(new ClearTask(this), NS_DISPATCH_NORMAL); } + + void Send() { mThread->Dispatch(new SendTask(this), NS_DISPATCH_NORMAL); } + + protected: + virtual ~DebugDataSender() = default; + void RemoveData() { + MOZ_ASSERT(mThread->SerialEventTarget()->IsOnCurrentThread()); + if (mList.isEmpty()) return; + + DebugGLData* d; + while ((d = mList.popFirst()) != nullptr) delete d; + } + + // We can only modify or aceess mList on mThread. + LinkedList<DebugGLData> mList; + nsCOMPtr<nsIThread> mThread; +}; + +NS_IMPL_ISUPPORTS(DebugDataSender::AppendTask, nsIRunnable); +NS_IMPL_ISUPPORTS(DebugDataSender::ClearTask, nsIRunnable); +NS_IMPL_ISUPPORTS(DebugDataSender::SendTask, nsIRunnable); + +/* + * LayerScope SendXXX Structure + * 1. SendLayer + * 2. SendEffectChain + * 1. SendTexturedEffect + * -> SendTextureSource + * 2. SendMaskEffect + * -> SendTextureSource + * 3. SendYCbCrEffect + * -> SendTextureSource + * 4. 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); + + static void SetLayersTreeSendable(bool aSet) { sLayersTreeSendable = aSet; } + + static void SetLayersBufferSendable(bool aSet) { + sLayersBufferSendable = aSet; + } + + static bool GetLayersTreeSendable() { return sLayersTreeSendable; } + + static void ClearSentTextureIds(); + + // Sender private functions + private: + static void SendColor(void* aLayerRef, const DeviceColor& aColor, int aWidth, + int aHeight); + static void SendTextureSource(GLContext* aGLContext, void* aLayerRef, + TextureSourceOGL* aSource, bool aFlipY, + bool aIsMask, UniquePtr<Packet> aPacket); + static void SetAndSendTexture(GLContext* aGLContext, void* aLayerRef, + TextureSourceOGL* aSource, + const TexturedEffect* aEffect); + static void SendTexturedEffect(GLContext* aGLContext, void* aLayerRef, + const TexturedEffect* aEffect); + static void SendMaskEffect(GLContext* aGLContext, void* aLayerRef, + const EffectMask* aEffect); + static void SendYCbCrEffect(GLContext* aGLContext, void* aLayerRef, + const EffectYCbCr* aEffect); + static GLuint GetTextureID(GLContext* aGLContext, TextureSourceOGL* aSource); + static bool HasTextureIdBeenSent(GLuint aTextureId); + // Data fields + private: + static bool sLayersTreeSendable; + static bool sLayersBufferSendable; + static std::vector<GLuint> sSentTextureIds; +}; + +bool SenderHelper::sLayersTreeSendable = true; +bool SenderHelper::sLayersBufferSendable = true; +std::vector<GLuint> SenderHelper::sSentTextureIds; + +// ---------------------------------------------- +// SenderHelper implementation +// ---------------------------------------------- +void SenderHelper::ClearSentTextureIds() { sSentTextureIds.clear(); } + +bool SenderHelper::HasTextureIdBeenSent(GLuint aTextureId) { + return std::find(sSentTextureIds.begin(), sSentTextureIds.end(), + aTextureId) != sSentTextureIds.end(); +} + +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); + + LayerScope::DrawBegin(); + LayerScope::DrawEnd(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(); + TextureSourceProvider* provider = compHost->GetTextureSourceProvider(); + Compositor* comp = provider->AsCompositor(); + // Send EffectChain only for CompositorOGL + if (LayersBackend::LAYERS_OPENGL == comp->GetBackendType()) { + CompositorOGL* compOGL = comp->AsCompositorOGL(); + EffectChain effect; + // Generate primary effect (lock and gen) + AutoLockCompositableHost lock(compHost); + aLayer->GenEffectChain(effect); + + LayerScope::DrawBegin(); + LayerScope::DrawEnd(compOGL->gl(), effect, aWidth, aHeight); + } + break; + } + case Layer::TYPE_CONTAINER: + default: + break; + } +} + +void SenderHelper::SendColor(void* aLayerRef, const DeviceColor& aColor, + int aWidth, int aHeight) { + gLayerScopeManager.GetSocketManager()->AppendDebugData( + new DebugGLColorData(aLayerRef, aColor, aWidth, aHeight)); +} + +GLuint SenderHelper::GetTextureID(GLContext* aGLContext, + TextureSourceOGL* aSource) { + GLenum textureTarget = aSource->GetTextureTarget(); + aSource->BindTexture(LOCAL_GL_TEXTURE0, gfx::SamplingFilter::LINEAR); + + GLuint texID = 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, &texID); + } else if (textureTarget == LOCAL_GL_TEXTURE_EXTERNAL) { + aGLContext->GetUIntegerv(LOCAL_GL_TEXTURE_BINDING_EXTERNAL, &texID); + } else if (textureTarget == LOCAL_GL_TEXTURE_RECTANGLE) { + aGLContext->GetUIntegerv(LOCAL_GL_TEXTURE_BINDING_RECTANGLE, &texID); + } + + return texID; +} + +void SenderHelper::SendTextureSource(GLContext* aGLContext, void* aLayerRef, + TextureSourceOGL* aSource, bool aFlipY, + bool aIsMask, UniquePtr<Packet> aPacket) { + MOZ_ASSERT(aGLContext); + if (!aGLContext) { + return; + } + GLuint texID = GetTextureID(aGLContext, aSource); + if (HasTextureIdBeenSent(texID)) { + return; + } + + GLenum textureTarget = aSource->GetTextureTarget(); + ShaderConfigOGL config = + ShaderConfigFromTargetAndFormat(textureTarget, aSource->GetFormat()); + int shaderConfig = config.mFeatures; + + gfx::IntSize size = aSource->GetSize(); + + // By sending 0 to ReadTextureImage rely upon aSource->BindTexture binding + // texture correctly. texID is used for tracking in DebugGLTextureData. + RefPtr<DataSourceSurface> img = + aGLContext->ReadTexImageHelper()->ReadTexImage(0, textureTarget, size, + shaderConfig, aFlipY); + gLayerScopeManager.GetSocketManager()->AppendDebugData( + new DebugGLTextureData(aGLContext, aLayerRef, textureTarget, texID, img, + aIsMask, std::move(aPacket))); + + sSentTextureIds.push_back(texID); + gLayerScopeManager.CurrentSession().mTexIDs.push_back(texID); +} + +void SenderHelper::SetAndSendTexture(GLContext* aGLContext, void* aLayerRef, + TextureSourceOGL* aSource, + const TexturedEffect* aEffect) { + // Expose packet creation here, so we could dump primary texture effect + // attributes. + auto packet = MakeUnique<layerscope::Packet>(); + layerscope::TexturePacket* texturePacket = packet->mutable_texture(); + texturePacket->set_mpremultiplied(aEffect->mPremultiplied); + DumpFilter(texturePacket, aEffect->mSamplingFilter); + DumpRect(texturePacket->mutable_mtexturecoords(), aEffect->mTextureCoords); + SendTextureSource(aGLContext, aLayerRef, aSource, false, false, + std::move(packet)); +} + +void SenderHelper::SendTexturedEffect(GLContext* aGLContext, void* aLayerRef, + const TexturedEffect* aEffect) { + TextureSourceOGL* source = aEffect->mTexture->AsSourceOGL(); + if (!source) { + return; + } + + // Fallback texture sending path. + SetAndSendTexture(aGLContext, aLayerRef, source, aEffect); +} + +void SenderHelper::SendMaskEffect(GLContext* aGLContext, void* aLayerRef, + const EffectMask* aEffect) { + TextureSourceOGL* source = aEffect->mMaskTexture->AsSourceOGL(); + if (!source) { + return; + } + + // Expose packet creation here, so we could dump secondary mask effect + // attributes. + auto packet = MakeUnique<layerscope::Packet>(); + TexturePacket::EffectMask* mask = packet->mutable_texture()->mutable_mask(); + mask->mutable_msize()->set_w(aEffect->mSize.width); + mask->mutable_msize()->set_h(aEffect->mSize.height); + auto element = reinterpret_cast<const Float*>(&(aEffect->mMaskTransform)); + for (int i = 0; i < 16; i++) { + mask->mutable_mmasktransform()->add_m(*element++); + } + + SendTextureSource(aGLContext, aLayerRef, source, false, true, + std::move(packet)); +} + +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* sources[] = {sourceYCbCr->GetSubSource(Y)->AsSourceOGL(), + sourceYCbCr->GetSubSource(Cb)->AsSourceOGL(), + sourceYCbCr->GetSubSource(Cr)->AsSourceOGL()}; + + for (auto source : sources) { + SetAndSendTexture(aGLContext, aLayerRef, source, aEffect); + } +} + +void SenderHelper::SendEffectChain(GLContext* aGLContext, + const EffectChain& aEffectChain, int aWidth, + int aHeight) { + if (!sLayersBufferSendable) return; + + const Effect* primaryEffect = aEffectChain.mPrimaryEffect; + MOZ_ASSERT(primaryEffect); + + if (!primaryEffect) { + return; + } + + 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); + SendColor(aEffectChain.mLayerRef, solidColorEffect->mColor, aWidth, + aHeight); + break; + } + case EffectTypes::COMPONENT_ALPHA: + case EffectTypes::RENDER_TARGET: + default: + break; + } + + if (aEffectChain.mSecondaryEffects[EffectTypes::MASK]) { + const EffectMask* effectMask = static_cast<const EffectMask*>( + aEffectChain.mSecondaryEffects[EffectTypes::MASK].get()); + SendMaskEffect(aGLContext, aEffectChain.mLayerRef, effectMask); + } +} + +void LayerScope::ContentChanged(TextureHost* host) { + if (!CheckSendable()) { + return; + } + + gLayerScopeManager.GetContentMonitor()->SetChangedHost(host); +} + +// ---------------------------------------------- +// SocketHandler implementation +// ---------------------------------------------- +void LayerScopeWebSocketManager::SocketHandler::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, GetCurrentEventTarget()); +} + +bool LayerScopeWebSocketManager::SocketHandler::WriteToStream(void* aPtr, + uint32_t aSize) { + if (mState == NoHandshake) { + // Not yet handshake, just return true in case of + // LayerScope remove this handle + return true; + } else if (mState == HandshakeFailed) { + return false; + } + + if (!mOutputStream) { + 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 (aSize <= 125) { + wsHeaderSize = 2; + wsHeader[1] = aSize; + } else if (aSize < 65536) { + wsHeaderSize = 4; + wsHeader[1] = 0x7E; + NetworkEndian::writeUint16(wsHeader + 2, aSize); + } else { + wsHeaderSize = 10; + wsHeader[1] = 0x7F; + NetworkEndian::writeUint64(wsHeader + 2, aSize); + } + + // 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 < aSize) { + uint32_t cnt; + rv = mOutputStream->Write(reinterpret_cast<char*>(aPtr) + written, + aSize - written, &cnt); + if (NS_FAILED(rv)) return false; + + written += cnt; + } + + return true; +} + +NS_IMETHODIMP +LayerScopeWebSocketManager::SocketHandler::OnInputStreamReady( + nsIAsyncInputStream* aStream) { + MOZ_ASSERT(mInputStream); + + if (!mInputStream) { + return NS_OK; + } + + if (!mConnected) { + nsTArray<nsCString> protocolString; + ReadInputStreamData(protocolString); + + if (WebSocketHandshake(protocolString)) { + mState = HandshakeSuccess; + mConnected = true; + mInputStream->AsyncWait(this, 0, 0, GetCurrentEventTarget()); + } else { + mState = HandshakeFailed; + } + return NS_OK; + } else { + return HandleSocketMessage(aStream); + } +} + +void LayerScopeWebSocketManager::SocketHandler::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 LayerScopeWebSocketManager::SocketHandler::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; + } + + if (!mOutputStream) { + 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); + nsCString response("HTTP/1.1 101 Switching Protocols\r\n"); + response.AppendLiteral("Upgrade: websocket\r\n"); + response.AppendLiteral("Connection: Upgrade\r\n"); + response.AppendLiteral("Sec-WebSocket-Accept: "); + rv = Base64EncodeAppend(newString, response); + if (NS_FAILED(rv)) { + return false; + } + response.AppendLiteral("\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; +} + +nsresult LayerScopeWebSocketManager::SocketHandler::HandleSocketMessage( + nsIAsyncInputStream* aStream) { + // The reading and parsing of this input stream is customized for layer + // viewer. + const uint32_t cPacketSize = 1024; + char buffer[cPacketSize]; + uint32_t count = 0; + nsresult rv = NS_OK; + + do { + rv = mInputStream->Read((char*)buffer, cPacketSize, &count); + + // TODO: combine packets if we have to read more than once + + if (rv == NS_BASE_STREAM_WOULD_BLOCK) { + mInputStream->AsyncWait(this, 0, 0, GetCurrentEventTarget()); + return NS_OK; + } + + if (NS_FAILED(rv)) { + break; + } + + if (count == 0) { + // NS_BASE_STREAM_CLOSED + CloseConnection(); + break; + } + + rv = ProcessInput(reinterpret_cast<uint8_t*>(buffer), count); + } while (NS_SUCCEEDED(rv) && mInputStream); + return rv; +} + +nsresult LayerScopeWebSocketManager::SocketHandler::ProcessInput( + uint8_t* aBuffer, uint32_t aCount) { + uint32_t avail = aCount; + + // Decode Websocket data frame + if (avail <= 2) { + NS_WARNING("Packet size is less than 2 bytes"); + return NS_OK; + } + + // First byte, data type, only care the opcode + // rsvBits: aBuffer[0] & 0x70 (0111 0000) + uint8_t finBit = aBuffer[0] & 0x80; // 1000 0000 + uint8_t opcode = aBuffer[0] & 0x0F; // 0000 1111 + + if (!finBit) { + NS_WARNING( + "We cannot handle multi-fragments messages in Layerscope websocket " + "parser."); + return NS_OK; + } + + // Second byte, data length + uint8_t maskBit = aBuffer[1] & 0x80; // 1000 0000 + int64_t payloadLength64 = aBuffer[1] & 0x7F; // 0111 1111 + + if (!maskBit) { + NS_WARNING("Client to Server should set the mask bit"); + return NS_OK; + } + + uint32_t framingLength = 2 + 4; // 4 for masks + + if (payloadLength64 < 126) { + if (avail < framingLength) return NS_OK; + } else if (payloadLength64 == 126) { + // 16 bit length field + framingLength += 2; + if (avail < framingLength) { + return NS_OK; + } + + payloadLength64 = aBuffer[2] << 8 | aBuffer[3]; + } else { + // 64 bit length + framingLength += 8; + if (avail < framingLength) { + return NS_OK; + } + + if (aBuffer[2] & 0x80) { + // Section 4.2 says that the most significant bit MUST be + // 0. (i.e. this is really a 63 bit value) + NS_WARNING("High bit of 64 bit length set"); + return NS_ERROR_ILLEGAL_VALUE; + } + + // copy this in case it is unaligned + payloadLength64 = NetworkEndian::readInt64(aBuffer + 2); + } + + uint8_t* payload = aBuffer + framingLength; + avail -= framingLength; + + uint32_t payloadLength = static_cast<uint32_t>(payloadLength64); + if (avail < payloadLength) { + NS_WARNING("Packet size mismatch the payload length"); + return NS_OK; + } + + // Apply mask + uint32_t mask = NetworkEndian::readUint32(payload - 4); + ApplyMask(mask, payload, payloadLength); + + if (opcode == 0x8) { + // opcode == 0x8 means connection close + CloseConnection(); + return NS_BASE_STREAM_CLOSED; + } + + if (!HandleDataFrame(payload, payloadLength)) { + NS_WARNING("Cannot decode payload data by the protocol buffer"); + } + + return NS_OK; +} + +void LayerScopeWebSocketManager::SocketHandler::ApplyMask(uint32_t aMask, + uint8_t* aData, + uint64_t aLen) { + if (!aData || aLen == 0) { + return; + } + + // Optimally we want to apply the mask 32 bits at a time, + // but the buffer might not be alligned. So we first deal with + // 0 to 3 bytes of preamble individually + while (aLen && (reinterpret_cast<uintptr_t>(aData) & 3)) { + *aData ^= aMask >> 24; + aMask = RotateLeft(aMask, 8); + aData++; + aLen--; + } + + // perform mask on full words of data + uint32_t* iData = reinterpret_cast<uint32_t*>(aData); + uint32_t* end = iData + (aLen >> 2); + NetworkEndian::writeUint32(&aMask, aMask); + for (; iData < end; iData++) { + *iData ^= aMask; + } + aMask = NetworkEndian::readUint32(&aMask); + aData = (uint8_t*)iData; + aLen = aLen % 4; + + // There maybe up to 3 trailing bytes that need to be dealt with + // individually + while (aLen) { + *aData ^= aMask >> 24; + aMask = RotateLeft(aMask, 8); + aData++; + aLen--; + } +} + +bool LayerScopeWebSocketManager::SocketHandler::HandleDataFrame( + uint8_t* aData, uint32_t aSize) { + // Handle payload data by protocol buffer + auto p = MakeUnique<CommandPacket>(); + p->ParseFromArray(static_cast<void*>(aData), aSize); + + if (!p->has_type()) { + MOZ_ASSERT(false, "Protocol buffer decoding failed or cannot recongize it"); + return false; + } + + switch (p->type()) { + case CommandPacket::LAYERS_TREE: + if (p->has_value()) { + SenderHelper::SetLayersTreeSendable(p->value()); + } + break; + + case CommandPacket::LAYERS_BUFFER: + if (p->has_value()) { + SenderHelper::SetLayersBufferSendable(p->value()); + } + break; + + case CommandPacket::NO_OP: + default: + NS_WARNING("Invalid message type"); + break; + } + return true; +} + +void LayerScopeWebSocketManager::SocketHandler::CloseConnection() { + gLayerScopeManager.GetSocketManager()->CleanDebugData(); + if (mInputStream) { + mInputStream->AsyncWait(nullptr, 0, 0, nullptr); + mInputStream = nullptr; + } + if (mOutputStream) { + mOutputStream = nullptr; + } + if (mTransport) { + mTransport->Close(NS_BASE_STREAM_CLOSED); + mTransport = nullptr; + } + mConnected = false; +} + +// ---------------------------------------------- +// LayerScopeWebSocketManager implementation +// ---------------------------------------------- +LayerScopeWebSocketManager::LayerScopeWebSocketManager() + : mHandlerMutex("LayerScopeWebSocketManager::mHandlerMutex") { + NS_NewNamedThread("LayerScope", getter_AddRefs(mDebugSenderThread)); + + mServerSocket = do_CreateInstance(NS_SERVERSOCKET_CONTRACTID); + int port = StaticPrefs::gfx_layerscope_port(); + mServerSocket->Init(port, false, -1); + mServerSocket->AsyncListen(new SocketListener); +} + +LayerScopeWebSocketManager::~LayerScopeWebSocketManager() { + mServerSocket->Close(); +} + +void LayerScopeWebSocketManager::AppendDebugData(DebugGLData* aDebugData) { + if (!mCurrentSender) { + mCurrentSender = new DebugDataSender(mDebugSenderThread); + } + + mCurrentSender->Append(aDebugData); +} + +void LayerScopeWebSocketManager::CleanDebugData() { + if (mCurrentSender) { + mCurrentSender->Cleanup(); + } +} + +void LayerScopeWebSocketManager::DispatchDebugData() { + MOZ_ASSERT(mCurrentSender.get() != nullptr); + + mCurrentSender->Send(); + mCurrentSender = nullptr; +} + +NS_IMETHODIMP LayerScopeWebSocketManager::SocketListener::OnSocketAccepted( + nsIServerSocket* aServ, nsISocketTransport* aTransport) { + if (!gLayerScopeManager.GetSocketManager()) return NS_OK; + + printf_stderr("*** LayerScope: Accepted connection\n"); + gLayerScopeManager.GetSocketManager()->AddConnection(aTransport); + gLayerScopeManager.GetContentMonitor()->Empty(); + return NS_OK; +} + +// ---------------------------------------------- +// LayerScope implementation +// ---------------------------------------------- +/*static*/ +void LayerScope::Init() { + if (!StaticPrefs::gfx_layerscope_enabled() || XRE_IsGPUProcess()) { + return; + } + + gLayerScopeManager.CreateServerSocket(); +} + +/*static*/ +void LayerScope::DrawBegin() { + if (!CheckSendable()) { + return; + } + + gLayerScopeManager.NewDrawSession(); +} + +/*static*/ +void LayerScope::SetRenderOffset(float aX, float aY) { + if (!CheckSendable()) { + return; + } + + gLayerScopeManager.CurrentSession().mOffsetX = aX; + gLayerScopeManager.CurrentSession().mOffsetY = aY; +} + +/*static*/ +void LayerScope::SetLayerTransform(const gfx::Matrix4x4& aMatrix) { + if (!CheckSendable()) { + return; + } + + gLayerScopeManager.CurrentSession().mMVMatrix = aMatrix; +} + +/*static*/ +void LayerScope::SetDrawRects(size_t aRects, const gfx::Rect* aLayerRects, + const gfx::Rect* aTextureRects) { + if (!CheckSendable()) { + return; + } + + MOZ_ASSERT(aRects > 0 && aRects <= 4); + MOZ_ASSERT(aLayerRects); + + gLayerScopeManager.CurrentSession().mRects = aRects; + + for (size_t i = 0; i < aRects; i++) { + gLayerScopeManager.CurrentSession().mLayerRects[i] = aLayerRects[i]; + gLayerScopeManager.CurrentSession().mTextureRects[i] = aTextureRects[i]; + } +} + +/*static*/ +void LayerScope::DrawEnd(gl::GLContext* aGLContext, + const EffectChain& aEffectChain, int aWidth, + int aHeight) { + // Protect this public function + if (!CheckSendable()) { + return; + } + + // 1. Send textures. + SenderHelper::SendEffectChain(aGLContext, aEffectChain, aWidth, aHeight); + + // 2. Send parameters of draw call, such as uniforms and attributes of + // vertex adnd fragment shader. + DrawSession& draws = gLayerScopeManager.CurrentSession(); + gLayerScopeManager.GetSocketManager()->AppendDebugData( + new DebugGLDrawData(draws.mOffsetX, draws.mOffsetY, draws.mMVMatrix, + draws.mRects, draws.mLayerRects, draws.mTextureRects, + draws.mTexIDs, aEffectChain.mLayerRef)); +} + +/*static*/ +void LayerScope::SendLayer(LayerComposite* aLayer, int aWidth, int aHeight) { + // Protect this public function + if (!CheckSendable()) { + return; + } + SenderHelper::SendLayer(aLayer, aWidth, aHeight); +} + +/*static*/ +void LayerScope::SendLayerDump(UniquePtr<Packet> aPacket) { + // Protect this public function + if (!CheckSendable() || !SenderHelper::GetLayersTreeSendable()) { + return; + } + gLayerScopeManager.GetSocketManager()->AppendDebugData( + new DebugGLLayersData(std::move(aPacket))); +} + +/*static*/ +bool LayerScope::CheckSendable() { + // Only compositor threads check LayerScope status + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread() || gIsGtest); + + if (!StaticPrefs::gfx_layerscope_enabled()) { + return false; + } + if (!gLayerScopeManager.GetSocketManager()) { + Init(); + return false; + } + if (!gLayerScopeManager.GetSocketManager()->IsConnected()) { + return false; + } + return true; +} + +/*static*/ +void LayerScope::CleanLayer() { + if (CheckSendable()) { + gLayerScopeManager.GetSocketManager()->CleanDebugData(); + } +} + +/*static*/ +void LayerScope::SetHWComposed() { + if (CheckSendable()) { + gLayerScopeManager.GetSocketManager()->AppendDebugData( + new DebugGLMetaData(Packet::META, true)); + } +} + +/*static*/ +void LayerScope::SetPixelScale(double devPixelsPerCSSPixel) { + gLayerScopeManager.SetPixelScale(devPixelsPerCSSPixel); +} + +// ---------------------------------------------- +// 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; + } + SenderHelper::ClearSentTextureIds(); + + gLayerScopeManager.GetSocketManager()->AppendDebugData( + new DebugGLFrameStatusData(Packet::FRAMESTART, aFrameStamp)); +} + +void LayerScopeAutoFrame::EndFrame() { + if (!LayerScope::CheckSendable()) { + return; + } + + gLayerScopeManager.GetSocketManager()->AppendDebugData( + new DebugGLFrameStatusData(Packet::FRAMEEND)); + gLayerScopeManager.GetSocketManager()->DispatchDebugData(); +} + +} // namespace layers +} // namespace mozilla |