From f608e1f1e020a667804b7ee5e9530045f1cd72a2 Mon Sep 17 00:00:00 2001 From: Kelsey Gilbert Date: Tue, 9 Aug 2022 21:01:55 +0000 Subject: [PATCH] Bug 1778808 - Use stricter TiedFields instead of IsTriviallySerializable in WebGL's QueueParamTraits. r=lsalzman,nika ``` // We guarantee our robustness via these requirements: // * Object.MutTiedFields() gives us a tuple, // * where the combined sizeofs all field types sums to sizeof(Object), // * (thus we know we are exhaustively listing all fields) // * where feeding each field back into ParamTraits succeeds, // * and ParamTraits is only automated for BytesAlwaysValidT types. // (BytesAlwaysValidT rejects bool and enum types, and only accepts int/float // types, or array or std::arrays of such types) // (Yes, bit-field fields are rejected by MutTiedFields too) ``` BytesAlwaysValidT is the same as the IsTriviallySerializable that it replaces, however the emphasis is different, and should discourage tagging structs as IsTriviallySerializable, since they more clearly aren't BytesAlwaysValid. Differential Revision: https://phabricator.services.mozilla.com/D151676 --- dom/canvas/ClientWebGLContext.cpp | 16 +- dom/canvas/QueueParamTraits.h | 170 ++++++++++++++----- dom/canvas/TiedFields.h | 253 ++++++++++++++++++++++++++++ dom/canvas/TupleUtils.h | 61 +++++++ dom/canvas/WebGL2ContextMRTs.cpp | 6 +- dom/canvas/WebGLContext.cpp | 16 -- dom/canvas/WebGLContextVertices.cpp | 13 +- dom/canvas/WebGLQueueParamTraits.h | 136 +++++++++------ dom/canvas/WebGLTypes.h | 97 +++++++++-- dom/canvas/moz.build | 2 + gfx/layers/LayersTypes.h | 9 + 11 files changed, 639 insertions(+), 140 deletions(-) create mode 100644 dom/canvas/TiedFields.h create mode 100644 dom/canvas/TupleUtils.h diff --git a/dom/canvas/ClientWebGLContext.cpp b/dom/canvas/ClientWebGLContext.cpp index 922872287fff..676c008a8d57 100644 --- a/dom/canvas/ClientWebGLContext.cpp +++ b/dom/canvas/ClientWebGLContext.cpp @@ -800,7 +800,7 @@ bool ClientWebGLContext::CreateHostContext(const uvec2& requestedSize) { { webgl::TypedQuad initVal; const float fData[4] = {0, 0, 0, 1}; - memcpy(initVal.data, fData, sizeof(initVal.data)); + memcpy(initVal.data.data(), fData, initVal.data.size()); state.mGenericVertexAttribs.resize(limits.maxVertexAttribs, initVal); } @@ -2780,7 +2780,7 @@ void ClientWebGLContext::ClearBufferTv(const GLenum buffer, webgl::TypedQuad data; data.type = type; - auto dataSize = sizeof(data.data); + auto dataSize = data.data.size(); switch (buffer) { case LOCAL_GL_COLOR: break; @@ -2804,7 +2804,7 @@ void ClientWebGLContext::ClearBufferTv(const GLenum buffer, return; } - memcpy(data.data, view.begin().get() + byteOffset.value(), dataSize); + memcpy(data.data.data(), view.begin().get() + byteOffset.value(), dataSize); Run(buffer, drawBuffer, data); AfterDrawCall(); @@ -4550,15 +4550,17 @@ void ClientWebGLContext::GetVertexAttrib(JSContext* cx, GLuint index, switch (attrib.type) { case webgl::AttribBaseType::Float: obj = dom::Float32Array::Create( - cx, this, 4, reinterpret_cast(attrib.data)); + cx, this, 4, reinterpret_cast(attrib.data.data())); break; case webgl::AttribBaseType::Int: obj = dom::Int32Array::Create( - cx, this, 4, reinterpret_cast(attrib.data)); + cx, this, 4, + reinterpret_cast(attrib.data.data())); break; case webgl::AttribBaseType::Uint: obj = dom::Uint32Array::Create( - cx, this, 4, reinterpret_cast(attrib.data)); + cx, this, 4, + reinterpret_cast(attrib.data.data())); break; case webgl::AttribBaseType::Boolean: MOZ_CRASH("impossible"); @@ -4752,7 +4754,7 @@ void ClientWebGLContext::VertexAttrib4Tv(GLuint index, webgl::AttribBaseType t, auto& attrib = list[index]; attrib.type = t; - memcpy(attrib.data, src.begin().get(), sizeof(attrib.data)); + memcpy(attrib.data.data(), src.begin().get(), attrib.data.size()); Run(index, attrib); } diff --git a/dom/canvas/QueueParamTraits.h b/dom/canvas/QueueParamTraits.h index f627b4d32d8a..81b7bc592743 100644 --- a/dom/canvas/QueueParamTraits.h +++ b/dom/canvas/QueueParamTraits.h @@ -21,6 +21,8 @@ #include "nsString.h" #include "WebGLTypes.h" +#include + namespace mozilla::webgl { template @@ -29,11 +31,6 @@ struct RemoveCVR { typename std::remove_reference::type>::type; }; -template -struct IsTriviallySerializable - : public std::integral_constant::value && - !std::is_same::value> {}; - /** * QueueParamTraits provide the user with a way to implement PCQ argument * (de)serialization. It uses a PcqView, which permits the system to @@ -67,6 +64,37 @@ inline Range AsRange(T* const begin, T* const end) { return {begin, *size}; } +// - +// BytesAlwaysValidT + +template +struct BytesAlwaysValidT { + using non_cv = typename std::remove_cv::type; + static constexpr bool value = + std::is_arithmetic::value && !std::is_same::value; +}; +static_assert(BytesAlwaysValidT::value); +static_assert(!BytesAlwaysValidT::value); +static_assert(!BytesAlwaysValidT::value); +static_assert(!BytesAlwaysValidT::value); +static_assert(BytesAlwaysValidT::value); + +template +struct BytesAlwaysValidT> { + static constexpr bool value = BytesAlwaysValidT::value; +}; +static_assert(BytesAlwaysValidT>::value); +static_assert(!BytesAlwaysValidT>::value); + +template +struct BytesAlwaysValidT { + static constexpr bool value = BytesAlwaysValidT::value; +}; +static_assert(BytesAlwaysValidT::value); +static_assert(!BytesAlwaysValidT::value); + +// - + /** * Used to give QueueParamTraits a way to write to the Producer without * actually altering it, in case the transaction fails. @@ -82,6 +110,7 @@ class ProducerView { template bool WriteFromRange(const Range& src) { + static_assert(BytesAlwaysValidT::value); if (MOZ_LIKELY(mOk)) { mOk &= mProducer->WriteFromRange(src); } @@ -98,13 +127,6 @@ class ProducerView { return WriteFromRange(AsRange(begin, end)); } - template - inline bool WritePod(const T& in) { - static_assert(std::is_trivially_copyable_v); - const auto begin = ∈ - return Write(begin, begin + 1); - } - /** * Serialize aArg using Arg's QueueParamTraits. */ @@ -155,18 +177,13 @@ class ConsumerView { /// Return a view wrapping the shmem. template inline Maybe> ReadRange(const size_t elemCount) { + static_assert(BytesAlwaysValidT::value); if (MOZ_UNLIKELY(!mOk)) return {}; const auto view = mConsumer->template ReadRange(elemCount); mOk &= bool(view); return view; } - template - inline bool ReadPod(T* out) { - static_assert(std::is_trivially_copyable_v); - return Read(out, out + 1); - } - /** * Deserialize aArg using Arg's QueueParamTraits. * If the return value is not Success then aArg is not changed. @@ -185,28 +202,25 @@ class ConsumerView { bool mOk = true; }; -// --------------------------------------------------------------- +// - -/** - * True for types that can be (de)serialized by memcpy. - */ template struct QueueParamTraits { - template - static bool Write(ProducerView& aProducerView, const Arg& aArg) { - static_assert(std::is_trivially_copyable::value, + template + static bool Write(ProducerView& aProducerView, const Arg& aArg) { + static_assert(BytesAlwaysValidT::value, "No QueueParamTraits specialization was found for this type " - "and it does not satisfy is_trivially_copyable."); + "and it does not satisfy BytesAlwaysValid."); // Write self as binary - const auto begin = &aArg; - return aProducerView.Write(begin, begin + 1); + const auto pArg = &aArg; + return aProducerView.Write(pArg, pArg + 1); } - template - static bool Read(ConsumerView& aConsumerView, Arg* aArg) { - static_assert(std::is_trivially_copyable::value, + template + static bool Read(ConsumerView& aConsumerView, Arg* aArg) { + static_assert(BytesAlwaysValidT::value, "No QueueParamTraits specialization was found for this type " - "and it does not satisfy is_trivially_copyable."); + "and it does not satisfy BytesAlwaysValid."); // Read self as binary return aConsumerView.Read(aArg, aArg + 1); } @@ -237,6 +251,84 @@ struct QueueParamTraits { // --------------------------------------------------------------- +template > +Maybe AsValidEnum(const UT raw_val) { + const auto raw_enum = T{raw_val}; // This is the risk we prevent! + if (!IsEnumCase(raw_enum)) return {}; + return Some(raw_enum); +} + +// - + +template +struct QueueParamTraits_IsEnumCase { + using T = TT; + using UT = std::underlying_type_t; + + template + static bool Write(ProducerView& aProducerView, const T& aArg) { + MOZ_ASSERT(IsEnumCase(aArg)); + const auto shadow = static_cast(aArg); + aProducerView.WriteParam(shadow); + return true; + } + + template + static bool Read(ConsumerView& aConsumerView, T* aArg) { + auto shadow = UT{}; + aConsumerView.ReadParam(&shadow); + const auto e = AsValidEnum(shadow); + if (!e) return false; + *aArg = *e; + return true; + } +}; + +// --------------------------------------------------------------- + +// We guarantee our robustness via these requirements: +// * Object.MutTiedFields() gives us a tuple, +// * where the combined sizeofs all field types sums to sizeof(Object), +// * (thus we know we are exhaustively listing all fields) +// * where feeding each field back into ParamTraits succeeds, +// * and ParamTraits is only automated for BytesAlwaysValidT types. +// (BytesAlwaysValidT rejects bool and enum types, and only accepts int/float +// types, or array or std::arrays of such types) +// (Yes, bit-field fields are rejected by MutTiedFields too) + +template +struct QueueParamTraits_TiedFields { + using T = TT; + + template + static bool Write(ProducerView& aProducerView, const T& aArg) { + const auto fields = TiedFields(aArg); + static_assert(SizeofTupleArgs(fields) == sizeof(T), "Are there missing fields or padding between fields?"); + + bool ok = true; + MapTuple(fields, [&](const auto& field) { + ok &= aProducerView.WriteParam(field); + return true; + }); + return ok; + } + + template + static bool Read(ConsumerView& aConsumerView, T* aArg) { + const auto fields = TiedFields(*aArg); + static_assert(SizeofTupleArgs(fields) == sizeof(T)); + + bool ok = true; + MapTuple(fields, [&](auto& field) { + ok &= aConsumerView.ReadParam(&field); + return true; + }); + return ok; + } +}; + +// --------------------------------------------------------------- + // Adapted from IPC::EnumSerializer, this class safely handles enum values, // validating that they are in range using the same EnumValidators as IPDL // (namely ContiguousEnumValidator and ContiguousEnumValidatorInclusive). @@ -485,11 +577,10 @@ struct QueueParamTraits : public QueueParamTraits { // --------------------------------------------------------------- template ::value> + bool = BytesAlwaysValidT::value> struct NSArrayQueueParamTraits; -// For ElementTypes that are !IsTriviallySerializable +// For ElementTypes that are !BytesAlwaysValidT template struct NSArrayQueueParamTraits, false> { using ElementType = _ElementType; @@ -523,7 +614,7 @@ struct NSArrayQueueParamTraits, false> { } }; -// For ElementTypes that are IsTriviallySerializable +// For ElementTypes that are BytesAlwaysValidT template struct NSArrayQueueParamTraits, true> { using ElementType = _ElementType; @@ -561,11 +652,10 @@ struct QueueParamTraits> // --------------------------------------------------------------- template ::value> + bool = BytesAlwaysValidT::value> struct ArrayQueueParamTraits; -// For ElementTypes that are !IsTriviallySerializable +// For ElementTypes that are !BytesAlwaysValidT template struct ArrayQueueParamTraits, false> { using ElementType = _ElementType; @@ -588,7 +678,7 @@ struct ArrayQueueParamTraits, false> { } }; -// For ElementTypes that are IsTriviallySerializable +// For ElementTypes that are BytesAlwaysValidT template struct ArrayQueueParamTraits, true> { using ElementType = _ElementType; diff --git a/dom/canvas/TiedFields.h b/dom/canvas/TiedFields.h new file mode 100644 index 000000000000..da46dda6b6e9 --- /dev/null +++ b/dom/canvas/TiedFields.h @@ -0,0 +1,253 @@ +/* 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 DOM_CANVAS_TIED_FIELDS_H +#define DOM_CANVAS_TIED_FIELDS_H + +#include "TupleUtils.h" + +namespace mozilla { + +// - + +/** + * TiedFields(T&) -> std::tuple + * TiedFields(const T&) -> std::tuple + * + * You can also overload TiedFields without adding T::MutTiedFields: + * template<> + * inline auto TiedFields(gfx::IntSize& a) { + * return std::tie(a.width, a.height); + * } + */ +template +constexpr auto TiedFields(T& t) { + const auto fields = t.MutTiedFields(); + return fields; +} +template > +constexpr auto TiedFields(const T& t) { + // Uncast const to get mutable-fields tuple, but reapply const to tuple args. + const auto mutFields = TiedFields(const_cast(t)); + return ToTupleOfConstRefs(mutFields); +} + +/** + * Returns true if all bytes in T are accounted for via size of all tied fields. + * Returns false if there's bytes unaccounted for, which might indicate either + * unaccounted-for padding or missing fields. + * The goal is to check that TiedFields returns every field in T, and this + * returns false if it suspects there are bytes that are not accounted for by + * TiedFields. + * + * `constexpr` effectively cannot do math on pointers, so it's not possible to + * figure out via `constexpr` whether fields are consecutive or dense. + * However, we can at least compare `sizeof(T)` to the sum of `sizeof(Args...)` + * for `TiedFields(T) -> std::tuple`. + * + * See TiedFieldsExamples. + */ +template +constexpr bool AreAllBytesTiedFields(const T& t) { + const auto fields = TiedFields(t); + const auto fields_size_sum = SizeofTupleArgs(fields); + const auto t_size = sizeof(T); + return fields_size_sum == t_size; +} + +// - + +/** + * Padding can be used to pad out a struct so that it's not implicitly + * padded by struct rules. + * You can also just add your padding to TiedFields, but by explicitly typing + * padding like this, serialization can make a choice whether to copy Padding, + * or instead to omit the copy. + * + * Omitting the copy isn't always faster. + * struct Entry { + * uint16_t key; + * Padding padding; + * uint32_t val; + * auto MutTiedFields() { return std::tie(key, padding, val); } + * }; + * If you serialize Padding, the serialized size is 8, and the compiler will + * optimize serialization to a single 8-byte memcpy. + * If your serialization omits Padding, the serialized size of Entry shrinks + * by 25%. If you have a big list of Entrys, maybe this is a big savings! + * However, by optimizing for size here you sacrifice speed, because this splits + * the single memcpy into two: a 2-byte memcpy and a 4-byte memcpy. + * + * Explicitly marking padding gives callers the option of choosing. + */ +template +struct Padding { + T ignored; + + friend constexpr bool operator==(const Padding&, const Padding&) { + return true; + } + friend constexpr bool operator<(const Padding&, const Padding&) { + return false; + } +}; +static_assert(sizeof(Padding) == 1); +static_assert(sizeof(Padding) == 2); +static_assert(sizeof(Padding) == 4); + +// - + +template +struct DecayDownT : public DecayDownT {}; + +template <> +struct DecayDownT<0> {}; + +// - + +/// Warning, IsDenseNestedScalars should not be used to indicate the safety of +/// a memcpy. +/// If you trust both the source and destination, you should use +/// std::is_trivially_copyable. +template +constexpr bool IsDenseNestedScalars(const T& t); + +// - + +namespace IsDenseNestedScalarsDetails { + +template ::value, bool> = true> +constexpr bool Impl(const T&, DecayDownT<3>) { + return true; +} + +template +constexpr bool Impl(const T (&t)[N], DecayDownT<3>) { + return IsDenseNestedScalars(t[0]); +} + +template +constexpr bool Impl(const std::array& t, DecayDownT<3>) { + return IsDenseNestedScalars(t[0]); +} + +template +constexpr bool Impl(const Padding& t, DecayDownT<3>) { + return IsDenseNestedScalars(t.ignored); +} + +// - + +template +constexpr bool Impl(const T& t, DecayDownT<1>) { + const auto tup = TiedFields(t); + bool ok = AreAllBytesTiedFields(t); + MapTuple(tup, [&](const auto& field) { + ok &= IsDenseNestedScalars(field); + return true; + }); + return ok; +} + +} // namespace IsDenseNestedScalarsDetails + +// - + +template +constexpr bool IsDenseNestedScalars(const T& t) { + return IsDenseNestedScalarsDetails::Impl(t, DecayDownT<5>{}); +} +static_assert(IsDenseNestedScalars(1)); +// Compile error: Missing TiedFields: +// static_assert(IsDenseNestedScalars(std::pair{})); + +namespace TiedFieldsExamples { + +struct Cat { + int i; + bool b; + + constexpr auto MutTiedFields() { return std::tie(i, b); } +}; +static_assert(sizeof(Cat) == 8); +static_assert(!AreAllBytesTiedFields(Cat{})); +static_assert(!IsDenseNestedScalars(Cat{})); + +struct Dog { + bool b; + int i; + + constexpr auto MutTiedFields() { return std::tie(i, b); } +}; +static_assert(sizeof(Dog) == 8); +static_assert(!AreAllBytesTiedFields(Dog{})); +static_assert(!IsDenseNestedScalars(Dog{})); + +struct Fish { + bool b; + bool padding[3]; + int i; + + constexpr auto MutTiedFields() { return std::tie(i, b, padding); } +}; +static_assert(sizeof(Fish) == 8); +static_assert(AreAllBytesTiedFields(Fish{})); +static_assert(IsDenseNestedScalars(Fish{})); + +struct Eel { // Like a Fish, but you can skip serializing the padding. + bool b; + Padding padding[3]; + int i; + + constexpr auto MutTiedFields() { return std::tie(i, b, padding); } +}; +static_assert(sizeof(Eel) == 8); +static_assert(AreAllBytesTiedFields(Eel{})); +static_assert(IsDenseNestedScalars(Eel{})); + +// - + +//#define LETS_USE_BIT_FIELDS +#ifdef LETS_USE_BIT_FIELDS +# undef LETS_USE_BIT_FIELDS + +struct Platypus { + short s : 1; + short s2 : 1; + int i; + + constexpr auto MutTiedFields() { + return std::tie(s, s2, i); // Error: Can't take reference to bit-field. + } +}; + +#endif + +// - + +struct FishTank { + Fish f; + int i2; + + constexpr auto MutTiedFields() { return std::tie(f, i2); } +}; +static_assert(sizeof(FishTank) == 12); +static_assert(AreAllBytesTiedFields(FishTank{})); +static_assert(IsDenseNestedScalars(FishTank{})); + +struct CatCarrier { + Cat c; + int i2; + + constexpr auto MutTiedFields() { return std::tie(c, i2); } +}; +static_assert(sizeof(CatCarrier) == 12); +static_assert(AreAllBytesTiedFields(CatCarrier{})); +static_assert(!IsDenseNestedScalars(CatCarrier{})); // Because: +static_assert(!AreAllBytesTiedFields(CatCarrier{}.c)); + +} // namespace TiedFieldsExamples +} // namespace mozilla + +#endif // DOM_CANVAS_TIED_FIELDS_H diff --git a/dom/canvas/TupleUtils.h b/dom/canvas/TupleUtils.h new file mode 100644 index 000000000000..6de71b007e06 --- /dev/null +++ b/dom/canvas/TupleUtils.h @@ -0,0 +1,61 @@ +/* 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 DOM_CANVAS_TUPLE_UTILS_H +#define DOM_CANVAS_TUPLE_UTILS_H + +#include + +namespace mozilla { + +// - +// ToTupleOfConstRefs + +template +constexpr const auto& ToConstRef1(T& ret) { + return ret; +} + +template +constexpr auto ToTupleOfConstRefsN(const Tup& tup, + const std::index_sequence&) { + return std::tie(ToConstRef1(std::get(tup))...); +} + +template > +constexpr auto ToTupleOfConstRefs(const Tup& tup) { + return ToTupleOfConstRefsN( + tup, std::make_index_sequence>()); +} + +// - +// MapTuple + +template +constexpr auto MapTupleN(Tup&& tup, Callable&& fn, + const std::index_sequence&) { + return std::make_tuple(fn(std::get(tup))...); +} + +/// Callable::operator()(T) cannot return void or you will get weird errors. +template > +constexpr auto MapTuple(Tup&& t, Callable&& fn) { + using Tup2 = typename std::remove_reference::type; + return MapTupleN(t, fn, std::make_index_sequence>()); +} + +// - + +template +constexpr auto SizeofTupleArgs(const std::tuple&) { + size_t total = 0; + for (const auto s : {sizeof(Args)...}) { + total += s; + } + return total; +} + +} // namespace mozilla + +#endif // DOM_CANVAS_TUPLE_UTILS_H diff --git a/dom/canvas/WebGL2ContextMRTs.cpp b/dom/canvas/WebGL2ContextMRTs.cpp index 3820ee40d7ab..544f8e024a2d 100644 --- a/dom/canvas/WebGL2ContextMRTs.cpp +++ b/dom/canvas/WebGL2ContextMRTs.cpp @@ -119,17 +119,17 @@ void WebGL2Context::ClearBufferTv(GLenum buffer, GLint drawBuffer, case webgl::AttribBaseType::Float: gl->fClearBufferfv(buffer, drawBuffer, - reinterpret_cast(data.data)); + reinterpret_cast(data.data.data())); break; case webgl::AttribBaseType::Int: gl->fClearBufferiv(buffer, drawBuffer, - reinterpret_cast(data.data)); + reinterpret_cast(data.data.data())); break; case webgl::AttribBaseType::Uint: gl->fClearBufferuiv(buffer, drawBuffer, - reinterpret_cast(data.data)); + reinterpret_cast(data.data.data())); break; } } diff --git a/dom/canvas/WebGLContext.cpp b/dom/canvas/WebGLContext.cpp index bfb65c8a3b48..5a7ba1bf36f9 100644 --- a/dom/canvas/WebGLContext.cpp +++ b/dom/canvas/WebGLContext.cpp @@ -106,22 +106,6 @@ WebGLContextOptions::WebGLContextOptions() { antialias = StaticPrefs::webgl_default_antialias(); } -bool WebGLContextOptions::operator==(const WebGLContextOptions& r) const { - bool eq = true; - eq &= (alpha == r.alpha); - eq &= (depth == r.depth); - eq &= (stencil == r.stencil); - eq &= (premultipliedAlpha == r.premultipliedAlpha); - eq &= (antialias == r.antialias); - eq &= (preserveDrawingBuffer == r.preserveDrawingBuffer); - eq &= (failIfMajorPerformanceCaveat == r.failIfMajorPerformanceCaveat); - eq &= (xrCompatible == r.xrCompatible); - eq &= (powerPreference == r.powerPreference); - eq &= (colorSpace == r.colorSpace); - eq &= (ignoreColorSpace == r.ignoreColorSpace); - return eq; -} - StaticMutex WebGLContext::sLruMutex; std::list WebGLContext::sLru; diff --git a/dom/canvas/WebGLContextVertices.cpp b/dom/canvas/WebGLContextVertices.cpp index 7f59514ea4b2..e50f5f1c3d33 100644 --- a/dom/canvas/WebGLContextVertices.cpp +++ b/dom/canvas/WebGLContextVertices.cpp @@ -57,15 +57,16 @@ void WebGLContext::VertexAttrib4T(GLuint index, const webgl::TypedQuad& src) { switch (src.type) { case webgl::AttribBaseType::Boolean: case webgl::AttribBaseType::Float: - gl->fVertexAttrib4fv(index, reinterpret_cast(src.data)); + gl->fVertexAttrib4fv(index, + reinterpret_cast(src.data.data())); break; case webgl::AttribBaseType::Int: - gl->fVertexAttribI4iv(index, - reinterpret_cast(src.data)); + gl->fVertexAttribI4iv( + index, reinterpret_cast(src.data.data())); break; case webgl::AttribBaseType::Uint: - gl->fVertexAttribI4uiv(index, - reinterpret_cast(src.data)); + gl->fVertexAttribI4uiv( + index, reinterpret_cast(src.data.data())); break; } } @@ -76,7 +77,7 @@ void WebGLContext::VertexAttrib4T(GLuint index, const webgl::TypedQuad& src) { mGenericVertexAttribTypeInvalidator.InvalidateCaches(); if (!index) { - memcpy(mGenericVertexAttrib0Data, src.data, + memcpy(mGenericVertexAttrib0Data, src.data.data(), sizeof(mGenericVertexAttrib0Data)); } } diff --git a/dom/canvas/WebGLQueueParamTraits.h b/dom/canvas/WebGLQueueParamTraits.h index f30faaee9a44..033fddf1e056 100644 --- a/dom/canvas/WebGLQueueParamTraits.h +++ b/dom/canvas/WebGLQueueParamTraits.h @@ -14,69 +14,104 @@ #include "WebGLContext.h" #include "WebGLTypes.h" -namespace mozilla::webgl { +namespace mozilla { +namespace webgl { template struct QueueParamTraits; -template <> -struct IsTriviallySerializable : std::true_type {}; +// - -template <> -struct IsTriviallySerializable : std::true_type { -}; +#define USE_TIED_FIELDS(T) \ + template <> \ + struct QueueParamTraits : QueueParamTraits_TiedFields {}; -template <> -struct IsTriviallySerializable : std::true_type {}; +// - -template <> -struct IsTriviallySerializable : std::true_type {}; +USE_TIED_FIELDS(layers::RemoteTextureId) +USE_TIED_FIELDS(layers::RemoteTextureOwnerId) +USE_TIED_FIELDS(WebGLContextOptions) +USE_TIED_FIELDS(webgl::PixelUnpackStateWebgl) +USE_TIED_FIELDS(webgl::SwapChainOptions) +USE_TIED_FIELDS(webgl::ReadPixelsDesc) +USE_TIED_FIELDS(webgl::VertAttribPointerDesc) +USE_TIED_FIELDS(webgl::PackingInfo) +USE_TIED_FIELDS(webgl::TypedQuad) +USE_TIED_FIELDS(webgl::PixelPackingState) +USE_TIED_FIELDS(FloatOrInt) +} // namespace webgl template <> -struct IsTriviallySerializable : std::true_type {}; +inline auto TiedFields(gfx::IntSize& a) { + return std::tie(a.width, a.height); +} +namespace webgl { +USE_TIED_FIELDS(gfx::IntSize) -template <> -struct IsTriviallySerializable : std::true_type {}; -template <> -struct IsTriviallySerializable : std::true_type {}; +// - -template <> -struct IsTriviallySerializable : std::true_type {}; -template <> -struct IsTriviallySerializable - : std::true_type {}; -template <> -struct IsTriviallySerializable - : std::true_type {}; +#undef USE_TIED_FIELDS -template <> -struct IsTriviallySerializable : std::true_type {}; +// - -template -struct IsTriviallySerializable> : std::true_type {}; -template -struct IsTriviallySerializable> : std::true_type {}; +template +struct QueueParamTraits> : QueueParamTraits_TiedFields> {}; -template <> -struct IsTriviallySerializable : std::true_type {}; +template +struct QueueParamTraits> : QueueParamTraits_TiedFields> {}; -template <> -struct IsTriviallySerializable : std::true_type {}; -template <> -struct IsTriviallySerializable : std::true_type { -}; -template <> -struct IsTriviallySerializable : std::true_type {}; -// template <> -// struct IsTriviallySerializable : std::true_type -// {}; -// SurfaceDescriptorBuffer is *not* trivial. -template <> -struct IsTriviallySerializable : std::true_type {}; -template <> -struct IsTriviallySerializable : std::true_type { -}; -template <> -struct IsTriviallySerializable : std::true_type {}; +// --------------------------------------------------------------------- +// Enums! + +inline constexpr bool IsEnumCase(const dom::WebGLPowerPreference raw) { + switch (raw) { + case dom::WebGLPowerPreference::Default: + case dom::WebGLPowerPreference::Low_power: + case dom::WebGLPowerPreference::High_performance: + return true; + case dom::WebGLPowerPreference::EndGuard_: + break; + } + return false; +} + +inline constexpr bool IsEnumCase(const dom::PredefinedColorSpace raw) { + switch (raw) { + case dom::PredefinedColorSpace::Srgb: + case dom::PredefinedColorSpace::Display_p3: + return true; + case dom::PredefinedColorSpace::EndGuard_: + break; + } + return false; +} + +inline constexpr bool IsEnumCase(const webgl::AttribBaseType raw) { + switch (raw) { + case webgl::AttribBaseType::Boolean: + case webgl::AttribBaseType::Float: + case webgl::AttribBaseType::Int: + case webgl::AttribBaseType::Uint: + return true; + } + return false; +} + +static_assert(IsEnumCase(dom::WebGLPowerPreference(2))); +static_assert(!IsEnumCase(dom::WebGLPowerPreference(3))); +static_assert(!IsEnumCase(dom::WebGLPowerPreference(5))); + +#define USE_IS_ENUM_CASE(T) \ + template <> \ + struct QueueParamTraits : QueueParamTraits_IsEnumCase {}; + +USE_IS_ENUM_CASE(dom::WebGLPowerPreference) +USE_IS_ENUM_CASE(dom::PredefinedColorSpace) +USE_IS_ENUM_CASE(webgl::AttribBaseType) + +#undef USE_IS_ENUM_CASE + +// --------------------------------------------------------------------- +// Custom QueueParamTraits template struct QueueParamTraits> { @@ -267,6 +302,7 @@ struct QueueParamTraits : public ContiguousEnumSerializerInclusive< gfxAlphaType, gfxAlphaType::Opaque, gfxAlphaType::NonPremult> {}; -} // namespace mozilla::webgl +} // namespace webgl +} // namespace mozilla #endif // WEBGLQUEUEPARAMTRAITS_H_ diff --git a/dom/canvas/WebGLTypes.h b/dom/canvas/WebGLTypes.h index a70db31e18ce..2a302d2f6291 100644 --- a/dom/canvas/WebGLTypes.h +++ b/dom/canvas/WebGLTypes.h @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -34,6 +35,7 @@ #include "nsString.h" #include "mozilla/dom/WebGLRenderingContextBinding.h" #include "mozilla/ipc/SharedMemoryBasic.h" +#include "TiedFields.h" // Manual reflection of WebIDL typedefs that are different from their // OpenGL counterparts. @@ -344,37 +346,68 @@ enum class BufferKind : uint8_t { struct FloatOrInt final // For TexParameter[fi] and friends. { bool isFloat = false; + uint8_t padding[3] = {}; GLfloat f = 0; GLint i = 0; explicit FloatOrInt(GLint x = 0) : isFloat(false), f(x), i(x) {} explicit FloatOrInt(GLfloat x) : isFloat(true), f(x), i(roundf(x)) {} + + auto MutTiedFields() { return std::tie(isFloat, padding, f, i); } }; +// - + struct WebGLContextOptions { bool alpha = true; bool depth = true; bool stencil = false; bool premultipliedAlpha = true; + bool antialias = true; bool preserveDrawingBuffer = false; bool failIfMajorPerformanceCaveat = false; bool xrCompatible = false; + dom::WebGLPowerPreference powerPreference = dom::WebGLPowerPreference::Default; dom::PredefinedColorSpace colorSpace = dom::PredefinedColorSpace::Srgb; bool ignoreColorSpace = true; // Our legacy behavior. bool shouldResistFingerprinting = true; + bool enableDebugRendererInfo = false; + auto MutTiedFields() { + // clang-format off + return std::tie( + alpha, + depth, + stencil, + premultipliedAlpha, + + antialias, + preserveDrawingBuffer, + failIfMajorPerformanceCaveat, + xrCompatible, + + powerPreference, + colorSpace, + ignoreColorSpace, + shouldResistFingerprinting, + + enableDebugRendererInfo); + // clang-format on + } + WebGLContextOptions(); WebGLContextOptions(const WebGLContextOptions&) = default; - bool operator==(const WebGLContextOptions&) const; - bool operator!=(const WebGLContextOptions& rhs) const { - return !(*this == rhs); + using Self = WebGLContextOptions; + friend bool operator==(const Self& a, const Self& b) { + return TiedFields(a) == TiedFields(b); } + friend bool operator!=(const Self& a, const Self& b) { return !(a == b); } }; namespace gfx { @@ -402,6 +435,8 @@ struct avec2 { T x = T(); T y = T(); + auto MutTiedFields() { return std::tie(x, y); } + template static Maybe From(const U _x, const V _y) { const auto x = CheckedInt(_x); @@ -477,6 +512,8 @@ struct avec3 { T y = T(); T z = T(); + auto MutTiedFields() { return std::tie(x, y, z); } + template static Maybe From(const U _x, const V _y, const V _z) { const auto x = CheckedInt(_x); @@ -515,14 +552,14 @@ struct PackingInfo final { GLenum format = 0; GLenum type = 0; - bool operator<(const PackingInfo& x) const { - if (format != x.format) return format < x.format; + auto MutTiedFields() { return std::tie(format, type); } - return type < x.type; + using Self = PackingInfo; + friend bool operator<(const Self& a, const Self& b) { + return TiedFields(a) < TiedFields(b); } - - bool operator==(const PackingInfo& x) const { - return (format == x.format && type == x.type); + friend bool operator==(const Self& a, const Self& b) { + return TiedFields(a) == TiedFields(b); } template @@ -682,10 +719,15 @@ struct OpaqueFramebufferOptions final { // - struct SwapChainOptions final { - bool bgra = false; - bool forceAsyncPresent = false; layers::RemoteTextureId remoteTextureId; layers::RemoteTextureOwnerId remoteTextureOwnerId; + bool bgra = false; + bool forceAsyncPresent = false; + uint16_t padding1; + uint32_t padding2; // Pad to sizeof(u64) + + auto MutTiedFields() { return std::tie( + remoteTextureId, remoteTextureOwnerId, bgra, forceAsyncPresent, padding1, padding2); } }; // - @@ -739,8 +781,11 @@ struct LinkResult final { /// 4x32-bit primitives, with a type tag. struct TypedQuad final { - alignas(alignof(float)) uint8_t data[4 * sizeof(float)] = {}; + alignas(alignof(float)) std::array data = {}; webgl::AttribBaseType type = webgl::AttribBaseType::Float; + uint8_t padding[3] = {}; + + constexpr auto MutTiedFields() { return std::tie(data, type, padding); } }; /// [1-16]x32-bit primitives, with a type tag. @@ -770,6 +815,11 @@ struct VertAttribPointerDesc final { uint8_t byteStrideOrZero = 0; GLenum type = LOCAL_GL_FLOAT; uint64_t byteOffset = 0; + + auto MutTiedFields() { + return std::tie(intFunc, channels, normalized, byteStrideOrZero, type, + byteOffset); + } }; struct VertAttribPointerCalculated final { @@ -985,12 +1035,14 @@ struct PixelPackingState : public DeriveNotEq { uint32_t skipRows = 0; uint32_t skipImages = 0; - // C++20's default comparison operators can't come soon enough! - bool operator==(const PixelPackingState& rhs) const { - return alignmentInTypeElems == rhs.alignmentInTypeElems && - rowLength == rhs.rowLength && imageHeight == rhs.imageHeight && - skipPixels == rhs.skipPixels && skipRows == rhs.skipRows && - skipImages == rhs.skipImages; + auto MutTiedFields() { + return std::tie(alignmentInTypeElems, rowLength, imageHeight, skipPixels, + skipRows, skipImages); + } + + using Self = PixelPackingState; + friend bool operator==(const Self& a, const Self& b) { + return TiedFields(a) == TiedFields(b); } static void AssertDefaultUnpack(gl::GLContext& gl, const bool isWebgl2) { @@ -1008,6 +1060,13 @@ struct PixelUnpackStateWebgl final : public PixelPackingState { bool flipY = false; bool premultiplyAlpha = false; bool requireFastPath = false; + uint8_t padding = {}; + + auto MutTiedFields() { + return std::tuple_cat(PixelPackingState::MutTiedFields(), + std::tie(colorspaceConversion, flipY, + premultiplyAlpha, requireFastPath, padding)); + } }; struct ExplicitPixelPackingState final { @@ -1041,6 +1100,8 @@ struct ReadPixelsDesc final { uvec2 size; PackingInfo pi = {LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE}; PixelPackingState packState; + + auto MutTiedFields() { return std::tie(srcOffset, size, pi, packState); } }; } // namespace webgl diff --git a/dom/canvas/moz.build b/dom/canvas/moz.build index f72646388966..d9d5268d186f 100644 --- a/dom/canvas/moz.build +++ b/dom/canvas/moz.build @@ -64,6 +64,8 @@ EXPORTS.mozilla.dom += [ "OffscreenCanvasRenderingContext2D.h", "QueueParamTraits.h", "TextMetrics.h", + "TiedFields.h", + "TupleUtils.h", "WebGLChild.h", "WebGLCommandQueue.h", "WebGLIpdl.h", diff --git a/gfx/layers/LayersTypes.h b/gfx/layers/LayersTypes.h index 8ff40104e1c4..70497a302017 100644 --- a/gfx/layers/LayersTypes.h +++ b/gfx/layers/LayersTypes.h @@ -10,6 +10,7 @@ #include // for ostream #include // for uint32_t #include // FILE +#include #include "Units.h" #include "mozilla/DefineEnum.h" // for MOZ_DEFINE_ENUM_CLASS_WITH_BASE @@ -331,6 +332,10 @@ enum class CompositableHandleOwner : uint8_t { struct RemoteTextureId { uint64_t mId = 0; + auto MutTiedFields() { + return std::tie(mId); + } + static RemoteTextureId GetNext(); bool IsValid() const { return mId != 0; } @@ -370,6 +375,10 @@ struct RemoteTextureId { struct RemoteTextureOwnerId { uint64_t mId = 0; + auto MutTiedFields() { + return std::tie(mId); + } + static RemoteTextureOwnerId GetNext(); bool IsValid() const { return mId != 0; }