/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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/. */ #ifndef MOZILLA_GFX_RECORDEDEVENT_H_ #define MOZILLA_GFX_RECORDEDEVENT_H_ #include #include #include #include #include #include "RecordingTypes.h" #include "mozilla/gfx/Point.h" #include "mozilla/gfx/Types.h" #include "mozilla/ipc/ByteBuf.h" #include "nsRefPtrHashtable.h" namespace mozilla { namespace gfx { const uint32_t kMagicInt = 0xc001feed; // A change in major revision means a change in event binary format, causing // loss of backwards compatibility. Old streams will not work in a player // using a newer major revision. And new streams will not work in a player // using an older major revision. const uint16_t kMajorRevision = 10; // A change in minor revision means additions of new events. New streams will // not play in older players. const uint16_t kMinorRevision = 3; struct ReferencePtr { ReferencePtr() : mLongPtr(0) {} MOZ_IMPLICIT ReferencePtr(const void* aLongPtr) : mLongPtr(uint64_t(aLongPtr)) {} template MOZ_IMPLICIT ReferencePtr(const RefPtr& aPtr) : mLongPtr(uint64_t(aPtr.get())) {} ReferencePtr& operator=(const void* aLongPtr) { mLongPtr = uint64_t(aLongPtr); return *this; } template ReferencePtr& operator=(const RefPtr& aPtr) { mLongPtr = uint64_t(aPtr.get()); return *this; } operator void*() const { return (void*)mLongPtr; } uint64_t mLongPtr; }; struct RecordedFontDetails { uint64_t fontDataKey = 0; uint32_t size = 0; uint32_t index = 0; }; struct RecordedDependentSurface { NS_INLINE_DECL_REFCOUNTING(RecordedDependentSurface); RecordedDependentSurface(const IntSize& aSize, mozilla::ipc::ByteBuf&& aRecording) : mSize(aSize), mRecording(std::move(aRecording)) {} IntSize mSize; mozilla::ipc::ByteBuf mRecording; private: ~RecordedDependentSurface() = default; }; // Used by the Azure drawing debugger (player2d) inline std::string StringFromPtr(ReferencePtr aPtr) { std::stringstream stream; stream << aPtr; return stream.str(); } class Translator { public: virtual ~Translator() = default; virtual DrawTarget* LookupDrawTarget(ReferencePtr aRefPtr) = 0; virtual Path* LookupPath(ReferencePtr aRefPtr) = 0; virtual SourceSurface* LookupSourceSurface(ReferencePtr aRefPtr) = 0; virtual FilterNode* LookupFilterNode(ReferencePtr aRefPtr) = 0; virtual already_AddRefed LookupGradientStops( ReferencePtr aRefPtr) = 0; virtual ScaledFont* LookupScaledFont(ReferencePtr aRefPtr) = 0; virtual UnscaledFont* LookupUnscaledFont(ReferencePtr aRefPtr) = 0; virtual NativeFontResource* LookupNativeFontResource(uint64_t aKey) = 0; virtual already_AddRefed LookupExternalSurface(uint64_t aKey) { return nullptr; } virtual already_AddRefed LookupSourceSurfaceFromSurfaceDescriptor( const layers::SurfaceDescriptor& aDesc) { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); return nullptr; } void DrawDependentSurface(uint64_t aKey, const Rect& aRect); virtual void AddDrawTarget(ReferencePtr aRefPtr, DrawTarget* aDT) = 0; virtual void RemoveDrawTarget(ReferencePtr aRefPtr) = 0; virtual bool SetCurrentDrawTarget(ReferencePtr aRefPtr) = 0; virtual void AddPath(ReferencePtr aRefPtr, Path* aPath) = 0; virtual void RemovePath(ReferencePtr aRefPtr) = 0; virtual void AddSourceSurface(ReferencePtr aRefPtr, SourceSurface* aPath) = 0; virtual void RemoveSourceSurface(ReferencePtr aRefPtr) = 0; virtual void AddFilterNode(mozilla::gfx::ReferencePtr aRefPtr, FilterNode* aSurface) = 0; virtual void RemoveFilterNode(mozilla::gfx::ReferencePtr aRefPtr) = 0; /** * Get GradientStops compatible with the translation DrawTarget type. * @param aRawStops array of raw gradient stops required * @param aNumStops length of aRawStops * @param aExtendMode extend mode required * @return an already addrefed GradientStops for our DrawTarget type */ virtual already_AddRefed GetOrCreateGradientStops( DrawTarget* aDrawTarget, GradientStop* aRawStops, uint32_t aNumStops, ExtendMode aExtendMode) { return aDrawTarget->CreateGradientStops(aRawStops, aNumStops, aExtendMode); } virtual void AddGradientStops(ReferencePtr aRefPtr, GradientStops* aPath) = 0; virtual void RemoveGradientStops(ReferencePtr aRefPtr) = 0; virtual void AddScaledFont(ReferencePtr aRefPtr, ScaledFont* aScaledFont) = 0; virtual void RemoveScaledFont(ReferencePtr aRefPtr) = 0; virtual void AddUnscaledFont(ReferencePtr aRefPtr, UnscaledFont* aUnscaledFont) = 0; virtual void RemoveUnscaledFont(ReferencePtr aRefPtr) = 0; virtual void AddNativeFontResource( uint64_t aKey, NativeFontResource* aNativeFontResource) = 0; virtual already_AddRefed CreateDrawTarget(ReferencePtr aRefPtr, const IntSize& aSize, SurfaceFormat aFormat); virtual DrawTarget* GetReferenceDrawTarget() = 0; virtual Matrix GetReferenceDrawTargetTransform() { return Matrix(); } virtual void* GetFontContext() { return nullptr; } void SetDependentSurfaces( nsRefPtrHashtable* aDependentSurfaces) { mDependentSurfaces = aDependentSurfaces; } DrawTarget* GetCurrentDrawTarget() const { return mCurrentDT; } nsRefPtrHashtable* mDependentSurfaces = nullptr; DrawTarget* mCurrentDT = nullptr; }; struct ColorPatternStorage { DeviceColor mColor; }; struct LinearGradientPatternStorage { Point mBegin; Point mEnd; ReferencePtr mStops; Matrix mMatrix; }; struct RadialGradientPatternStorage { Point mCenter1; Point mCenter2; Float mRadius1; Float mRadius2; ReferencePtr mStops; Matrix mMatrix; }; struct ConicGradientPatternStorage { Point mCenter; Float mAngle; Float mStartOffset; Float mEndOffset; ReferencePtr mStops; Matrix mMatrix; }; struct SurfacePatternStorage { ExtendMode mExtend; SamplingFilter mSamplingFilter; ReferencePtr mSurface; Matrix mMatrix; IntRect mSamplingRect; }; struct PatternStorage { PatternType mType; union { char* mStorage; char mColor[sizeof(ColorPatternStorage)]; char mLinear[sizeof(LinearGradientPatternStorage)]; char mRadial[sizeof(RadialGradientPatternStorage)]; char mConic[sizeof(ConicGradientPatternStorage)]; char mSurface[sizeof(SurfacePatternStorage)]; }; }; /* SizeCollector and MemWriter are used * in a pair to first collect the size of the * event that we're going to write and then * to write it without checking each individual * size. */ struct SizeCollector { SizeCollector() : mTotalSize(0) {} void write(const char*, size_t s) { mTotalSize += s; } size_t mTotalSize; }; struct MemWriter { constexpr explicit MemWriter(char* aPtr) : mPtr(aPtr) {} void write(const char* aData, size_t aSize) { memcpy(mPtr, aData, aSize); mPtr += aSize; } char* mPtr; }; // An istream like class for reading from memory struct MemReader { constexpr MemReader(char* aData, size_t aLen) : mData(aData), mEnd(aData + aLen) {} void read(char* s, std::streamsize n) { if (n <= (mEnd - mData)) { memcpy(s, mData, n); mData += n; } else { // We've requested more data than is available // set the Reader into an eof state SetIsBad(); } } bool eof() { return mData > mEnd; } bool good() { return !eof(); } void SetIsBad() { mData = mEnd + 1; } char* mData; char* mEnd; }; class ContiguousBuffer { public: ContiguousBuffer(char* aStart, size_t aSize) : mWriter(aStart), mEnd(aStart + aSize) {} constexpr MOZ_IMPLICIT ContiguousBuffer(std::nullptr_t) : mWriter(nullptr) {} MemWriter& Writer() { return mWriter; } size_t SizeRemaining() { return mWriter.mPtr ? mEnd - mWriter.mPtr : 0; } bool IsValid() { return !!mWriter.mPtr; } private: MemWriter mWriter; char* mEnd = nullptr; }; // Allows a derived class to provide guaranteed contiguous buffer. class ContiguousBufferStream { public: /** * Templated RecordEvent function so that we can record into the buffer * quickly using MemWriter. * * @param aRecordedEvent the event to record */ template void RecordEvent(const RE* aRecordedEvent) { SizeCollector size; WriteElement(size, aRecordedEvent->GetType()); aRecordedEvent->Record(size); auto& buffer = GetContiguousBuffer(size.mTotalSize); if (!buffer.IsValid()) { return; } MOZ_ASSERT(size.mTotalSize <= buffer.SizeRemaining()); WriteElement(buffer.Writer(), aRecordedEvent->GetType()); aRecordedEvent->Record(buffer.Writer()); IncrementEventCount(); } protected: /** * Provide a contiguous buffer with at least aSize remaining. */ virtual ContiguousBuffer& GetContiguousBuffer(size_t aSize) = 0; virtual void IncrementEventCount() = 0; }; struct MemStream { char* mData; size_t mLength; size_t mCapacity; bool mValid = true; bool Resize(size_t aSize) { if (!mValid) { return false; } mLength = aSize; if (mLength > mCapacity) { mCapacity = mCapacity * 2; // check if the doubled capacity is enough // otherwise use double mLength if (mLength > mCapacity) { mCapacity = mLength * 2; } char* data = (char*)realloc(mData, mCapacity); if (!data) { free(mData); } mData = data; } if (mData) { return true; } NS_ERROR("Failed to allocate MemStream!"); mValid = false; mLength = 0; mCapacity = 0; return false; } void reset() { free(mData); mData = nullptr; mValid = true; mLength = 0; mCapacity = 0; } MemStream(const MemStream&) = delete; MemStream(MemStream&&) = delete; MemStream& operator=(const MemStream&) = delete; MemStream& operator=(MemStream&&) = delete; void write(const char* aData, size_t aSize) { if (Resize(mLength + aSize)) { memcpy(mData + mLength - aSize, aData, aSize); } } MemStream() : mData(nullptr), mLength(0), mCapacity(0) {} ~MemStream() { free(mData); } }; class EventStream { public: virtual void write(const char* aData, size_t aSize) = 0; virtual void read(char* aOut, size_t aSize) = 0; virtual bool good() = 0; virtual void SetIsBad() = 0; }; class RecordedEvent { public: enum EventType : uint8_t { INVALID = 0, DRAWTARGETCREATION, DRAWTARGETDESTRUCTION, SETCURRENTDRAWTARGET, FILLRECT, STROKERECT, STROKELINE, STROKECIRCLE, CLEARRECT, COPYSURFACE, SETPERMITSUBPIXELAA, SETTRANSFORM, PUSHCLIP, PUSHCLIPRECT, POPCLIP, FILL, FILLCIRCLE, FILLGLYPHS, STROKEGLYPHS, MASK, STROKE, DRAWSURFACE, DRAWSURFACEDESCRIPTOR, DRAWDEPENDENTSURFACE, DRAWSURFACEWITHSHADOW, DRAWSHADOW, PATHCREATION, PATHDESTRUCTION, SOURCESURFACECREATION, SOURCESURFACEDESTRUCTION, GRADIENTSTOPSCREATION, GRADIENTSTOPSDESTRUCTION, SNAPSHOT, SCALEDFONTCREATION, SCALEDFONTDESTRUCTION, MASKSURFACE, FILTERNODECREATION, FILTERNODEDESTRUCTION, DRAWFILTER, FILTERNODESETATTRIBUTE, FILTERNODESETINPUT, CREATESIMILARDRAWTARGET, CREATECLIPPEDDRAWTARGET, CREATEDRAWTARGETFORFILTER, FONTDATA, FONTDESC, PUSHLAYER, PUSHLAYERWITHBLEND, POPLAYER, UNSCALEDFONTCREATION, UNSCALEDFONTDESTRUCTION, INTOLUMINANCE, EXTRACTSUBRECT, EXTERNALSURFACECREATION, FLUSH, DETACHALLSNAPSHOTS, OPTIMIZESOURCESURFACE, LINK, DESTINATION, LAST, }; virtual ~RecordedEvent() = default; static std::string GetEventName(EventType aType); /** * Play back this event using the translator. Note that derived classes * should * only return false when there is a fatal error, as it will probably mean * the * translation will abort. * @param aTranslator Translator to be used for retrieving other referenced * objects and making playback decisions. * @return true unless a fatal problem has occurred and playback should * abort. */ virtual bool PlayEvent(Translator* aTranslator) const { return true; } virtual void RecordToStream(std::ostream& aStream) const = 0; virtual void RecordToStream(EventStream& aStream) const = 0; virtual void RecordToStream(ContiguousBufferStream& aStream) const = 0; virtual void RecordToStream(MemStream& aStream) const = 0; virtual void OutputSimpleEventInfo(std::stringstream& aStringStream) const {} template void RecordPatternData(S& aStream, const PatternStorage& aPatternStorage) const; template void ReadPatternData(S& aStream, PatternStorage& aPatternStorage) const; void StorePattern(PatternStorage& aDestination, const Pattern& aSource) const; template void RecordStrokeOptions(S& aStream, const StrokeOptions& aStrokeOptions) const; template void ReadStrokeOptions(S& aStream, StrokeOptions& aStrokeOptions); virtual std::string GetName() const = 0; virtual ReferencePtr GetDestinedDT() { return nullptr; } void OutputSimplePatternInfo(const PatternStorage& aStorage, std::stringstream& aOutput) const; template static bool DoWithEvent(S& aStream, EventType aType, const std::function& aAction); static bool DoWithEventFromStream( EventStream& aStream, EventType aType, const std::function& aAction); static bool DoWithEventFromReader( MemReader& aReader, EventType aType, const std::function& aAction); EventType GetType() const { return (EventType)mType; } protected: friend class DrawEventRecorderPrivate; friend class DrawEventRecorderMemory; static void RecordUnscaledFont(UnscaledFont* aUnscaledFont, std::ostream* aOutput); static void RecordUnscaledFont(UnscaledFont* aUnscaledFont, MemStream& aOutput); template static void RecordUnscaledFontImpl(UnscaledFont* aUnscaledFont, S& aOutput); MOZ_IMPLICIT RecordedEvent(EventType aType) : mType(aType) {} EventType mType; std::vector mDashPatternStorage; }; template class RecordedEventDerived : public RecordedEvent { using RecordedEvent::RecordedEvent; public: void RecordToStream(std::ostream& aStream) const override { WriteElement(aStream, this->mType); static_cast(this)->Record(aStream); } void RecordToStream(EventStream& aStream) const override { WriteElement(aStream, this->mType); static_cast(this)->Record(aStream); } void RecordToStream(ContiguousBufferStream& aStream) const final { aStream.RecordEvent(static_cast(this)); } void RecordToStream(MemStream& aStream) const override { SizeCollector size; WriteElement(size, this->mType); static_cast(this)->Record(size); if (!aStream.Resize(aStream.mLength + size.mTotalSize)) { return; } MemWriter writer(aStream.mData + aStream.mLength - size.mTotalSize); WriteElement(writer, this->mType); static_cast(this)->Record(writer); } }; } // namespace gfx } // namespace mozilla #endif