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<T> 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<T> is the same as the IsTriviallySerializable<T> 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
This commit is contained in:
Kelsey Gilbert 2022-08-09 21:01:55 +00:00
Родитель 5f9d36e992
Коммит f608e1f1e0
11 изменённых файлов: 639 добавлений и 140 удалений

Просмотреть файл

@ -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<RPROC(ClearBufferTv)>(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<const float*>(attrib.data));
cx, this, 4, reinterpret_cast<const float*>(attrib.data.data()));
break;
case webgl::AttribBaseType::Int:
obj = dom::Int32Array::Create(
cx, this, 4, reinterpret_cast<const int32_t*>(attrib.data));
cx, this, 4,
reinterpret_cast<const int32_t*>(attrib.data.data()));
break;
case webgl::AttribBaseType::Uint:
obj = dom::Uint32Array::Create(
cx, this, 4, reinterpret_cast<const uint32_t*>(attrib.data));
cx, this, 4,
reinterpret_cast<const uint32_t*>(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<RPROC(VertexAttrib4T)>(index, attrib);
}

Просмотреть файл

@ -21,6 +21,8 @@
#include "nsString.h"
#include "WebGLTypes.h"
#include <optional>
namespace mozilla::webgl {
template <typename T>
@ -29,11 +31,6 @@ struct RemoveCVR {
typename std::remove_reference<typename std::remove_cv<T>::type>::type;
};
template <typename T>
struct IsTriviallySerializable
: public std::integral_constant<bool, std::is_arithmetic<T>::value &&
!std::is_same<T, bool>::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<T> AsRange(T* const begin, T* const end) {
return {begin, *size};
}
// -
// BytesAlwaysValidT
template <class T>
struct BytesAlwaysValidT {
using non_cv = typename std::remove_cv<T>::type;
static constexpr bool value =
std::is_arithmetic<T>::value && !std::is_same<non_cv, bool>::value;
};
static_assert(BytesAlwaysValidT<float>::value);
static_assert(!BytesAlwaysValidT<bool>::value);
static_assert(!BytesAlwaysValidT<const bool>::value);
static_assert(!BytesAlwaysValidT<int*>::value);
static_assert(BytesAlwaysValidT<intptr_t>::value);
template <class T, size_t N>
struct BytesAlwaysValidT<std::array<T, N>> {
static constexpr bool value = BytesAlwaysValidT<T>::value;
};
static_assert(BytesAlwaysValidT<std::array<int, 4>>::value);
static_assert(!BytesAlwaysValidT<std::array<bool, 4>>::value);
template <class T, size_t N>
struct BytesAlwaysValidT<T[N]> {
static constexpr bool value = BytesAlwaysValidT<T>::value;
};
static_assert(BytesAlwaysValidT<int[4]>::value);
static_assert(!BytesAlwaysValidT<bool[4]>::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 <typename T>
bool WriteFromRange(const Range<const T>& src) {
static_assert(BytesAlwaysValidT<T>::value);
if (MOZ_LIKELY(mOk)) {
mOk &= mProducer->WriteFromRange(src);
}
@ -98,13 +127,6 @@ class ProducerView {
return WriteFromRange(AsRange(begin, end));
}
template <typename T>
inline bool WritePod(const T& in) {
static_assert(std::is_trivially_copyable_v<T>);
const auto begin = &in;
return Write(begin, begin + 1);
}
/**
* Serialize aArg using Arg's QueueParamTraits.
*/
@ -155,18 +177,13 @@ class ConsumerView {
/// Return a view wrapping the shmem.
template <typename T>
inline Maybe<Range<const T>> ReadRange(const size_t elemCount) {
static_assert(BytesAlwaysValidT<T>::value);
if (MOZ_UNLIKELY(!mOk)) return {};
const auto view = mConsumer->template ReadRange<T>(elemCount);
mOk &= bool(view);
return view;
}
template <typename T>
inline bool ReadPod(T* out) {
static_assert(std::is_trivially_copyable_v<T>);
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 <typename Arg>
struct QueueParamTraits {
template <typename U>
static bool Write(ProducerView<U>& aProducerView, const Arg& aArg) {
static_assert(std::is_trivially_copyable<Arg>::value,
template <typename ProducerView>
static bool Write(ProducerView& aProducerView, const Arg& aArg) {
static_assert(BytesAlwaysValidT<Arg>::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 <typename U>
static bool Read(ConsumerView<U>& aConsumerView, Arg* aArg) {
static_assert(std::is_trivially_copyable<Arg>::value,
template <typename ConsumerView>
static bool Read(ConsumerView& aConsumerView, Arg* aArg) {
static_assert(BytesAlwaysValidT<Arg>::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<bool> {
// ---------------------------------------------------------------
template <class T, class UT = std::underlying_type_t<T>>
Maybe<T> 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 <class TT>
struct QueueParamTraits_IsEnumCase {
using T = TT;
using UT = std::underlying_type_t<T>;
template <typename ProducerView>
static bool Write(ProducerView& aProducerView, const T& aArg) {
MOZ_ASSERT(IsEnumCase(aArg));
const auto shadow = static_cast<UT>(aArg);
aProducerView.WriteParam(shadow);
return true;
}
template <typename ConsumerView>
static bool Read(ConsumerView& aConsumerView, T* aArg) {
auto shadow = UT{};
aConsumerView.ReadParam(&shadow);
const auto e = AsValidEnum<T>(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<T> 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 <class TT>
struct QueueParamTraits_TiedFields {
using T = TT;
template <typename ProducerView>
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 <typename ConsumerView>
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<nsString> : public QueueParamTraits<nsAString> {
// ---------------------------------------------------------------
template <typename NSTArrayType,
bool =
IsTriviallySerializable<typename NSTArrayType::value_type>::value>
bool = BytesAlwaysValidT<typename NSTArrayType::value_type>::value>
struct NSArrayQueueParamTraits;
// For ElementTypes that are !IsTriviallySerializable
// For ElementTypes that are !BytesAlwaysValidT
template <typename _ElementType>
struct NSArrayQueueParamTraits<nsTArray<_ElementType>, false> {
using ElementType = _ElementType;
@ -523,7 +614,7 @@ struct NSArrayQueueParamTraits<nsTArray<_ElementType>, false> {
}
};
// For ElementTypes that are IsTriviallySerializable
// For ElementTypes that are BytesAlwaysValidT
template <typename _ElementType>
struct NSArrayQueueParamTraits<nsTArray<_ElementType>, true> {
using ElementType = _ElementType;
@ -561,11 +652,10 @@ struct QueueParamTraits<nsTArray<ElementType>>
// ---------------------------------------------------------------
template <typename ArrayType,
bool =
IsTriviallySerializable<typename ArrayType::ElementType>::value>
bool = BytesAlwaysValidT<typename ArrayType::ElementType>::value>
struct ArrayQueueParamTraits;
// For ElementTypes that are !IsTriviallySerializable
// For ElementTypes that are !BytesAlwaysValidT
template <typename _ElementType, size_t Length>
struct ArrayQueueParamTraits<Array<_ElementType, Length>, false> {
using ElementType = _ElementType;
@ -588,7 +678,7 @@ struct ArrayQueueParamTraits<Array<_ElementType, Length>, false> {
}
};
// For ElementTypes that are IsTriviallySerializable
// For ElementTypes that are BytesAlwaysValidT
template <typename _ElementType, size_t Length>
struct ArrayQueueParamTraits<Array<_ElementType, Length>, true> {
using ElementType = _ElementType;

253
dom/canvas/TiedFields.h Normal file
Просмотреть файл

@ -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<Fields&...>
* TiedFields(const T&) -> std::tuple<const Fields&...>
*
* You can also overload TiedFields without adding T::MutTiedFields:
* template<>
* inline auto TiedFields<gfx::IntSize>(gfx::IntSize& a) {
* return std::tie(a.width, a.height);
* }
*/
template <class T>
constexpr auto TiedFields(T& t) {
const auto fields = t.MutTiedFields();
return fields;
}
template <class T, class... Args, class Tup = std::tuple<Args&...>>
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&>(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<Args...>`.
*
* See TiedFieldsExamples.
*/
template <class T>
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<T> 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<uint16_t> 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 <class T>
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<bool>) == 1);
static_assert(sizeof(Padding<bool[2]>) == 2);
static_assert(sizeof(Padding<int>) == 4);
// -
template <size_t I>
struct DecayDownT : public DecayDownT<I - 1> {};
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 <class T>
constexpr bool IsDenseNestedScalars(const T& t);
// -
namespace IsDenseNestedScalarsDetails {
template <class T, std::enable_if_t<std::is_scalar<T>::value, bool> = true>
constexpr bool Impl(const T&, DecayDownT<3>) {
return true;
}
template <class T, size_t N>
constexpr bool Impl(const T (&t)[N], DecayDownT<3>) {
return IsDenseNestedScalars(t[0]);
}
template <class T, size_t N>
constexpr bool Impl(const std::array<T, N>& t, DecayDownT<3>) {
return IsDenseNestedScalars(t[0]);
}
template <class T>
constexpr bool Impl(const Padding<T>& t, DecayDownT<3>) {
return IsDenseNestedScalars(t.ignored);
}
// -
template <class T>
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 <class T>
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<int,int>{}));
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<bool> 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

61
dom/canvas/TupleUtils.h Normal file
Просмотреть файл

@ -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 <tuple>
namespace mozilla {
// -
// ToTupleOfConstRefs
template <class T>
constexpr const auto& ToConstRef1(T& ret) {
return ret;
}
template <class Tup, size_t... Ids>
constexpr auto ToTupleOfConstRefsN(const Tup& tup,
const std::index_sequence<Ids...>&) {
return std::tie(ToConstRef1(std::get<Ids>(tup))...);
}
template <class... Args, size_t... Ids, class Tup = std::tuple<Args...>>
constexpr auto ToTupleOfConstRefs(const Tup& tup) {
return ToTupleOfConstRefsN(
tup, std::make_index_sequence<std::tuple_size_v<Tup>>());
}
// -
// MapTuple
template <class Tup, class Callable, size_t... Ids>
constexpr auto MapTupleN(Tup&& tup, Callable&& fn,
const std::index_sequence<Ids...>&) {
return std::make_tuple(fn(std::get<Ids>(tup))...);
}
/// Callable::operator()(T) cannot return void or you will get weird errors.
template <class... Args, class Callable, class Tup = std::tuple<Args...>>
constexpr auto MapTuple(Tup&& t, Callable&& fn) {
using Tup2 = typename std::remove_reference<Tup>::type;
return MapTupleN(t, fn, std::make_index_sequence<std::tuple_size_v<Tup2>>());
}
// -
template <class... Args>
constexpr auto SizeofTupleArgs(const std::tuple<Args...>&) {
size_t total = 0;
for (const auto s : {sizeof(Args)...}) {
total += s;
}
return total;
}
} // namespace mozilla
#endif // DOM_CANVAS_TUPLE_UTILS_H

Просмотреть файл

@ -119,17 +119,17 @@ void WebGL2Context::ClearBufferTv(GLenum buffer, GLint drawBuffer,
case webgl::AttribBaseType::Float:
gl->fClearBufferfv(buffer, drawBuffer,
reinterpret_cast<const float*>(data.data));
reinterpret_cast<const float*>(data.data.data()));
break;
case webgl::AttribBaseType::Int:
gl->fClearBufferiv(buffer, drawBuffer,
reinterpret_cast<const int32_t*>(data.data));
reinterpret_cast<const int32_t*>(data.data.data()));
break;
case webgl::AttribBaseType::Uint:
gl->fClearBufferuiv(buffer, drawBuffer,
reinterpret_cast<const uint32_t*>(data.data));
reinterpret_cast<const uint32_t*>(data.data.data()));
break;
}
}

Просмотреть файл

@ -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*> WebGLContext::sLru;

Просмотреть файл

@ -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<const float*>(src.data));
gl->fVertexAttrib4fv(index,
reinterpret_cast<const float*>(src.data.data()));
break;
case webgl::AttribBaseType::Int:
gl->fVertexAttribI4iv(index,
reinterpret_cast<const int32_t*>(src.data));
gl->fVertexAttribI4iv(
index, reinterpret_cast<const int32_t*>(src.data.data()));
break;
case webgl::AttribBaseType::Uint:
gl->fVertexAttribI4uiv(index,
reinterpret_cast<const uint32_t*>(src.data));
gl->fVertexAttribI4uiv(
index, reinterpret_cast<const uint32_t*>(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));
}
}

Просмотреть файл

@ -14,69 +14,104 @@
#include "WebGLContext.h"
#include "WebGLTypes.h"
namespace mozilla::webgl {
namespace mozilla {
namespace webgl {
template <typename T>
struct QueueParamTraits;
template <>
struct IsTriviallySerializable<FloatOrInt> : std::true_type {};
// -
template <>
struct IsTriviallySerializable<webgl::ShaderPrecisionFormat> : std::true_type {
};
#define USE_TIED_FIELDS(T) \
template <> \
struct QueueParamTraits<T> : QueueParamTraits_TiedFields<T> {};
template <>
struct IsTriviallySerializable<WebGLContextOptions> : std::true_type {};
// -
template <>
struct IsTriviallySerializable<WebGLTexImageData> : 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<WebGLTexPboOffset> : std::true_type {};
inline auto TiedFields<gfx::IntSize>(gfx::IntSize& a) {
return std::tie(a.width, a.height);
}
namespace webgl {
USE_TIED_FIELDS(gfx::IntSize)
template <>
struct IsTriviallySerializable<webgl::ExtensionBits> : std::true_type {};
template <>
struct IsTriviallySerializable<webgl::GetUniformData> : std::true_type {};
// -
template <>
struct IsTriviallySerializable<mozilla::webgl::PackingInfo> : std::true_type {};
template <>
struct IsTriviallySerializable<mozilla::webgl::PixelPackingState>
: std::true_type {};
template <>
struct IsTriviallySerializable<mozilla::webgl::PixelUnpackStateWebgl>
: std::true_type {};
#undef USE_TIED_FIELDS
template <>
struct IsTriviallySerializable<gfx::IntSize> : std::true_type {};
// -
template <typename T>
struct IsTriviallySerializable<avec2<T>> : std::true_type {};
template <typename T>
struct IsTriviallySerializable<avec3<T>> : std::true_type {};
template <class T>
struct QueueParamTraits<avec2<T>> : QueueParamTraits_TiedFields<avec2<T>> {};
template <>
struct IsTriviallySerializable<webgl::TexUnpackBlob> : std::true_type {};
template <class T>
struct QueueParamTraits<avec3<T>> : QueueParamTraits_TiedFields<avec3<T>> {};
template <>
struct IsTriviallySerializable<webgl::TypedQuad> : std::true_type {};
template <>
struct IsTriviallySerializable<webgl::VertAttribPointerDesc> : std::true_type {
};
template <>
struct IsTriviallySerializable<webgl::ReadPixelsDesc> : std::true_type {};
// template <>
// struct IsTriviallySerializable<layers::SurfaceDescriptor> : std::true_type
// {};
// SurfaceDescriptorBuffer is *not* trivial.
template <>
struct IsTriviallySerializable<layers::RemoteTextureId> : std::true_type {};
template <>
struct IsTriviallySerializable<layers::RemoteTextureOwnerId> : std::true_type {
};
template <>
struct IsTriviallySerializable<webgl::SwapChainOptions> : 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<T> : QueueParamTraits_IsEnumCase<T> {};
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 <typename T>
struct QueueParamTraits<RawBuffer<T>> {
@ -267,6 +302,7 @@ struct QueueParamTraits<gfxAlphaType>
: public ContiguousEnumSerializerInclusive<
gfxAlphaType, gfxAlphaType::Opaque, gfxAlphaType::NonPremult> {};
} // namespace mozilla::webgl
} // namespace webgl
} // namespace mozilla
#endif // WEBGLQUEUEPARAMTRAITS_H_

Просмотреть файл

@ -8,6 +8,7 @@
#include <limits>
#include <string>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <vector>
@ -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 <typename U, typename V>
static Maybe<avec2> From(const U _x, const V _y) {
const auto x = CheckedInt<T>(_x);
@ -477,6 +512,8 @@ struct avec3 {
T y = T();
T z = T();
auto MutTiedFields() { return std::tie(x, y, z); }
template <typename U, typename V>
static Maybe<avec3> From(const U _x, const V _y, const V _z) {
const auto x = CheckedInt<T>(_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 <class T>
@ -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<uint8_t, 4 * sizeof(float)> 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<PixelPackingState> {
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

Просмотреть файл

@ -64,6 +64,8 @@ EXPORTS.mozilla.dom += [
"OffscreenCanvasRenderingContext2D.h",
"QueueParamTraits.h",
"TextMetrics.h",
"TiedFields.h",
"TupleUtils.h",
"WebGLChild.h",
"WebGLCommandQueue.h",
"WebGLIpdl.h",

Просмотреть файл

@ -10,6 +10,7 @@
#include <iosfwd> // for ostream
#include <stdint.h> // for uint32_t
#include <stdio.h> // FILE
#include <tuple>
#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; }