no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / dom / canvas / QueueParamTraits.h
blobfbedcdea0bf59d8c8120e56246375f4c4b9c5f85
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: sw=2 ts=4 et :
3 */
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #ifndef _QUEUEPARAMTRAITS_H_
9 #define _QUEUEPARAMTRAITS_H_ 1
11 #include "ipc/EnumSerializer.h"
12 #include "mozilla/gfx/2D.h"
13 #include "mozilla/Assertions.h"
14 #include "mozilla/IntegerRange.h"
15 #include "mozilla/ipc/ProtocolUtils.h"
16 #include "mozilla/ipc/SharedMemoryBasic.h"
17 #include "mozilla/Logging.h"
18 #include "mozilla/TimeStamp.h"
19 #include "nsExceptionHandler.h"
20 #include "nsString.h"
21 #include "WebGLTypes.h"
23 #include <optional>
25 namespace mozilla::webgl {
27 template <typename T>
28 struct RemoveCVR {
29 using Type =
30 typename std::remove_reference<typename std::remove_cv<T>::type>::type;
33 /**
34 * QueueParamTraits provide the user with a way to implement PCQ argument
35 * (de)serialization. It uses a PcqView, which permits the system to
36 * abandon all changes to the underlying PCQ if any operation fails.
38 * The transactional nature of PCQ operations make the ideal behavior a bit
39 * complex. Since the PCQ has a fixed amount of memory available to it,
40 * TryInsert operations operations are expected to sometimes fail and be
41 * re-issued later. We want these failures to be inexpensive. The same
42 * goes for TryRemove, which fails when there isn't enough data in
43 * the queue yet for them to complete.
45 * Their expected interface is:
47 * template<> struct QueueParamTraits<typename RemoveCVR<Arg>::Type> {
48 * // Write data from aArg into the PCQ.
49 * static QueueStatus Write(ProducerView& aProducerView, const Arg& aArg)
50 * {...};
52 * // Read data from the PCQ into aArg, or just skip the data if aArg is null.
53 * static QueueStatus Read(ConsumerView& aConsumerView, Arg* aArg) {...}
54 * };
56 template <typename Arg>
57 struct QueueParamTraits; // Todo: s/QueueParamTraits/SizedParamTraits/
59 template <typename T>
60 inline Range<T> AsRange(T* const begin, T* const end) {
61 const auto size = MaybeAs<size_t>(end - begin);
62 MOZ_RELEASE_ASSERT(size);
63 return {begin, *size};
66 // -
67 // BytesAlwaysValidT
69 template <class T>
70 struct BytesAlwaysValidT {
71 using non_cv = typename std::remove_cv<T>::type;
72 static constexpr bool value =
73 std::is_arithmetic<T>::value && !std::is_same<non_cv, bool>::value;
75 static_assert(BytesAlwaysValidT<float>::value);
76 static_assert(!BytesAlwaysValidT<bool>::value);
77 static_assert(!BytesAlwaysValidT<const bool>::value);
78 static_assert(!BytesAlwaysValidT<int*>::value);
79 static_assert(BytesAlwaysValidT<intptr_t>::value);
81 template <class T, size_t N>
82 struct BytesAlwaysValidT<std::array<T, N>> {
83 static constexpr bool value = BytesAlwaysValidT<T>::value;
85 static_assert(BytesAlwaysValidT<std::array<int, 4>>::value);
86 static_assert(!BytesAlwaysValidT<std::array<bool, 4>>::value);
88 template <class T, size_t N>
89 struct BytesAlwaysValidT<T[N]> {
90 static constexpr bool value = BytesAlwaysValidT<T>::value;
92 static_assert(BytesAlwaysValidT<int[4]>::value);
93 static_assert(!BytesAlwaysValidT<bool[4]>::value);
95 // -
97 template <>
98 struct BytesAlwaysValidT<webgl::UniformDataVal> {
99 static constexpr bool value = true;
102 // -
105 * Used to give QueueParamTraits a way to write to the Producer without
106 * actually altering it, in case the transaction fails.
107 * THis object maintains the error state of the transaction and
108 * discards commands issued after an error is encountered.
110 template <typename _Producer>
111 class ProducerView {
112 public:
113 using Producer = _Producer;
115 explicit ProducerView(Producer* aProducer) : mProducer(aProducer) {}
117 template <typename T>
118 bool WriteFromRange(const Range<const T>& src) {
119 static_assert(BytesAlwaysValidT<T>::value);
120 if (MOZ_LIKELY(mOk)) {
121 mOk &= mProducer->WriteFromRange(src);
123 return mOk;
127 * Copy bytes from aBuffer to the producer if there is enough room.
128 * aBufferSize must not be 0.
130 template <typename T>
131 inline bool Write(const T* begin, const T* end) {
132 MOZ_RELEASE_ASSERT(begin <= end);
133 return WriteFromRange(AsRange(begin, end));
137 * Serialize aArg using Arg's QueueParamTraits.
139 template <typename Arg>
140 bool WriteParam(const Arg& aArg) {
141 return mozilla::webgl::QueueParamTraits<
142 typename RemoveCVR<Arg>::Type>::Write(*this, aArg);
145 bool Ok() const { return mOk; }
147 private:
148 Producer* const mProducer;
149 bool mOk = true;
153 * Used to give QueueParamTraits a way to read from the Consumer without
154 * actually altering it, in case the transaction fails.
156 template <typename _Consumer>
157 class ConsumerView {
158 public:
159 using Consumer = _Consumer;
161 explicit ConsumerView(Consumer* aConsumer) : mConsumer(aConsumer) {}
164 * Read bytes from the consumer if there is enough data. aBuffer may
165 * be null (in which case the data is skipped)
167 template <typename T>
168 inline bool Read(T* const destBegin, T* const destEnd) {
169 MOZ_ASSERT(destBegin);
170 MOZ_RELEASE_ASSERT(destBegin <= destEnd);
172 const auto dest = AsRange(destBegin, destEnd);
173 const auto view = ReadRange<T>(dest.length());
174 if (MOZ_LIKELY(view)) {
175 const auto byteSize = ByteSize(dest);
176 if (MOZ_LIKELY(byteSize)) {
177 memcpy(dest.begin().get(), view->begin().get(), byteSize);
180 return mOk;
183 /// Return a view wrapping the shmem.
184 template <typename T>
185 inline Maybe<Range<const T>> ReadRange(const size_t elemCount) {
186 static_assert(BytesAlwaysValidT<T>::value);
187 if (MOZ_UNLIKELY(!mOk)) return {};
188 const auto view = mConsumer->template ReadRange<T>(elemCount);
189 mOk &= bool(view);
190 return view;
194 * Deserialize aArg using Arg's QueueParamTraits.
195 * If the return value is not Success then aArg is not changed.
197 template <typename Arg>
198 bool ReadParam(Arg* aArg) {
199 MOZ_ASSERT(aArg);
200 return mozilla::webgl::QueueParamTraits<std::remove_cv_t<Arg>>::Read(*this,
201 aArg);
204 bool Ok() const { return mOk; }
206 private:
207 Consumer* const mConsumer;
208 bool mOk = true;
211 // -
213 template <typename Arg>
214 struct QueueParamTraits {
215 template <typename ProducerView>
216 static bool Write(ProducerView& aProducerView, const Arg& aArg) {
217 static_assert(BytesAlwaysValidT<Arg>::value,
218 "No QueueParamTraits specialization was found for this type "
219 "and it does not satisfy BytesAlwaysValid.");
220 // Write self as binary
221 const auto pArg = &aArg;
222 return aProducerView.Write(pArg, pArg + 1);
225 template <typename ConsumerView>
226 static bool Read(ConsumerView& aConsumerView, Arg* aArg) {
227 static_assert(BytesAlwaysValidT<Arg>::value,
228 "No QueueParamTraits specialization was found for this type "
229 "and it does not satisfy BytesAlwaysValid.");
230 // Read self as binary
231 return aConsumerView.Read(aArg, aArg + 1);
235 // ---------------------------------------------------------------
237 template <>
238 struct QueueParamTraits<bool> {
239 using ParamType = bool;
241 template <typename U>
242 static auto Write(ProducerView<U>& aProducerView, const ParamType& aArg) {
243 uint8_t temp = aArg ? 1 : 0;
244 return aProducerView.WriteParam(temp);
247 template <typename U>
248 static auto Read(ConsumerView<U>& aConsumerView, ParamType* aArg) {
249 uint8_t temp;
250 if (aConsumerView.ReadParam(&temp)) {
251 MOZ_ASSERT(temp == 1 || temp == 0);
252 *aArg = temp ? true : false;
254 return aConsumerView.Ok();
258 // ---------------------------------------------------------------
260 template <class T>
261 Maybe<T> AsValidEnum(const std::underlying_type_t<T> raw_val) {
262 const auto raw_enum = T{raw_val}; // This is the risk we prevent!
263 if (!IsEnumCase(raw_enum)) return {};
264 return Some(raw_enum);
267 // -
269 template <class T>
270 struct QueueParamTraits_IsEnumCase {
271 template <typename ProducerView>
272 static bool Write(ProducerView& aProducerView, const T& aArg) {
273 MOZ_ASSERT(IsEnumCase(aArg));
274 const auto shadow = static_cast<std::underlying_type_t<T>>(aArg);
275 aProducerView.WriteParam(shadow);
276 return true;
279 template <typename ConsumerView>
280 static bool Read(ConsumerView& aConsumerView, T* aArg) {
281 auto shadow = std::underlying_type_t<T>{};
282 aConsumerView.ReadParam(&shadow);
283 const auto e = AsValidEnum<T>(shadow);
284 if (!e) return false;
285 *aArg = *e;
286 return true;
290 // ---------------------------------------------------------------
292 // We guarantee our robustness via these requirements:
293 // * Object.MutTiedFields() gives us a tuple,
294 // * where the combined sizeofs all field types sums to sizeof(Object),
295 // * (thus we know we are exhaustively listing all fields)
296 // * where feeding each field back into ParamTraits succeeds,
297 // * and ParamTraits is only automated for BytesAlwaysValidT<T> types.
298 // (BytesAlwaysValidT rejects bool and enum types, and only accepts int/float
299 // types, or array or std::arrays of such types)
300 // (Yes, bit-field fields are rejected by MutTiedFields too)
302 template <class T>
303 struct QueueParamTraits_TiedFields {
304 template <typename ProducerView>
305 static bool Write(ProducerView& aProducerView, const T& aArg) {
306 const auto fields = TiedFields(aArg);
307 static_assert(AreAllBytesTiedFields<T>(),
308 "Are there missing fields or padding between fields?");
310 bool ok = true;
311 MapTuple(fields, [&](const auto& field) {
312 ok &= aProducerView.WriteParam(field);
313 return true;
315 return ok;
318 template <typename ConsumerView>
319 static bool Read(ConsumerView& aConsumerView, T* aArg) {
320 const auto fields = TiedFields(*aArg);
321 static_assert(AreAllBytesTiedFields<T>());
323 bool ok = true;
324 MapTuple(fields, [&](auto& field) {
325 ok &= aConsumerView.ReadParam(&field);
326 return true;
328 return ok;
332 // ---------------------------------------------------------------
334 // Adapted from IPC::EnumSerializer, this class safely handles enum values,
335 // validating that they are in range using the same EnumValidators as IPDL
336 // (namely ContiguousEnumValidator and ContiguousEnumValidatorInclusive).
337 template <typename E, typename EnumValidator>
338 struct EnumSerializer {
339 using ParamType = E;
340 using DataType = typename std::underlying_type<E>::type;
342 template <typename U>
343 static auto Write(ProducerView<U>& aProducerView, const ParamType& aValue) {
344 MOZ_RELEASE_ASSERT(
345 EnumValidator::IsLegalValue(static_cast<DataType>(aValue)));
346 return aProducerView.WriteParam(DataType(aValue));
349 template <typename U>
350 static bool Read(ConsumerView<U>& aConsumerView, ParamType* aResult) {
351 DataType value;
352 if (!aConsumerView.ReadParam(&value)) {
353 CrashReporter::AnnotateCrashReport(
354 CrashReporter::Annotation::IPCReadErrorReason, "Bad iter"_ns);
355 return false;
357 if (!EnumValidator::IsLegalValue(static_cast<DataType>(value))) {
358 CrashReporter::AnnotateCrashReport(
359 CrashReporter::Annotation::IPCReadErrorReason, "Illegal value"_ns);
360 return false;
363 *aResult = ParamType(value);
364 return true;
368 using IPC::ContiguousEnumValidator;
369 using IPC::ContiguousEnumValidatorInclusive;
371 template <typename E, E MinLegal, E HighBound>
372 struct ContiguousEnumSerializer
373 : EnumSerializer<E, ContiguousEnumValidator<E, MinLegal, HighBound>> {};
375 template <typename E, E MinLegal, E MaxLegal>
376 struct ContiguousEnumSerializerInclusive
377 : EnumSerializer<E,
378 ContiguousEnumValidatorInclusive<E, MinLegal, MaxLegal>> {
381 // ---------------------------------------------------------------
383 template <>
384 struct QueueParamTraits<webgl::TexUnpackBlobDesc> {
385 using ParamType = webgl::TexUnpackBlobDesc;
387 template <typename U>
388 static bool Write(ProducerView<U>& view, const ParamType& in) {
389 MOZ_RELEASE_ASSERT(!in.image);
390 MOZ_RELEASE_ASSERT(!in.sd);
391 const bool isDataSurf = bool(in.dataSurf);
392 if (!view.WriteParam(in.imageTarget) || !view.WriteParam(in.size) ||
393 !view.WriteParam(in.srcAlphaType) || !view.WriteParam(in.unpacking) ||
394 !view.WriteParam(in.cpuData) || !view.WriteParam(in.pboOffset) ||
395 !view.WriteParam(in.structuredSrcSize) ||
396 !view.WriteParam(in.applyUnpackTransforms) ||
397 !view.WriteParam(isDataSurf)) {
398 return false;
400 if (isDataSurf) {
401 const auto& surf = in.dataSurf;
402 gfx::DataSourceSurface::ScopedMap map(surf, gfx::DataSourceSurface::READ);
403 if (!map.IsMapped()) {
404 return false;
406 const auto& surfSize = surf->GetSize();
407 const auto stride = *MaybeAs<size_t>(map.GetStride());
408 if (!view.WriteParam(surfSize) || !view.WriteParam(surf->GetFormat()) ||
409 !view.WriteParam(stride)) {
410 return false;
413 const size_t dataSize = stride * surfSize.height;
414 const auto& begin = map.GetData();
415 const auto range = Range<const uint8_t>{begin, dataSize};
416 if (!view.WriteFromRange(range)) {
417 return false;
420 return true;
423 template <typename U>
424 static bool Read(ConsumerView<U>& view, ParamType* const out) {
425 bool isDataSurf;
426 if (!view.ReadParam(&out->imageTarget) || !view.ReadParam(&out->size) ||
427 !view.ReadParam(&out->srcAlphaType) ||
428 !view.ReadParam(&out->unpacking) || !view.ReadParam(&out->cpuData) ||
429 !view.ReadParam(&out->pboOffset) ||
430 !view.ReadParam(&out->structuredSrcSize) ||
431 !view.ReadParam(&out->applyUnpackTransforms) ||
432 !view.ReadParam(&isDataSurf)) {
433 return false;
435 if (isDataSurf) {
436 gfx::IntSize surfSize;
437 gfx::SurfaceFormat format;
438 size_t stride;
439 if (!view.ReadParam(&surfSize) || !view.ReadParam(&format) ||
440 !view.ReadParam(&stride)) {
441 return false;
443 const size_t dataSize = stride * surfSize.height;
444 const auto range = view.template ReadRange<uint8_t>(dataSize);
445 if (!range) return false;
447 // DataSourceSurface demands pointer-to-mutable.
448 const auto bytes = const_cast<uint8_t*>(range->begin().get());
449 out->dataSurf = gfx::Factory::CreateWrappingDataSourceSurface(
450 bytes, stride, surfSize, format);
451 MOZ_ASSERT(out->dataSurf);
453 return true;
457 // ---------------------------------------------------------------
459 template <>
460 struct QueueParamTraits<nsACString> {
461 using ParamType = nsACString;
463 template <typename U>
464 static bool Write(ProducerView<U>& aProducerView, const ParamType& aArg) {
465 if ((!aProducerView.WriteParam(aArg.IsVoid())) || aArg.IsVoid()) {
466 return false;
469 uint32_t len = aArg.Length();
470 if ((!aProducerView.WriteParam(len)) || (len == 0)) {
471 return false;
474 return aProducerView.Write(aArg.BeginReading(), len);
477 template <typename U>
478 static bool Read(ConsumerView<U>& aConsumerView, ParamType* aArg) {
479 bool isVoid = false;
480 if (!aConsumerView.ReadParam(&isVoid)) {
481 return false;
483 aArg->SetIsVoid(isVoid);
484 if (isVoid) {
485 return true;
488 uint32_t len = 0;
489 if (!aConsumerView.ReadParam(&len)) {
490 return false;
493 if (len == 0) {
494 *aArg = "";
495 return true;
498 char* buf = new char[len + 1];
499 if (!buf) {
500 return false;
502 if (!aConsumerView.Read(buf, len)) {
503 return false;
505 buf[len] = '\0';
506 aArg->Adopt(buf, len);
507 return true;
511 template <>
512 struct QueueParamTraits<nsAString> {
513 using ParamType = nsAString;
515 template <typename U>
516 static bool Write(ProducerView<U>& aProducerView, const ParamType& aArg) {
517 if ((!aProducerView.WriteParam(aArg.IsVoid())) || (aArg.IsVoid())) {
518 return false;
520 // DLP: No idea if this includes null terminator
521 uint32_t len = aArg.Length();
522 if ((!aProducerView.WriteParam(len)) || (len == 0)) {
523 return false;
525 constexpr const uint32_t sizeofchar = sizeof(typename ParamType::char_type);
526 return aProducerView.Write(aArg.BeginReading(), len * sizeofchar);
529 template <typename U>
530 static bool Read(ConsumerView<U>& aConsumerView, ParamType* aArg) {
531 bool isVoid = false;
532 if (!aConsumerView.ReadParam(&isVoid)) {
533 return false;
535 aArg->SetIsVoid(isVoid);
536 if (isVoid) {
537 return true;
540 // DLP: No idea if this includes null terminator
541 uint32_t len = 0;
542 if (!aConsumerView.ReadParam(&len)) {
543 return false;
546 if (len == 0) {
547 *aArg = nsString();
548 return true;
551 uint32_t sizeofchar = sizeof(typename ParamType::char_type);
552 typename ParamType::char_type* buf = nullptr;
553 buf = static_cast<typename ParamType::char_type*>(
554 malloc((len + 1) * sizeofchar));
555 if (!buf) {
556 return false;
559 if (!aConsumerView.Read(buf, len * sizeofchar)) {
560 return false;
563 buf[len] = L'\0';
564 aArg->Adopt(buf, len);
565 return true;
569 template <>
570 struct QueueParamTraits<nsCString> : public QueueParamTraits<nsACString> {
571 using ParamType = nsCString;
574 template <>
575 struct QueueParamTraits<nsString> : public QueueParamTraits<nsAString> {
576 using ParamType = nsString;
579 // ---------------------------------------------------------------
581 template <typename NSTArrayType,
582 bool = BytesAlwaysValidT<typename NSTArrayType::value_type>::value>
583 struct NSArrayQueueParamTraits;
585 // For ElementTypes that are !BytesAlwaysValidT
586 template <typename _ElementType>
587 struct NSArrayQueueParamTraits<nsTArray<_ElementType>, false> {
588 using ElementType = _ElementType;
589 using ParamType = nsTArray<ElementType>;
591 template <typename U>
592 static bool Write(ProducerView<U>& aProducerView, const ParamType& aArg) {
593 aProducerView.WriteParam(aArg.Length());
594 for (auto& elt : aArg) {
595 aProducerView.WriteParam(elt);
597 return true;
600 template <typename U>
601 static bool Read(ConsumerView<U>& aConsumerView, ParamType* aArg) {
602 size_t arrayLen;
603 if (!aConsumerView.ReadParam(&arrayLen)) {
604 return false;
607 if (!aArg->AppendElements(arrayLen, fallible)) {
608 return false;
611 for (auto i : IntegerRange(arrayLen)) {
612 ElementType& elt = aArg->ElementAt(i);
613 aConsumerView.ReadParam(elt);
615 return aConsumerView.Ok();
619 // For ElementTypes that are BytesAlwaysValidT
620 template <typename _ElementType>
621 struct NSArrayQueueParamTraits<nsTArray<_ElementType>, true> {
622 using ElementType = _ElementType;
623 using ParamType = nsTArray<ElementType>;
625 // TODO: Are there alignment issues?
626 template <typename U>
627 static bool Write(ProducerView<U>& aProducerView, const ParamType& aArg) {
628 size_t arrayLen = aArg.Length();
629 aProducerView.WriteParam(arrayLen);
630 return aProducerView.Write(&aArg[0], aArg.Length() * sizeof(ElementType));
633 template <typename U>
634 static bool Read(ConsumerView<U>& aConsumerView, ParamType* aArg) {
635 size_t arrayLen;
636 if (!aConsumerView.ReadParam(&arrayLen)) {
637 return false;
640 if (!aArg->AppendElements(arrayLen, fallible)) {
641 return false;
644 return aConsumerView.Read(aArg->Elements(), arrayLen * sizeof(ElementType));
648 template <typename ElementType>
649 struct QueueParamTraits<nsTArray<ElementType>>
650 : public NSArrayQueueParamTraits<nsTArray<ElementType>> {
651 using ParamType = nsTArray<ElementType>;
654 // ---------------------------------------------------------------
656 template <typename ArrayType,
657 bool = BytesAlwaysValidT<typename ArrayType::ElementType>::value>
658 struct ArrayQueueParamTraits;
660 // For ElementTypes that are !BytesAlwaysValidT
661 template <typename _ElementType, size_t Length>
662 struct ArrayQueueParamTraits<Array<_ElementType, Length>, false> {
663 using ElementType = _ElementType;
664 using ParamType = Array<ElementType, Length>;
666 template <typename U>
667 static auto Write(ProducerView<U>& aProducerView, const ParamType& aArg) {
668 for (const auto& elt : aArg) {
669 aProducerView.WriteParam(elt);
671 return aProducerView.Ok();
674 template <typename U>
675 static auto Read(ConsumerView<U>& aConsumerView, ParamType* aArg) {
676 for (auto& elt : *aArg) {
677 aConsumerView.ReadParam(elt);
679 return aConsumerView.Ok();
683 // For ElementTypes that are BytesAlwaysValidT
684 template <typename _ElementType, size_t Length>
685 struct ArrayQueueParamTraits<Array<_ElementType, Length>, true> {
686 using ElementType = _ElementType;
687 using ParamType = Array<ElementType, Length>;
689 template <typename U>
690 static auto Write(ProducerView<U>& aProducerView, const ParamType& aArg) {
691 return aProducerView.Write(aArg.begin(), sizeof(ElementType[Length]));
694 template <typename U>
695 static auto Read(ConsumerView<U>& aConsumerView, ParamType* aArg) {
696 return aConsumerView.Read(aArg->begin(), sizeof(ElementType[Length]));
700 template <typename ElementType, size_t Length>
701 struct QueueParamTraits<Array<ElementType, Length>>
702 : public ArrayQueueParamTraits<Array<ElementType, Length>> {
703 using ParamType = Array<ElementType, Length>;
706 // ---------------------------------------------------------------
708 template <typename ElementType>
709 struct QueueParamTraits<Maybe<ElementType>> {
710 using ParamType = Maybe<ElementType>;
712 template <typename U>
713 static bool Write(ProducerView<U>& aProducerView, const ParamType& aArg) {
714 aProducerView.WriteParam(static_cast<bool>(aArg));
715 if (aArg) {
716 aProducerView.WriteParam(aArg.ref());
718 return aProducerView.Ok();
721 template <typename U>
722 static bool Read(ConsumerView<U>& aConsumerView, ParamType* aArg) {
723 bool isSome;
724 if (!aConsumerView.ReadParam(&isSome)) {
725 return false;
728 if (!isSome) {
729 aArg->reset();
730 return true;
733 aArg->emplace();
734 return aConsumerView.ReadParam(aArg->ptr());
738 // ---------------------------------------------------------------
740 template <typename TypeA, typename TypeB>
741 struct QueueParamTraits<std::pair<TypeA, TypeB>> {
742 using ParamType = std::pair<TypeA, TypeB>;
744 template <typename U>
745 static bool Write(ProducerView<U>& aProducerView, const ParamType& aArg) {
746 aProducerView.WriteParam(aArg.first());
747 return aProducerView.WriteParam(aArg.second());
750 template <typename U>
751 static bool Read(ConsumerView<U>& aConsumerView, ParamType* aArg) {
752 aConsumerView.ReadParam(aArg->first());
753 return aConsumerView.ReadParam(aArg->second());
757 } // namespace mozilla::webgl
759 #endif // _QUEUEPARAMTRAITS_H_